diff --git a/CLAUDE.md b/CLAUDE.md index 62870a07..022c5c80 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -101,6 +101,9 @@ has seen the raw output. Triage into new roadmap passes after. - `players` is INTEGER. Scraper/AI ranges like `"1-4"` are normalized to the max (4) before saving. - Sort title is auto-generated during scraping and AI Fill via `generate_sort_title()` in `services/game_utils.py`. - When a system has curated default controllers in the DB, they always override scraped/AI controller values. +- `units_sold` (INTEGER, migration 023) is filled only by the AI — scrape gap-fill and AI Fill; `scrape_ai.parse_units_sold()` turns "2.4 million" etc. into an int. +- `release_dates` (TEXT, migration 024) is a JSON object of region code → date (`{"JP": "2002-11-17", "NA": "2002-11"}`), filled only by the AI — scrape gap-fill and AI Fill. `services/release_dates.py` parses, stores and displays it; `release_date` stays the row's own release. +- RPCS3 compatibility (PS3) is never stored: `services/rpcs3_compat.py` reads the local RPCS3's `GuiConfigs/compat_database.dat` (re-read when it changes) and looks games up by `games.disc_serial`. Local ROMs only, never imported games. - Regional versions (PASS-59-106): only `services/version_groups.py` links rows through `games.version_group_id`. `_build_games_query` shows one row per group unless `group_versions=False`. Contract: `docs/specs/PASS-59-106-regional-versions.md`. ### Media handling @@ -116,6 +119,7 @@ Moved to `.claude/rules/ratings.md`, which loads when a matching file is read. R ### Page wiring - Both `/games` (Library) and `/system/` use `AllGamesController` — keep them in sync. +- The game page's Previous / Next links follow the list it was opened from (`?from=library`, else its console). `AllGamesController` saves each list's filter and sort params per tab via `RetroDB.saveGameNavContext`; the game page sends them to `/api/games/neighbors`. --- diff --git a/config.example.py b/config.example.py index d191b709..f58cd4d2 100644 --- a/config.example.py +++ b/config.example.py @@ -129,8 +129,8 @@ # Application metadata APP_NAME = "RetroDB" -APP_VERSION = "3.33.0" -APP_LAST_UPDATE = "2026-10-23" +APP_VERSION = "3.35.0" +APP_LAST_UPDATE = "2026-10-27" APP_DESCRIPTION = "Retro Gaming ROM Library Manager" # Supported image extensions for boxart/screenshots diff --git a/data/changelog.yaml b/data/changelog.yaml index 039f79b1..4f9ddf18 100644 --- a/data/changelog.yaml +++ b/data/changelog.yaml @@ -1,3 +1,104 @@ +- version: 3.35.0 + date: 2026-10-27 + tags: + - type: feature + label: Release dates for every region + - type: feature + label: Previous and next game on the game page + - type: feature + label: Uploaded covers and screenshots are processed like scraped ones + - type: fix + label: RetroAchievements shows on the game page for every game that has it + - type: fix + label: The Systems page's unscraped count matches the Unscraped filter + body: | + +- version: 3.34.0 + date: 2026-10-27 + tags: + - type: feature + label: See how well each PS3 game runs in RPCS3 + - type: feature + label: Sales figures for your games + - type: fix + label: The Franchise / Series switch works on the game page + - type: fix + label: Every Achievements tab is the same width + - type: fix + label: RetroDB finds RPCS3's settings on Windows and macOS + - type: fix + label: AI Fill works for games with a score but no age rating + - type: fix + label: Game card labels follow your language + body: | + - version: 3.33.0 date: 2026-10-23 tags: diff --git a/docs/specs/api-contracts.md b/docs/specs/api-contracts.md index 33ea8a9d..708a02e2 100644 --- a/docs/specs/api-contracts.md +++ b/docs/specs/api-contracts.md @@ -428,7 +428,13 @@ shown rows only. trust the client. New paginated routes must do the same — `min(value, ceiling)` inline, not after a "validate" call that might be skipped. -Bulk-id endpoints (`/api/games/ids`, `/api/games/card-data`) use a different +`/api/games/neighbors?id=N&` returns +`{found, position, total, prev, next}` for the game page's Previous / Next +links, `prev` / `next` being `{id, title}` or null. A version the list folds +into its group's card is placed at that card; `found` is false when the game +is not in the list. + +Bulk-id endpoints (`/api/games/ids`, `/api/games/neighbors`, `/api/games/card-data`) use a different cap pattern — see Pass 25.8: `LIMIT 20 * MAX_LIST_ROWS` (default 10 000) on id-only fetches, and a hard `[1, 50]` range on card-data ids. Match the relevant pattern by route shape; the rule is "always cap", not "always paginate". diff --git a/docs/specs/migrations.md b/docs/specs/migrations.md index 4767cf28..62640d7f 100644 --- a/docs/specs/migrations.md +++ b/docs/specs/migrations.md @@ -538,6 +538,8 @@ your migration — not in the runner. | 020 | `020_player.py` | Add `emulators.is_player`; insert the bundled player's row (`RetroDB Player`, `retrodb-player`) with no `system_emulators` rows. | PASS-59-64 §4.4. Additive. The row's `args_template` is unused: the resolver builds the player's argv. | | 021 | `021_official_names.py` | Add `games.title_locked` (default 0); create `title_proposals`; trigger `trg_games_delete_title_proposal` removes a deleted game's proposal. | PASS-59-159 §4.1. Additive. | | 022 | `022_player_name.py` | Rename the player's row from migration 020's placeholder `RetroDB Player` to `RetroArch (RetroDB)`. | PASS-59-139. Renames only the placeholder, and only when the new name is free, so a name the user chose is kept. | +| 023 | `023_games_units_sold.py` | Add `games.units_sold` (INTEGER, lifetime copies sold on the row's platform). | Additive. Filled only by the AI (scrape gap-fill and AI Fill); NULL = not known. | +| 024 | `024_games_release_dates.py` | Add `games.release_dates` (TEXT, JSON object of region code → date). | Additive. Filled only by the AI (scrape gap-fill and AI Fill); NULL = not known. | --- diff --git a/docs/specs/scrapers.md b/docs/specs/scrapers.md index e9bf1aa8..25a088ea 100644 --- a/docs/specs/scrapers.md +++ b/docs/specs/scrapers.md @@ -278,7 +278,11 @@ Text-only. AI never returns images, screenshots, videos, or manuals — `AI_FILL **`PLATFORM_ALIASES`** maps non-canonical platform names the AI may produce (`ps1` → `PlayStation`, `playstation portable` → `PSP`). -**Integer-column trap (Pass 45.3):** AI Fill writes through `routes/games_ai.py`. The integer columns (`players`, `critic_score`, `critic_score_count`, `user_score`, `user_score_count`) coerce via `int(float(value))`. If the AI returns `"0"`, that becomes `0` — and a bare `field = ?` UPDATE then clobbers a curated `players=4` with `0`. Fix: any integer coerce to `0` is **skipped** entirely from the UPDATE clause (chosen over `COALESCE(NULLIF(?, 0), col)` because the existing `should_apply` filter already guarantees we only reach the int-coerce on values worth writing). Regression tests in `tests/test_pass45_security.py::TestPass45_3*`. +**Integer-column trap (Pass 45.3):** AI Fill writes through `routes/games_ai.py`. The integer columns (`players`, `critic_score`, `critic_score_count`, `user_score`, `user_score_count`, `units_sold`) coerce via `int(float(value))`. If the AI returns `"0"`, that becomes `0` — and a bare `field = ?` UPDATE then clobbers a curated `players=4` with `0`. Fix: any integer coerce to `0` is **skipped** entirely from the UPDATE clause (chosen over `COALESCE(NULLIF(?, 0), col)` because the existing `should_apply` filter already guarantees we only reach the int-coerce on values worth writing). Regression tests in `tests/test_pass45_security.py::TestPass45_3*`. + +**Sales (`units_sold`):** only the AI fills it, in the scrape's AI gap-fill and in AI Fill; no API scraper publishes sales. The prompt asks for lifetime copies sold on the row's platform from free published sources and for null when only a multi-platform total exists. `parse_units_sold()` turns what a model writes (`"2.4 million"`, `"~850k"`, `"2,400,000"`) into an integer and drops anything above 500 million. + +**Release dates by region (`release_dates`):** only the AI fills it, in the scrape's AI gap-fill and in AI Fill, fill-only like every other field. The prompt asks for an object keyed by region code (`NA`, `EU`, `JP`, `AU`, `KR`, `CN`, `TW`, `BR`, `AS`, `WW`) with `YYYY-MM-DD`, `YYYY-MM` or `YYYY` dates. `services/release_dates.parse_release_dates()` also takes region names (`"Japan"`, `"PAL"`) and a list of `{region, date}`, drops unknown regions and impossible dates, and the column stores compact JSON in region order. `release_date` stays the date of the row's own release. **Circuit breaker:** `_ai_breaker` (5 failures / 120 s recovery) wraps `fetch_ai_metadata` calls from `hybrid_scraper` so a repeatedly-failing provider doesn't burn a quota on every gap-fill attempt. diff --git a/messages.pot b/messages.pot index 0593f8a9..aa00eb38 100644 --- a/messages.pot +++ b/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-10-23 15:00+0200\n" +"POT-Creation-Date: 2026-10-27 15:30+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -54,8 +54,8 @@ msgstr "" #: routes/achievements.py:171 routes/achievements.py:440 #: routes/bonus_discs.py:265 routes/bulk_scrape.py:46 routes/collections.py:276 #: routes/collections.py:531 routes/emulators.py:271 routes/emulators.py:285 -#: routes/games.py:348 routes/games.py:808 routes/games.py:931 -#: routes/games.py:987 routes/games_ai.py:46 routes/games_media.py:37 +#: routes/games.py:390 routes/games.py:864 routes/games.py:991 +#: routes/games.py:1047 routes/games_ai.py:46 routes/games_media.py:37 #: routes/games_media.py:85 routes/games_media.py:146 routes/launch.py:240 #: routes/reports.py:693 routes/reports.py:818 routes/trophies.py:1278 #: services/hltb_service.py:125 @@ -84,7 +84,7 @@ msgstr "" #: routes/achievements.py:294 routes/controllers.py:412 #: routes/controllers.py:462 routes/museum.py:309 routes/museum.py:381 -#: routes/ra_sync.py:98 routes/systems.py:220 +#: routes/ra_sync.py:98 routes/systems.py:223 msgid "System not found" msgstr "" @@ -225,15 +225,15 @@ msgstr "" msgid "Bonus disc and parent game must be in the same system" msgstr "" -#: routes/bonus_discs.py:211 routes/bonus_discs.py:229 routes/games.py:1245 -#: routes/games.py:1275 routes/platform_import.py:289 +#: routes/bonus_discs.py:211 routes/bonus_discs.py:229 routes/games.py:1305 +#: routes/games.py:1335 routes/platform_import.py:289 #: routes/platform_import.py:658 routes/platform_import.py:1024 #: routes/scrape_logs.py:84 routes/scrape_logs.py:168 routes/scrape_logs.py:181 #: routes/scrape_logs.py:219 routes/scraper.py:200 routes/scraper.py:528 #: routes/scraper.py:917 routes/settings.py:317 routes/settings.py:341 #: routes/settings.py:456 routes/settings.py:533 routes/settings.py:547 #: routes/settings.py:591 routes/settings.py:607 routes/settings.py:643 -#: routes/steam_achievements.py:138 routes/systems.py:286 routes/systems.py:466 +#: routes/steam_achievements.py:138 routes/systems.py:289 routes/systems.py:469 #: routes/trophies.py:498 routes/trophies.py:694 routes/trophies.py:742 #: routes/trophies.py:1564 routes/trophies.py:1588 routes/trophies.py:1633 #: routes/trophies.py:1661 routes/trophies.py:1703 routes/trophies.py:1770 @@ -422,81 +422,88 @@ msgstr "" msgid "Emulator not found" msgstr "" -#: routes/games.py:240 +#: routes/games.py:247 routes/games.py:281 msgid "No IDs provided" msgstr "" -#: routes/games.py:248 +#: routes/games.py:289 msgid "Provide 1-50 valid IDs" msgstr "" -#: routes/games.py:504 +#: routes/games.py:551 #, python-format msgid "Video not saved: it must be an MP4, WebM or OGG file of up to %(mb)s MB." msgstr "" -#: routes/games.py:634 +#: routes/games.py:561 +#, python-format +msgid "" +"Screenshots not added because they match ones the game already has: " +"%(count)s" +msgstr "" + +#: routes/games.py:685 msgid "Game not found after update" msgstr "" -#: routes/games.py:935 +#: routes/games.py:995 msgid "No game selected." msgstr "" -#: routes/games.py:959 +#: routes/games.py:1019 msgid "This metadata apply is no longer running." msgstr "" -#: routes/games.py:968 +#: routes/games.py:1028 #, python-format msgid "" "Metadata applied from %(sources)s! (%(filled)d fields filled, %(missing)d" " still missing)" msgstr "" -#: routes/games.py:974 +#: routes/games.py:1034 msgid "Failed to apply metadata." msgstr "" -#: routes/games.py:1023 routes/games.py:1067 +#: routes/games.py:1083 routes/games.py:1127 msgid "No fields to update" msgstr "" -#: routes/games.py:1064 routes/platform_import.py:212 +#: routes/games.py:1124 routes/platform_import.py:212 #: routes/platform_import.py:572 routes/platform_import.py:928 #: routes/trophies.py:1852 msgid "No games selected" msgstr "" -#: routes/games.py:1074 +#: routes/games.py:1134 msgid "Invalid completion status" msgstr "" -#: routes/games.py:1122 +#: routes/games.py:1182 msgid "No valid fields to update" msgstr "" -#: routes/games.py:1238 +#: routes/games.py:1298 msgid "Invalid status" msgstr "" -#: routes/games.py:1263 +#: routes/games.py:1323 msgid "Not authenticated" msgstr "" -#: routes/games.py:1294 +#: routes/games.py:1354 msgid "Filter type and value required" msgstr "" -#: routes/games.py:1307 +#: routes/games.py:1367 msgid "Invalid filter type" msgstr "" -#: routes/games_ai.py:83 +#: routes/games_ai.py:77 msgid "AI returned no data. Check your API key and provider settings." msgstr "" -#: routes/games_ai.py:300 +#: routes/games_ai.py:294 msgid "AI scraper module not available" msgstr "" @@ -677,7 +684,7 @@ msgstr "" msgid "Image file not found" msgstr "" -#: routes/museum.py:740 routes/museum.py:744 services/js_i18n_strings.py:512 +#: routes/museum.py:740 routes/museum.py:744 services/js_i18n_strings.py:527 msgid "Background removal failed" msgstr "" @@ -942,11 +949,11 @@ msgstr "" msgid "Game not found or not a Steam game" msgstr "" -#: routes/systems.py:310 +#: routes/systems.py:313 msgid "System ID required" msgstr "" -#: routes/systems.py:438 +#: routes/systems.py:441 msgid "ES-DE scraper not available" msgstr "" @@ -1479,19 +1486,19 @@ msgstr "" msgid "Turn-Based" msgstr "" -#: services/js_i18n_strings.py:475 +#: services/js_i18n_strings.py:490 msgid "(Paused)" msgstr "" -#: services/js_i18n_strings.py:476 +#: services/js_i18n_strings.py:491 msgid "(none)" msgstr "" -#: services/js_i18n_strings.py:477 +#: services/js_i18n_strings.py:492 msgid "(not set)" msgstr "" -#: services/js_i18n_strings.py:478 templates/_bulk_edit_modal.html:25 +#: services/js_i18n_strings.py:493 templates/_bulk_edit_modal.html:25 #: templates/_bulk_edit_modal.html:38 templates/_bulk_edit_modal.html:83 #: templates/_bulk_edit_modal.html:95 templates/_bulk_edit_modal.html:120 #: templates/_bulk_edit_modal.html:137 templates/_bulk_edit_modal.html:153 @@ -1499,99 +1506,99 @@ msgstr "" msgid "-- Don't change --" msgstr "" -#: services/js_i18n_strings.py:479 +#: services/js_i18n_strings.py:494 msgid "-- Select log file --" msgstr "" -#: services/js_i18n_strings.py:480 +#: services/js_i18n_strings.py:495 #, python-brace-format msgid "... and {n} more" msgstr "" -#: services/js_i18n_strings.py:481 templates/_bulk_edit_modal.html:30 -#: templates/base.html:1014 +#: services/js_i18n_strings.py:496 templates/_bulk_edit_modal.html:30 +#: templates/base.html:1015 msgid "100%" msgstr "" -#: services/js_i18n_strings.py:482 templates/_settings_tabs/account.html:273 +#: services/js_i18n_strings.py:497 templates/_settings_tabs/account.html:273 #: templates/archive_scanner.html:726 templates/multi_disc_organizer.html:217 #: templates/reports.html:369 templates/reports.html:450 msgid "Actions" msgstr "" -#: services/js_i18n_strings.py:483 +#: services/js_i18n_strings.py:498 msgid "Added to queue - will start when current operation completes" msgstr "" -#: services/js_i18n_strings.py:484 +#: services/js_i18n_strings.py:499 msgid "All ROMs accounted for!" msgstr "" -#: services/js_i18n_strings.py:485 templates/logs.html:764 +#: services/js_i18n_strings.py:500 templates/logs.html:764 msgid "All Sources" msgstr "" -#: services/js_i18n_strings.py:486 templates/all_games.html:42 +#: services/js_i18n_strings.py:501 templates/all_games.html:42 #: templates/reports.html:328 templates/reports.html:407 #: templates/reports.html:480 msgid "All Systems" msgstr "" -#: services/js_i18n_strings.py:487 +#: services/js_i18n_strings.py:502 msgid "Already Syncing" msgstr "" -#: services/js_i18n_strings.py:488 +#: services/js_i18n_strings.py:503 #, python-brace-format msgid "Applied when you press Play, in the {emulator} settings for this game." msgstr "" -#: services/js_i18n_strings.py:489 +#: services/js_i18n_strings.py:504 #, python-brace-format msgid "Applied {n} update(s)." msgstr "" -#: services/js_i18n_strings.py:490 templates/_bulk_edit_modal.html:193 +#: services/js_i18n_strings.py:505 templates/_bulk_edit_modal.html:193 msgid "Apply Changes" msgstr "" -#: services/js_i18n_strings.py:491 +#: services/js_i18n_strings.py:506 msgid "Apply Normalization" msgstr "" -#: services/js_i18n_strings.py:492 +#: services/js_i18n_strings.py:507 #, python-brace-format msgid "Apply failed: {error}" msgstr "" -#: services/js_i18n_strings.py:493 +#: services/js_i18n_strings.py:508 #, python-brace-format msgid "Apply {n} Selected" msgstr "" -#: services/js_i18n_strings.py:494 +#: services/js_i18n_strings.py:509 #, python-brace-format msgid "Apply {n} normalization rule?" msgstr "" -#: services/js_i18n_strings.py:495 +#: services/js_i18n_strings.py:510 #, python-brace-format msgid "Apply {n} normalization rules?" msgstr "" -#: services/js_i18n_strings.py:496 +#: services/js_i18n_strings.py:511 msgid "Applying Metadata" msgstr "" -#: services/js_i18n_strings.py:497 +#: services/js_i18n_strings.py:512 msgid "Applying..." msgstr "" -#: services/js_i18n_strings.py:498 +#: services/js_i18n_strings.py:513 msgid "Applying…" msgstr "" -#: services/js_i18n_strings.py:499 +#: services/js_i18n_strings.py:514 msgid "" "Are you sure you want to cancel the bulk scrape?\n" "\n" @@ -1600,87 +1607,91 @@ msgid "" "during that window." msgstr "" -#: services/js_i18n_strings.py:500 +#: services/js_i18n_strings.py:515 msgid "Are you sure you want to cancel?" msgstr "" -#: services/js_i18n_strings.py:501 +#: services/js_i18n_strings.py:516 #, python-brace-format msgid "Are you sure you want to delete \"{filename}\"?" msgstr "" -#: services/js_i18n_strings.py:502 +#: services/js_i18n_strings.py:517 msgid "" "Are you sure you want to reset all metadata for this game? This will " "delete boxart and screenshots." msgstr "" -#: services/js_i18n_strings.py:503 +#: services/js_i18n_strings.py:518 msgid "Are you sure you want to restart the server?" msgstr "" -#: services/js_i18n_strings.py:504 +#: services/js_i18n_strings.py:519 msgid "" "Are you sure you want to shut down the server? You will need to start it " "again manually." msgstr "" -#: services/js_i18n_strings.py:505 templates/settings.html:67 +#: services/js_i18n_strings.py:520 templates/settings.html:67 msgid "Are you sure?" msgstr "" -#: services/js_i18n_strings.py:506 +#: services/js_i18n_strings.py:521 msgid "As the core sets it" msgstr "" -#: services/js_i18n_strings.py:507 +#: services/js_i18n_strings.py:522 msgid "Asking AI…" msgstr "" -#: services/js_i18n_strings.py:508 +#: services/js_i18n_strings.py:523 msgid "Asking the AI. This can take up to a minute." msgstr "" -#: services/js_i18n_strings.py:509 +#: services/js_i18n_strings.py:524 #, python-brace-format msgid "" "Asking the AI: game {current} of {total}. Names suggested: {proposals}. " "{system}" msgstr "" -#: services/js_i18n_strings.py:510 +#: services/js_i18n_strings.py:525 msgid "Aspect ratio" msgstr "" -#: services/js_i18n_strings.py:511 +#: services/js_i18n_strings.py:526 msgid "BIOS" msgstr "" -#: services/js_i18n_strings.py:513 +#: services/js_i18n_strings.py:528 msgid "Background removed" msgstr "" -#: services/js_i18n_strings.py:514 +#: services/js_i18n_strings.py:529 msgid "Bulk fetch failed" msgstr "" -#: services/js_i18n_strings.py:515 +#: services/js_i18n_strings.py:530 msgid "Bulk fetch request failed" msgstr "" -#: services/js_i18n_strings.py:516 +#: services/js_i18n_strings.py:531 msgid "Bulk scraping complete!" msgstr "" -#: services/js_i18n_strings.py:517 +#: services/js_i18n_strings.py:532 msgid "CLZ Imports" msgstr "" -#: services/js_i18n_strings.py:518 +#: services/js_i18n_strings.py:533 msgid "CRT screen" msgstr "" -#: services/js_i18n_strings.py:519 templates/_bulk_edit_modal.html:191 +#: services/js_i18n_strings.py:534 templates/game_detail.html:236 +msgid "Can be completed with playable performance and no game-breaking glitches." +msgstr "" + +#: services/js_i18n_strings.py:535 templates/_bulk_edit_modal.html:191 #: templates/_bulk_scrape_modal.html:49 templates/_modals/edit_modal.html:12 #: templates/_modals/rename_modal.html:19 #: templates/_settings_tabs/account.html:72 @@ -1688,9 +1699,9 @@ msgstr "" #: templates/_settings_tabs/customization.html:264 #: templates/achievement_game.html:178 templates/archive_scanner.html:661 #: templates/archive_scanner.html:798 templates/base.html:338 -#: templates/base.html:356 templates/base.html:1055 +#: templates/base.html:356 templates/base.html:1056 #: templates/chd_converter.html:170 templates/chd_verify.html:126 -#: templates/game_detail.html:518 templates/login.html:254 +#: templates/game_detail.html:564 templates/login.html:254 #: templates/museum.html:29 templates/museum_system.html:182 #: templates/psn_trophies.html:297 templates/psn_trophy_detail.html:182 #: templates/psn_trophy_detail.html:325 templates/reports.html:521 @@ -1701,85 +1712,85 @@ msgstr "" msgid "Cancel" msgstr "" -#: services/js_i18n_strings.py:520 +#: services/js_i18n_strings.py:536 msgid "Cancel Bulk Scrape" msgstr "" -#: services/js_i18n_strings.py:521 +#: services/js_i18n_strings.py:537 msgid "Cancel failed" msgstr "" -#: services/js_i18n_strings.py:522 +#: services/js_i18n_strings.py:538 msgid "Cancel request failed" msgstr "" -#: services/js_i18n_strings.py:523 +#: services/js_i18n_strings.py:539 msgid "Cancellation requested" msgstr "" -#: services/js_i18n_strings.py:524 +#: services/js_i18n_strings.py:540 msgid "Cancelled" msgstr "" -#: services/js_i18n_strings.py:525 +#: services/js_i18n_strings.py:541 msgid "Cancelled by user" msgstr "" -#: services/js_i18n_strings.py:526 +#: services/js_i18n_strings.py:542 msgid "Checking RetroAchievements…" msgstr "" -#: services/js_i18n_strings.py:527 +#: services/js_i18n_strings.py:543 msgid "Clean Missing ROMs" msgstr "" -#: services/js_i18n_strings.py:528 +#: services/js_i18n_strings.py:544 msgid "Clear CLZ Imports" msgstr "" -#: services/js_i18n_strings.py:529 +#: services/js_i18n_strings.py:545 msgid "Clear HLTB data for this game?" msgstr "" -#: services/js_i18n_strings.py:530 templates/achievements_system.html:109 +#: services/js_i18n_strings.py:546 templates/achievements_system.html:109 msgid "Clear RA Data" msgstr "" -#: services/js_i18n_strings.py:531 +#: services/js_i18n_strings.py:547 msgid "Clear RetroAchievements Data" msgstr "" -#: services/js_i18n_strings.py:532 +#: services/js_i18n_strings.py:548 #, python-brace-format msgid "Cleared RA data for {count} games in {system}" msgstr "" -#: services/js_i18n_strings.py:533 +#: services/js_i18n_strings.py:549 #, python-brace-format msgid "Cleared RA data for {n} games" msgstr "" -#: services/js_i18n_strings.py:534 +#: services/js_i18n_strings.py:550 #, python-brace-format msgid "Cleared RA data for {n} games in {system}" msgstr "" -#: services/js_i18n_strings.py:535 +#: services/js_i18n_strings.py:551 #, python-brace-format msgid "" "Cleared RetroAchievements data for {n} games. Run \"Refresh " "RetroAchievements\" to re-scan with updated matching." msgstr "" -#: services/js_i18n_strings.py:536 +#: services/js_i18n_strings.py:552 msgid "Clearing..." msgstr "" -#: services/js_i18n_strings.py:537 +#: services/js_i18n_strings.py:553 msgid "Click again to remove" msgstr "" -#: services/js_i18n_strings.py:538 templates/_bulk_edit_modal.html:8 +#: services/js_i18n_strings.py:554 templates/_bulk_edit_modal.html:8 #: templates/_bulk_scrape_modal.html:8 #: templates/_modals/boxart_zoom_modal.html:3 #: templates/_modals/manual_reader_modal.html:11 @@ -1787,48 +1798,52 @@ msgstr "" #: templates/_settings_tabs/customization.html:224 #: templates/archive_scanner.html:749 templates/archive_scanner.html:757 #: templates/base.html:249 templates/base.html:332 templates/base.html:349 -#: templates/base.html:695 templates/base.html:971 templates/base.html:1365 +#: templates/base.html:695 templates/base.html:972 templates/base.html:1366 #: templates/reports.html:472 templates/systems.html:121 #: templates/wishlist.html:56 msgid "Close" msgstr "" -#: services/js_i18n_strings.py:539 +#: services/js_i18n_strings.py:555 msgid "Close keyboard shortcuts" msgstr "" -#: services/js_i18n_strings.py:540 +#: services/js_i18n_strings.py:556 msgid "Close modal / cancel" msgstr "" -#: services/js_i18n_strings.py:541 templates/dashboard.html:179 +#: services/js_i18n_strings.py:557 templates/dashboard.html:179 #: templates/setup.html:20 msgid "Complete" msgstr "" -#: services/js_i18n_strings.py:542 +#: services/js_i18n_strings.py:558 msgid "Complete!" msgstr "" -#: services/js_i18n_strings.py:543 templates/_bulk_edit_modal.html:29 -#: templates/analytics.html:1664 templates/base.html:1013 -#: templates/dashboard.html:497 templates/game_detail.html:222 +#: services/js_i18n_strings.py:559 templates/_bulk_edit_modal.html:29 +#: templates/analytics.html:1664 templates/base.html:1014 +#: templates/dashboard.html:497 templates/game_detail.html:257 msgid "Completed" msgstr "" -#: services/js_i18n_strings.py:544 templates/_bulk_edit_modal.html:23 -#: templates/analytics.html:137 templates/base.html:1008 +#: services/js_i18n_strings.py:560 templates/_bulk_edit_modal.html:23 +#: templates/analytics.html:137 templates/base.html:1009 msgid "Completion Status" msgstr "" -#: services/js_i18n_strings.py:545 +#: services/js_i18n_strings.py:561 templates/game_detail.html:253 +msgid "Completion status" +msgstr "" + +#: services/js_i18n_strings.py:562 msgid "Completion status updated" msgstr "" -#: services/js_i18n_strings.py:546 templates/achievement_game.html:172 +#: services/js_i18n_strings.py:563 templates/achievement_game.html:172 #: templates/achievement_game.html:197 templates/achievement_game.html:211 -#: templates/base.html:1050 templates/game_detail.html:512 -#: templates/game_detail.html:537 templates/game_detail.html:551 +#: templates/base.html:1051 templates/game_detail.html:558 +#: templates/game_detail.html:583 templates/game_detail.html:597 #: templates/local_trophy_detail.html:175 templates/psn_trophy_detail.html:176 #: templates/psn_trophy_detail.html:209 #: templates/steam_achievement_game.html:134 @@ -1838,395 +1853,404 @@ msgstr "" msgid "Completionist" msgstr "" -#: services/js_i18n_strings.py:547 templates/settings.html:66 +#: services/js_i18n_strings.py:564 templates/settings.html:66 msgid "Confirm Action" msgstr "" -#: services/js_i18n_strings.py:548 +#: services/js_i18n_strings.py:565 msgid "Confirm Bulk Edit" msgstr "" -#: services/js_i18n_strings.py:549 +#: services/js_i18n_strings.py:566 msgid "Content generated successfully" msgstr "" -#: services/js_i18n_strings.py:550 +#: services/js_i18n_strings.py:567 msgid "Controller" msgstr "" -#: services/js_i18n_strings.py:551 +#: services/js_i18n_strings.py:568 msgid "Controller image saved" msgstr "" -#: services/js_i18n_strings.py:552 +#: services/js_i18n_strings.py:569 msgid "Controller image uploaded" msgstr "" -#: services/js_i18n_strings.py:553 +#: services/js_i18n_strings.py:570 msgid "Core" msgstr "" -#: services/js_i18n_strings.py:554 +#: services/js_i18n_strings.py:571 msgid "Could not accept these names." msgstr "" -#: services/js_i18n_strings.py:555 +#: services/js_i18n_strings.py:572 msgid "Could not apply metadata." msgstr "" -#: services/js_i18n_strings.py:556 +#: services/js_i18n_strings.py:573 #, python-brace-format msgid "" "Could not download the {system} core. Check the internet connection and " "try again." msgstr "" -#: services/js_i18n_strings.py:557 +#: services/js_i18n_strings.py:574 msgid "Could not link these versions." msgstr "" -#: services/js_i18n_strings.py:558 +#: services/js_i18n_strings.py:575 msgid "Could not load suggestions." msgstr "" -#: services/js_i18n_strings.py:559 +#: services/js_i18n_strings.py:576 msgid "Could not load the suggested names." msgstr "" -#: services/js_i18n_strings.py:560 +#: services/js_i18n_strings.py:577 msgid "Could not open this manual. Use \"Open PDF in a new tab\" instead." msgstr "" -#: services/js_i18n_strings.py:561 +#: services/js_i18n_strings.py:578 msgid "Could not read the run status." msgstr "" -#: services/js_i18n_strings.py:562 +#: services/js_i18n_strings.py:579 msgid "Could not reconnect to server. Please refresh manually." msgstr "" -#: services/js_i18n_strings.py:563 +#: services/js_i18n_strings.py:580 msgid "Could not reject these names." msgstr "" -#: services/js_i18n_strings.py:564 +#: services/js_i18n_strings.py:581 msgid "Could not save that setting." msgstr "" -#: services/js_i18n_strings.py:565 +#: services/js_i18n_strings.py:582 msgid "Could not save that." msgstr "" -#: services/js_i18n_strings.py:566 +#: services/js_i18n_strings.py:583 msgid "Could not show this page of the manual." msgstr "" -#: services/js_i18n_strings.py:567 +#: services/js_i18n_strings.py:584 msgid "Could not start the run." msgstr "" -#: services/js_i18n_strings.py:568 +#: services/js_i18n_strings.py:585 msgid "Could not stop the run." msgstr "" -#: services/js_i18n_strings.py:569 +#: services/js_i18n_strings.py:586 msgid "Could not stop the running instance." msgstr "" -#: services/js_i18n_strings.py:570 +#: services/js_i18n_strings.py:587 msgid "Could not unlink this version." msgstr "" -#: services/js_i18n_strings.py:571 +#: services/js_i18n_strings.py:588 msgid "Could not write the summary." msgstr "" -#: services/js_i18n_strings.py:572 +#: services/js_i18n_strings.py:589 +#, python-brace-format +msgid "Critic Score ({count} reviews)" +msgstr "" + +#: services/js_i18n_strings.py:590 msgid "Currently set" msgstr "" -#: services/js_i18n_strings.py:573 +#: services/js_i18n_strings.py:591 msgid "Default" msgstr "" -#: services/js_i18n_strings.py:574 +#: services/js_i18n_strings.py:592 #, python-brace-format msgid "Default emulator for {system}" msgstr "" -#: services/js_i18n_strings.py:575 +#: services/js_i18n_strings.py:593 msgid "Delete Log File" msgstr "" -#: services/js_i18n_strings.py:576 +#: services/js_i18n_strings.py:594 #, python-brace-format msgid "Detect failed: {error}" msgstr "" -#: services/js_i18n_strings.py:577 +#: services/js_i18n_strings.py:595 msgid "Detected path" msgstr "" -#: services/js_i18n_strings.py:578 +#: services/js_i18n_strings.py:596 msgid "Detecting…" msgstr "" -#: services/js_i18n_strings.py:579 templates/_bulk_scrape_modal.html:69 +#: services/js_i18n_strings.py:597 templates/game_detail.html:240 +msgid "Does not start, or crashes the emulator." +msgstr "" + +#: services/js_i18n_strings.py:598 templates/_bulk_scrape_modal.html:69 #: templates/all_games.html:223 templates/psn_trophies.html:247 #: templates/psn_trophies.html:307 templates/reports.html:525 #: templates/system_games.html:203 msgid "Done" msgstr "" -#: services/js_i18n_strings.py:580 +#: services/js_i18n_strings.py:599 #, python-brace-format msgid "Done. Games asked: {total}. Names suggested: {proposals}." msgstr "" -#: services/js_i18n_strings.py:581 +#: services/js_i18n_strings.py:600 msgid "Downloading 3D box art…" msgstr "" -#: services/js_i18n_strings.py:582 +#: services/js_i18n_strings.py:601 msgid "Downloading RetroArch… This can take a few minutes." msgstr "" -#: services/js_i18n_strings.py:583 +#: services/js_i18n_strings.py:602 msgid "Downloading box art…" msgstr "" -#: services/js_i18n_strings.py:584 +#: services/js_i18n_strings.py:603 msgid "Downloading fan art…" msgstr "" -#: services/js_i18n_strings.py:585 +#: services/js_i18n_strings.py:604 msgid "Downloading manual…" msgstr "" -#: services/js_i18n_strings.py:586 +#: services/js_i18n_strings.py:605 msgid "Downloading media…" msgstr "" -#: services/js_i18n_strings.py:587 +#: services/js_i18n_strings.py:606 msgid "Downloading screenshots…" msgstr "" -#: services/js_i18n_strings.py:588 +#: services/js_i18n_strings.py:607 #, python-brace-format msgid "Downloading the {system} core…" msgstr "" -#: services/js_i18n_strings.py:589 +#: services/js_i18n_strings.py:608 msgid "Downloading video…" msgstr "" -#: services/js_i18n_strings.py:590 +#: services/js_i18n_strings.py:609 #, python-brace-format msgid "Downloading {core} ({done} of {total})…" msgstr "" -#: services/js_i18n_strings.py:591 templates/_settings_tabs/account.html:318 -#: templates/base.html:969 templates/game_detail.html:105 +#: services/js_i18n_strings.py:610 templates/_settings_tabs/account.html:318 +#: templates/base.html:970 templates/game_detail.html:119 #: templates/wishlist.html:641 msgid "Edit" msgstr "" -#: services/js_i18n_strings.py:592 +#: services/js_i18n_strings.py:611 msgid "Edit game" msgstr "" -#: services/js_i18n_strings.py:593 +#: services/js_i18n_strings.py:612 #, python-brace-format msgid "Edit: {title}" msgstr "" -#: services/js_i18n_strings.py:594 +#: services/js_i18n_strings.py:613 msgid "Emulator" msgstr "" -#: services/js_i18n_strings.py:595 +#: services/js_i18n_strings.py:614 msgid "Enter one folder path." msgstr "" -#: services/js_i18n_strings.py:596 +#: services/js_i18n_strings.py:615 msgid "Error" msgstr "" -#: services/js_i18n_strings.py:597 +#: services/js_i18n_strings.py:616 msgid "Error clearing HLTB data" msgstr "" -#: services/js_i18n_strings.py:598 +#: services/js_i18n_strings.py:617 msgid "Error connecting to HowLongToBeat" msgstr "" -#: services/js_i18n_strings.py:599 +#: services/js_i18n_strings.py:618 msgid "Error deleting log file" msgstr "" -#: services/js_i18n_strings.py:600 +#: services/js_i18n_strings.py:619 msgid "Error loading file" msgstr "" -#: services/js_i18n_strings.py:601 +#: services/js_i18n_strings.py:620 msgid "Error loading log file" msgstr "" -#: services/js_i18n_strings.py:602 +#: services/js_i18n_strings.py:621 msgid "Error looking up HLTB" msgstr "" -#: services/js_i18n_strings.py:603 +#: services/js_i18n_strings.py:622 msgid "Error refreshing RetroAchievements: " msgstr "" -#: services/js_i18n_strings.py:604 +#: services/js_i18n_strings.py:623 msgid "Error saving HLTB data" msgstr "" -#: services/js_i18n_strings.py:605 +#: services/js_i18n_strings.py:624 msgid "Error saving game: " msgstr "" -#: services/js_i18n_strings.py:606 +#: services/js_i18n_strings.py:625 #, python-brace-format msgid "Error scanning library: {error}" msgstr "" -#: services/js_i18n_strings.py:607 +#: services/js_i18n_strings.py:626 msgid "Error updating completion status" msgstr "" -#: services/js_i18n_strings.py:608 +#: services/js_i18n_strings.py:627 msgid "Error updating status" msgstr "" -#: services/js_i18n_strings.py:609 +#: services/js_i18n_strings.py:628 msgid "Europe" msgstr "" -#: services/js_i18n_strings.py:610 templates/_modals/edit_modal.html:223 -#: templates/base.html:1241 +#: services/js_i18n_strings.py:629 templates/_modals/edit_modal.html:223 +#: templates/base.html:1242 msgid "Exclusive" msgstr "" -#: services/js_i18n_strings.py:611 +#: services/js_i18n_strings.py:630 msgid "Failed to apply bulk edit. Check console for details." msgstr "" -#: services/js_i18n_strings.py:612 +#: services/js_i18n_strings.py:631 msgid "Failed to apply normalization" msgstr "" -#: services/js_i18n_strings.py:613 +#: services/js_i18n_strings.py:632 msgid "Failed to clean missing ROMs" msgstr "" -#: services/js_i18n_strings.py:614 +#: services/js_i18n_strings.py:633 msgid "Failed to clear CLZ imports" msgstr "" -#: services/js_i18n_strings.py:615 +#: services/js_i18n_strings.py:634 msgid "Failed to clear HLTB: " msgstr "" -#: services/js_i18n_strings.py:616 +#: services/js_i18n_strings.py:635 msgid "Failed to clear RA data" msgstr "" -#: services/js_i18n_strings.py:617 +#: services/js_i18n_strings.py:636 msgid "Failed to delete" msgstr "" -#: services/js_i18n_strings.py:618 +#: services/js_i18n_strings.py:637 msgid "Failed to fetch controller image" msgstr "" -#: services/js_i18n_strings.py:619 +#: services/js_i18n_strings.py:638 msgid "Failed to fetch image" msgstr "" -#: services/js_i18n_strings.py:620 +#: services/js_i18n_strings.py:639 msgid "Failed to load game" msgstr "" -#: services/js_i18n_strings.py:621 +#: services/js_i18n_strings.py:640 msgid "Failed to load normalization data" msgstr "" -#: services/js_i18n_strings.py:622 +#: services/js_i18n_strings.py:641 msgid "Failed to save HLTB: " msgstr "" -#: services/js_i18n_strings.py:623 +#: services/js_i18n_strings.py:642 msgid "Failed to save: " msgstr "" -#: services/js_i18n_strings.py:624 +#: services/js_i18n_strings.py:643 msgid "Failed to start RetroAchievements refresh" msgstr "" -#: services/js_i18n_strings.py:625 +#: services/js_i18n_strings.py:644 msgid "Failed to start bulk scrape" msgstr "" -#: services/js_i18n_strings.py:626 +#: services/js_i18n_strings.py:645 msgid "Failed to start generation" msgstr "" -#: services/js_i18n_strings.py:627 +#: services/js_i18n_strings.py:646 msgid "Failed to update completion status: " msgstr "" -#: services/js_i18n_strings.py:628 +#: services/js_i18n_strings.py:647 #, python-brace-format msgid "Failed to update status: {error}" msgstr "" -#: services/js_i18n_strings.py:629 +#: services/js_i18n_strings.py:648 #, python-brace-format msgid "Failed: {error}" msgstr "" -#: services/js_i18n_strings.py:630 +#: services/js_i18n_strings.py:649 msgid "Fetch All Controller Images" msgstr "" -#: services/js_i18n_strings.py:631 +#: services/js_i18n_strings.py:650 #, python-brace-format msgid "Fetching from {source}…" msgstr "" -#: services/js_i18n_strings.py:632 +#: services/js_i18n_strings.py:651 #, python-brace-format msgid "Fetching images: {done} of {total}" msgstr "" -#: services/js_i18n_strings.py:633 +#: services/js_i18n_strings.py:652 msgid "Focus search box" msgstr "" -#: services/js_i18n_strings.py:634 templates/logs.html:743 +#: services/js_i18n_strings.py:653 templates/logs.html:743 msgid "From" msgstr "" -#: services/js_i18n_strings.py:635 +#: services/js_i18n_strings.py:654 msgid "From the system setting" msgstr "" -#: services/js_i18n_strings.py:636 +#: services/js_i18n_strings.py:655 msgid "Fullscreen" msgstr "" -#: services/js_i18n_strings.py:637 +#: services/js_i18n_strings.py:656 msgid "Game Page" msgstr "" -#: services/js_i18n_strings.py:638 +#: services/js_i18n_strings.py:657 msgid "Game details updated successfully." msgstr "" -#: services/js_i18n_strings.py:639 templates/_settings_tabs/data.html:186 +#: services/js_i18n_strings.py:658 templates/_settings_tabs/data.html:186 #: templates/achievements_system.html:119 templates/analytics.html:571 #: templates/analytics.html:1584 templates/analytics.html:1625 #: templates/analytics.html:1947 templates/analytics.html:2122 @@ -2236,210 +2260,221 @@ msgstr "" msgid "Games" msgstr "" -#: services/js_i18n_strings.py:640 +#: services/js_i18n_strings.py:659 msgid "Generate All Museum Content" msgstr "" -#: services/js_i18n_strings.py:641 templates/museum_system.html:45 +#: services/js_i18n_strings.py:660 templates/museum_system.html:45 msgid "Generate Content" msgstr "" -#: services/js_i18n_strings.py:642 templates/museum.html:28 +#: services/js_i18n_strings.py:661 templates/museum.html:28 msgid "Generating content..." msgstr "" -#: services/js_i18n_strings.py:643 +#: services/js_i18n_strings.py:662 msgid "Generating..." msgstr "" -#: services/js_i18n_strings.py:644 +#: services/js_i18n_strings.py:663 msgid "Generation cancelled" msgstr "" -#: services/js_i18n_strings.py:645 +#: services/js_i18n_strings.py:664 #, python-brace-format msgid "" "Generation complete: {generated} generated, {skipped} skipped, {failed} " "failed" msgstr "" -#: services/js_i18n_strings.py:646 +#: services/js_i18n_strings.py:665 msgid "Generation failed" msgstr "" -#: services/js_i18n_strings.py:647 +#: services/js_i18n_strings.py:666 msgid "Generation request failed" msgstr "" -#: services/js_i18n_strings.py:648 +#: services/js_i18n_strings.py:667 templates/game_detail.html:237 +msgid "" +"Gets in game, but cannot be finished, has serious glitches or runs too " +"slowly." +msgstr "" + +#: services/js_i18n_strings.py:668 msgid "Go to Analytics" msgstr "" -#: services/js_i18n_strings.py:649 +#: services/js_i18n_strings.py:669 msgid "Go to Changelog" msgstr "" -#: services/js_i18n_strings.py:650 +#: services/js_i18n_strings.py:670 msgid "Go to Dashboard" msgstr "" -#: services/js_i18n_strings.py:651 +#: services/js_i18n_strings.py:671 msgid "Go to Help" msgstr "" -#: services/js_i18n_strings.py:652 +#: services/js_i18n_strings.py:672 msgid "Go to Library" msgstr "" -#: services/js_i18n_strings.py:653 +#: services/js_i18n_strings.py:673 msgid "Go to Settings" msgstr "" -#: services/js_i18n_strings.py:654 +#: services/js_i18n_strings.py:674 msgid "Go to Systems" msgstr "" -#: services/js_i18n_strings.py:655 +#: services/js_i18n_strings.py:675 msgid "HLTB data cleared" msgstr "" -#: services/js_i18n_strings.py:656 +#: services/js_i18n_strings.py:676 msgid "HLTB data saved" msgstr "" -#: services/js_i18n_strings.py:657 +#: services/js_i18n_strings.py:677 msgid "HLTB lookup cancelled" msgstr "" -#: services/js_i18n_strings.py:658 +#: services/js_i18n_strings.py:678 msgid "Handheld LCD" msgstr "" -#: services/js_i18n_strings.py:659 templates/all_games.html:99 +#: services/js_i18n_strings.py:679 templates/all_games.html:99 #: templates/system_games.html:90 msgid "Has Achievements" msgstr "" -#: services/js_i18n_strings.py:660 templates/logs.html:719 +#: services/js_i18n_strings.py:680 templates/logs.html:719 msgid "Image Resize" msgstr "" -#: services/js_i18n_strings.py:661 +#: services/js_i18n_strings.py:681 msgid "Image fetch cancelled." msgstr "" -#: services/js_i18n_strings.py:662 +#: services/js_i18n_strings.py:682 msgid "Image must be under 10MB" msgstr "" -#: services/js_i18n_strings.py:663 templates/_bulk_edit_modal.html:27 +#: services/js_i18n_strings.py:683 templates/_bulk_edit_modal.html:27 #: templates/achievements_system.html:151 #: templates/achievements_system.html:170 templates/analytics.html:1664 -#: templates/base.html:1011 templates/dashboard.html:353 -#: templates/dashboard.html:493 templates/game_detail.html:220 +#: templates/base.html:1012 templates/dashboard.html:353 +#: templates/dashboard.html:493 templates/game_detail.html:255 #: templates/steam_achievements.html:77 templates/xbox_achievements.html:77 msgid "In Progress" msgstr "" -#: services/js_i18n_strings.py:664 +#: services/js_i18n_strings.py:684 msgid "Initializing bulk scrape..." msgstr "" -#: services/js_i18n_strings.py:665 templates/_bulk_scrape_modal.html:23 +#: services/js_i18n_strings.py:685 templates/_bulk_scrape_modal.html:23 #: templates/duplicate_finder.html:250 templates/screenshot_dedup.html:276 msgid "Initializing..." msgstr "" -#: services/js_i18n_strings.py:666 +#: services/js_i18n_strings.py:686 msgid "Input lag reduction" msgstr "" -#: services/js_i18n_strings.py:667 +#: services/js_i18n_strings.py:687 #, python-brace-format msgid "Installed {n} cores." msgstr "" -#: services/js_i18n_strings.py:668 +#: services/js_i18n_strings.py:688 #, python-brace-format msgid "Installed {n} cores. Failed: {failed}" msgstr "" -#: services/js_i18n_strings.py:669 +#: services/js_i18n_strings.py:689 msgid "Integer scaling" msgstr "" -#: services/js_i18n_strings.py:670 +#: services/js_i18n_strings.py:690 msgid "Japan" msgstr "" -#: services/js_i18n_strings.py:671 +#: services/js_i18n_strings.py:691 msgid "Just synced" msgstr "" -#: services/js_i18n_strings.py:672 +#: services/js_i18n_strings.py:692 msgid "Keyboard Shortcuts" msgstr "" -#: services/js_i18n_strings.py:673 +#: services/js_i18n_strings.py:693 msgid "Kill" msgstr "" -#: services/js_i18n_strings.py:674 +#: services/js_i18n_strings.py:694 +#, python-brace-format +msgid "Last tested {date}." +msgstr "" + +#: services/js_i18n_strings.py:695 #, python-brace-format msgid "Launch failed: {error}" msgstr "" -#: services/js_i18n_strings.py:675 +#: services/js_i18n_strings.py:696 #, python-brace-format msgid "Launched (pid {pid})" msgstr "" -#: services/js_i18n_strings.py:676 +#: services/js_i18n_strings.py:697 msgid "Launched (replaced previous instance)" msgstr "" -#: services/js_i18n_strings.py:677 +#: services/js_i18n_strings.py:698 msgid "Launching…" msgstr "" -#: services/js_i18n_strings.py:678 +#: services/js_i18n_strings.py:699 #, python-brace-format msgid "Library scan complete! Found {n} new games." msgstr "" -#: services/js_i18n_strings.py:679 +#: services/js_i18n_strings.py:700 msgid "Link" msgstr "" -#: services/js_i18n_strings.py:680 +#: services/js_i18n_strings.py:701 msgid "Link ticked games" msgstr "" -#: services/js_i18n_strings.py:681 +#: services/js_i18n_strings.py:702 msgid "Loading log file..." msgstr "" -#: services/js_i18n_strings.py:682 +#: services/js_i18n_strings.py:703 msgid "Loading the manual…" msgstr "" -#: services/js_i18n_strings.py:683 templates/settings_emulators.html:132 +#: services/js_i18n_strings.py:704 templates/settings_emulators.html:132 #: templates/settings_emulators.html:202 msgid "Loading…" msgstr "" -#: services/js_i18n_strings.py:684 +#: services/js_i18n_strings.py:705 msgid "Log file deleted" msgstr "" -#: services/js_i18n_strings.py:685 +#: services/js_i18n_strings.py:706 msgid "Looking for versions…" msgstr "" -#: services/js_i18n_strings.py:686 templates/achievement_game.html:168 +#: services/js_i18n_strings.py:707 templates/achievement_game.html:168 #: templates/achievement_game.html:197 templates/achievement_game.html:207 -#: templates/base.html:1046 templates/game_detail.html:508 -#: templates/game_detail.html:537 templates/game_detail.html:547 +#: templates/base.html:1047 templates/game_detail.html:554 +#: templates/game_detail.html:583 templates/game_detail.html:593 #: templates/local_trophy_detail.html:175 templates/psn_trophy_detail.html:172 #: templates/psn_trophy_detail.html:205 #: templates/steam_achievement_game.html:133 @@ -2449,10 +2484,10 @@ msgstr "" msgid "Main + Extras" msgstr "" -#: services/js_i18n_strings.py:687 templates/achievement_game.html:164 +#: services/js_i18n_strings.py:708 templates/achievement_game.html:164 #: templates/achievement_game.html:197 templates/achievement_game.html:203 -#: templates/base.html:1042 templates/game_detail.html:504 -#: templates/game_detail.html:537 templates/game_detail.html:543 +#: templates/base.html:1043 templates/game_detail.html:550 +#: templates/game_detail.html:583 templates/game_detail.html:589 #: templates/local_trophy_detail.html:175 templates/psn_trophy_detail.html:168 #: templates/psn_trophy_detail.html:201 #: templates/steam_achievement_game.html:132 @@ -2462,312 +2497,312 @@ msgstr "" msgid "Main Story" msgstr "" -#: services/js_i18n_strings.py:688 +#: services/js_i18n_strings.py:709 msgid "Marked as not versions." msgstr "" -#: services/js_i18n_strings.py:689 +#: services/js_i18n_strings.py:710 msgid "Match found - please confirm to save" msgstr "" -#: services/js_i18n_strings.py:690 +#: services/js_i18n_strings.py:711 msgid "" "Matched using this alternate/regional title — confirm it refers to the " "same game before saving." msgstr "" -#: services/js_i18n_strings.py:691 +#: services/js_i18n_strings.py:712 #, python-brace-format msgid "" "Matched via alternate title \"{term}\" — HLTB shows it as \"{match}\". " "Confirm before saving." msgstr "" -#: services/js_i18n_strings.py:692 +#: services/js_i18n_strings.py:713 #, python-brace-format msgid "Missing: {files}" msgstr "" -#: services/js_i18n_strings.py:693 +#: services/js_i18n_strings.py:714 msgid "Multi-System" msgstr "" -#: services/js_i18n_strings.py:694 +#: services/js_i18n_strings.py:715 msgid "Multi-platform" msgstr "" -#: services/js_i18n_strings.py:695 +#: services/js_i18n_strings.py:716 msgid "Museum generation started" msgstr "" -#: services/js_i18n_strings.py:696 +#: services/js_i18n_strings.py:717 msgid "NOT" msgstr "" -#: services/js_i18n_strings.py:697 +#: services/js_i18n_strings.py:718 #, python-brace-format msgid "Names changed: {accepted}." msgstr "" -#: services/js_i18n_strings.py:698 +#: services/js_i18n_strings.py:719 #, python-brace-format msgid "" "Names changed: {accepted}. Skipped because their title changed since: " "{skipped}." msgstr "" -#: services/js_i18n_strings.py:699 +#: services/js_i18n_strings.py:720 #, python-brace-format msgid "Names rejected: {rejected}. They will not be suggested again." msgstr "" -#: services/js_i18n_strings.py:700 +#: services/js_i18n_strings.py:721 msgid "Navigation" msgstr "" -#: services/js_i18n_strings.py:701 +#: services/js_i18n_strings.py:722 msgid "Network error" msgstr "" -#: services/js_i18n_strings.py:702 +#: services/js_i18n_strings.py:723 msgid "Network error starting bulk scrape" msgstr "" -#: services/js_i18n_strings.py:703 +#: services/js_i18n_strings.py:724 msgid "Next disk" msgstr "" -#: services/js_i18n_strings.py:704 +#: services/js_i18n_strings.py:725 msgid "Next disk failed" msgstr "" -#: services/js_i18n_strings.py:705 templates/_bulk_edit_modal.html:171 -#: templates/_modals/edit_modal.html:143 templates/game_detail.html:666 -#: templates/game_detail.html:734 +#: services/js_i18n_strings.py:726 templates/_bulk_edit_modal.html:171 +#: templates/_modals/edit_modal.html:143 templates/game_detail.html:712 +#: templates/game_detail.html:780 msgid "No" msgstr "" -#: services/js_i18n_strings.py:706 +#: services/js_i18n_strings.py:727 msgid "No (already set)" msgstr "" -#: services/js_i18n_strings.py:707 +#: services/js_i18n_strings.py:728 msgid "No CLZ Import games found" msgstr "" -#: services/js_i18n_strings.py:708 +#: services/js_i18n_strings.py:729 msgid "No Changes" msgstr "" -#: services/js_i18n_strings.py:709 +#: services/js_i18n_strings.py:730 msgid "No Games Selected" msgstr "" -#: services/js_i18n_strings.py:710 +#: services/js_i18n_strings.py:731 msgid "No HLTB data found" msgstr "" -#: services/js_i18n_strings.py:711 +#: services/js_i18n_strings.py:732 msgid "No achievements found" msgstr "" -#: services/js_i18n_strings.py:712 templates/achievement_game.html:72 +#: services/js_i18n_strings.py:733 templates/achievement_game.html:72 #: templates/steam_achievement_game.html:69 #: templates/xbox_achievement_game.html:59 msgid "No description available." msgstr "" -#: services/js_i18n_strings.py:713 +#: services/js_i18n_strings.py:734 msgid "No emulator AppImages found in the scan paths." msgstr "" -#: services/js_i18n_strings.py:714 +#: services/js_i18n_strings.py:735 msgid "No emulators registered." msgstr "" -#: services/js_i18n_strings.py:715 +#: services/js_i18n_strings.py:736 msgid "No folders yet." msgstr "" -#: services/js_i18n_strings.py:716 +#: services/js_i18n_strings.py:737 msgid "No games or systems match" msgstr "" -#: services/js_i18n_strings.py:717 +#: services/js_i18n_strings.py:738 msgid "No log files in this category" msgstr "" -#: services/js_i18n_strings.py:718 +#: services/js_i18n_strings.py:739 msgid "No log lines match current filters" msgstr "" -#: services/js_i18n_strings.py:719 +#: services/js_i18n_strings.py:740 msgid "No mappings yet. Add an emulator folder above." msgstr "" -#: services/js_i18n_strings.py:720 +#: services/js_i18n_strings.py:741 msgid "No matching games on this system." msgstr "" -#: services/js_i18n_strings.py:721 +#: services/js_i18n_strings.py:742 msgid "No missing ROM files found." msgstr "" -#: services/js_i18n_strings.py:722 +#: services/js_i18n_strings.py:743 msgid "No options available" msgstr "" -#: services/js_i18n_strings.py:723 +#: services/js_i18n_strings.py:744 msgid "No ratings available" msgstr "" -#: services/js_i18n_strings.py:724 +#: services/js_i18n_strings.py:745 msgid "No results" msgstr "" -#: services/js_i18n_strings.py:725 +#: services/js_i18n_strings.py:746 msgid "No results found" msgstr "" -#: services/js_i18n_strings.py:726 +#: services/js_i18n_strings.py:747 msgid "No results found on HowLongToBeat" msgstr "" -#: services/js_i18n_strings.py:727 +#: services/js_i18n_strings.py:748 msgid "No sessions match current filters" msgstr "" -#: services/js_i18n_strings.py:728 +#: services/js_i18n_strings.py:749 msgid "No suggested names waiting. Press Ask the AI to look for some." msgstr "" -#: services/js_i18n_strings.py:729 +#: services/js_i18n_strings.py:750 msgid "No suggestions. Run Backfill Alternate Titles in Maintenance to find more." msgstr "" -#: services/js_i18n_strings.py:730 templates/game_detail.html:814 +#: services/js_i18n_strings.py:751 templates/game_detail.html:860 msgid "None" msgstr "" -#: services/js_i18n_strings.py:731 +#: services/js_i18n_strings.py:752 #, python-brace-format msgid "Normalization applied: {games} games updated, {rules} rules saved" msgstr "" -#: services/js_i18n_strings.py:732 +#: services/js_i18n_strings.py:753 msgid "Not versions" msgstr "" -#: services/js_i18n_strings.py:733 templates/base.html:1400 +#: services/js_i18n_strings.py:754 templates/base.html:1401 msgid "Now playing" msgstr "" -#: services/js_i18n_strings.py:734 templates/base.html:339 +#: services/js_i18n_strings.py:755 templates/base.html:339 msgid "OK" msgstr "" -#: services/js_i18n_strings.py:735 +#: services/js_i18n_strings.py:756 msgid "Off" msgstr "" -#: services/js_i18n_strings.py:736 +#: services/js_i18n_strings.py:757 msgid "On" msgstr "" -#: services/js_i18n_strings.py:737 +#: services/js_i18n_strings.py:758 msgid "Operation completed" msgstr "" -#: services/js_i18n_strings.py:738 +#: services/js_i18n_strings.py:759 msgid "Operation failed" msgstr "" -#: services/js_i18n_strings.py:739 +#: services/js_i18n_strings.py:760 msgid "Other" msgstr "" -#: services/js_i18n_strings.py:740 +#: services/js_i18n_strings.py:761 msgid "PSN Imports" msgstr "" -#: services/js_i18n_strings.py:741 +#: services/js_i18n_strings.py:762 msgid "Page:" msgstr "" -#: services/js_i18n_strings.py:742 +#: services/js_i18n_strings.py:763 msgid "Part of a Series" msgstr "" -#: services/js_i18n_strings.py:743 templates/_bulk_scrape_modal.html:46 +#: services/js_i18n_strings.py:764 templates/_bulk_scrape_modal.html:46 #: templates/archive_scanner.html:658 templates/archive_scanner.html:795 #: templates/psn_trophies.html:294 msgid "Pause" msgstr "" -#: services/js_i18n_strings.py:744 +#: services/js_i18n_strings.py:765 msgid "Paused - Click Resume to continue" msgstr "" -#: services/js_i18n_strings.py:745 +#: services/js_i18n_strings.py:766 msgid "Platform not found" msgstr "" -#: services/js_i18n_strings.py:746 templates/game_detail.html:85 +#: services/js_i18n_strings.py:767 templates/game_detail.html:99 msgid "Play" msgstr "" -#: services/js_i18n_strings.py:747 templates/_bulk_edit_modal.html:28 -#: templates/analytics.html:1664 templates/base.html:1012 -#: templates/dashboard.html:495 templates/game_detail.html:221 +#: services/js_i18n_strings.py:768 templates/_bulk_edit_modal.html:28 +#: templates/analytics.html:1664 templates/base.html:1013 +#: templates/dashboard.html:495 templates/game_detail.html:256 msgid "Played" msgstr "" -#: services/js_i18n_strings.py:748 +#: services/js_i18n_strings.py:769 msgid "Please change at least one field before applying." msgstr "" -#: services/js_i18n_strings.py:749 +#: services/js_i18n_strings.py:770 msgid "Please enter a game title" msgstr "" -#: services/js_i18n_strings.py:750 +#: services/js_i18n_strings.py:771 msgid "Please select at least one game to edit." msgstr "" -#: services/js_i18n_strings.py:751 +#: services/js_i18n_strings.py:772 msgid "Please select at least one game to scrape." msgstr "" -#: services/js_i18n_strings.py:752 +#: services/js_i18n_strings.py:773 msgid "Pre-session log entries" msgstr "" -#: services/js_i18n_strings.py:753 +#: services/js_i18n_strings.py:774 msgid "Processing images…" msgstr "" -#: services/js_i18n_strings.py:754 templates/game_imports.html:418 +#: services/js_i18n_strings.py:775 templates/game_imports.html:418 msgid "Processing..." msgstr "" -#: services/js_i18n_strings.py:755 +#: services/js_i18n_strings.py:776 #, python-brace-format msgid "Processing: {file}" msgstr "" -#: services/js_i18n_strings.py:756 +#: services/js_i18n_strings.py:777 msgid "Queued" msgstr "" -#: services/js_i18n_strings.py:757 +#: services/js_i18n_strings.py:778 msgid "RA Data Cleared" msgstr "" -#: services/js_i18n_strings.py:758 +#: services/js_i18n_strings.py:779 msgid "ROM Files" msgstr "" -#: services/js_i18n_strings.py:759 templates/_macros/nav_tabs.html:34 +#: services/js_i18n_strings.py:780 templates/_macros/nav_tabs.html:34 #: templates/archive_scanner.html:561 templates/base.html:174 #: templates/chd_converter.html:64 templates/chd_verify.html:80 #: templates/duplicate_finder.html:166 templates/logs.html:713 @@ -2777,401 +2812,429 @@ msgstr "" msgid "ROM Tools" msgstr "" -#: services/js_i18n_strings.py:760 +#: services/js_i18n_strings.py:781 templates/game_detail.html:237 +msgid "RPCS3: Ingame" +msgstr "" + +#: services/js_i18n_strings.py:782 templates/game_detail.html:238 +msgid "RPCS3: Intro" +msgstr "" + +#: services/js_i18n_strings.py:783 templates/game_detail.html:239 +msgid "RPCS3: Loadable" +msgstr "" + +#: services/js_i18n_strings.py:784 templates/game_detail.html:240 +msgid "RPCS3: Nothing" +msgstr "" + +#: services/js_i18n_strings.py:785 templates/game_detail.html:236 +msgid "RPCS3: Playable" +msgstr "" + +#: services/js_i18n_strings.py:786 msgid "Ready" msgstr "" -#: services/js_i18n_strings.py:761 +#: services/js_i18n_strings.py:787 msgid "Refresh RetroAchievements" msgstr "" -#: services/js_i18n_strings.py:762 +#: services/js_i18n_strings.py:788 #, python-brace-format msgid "Refresh: {system}" msgstr "" -#: services/js_i18n_strings.py:763 +#: services/js_i18n_strings.py:789 #, python-brace-format msgid "Refreshing RA for {system}..." msgstr "" -#: services/js_i18n_strings.py:764 +#: services/js_i18n_strings.py:790 msgid "Regional name matches" msgstr "" -#: services/js_i18n_strings.py:765 templates/compare_games.html:24 +#: services/js_i18n_strings.py:791 templates/compare_games.html:24 #: templates/compare_games.html:40 msgid "Remove" msgstr "" -#: services/js_i18n_strings.py:766 +#: services/js_i18n_strings.py:792 msgid "Remove background from the uploaded image?" msgstr "" -#: services/js_i18n_strings.py:767 +#: services/js_i18n_strings.py:793 msgid "Remove from queue" msgstr "" -#: services/js_i18n_strings.py:768 +#: services/js_i18n_strings.py:794 #, python-brace-format msgid "Removed {n} CLZ Import games" msgstr "" -#: services/js_i18n_strings.py:769 +#: services/js_i18n_strings.py:795 #, python-brace-format msgid "Removed {n} CLZ Import games:" msgstr "" -#: services/js_i18n_strings.py:770 +#: services/js_i18n_strings.py:796 #, python-brace-format msgid "Removed {n} games with missing ROMs" msgstr "" -#: services/js_i18n_strings.py:771 +#: services/js_i18n_strings.py:797 #, python-brace-format msgid "Removed {n} games:" msgstr "" -#: services/js_i18n_strings.py:772 +#: services/js_i18n_strings.py:798 msgid "Removing background…" msgstr "" -#: services/js_i18n_strings.py:773 +#: services/js_i18n_strings.py:799 msgid "Removing..." msgstr "" -#: services/js_i18n_strings.py:774 templates/_bulk_edit_modal.html:42 +#: services/js_i18n_strings.py:800 templates/_bulk_edit_modal.html:42 #: templates/_bulk_edit_modal.html:56 templates/_bulk_edit_modal.html:70 #: templates/_bulk_edit_modal.html:124 templates/_bulk_edit_modal.html:140 #: templates/_bulk_edit_modal.html:156 templates/_bulk_edit_modal.html:180 msgid "Replace" msgstr "" -#: services/js_i18n_strings.py:775 templates/logs.html:716 +#: services/js_i18n_strings.py:801 templates/logs.html:716 msgid "Reports" msgstr "" -#: services/js_i18n_strings.py:776 +#: services/js_i18n_strings.py:802 msgid "Resolution scale" msgstr "" -#: services/js_i18n_strings.py:777 +#: services/js_i18n_strings.py:803 msgid "Restarting server..." msgstr "" -#: services/js_i18n_strings.py:778 +#: services/js_i18n_strings.py:804 msgid "Resume" msgstr "" -#: services/js_i18n_strings.py:779 +#: services/js_i18n_strings.py:805 msgid "RetroArch is installed." msgstr "" -#: services/js_i18n_strings.py:780 +#: services/js_i18n_strings.py:806 msgid "RetroArch was not installed." msgstr "" -#: services/js_i18n_strings.py:781 +#: services/js_i18n_strings.py:807 #, python-brace-format msgid "RetroArch was not installed: {error}" msgstr "" -#: services/js_i18n_strings.py:782 +#: services/js_i18n_strings.py:808 msgid "RetroDB default" msgstr "" -#: services/js_i18n_strings.py:783 +#: services/js_i18n_strings.py:809 msgid "Run-ahead, 1 frame" msgstr "" -#: services/js_i18n_strings.py:784 +#: services/js_i18n_strings.py:810 msgid "Running" msgstr "" -#: services/js_i18n_strings.py:785 +#: services/js_i18n_strings.py:811 msgid "Running..." msgstr "" -#: services/js_i18n_strings.py:786 +#: services/js_i18n_strings.py:812 msgid "Same IGDB game" msgstr "" -#: services/js_i18n_strings.py:787 +#: services/js_i18n_strings.py:813 msgid "Same ScreenScraper game" msgstr "" -#: services/js_i18n_strings.py:788 +#: services/js_i18n_strings.py:814 msgid "Same title, different region" msgstr "" -#: services/js_i18n_strings.py:789 +#: services/js_i18n_strings.py:815 msgid "Save Failed" msgstr "" -#: services/js_i18n_strings.py:790 +#: services/js_i18n_strings.py:816 #, python-brace-format msgid "Save failed: {error}" msgstr "" -#: services/js_i18n_strings.py:791 +#: services/js_i18n_strings.py:817 msgid "Saved" msgstr "" -#: services/js_i18n_strings.py:792 +#: services/js_i18n_strings.py:818 msgid "Saved." msgstr "" -#: services/js_i18n_strings.py:793 +#: services/js_i18n_strings.py:819 msgid "Saved. It applies the next time you press Play." msgstr "" -#: services/js_i18n_strings.py:794 +#: services/js_i18n_strings.py:820 msgid "Saving to the database…" msgstr "" -#: services/js_i18n_strings.py:795 +#: services/js_i18n_strings.py:821 msgid "Saving..." msgstr "" -#: services/js_i18n_strings.py:796 +#: services/js_i18n_strings.py:822 msgid "Saving…" msgstr "" -#: services/js_i18n_strings.py:797 +#: services/js_i18n_strings.py:823 #, python-brace-format msgid "Scan failed (HTTP {status})" msgstr "" -#: services/js_i18n_strings.py:798 +#: services/js_i18n_strings.py:824 #, python-brace-format msgid "Scan failed: {error}" msgstr "" -#: services/js_i18n_strings.py:799 +#: services/js_i18n_strings.py:825 #, python-brace-format msgid "Scanned {paths} paths · found {found} matches." msgstr "" -#: services/js_i18n_strings.py:800 templates/multi_disc_organizer.html:204 +#: services/js_i18n_strings.py:826 templates/multi_disc_organizer.html:204 msgid "Scanning..." msgstr "" -#: services/js_i18n_strings.py:801 +#: services/js_i18n_strings.py:827 msgid "Scanning…" msgstr "" -#: services/js_i18n_strings.py:802 +#: services/js_i18n_strings.py:828 msgid "Scrape game" msgstr "" -#: services/js_i18n_strings.py:803 +#: services/js_i18n_strings.py:829 #, python-brace-format msgid "Scraped 0/{total} - 0%" msgstr "" -#: services/js_i18n_strings.py:804 +#: services/js_i18n_strings.py:830 #, python-brace-format msgid "Scraped {processed} / {total} - {percent}%" msgstr "" -#: services/js_i18n_strings.py:805 templates/logs.html:710 +#: services/js_i18n_strings.py:831 templates/logs.html:710 #: templates/wishlist.html:507 msgid "Scraping" msgstr "" -#: services/js_i18n_strings.py:806 +#: services/js_i18n_strings.py:832 msgid "Screen effect" msgstr "" -#: services/js_i18n_strings.py:807 templates/logs.html:781 +#: services/js_i18n_strings.py:833 templates/logs.html:781 #: templates/museum_system.html:197 templates/museum_system.html:204 #: templates/psn_trophy_detail.html:320 msgid "Search" msgstr "" -#: services/js_i18n_strings.py:808 +#: services/js_i18n_strings.py:834 msgid "Search failed." msgstr "" -#: services/js_i18n_strings.py:809 +#: services/js_i18n_strings.py:835 msgid "Search failed. Try again." msgstr "" -#: services/js_i18n_strings.py:810 +#: services/js_i18n_strings.py:836 msgid "Searching & removing background..." msgstr "" -#: services/js_i18n_strings.py:811 +#: services/js_i18n_strings.py:837 #, python-brace-format msgid "Searching {source}…" msgstr "" -#: services/js_i18n_strings.py:812 +#: services/js_i18n_strings.py:838 #, python-brace-format msgid "See all {n} results" msgstr "" -#: services/js_i18n_strings.py:813 +#: services/js_i18n_strings.py:839 msgid "Select Developer" msgstr "" -#: services/js_i18n_strings.py:814 +#: services/js_i18n_strings.py:840 msgid "Select Dimension" msgstr "" -#: services/js_i18n_strings.py:815 +#: services/js_i18n_strings.py:841 #: templates/_modals/select_filter_modal.html:15 msgid "Select Filter" msgstr "" -#: services/js_i18n_strings.py:816 +#: services/js_i18n_strings.py:842 msgid "Select Genre" msgstr "" -#: services/js_i18n_strings.py:817 +#: services/js_i18n_strings.py:843 msgid "Select Perspective" msgstr "" -#: services/js_i18n_strings.py:818 +#: services/js_i18n_strings.py:844 msgid "Select Play Mode" msgstr "" -#: services/js_i18n_strings.py:819 +#: services/js_i18n_strings.py:845 msgid "Select Publisher" msgstr "" -#: services/js_i18n_strings.py:820 +#: services/js_i18n_strings.py:846 msgid "Select Rating" msgstr "" -#: services/js_i18n_strings.py:821 +#: services/js_i18n_strings.py:847 msgid "Select Series" msgstr "" -#: services/js_i18n_strings.py:822 +#: services/js_i18n_strings.py:848 msgid "Select Source" msgstr "" -#: services/js_i18n_strings.py:823 +#: services/js_i18n_strings.py:849 #, python-brace-format msgid "Selected {count} games across all pages" msgstr "" -#: services/js_i18n_strings.py:824 +#: services/js_i18n_strings.py:850 #, python-brace-format msgid "Selected {count} unscraped games across all pages" msgstr "" -#: services/js_i18n_strings.py:825 +#: services/js_i18n_strings.py:851 msgid "Server restarted successfully!" msgstr "" -#: services/js_i18n_strings.py:826 +#: services/js_i18n_strings.py:852 msgid "Server stopped. You can close this tab." msgstr "" -#: services/js_i18n_strings.py:827 +#: services/js_i18n_strings.py:853 msgid "Set for this game" msgstr "" -#: services/js_i18n_strings.py:828 +#: services/js_i18n_strings.py:854 msgid "Set inside the emulator; RetroDB leaves it alone" msgstr "" -#: services/js_i18n_strings.py:829 +#: services/js_i18n_strings.py:855 msgid "Set per system, in Settings > Emulators" msgstr "" -#: services/js_i18n_strings.py:830 templates/_macros/nav_tabs.html:44 +#: services/js_i18n_strings.py:856 templates/_macros/nav_tabs.html:44 #: templates/base.html:186 templates/rom_tools_hub.html:119 #: templates/settings.html:5 templates/settings.html:12 #: templates/settings_emulators.html:4 msgid "Settings" msgstr "" -#: services/js_i18n_strings.py:831 +#: services/js_i18n_strings.py:857 msgid "Show keyboard shortcuts" msgstr "" -#: services/js_i18n_strings.py:832 +#: services/js_i18n_strings.py:858 msgid "Showing Bonus Discs" msgstr "" -#: services/js_i18n_strings.py:833 +#: services/js_i18n_strings.py:859 templates/game_detail.html:239 +msgid "Shows a black screen with a frame rate in the window title." +msgstr "" + +#: services/js_i18n_strings.py:860 templates/game_detail.html:238 +msgid "Shows an image but does not get past the menus." +msgstr "" + +#: services/js_i18n_strings.py:861 msgid "Shutting down server..." msgstr "" -#: services/js_i18n_strings.py:834 templates/_modals/edit_modal.html:50 -#: templates/base.html:1098 +#: services/js_i18n_strings.py:862 templates/_modals/edit_modal.html:50 +#: templates/base.html:1099 msgid "Standalone" msgstr "" -#: services/js_i18n_strings.py:835 +#: services/js_i18n_strings.py:863 msgid "Starting..." msgstr "" -#: services/js_i18n_strings.py:836 +#: services/js_i18n_strings.py:864 msgid "Starting…" msgstr "" -#: services/js_i18n_strings.py:837 +#: services/js_i18n_strings.py:865 msgid "Steam Imports" msgstr "" -#: services/js_i18n_strings.py:838 +#: services/js_i18n_strings.py:866 #, python-brace-format msgid "Stopped. Names suggested: {proposals}." msgstr "" -#: services/js_i18n_strings.py:839 +#: services/js_i18n_strings.py:867 #, python-brace-format msgid "Successfully updated {count} games" msgstr "" -#: services/js_i18n_strings.py:840 +#: services/js_i18n_strings.py:868 #, python-brace-format msgid "Suggested names waiting: {count}. Untick any you do not want, then accept." msgstr "" -#: services/js_i18n_strings.py:841 +#: services/js_i18n_strings.py:869 msgid "Summary written." msgstr "" -#: services/js_i18n_strings.py:842 +#: services/js_i18n_strings.py:870 msgid "Sync In Progress" msgstr "" -#: services/js_i18n_strings.py:843 templates/achievements_system.html:105 +#: services/js_i18n_strings.py:871 templates/achievements_system.html:105 msgid "Sync Progress" msgstr "" -#: services/js_i18n_strings.py:844 +#: services/js_i18n_strings.py:872 msgid "Sync Queued" msgstr "" -#: services/js_i18n_strings.py:845 +#: services/js_i18n_strings.py:873 msgid "Sync complete! Refresh page to see updated progress." msgstr "" -#: services/js_i18n_strings.py:846 +#: services/js_i18n_strings.py:874 msgid "Sync failed" msgstr "" -#: services/js_i18n_strings.py:847 +#: services/js_i18n_strings.py:875 #, python-brace-format msgid "Sync: {system}" msgstr "" -#: services/js_i18n_strings.py:848 +#: services/js_i18n_strings.py:876 msgid "Syncing..." msgstr "" -#: services/js_i18n_strings.py:849 +#: services/js_i18n_strings.py:877 #, python-brace-format msgid "Syncing: {processed} / {total}" msgstr "" -#: services/js_i18n_strings.py:850 +#: services/js_i18n_strings.py:878 #: templates/_settings_tabs/customization.html:299 #: templates/achievement_game.html:228 templates/analytics.html:569 #: templates/analytics.html:603 templates/compare_games.html:69 @@ -3185,7 +3248,7 @@ msgstr "" msgid "System" msgstr "" -#: services/js_i18n_strings.py:851 +#: services/js_i18n_strings.py:879 #: templates/_settings_tabs/customization.html:153 #: templates/_settings_tabs/customization.html:234 #: templates/_settings_tabs/data.html:19 templates/achievements_system.html:49 @@ -3194,125 +3257,125 @@ msgstr "" msgid "Systems" msgstr "" -#: services/js_i18n_strings.py:852 +#: services/js_i18n_strings.py:880 msgid "The core installed but the game still cannot start." msgstr "" -#: services/js_i18n_strings.py:853 +#: services/js_i18n_strings.py:881 #, python-brace-format msgid "The run stopped: {error}. Names already suggested are kept." msgstr "" -#: services/js_i18n_strings.py:854 +#: services/js_i18n_strings.py:882 msgid "This game is already running. Kill the existing process and relaunch?" msgstr "" -#: services/js_i18n_strings.py:855 +#: services/js_i18n_strings.py:883 msgid "This system is already being synced." msgstr "" -#: services/js_i18n_strings.py:856 +#: services/js_i18n_strings.py:884 msgid "" "This will clear ALL RetroAchievements game IDs and progress data from " "your database. You will need to run \"Refresh RetroAchievements\" again " "afterwards to re-scan your games. Continue?" msgstr "" -#: services/js_i18n_strings.py:857 +#: services/js_i18n_strings.py:885 #, python-brace-format msgid "" "This will clear RetroAchievements game IDs and progress data for " "\"{system}\". Continue?" msgstr "" -#: services/js_i18n_strings.py:858 +#: services/js_i18n_strings.py:886 #, python-brace-format msgid "This will remove \"{system}\" from the Achievements page. Continue?" msgstr "" -#: services/js_i18n_strings.py:859 +#: services/js_i18n_strings.py:887 msgid "" "This will remove ALL games imported via CLZ Games Import from the " "database. This cannot be undone. Continue?" msgstr "" -#: services/js_i18n_strings.py:860 +#: services/js_i18n_strings.py:888 msgid "" "This will remove database entries for ROM files that no longer exist on " "disk. This cannot be undone. Continue?" msgstr "" -#: services/js_i18n_strings.py:861 +#: services/js_i18n_strings.py:889 msgid "" "This will scan all games and update their RetroAchievements status. This " "may take several minutes for large collections. Continue?" msgstr "" -#: services/js_i18n_strings.py:862 +#: services/js_i18n_strings.py:890 msgid "" "This will search for and download images for all controllers without " "images. Continue?" msgstr "" -#: services/js_i18n_strings.py:863 +#: services/js_i18n_strings.py:891 msgid "" "This will use your configured AI provider to generate history and top " "games for all systems. This may take several minutes and use API credits." " Continue?" msgstr "" -#: services/js_i18n_strings.py:864 +#: services/js_i18n_strings.py:892 msgid "Tick at least one name." msgstr "" -#: services/js_i18n_strings.py:865 +#: services/js_i18n_strings.py:893 msgid "Tick at least two games." msgstr "" -#: services/js_i18n_strings.py:866 templates/logs.html:745 +#: services/js_i18n_strings.py:894 templates/logs.html:745 msgid "To" msgstr "" -#: services/js_i18n_strings.py:867 +#: services/js_i18n_strings.py:895 msgid "Try adjusting your search or filters" msgstr "" -#: services/js_i18n_strings.py:868 +#: services/js_i18n_strings.py:896 msgid "USA" msgstr "" -#: services/js_i18n_strings.py:869 +#: services/js_i18n_strings.py:897 msgid "Unexpected error" msgstr "" -#: services/js_i18n_strings.py:870 +#: services/js_i18n_strings.py:898 msgid "Unhandled rejection" msgstr "" -#: services/js_i18n_strings.py:871 templates/analytics.html:583 -#: templates/base.html:1198 templates/local_trophy_detail.html:220 +#: services/js_i18n_strings.py:899 templates/analytics.html:583 +#: templates/base.html:1199 templates/local_trophy_detail.html:220 #: templates/local_trophy_detail.html:224 msgid "Unknown" msgstr "" -#: services/js_i18n_strings.py:872 +#: services/js_i18n_strings.py:900 msgid "Unknown error" msgstr "" -#: services/js_i18n_strings.py:873 +#: services/js_i18n_strings.py:901 msgid "Unknown error occurred." msgstr "" -#: services/js_i18n_strings.py:874 templates/all_games.html:102 +#: services/js_i18n_strings.py:902 templates/all_games.html:102 #: templates/system_games.html:93 templates/wishlist.html:506 msgid "Unscraped" msgstr "" -#: services/js_i18n_strings.py:875 +#: services/js_i18n_strings.py:903 msgid "Update Failed" msgstr "" -#: services/js_i18n_strings.py:876 +#: services/js_i18n_strings.py:904 #, python-brace-format msgid "" "Update {count} game(s)?\n" @@ -3320,248 +3383,258 @@ msgid "" "Fields: {fields}" msgstr "" -#: services/js_i18n_strings.py:877 templates/museum_system.html:198 +#: services/js_i18n_strings.py:905 templates/museum_system.html:198 #: templates/museum_system.html:205 msgid "Upload" msgstr "" -#: services/js_i18n_strings.py:878 +#: services/js_i18n_strings.py:906 msgid "Upload Controller Image" msgstr "" -#: services/js_i18n_strings.py:879 +#: services/js_i18n_strings.py:907 msgid "Upload failed" msgstr "" -#: services/js_i18n_strings.py:880 +#: services/js_i18n_strings.py:908 msgid "Upload replacement image" msgstr "" -#: services/js_i18n_strings.py:881 +#: services/js_i18n_strings.py:909 msgid "Uploading & removing background…" msgstr "" -#: services/js_i18n_strings.py:882 +#: services/js_i18n_strings.py:910 msgid "Uploading…" msgstr "" -#: services/js_i18n_strings.py:883 +#: services/js_i18n_strings.py:911 msgid "Use the system setting" msgstr "" -#: services/js_i18n_strings.py:884 +#: services/js_i18n_strings.py:912 #, python-brace-format msgid "" "Used for every {system} game unless the game sets its own. Applied when " "you press Play." msgstr "" -#: services/js_i18n_strings.py:885 +#: services/js_i18n_strings.py:913 +#, python-brace-format +msgid "User Score ({count} ratings)" +msgstr "" + +#: services/js_i18n_strings.py:914 msgid "Version unlinked." msgstr "" -#: services/js_i18n_strings.py:886 +#: services/js_i18n_strings.py:915 msgid "Versions linked." msgstr "" -#: services/js_i18n_strings.py:887 +#: services/js_i18n_strings.py:916 msgid "Will change?" msgstr "" -#: services/js_i18n_strings.py:888 +#: services/js_i18n_strings.py:917 msgid "Works" msgstr "" -#: services/js_i18n_strings.py:889 +#: services/js_i18n_strings.py:918 msgid "World" msgstr "" -#: services/js_i18n_strings.py:890 templates/game_detail.html:755 +#: services/js_i18n_strings.py:919 templates/game_detail.html:801 msgid "Write again with AI" msgstr "" -#: services/js_i18n_strings.py:891 +#: services/js_i18n_strings.py:920 msgid "Xbox Imports" msgstr "" -#: services/js_i18n_strings.py:892 templates/_bulk_edit_modal.html:170 -#: templates/_modals/edit_modal.html:143 templates/game_detail.html:609 -#: templates/game_detail.html:666 templates/game_detail.html:734 +#: services/js_i18n_strings.py:921 templates/_bulk_edit_modal.html:170 +#: templates/_modals/edit_modal.html:143 templates/game_detail.html:655 +#: templates/game_detail.html:712 templates/game_detail.html:780 msgid "Yes" msgstr "" -#: services/js_i18n_strings.py:893 +#: services/js_i18n_strings.py:922 #, python-brace-format msgid "" "Your bulk scrape has been queued (position {position}). It will start " "automatically when the current scrape finishes." msgstr "" -#: services/js_i18n_strings.py:894 +#: services/js_i18n_strings.py:923 msgid "found" msgstr "" -#: services/js_i18n_strings.py:895 +#: services/js_i18n_strings.py:924 msgid "in queue" msgstr "" -#: services/js_i18n_strings.py:896 +#: services/js_i18n_strings.py:925 msgid "system" msgstr "" -#: services/js_i18n_strings.py:897 +#: services/js_i18n_strings.py:926 msgid "unknown" msgstr "" -#: services/js_i18n_strings.py:898 +#: services/js_i18n_strings.py:927 #, python-brace-format msgid "{confidence}% match" msgstr "" -#: services/js_i18n_strings.py:899 +#: services/js_i18n_strings.py:928 #, python-brace-format msgid "{count} Achievements" msgstr "" -#: services/js_i18n_strings.py:900 +#: services/js_i18n_strings.py:929 #, python-brace-format msgid "{count} regional versions" msgstr "" -#: services/js_i18n_strings.py:901 +#: services/js_i18n_strings.py:930 +#, python-brace-format +msgid "{count} sold" +msgstr "" + +#: services/js_i18n_strings.py:931 #, python-brace-format msgid "{count} suggested groups. Untick any game that does not belong, then link." msgstr "" -#: services/js_i18n_strings.py:902 +#: services/js_i18n_strings.py:932 #, python-brace-format msgid "{fetched} fetched, {skipped} skipped, {failed} failed" msgstr "" -#: services/js_i18n_strings.py:903 +#: services/js_i18n_strings.py:933 #, python-brace-format msgid "{job} Queued (#{position})" msgstr "" -#: services/js_i18n_strings.py:904 +#: services/js_i18n_strings.py:934 #, python-brace-format msgid "{job} cancelled" msgstr "" -#: services/js_i18n_strings.py:905 +#: services/js_i18n_strings.py:935 #, python-brace-format msgid "{job} completed ({n} processed)" msgstr "" -#: services/js_i18n_strings.py:906 +#: services/js_i18n_strings.py:936 #, python-brace-format msgid "{job} failed: {error}" msgstr "" -#: services/js_i18n_strings.py:907 +#: services/js_i18n_strings.py:937 #, python-brace-format msgid "{job}: {current} of {total} ({percent}%)" msgstr "" -#: services/js_i18n_strings.py:908 +#: services/js_i18n_strings.py:938 #, python-brace-format msgid "{n} Player(s)" msgstr "" -#: services/js_i18n_strings.py:909 +#: services/js_i18n_strings.py:939 #, python-brace-format msgid "{n} cores" msgstr "" -#: services/js_i18n_strings.py:910 +#: services/js_i18n_strings.py:940 #, python-brace-format msgid "{n} games" msgstr "" -#: services/js_i18n_strings.py:911 +#: services/js_i18n_strings.py:941 #, python-brace-format msgid "{n} lines" msgstr "" -#: services/js_i18n_strings.py:912 +#: services/js_i18n_strings.py:942 #, python-brace-format msgid "{n} results" msgstr "" -#: services/js_i18n_strings.py:913 +#: services/js_i18n_strings.py:943 #, python-brace-format msgid "{points} pts" msgstr "" -#: services/js_i18n_strings.py:914 +#: services/js_i18n_strings.py:944 #, python-brace-format msgid "{system} has been added to the sync queue." msgstr "" -#: services/js_i18n_strings.py:915 +#: services/js_i18n_strings.py:945 #, python-brace-format msgid "{system} · {file} · old name from: {region}" msgstr "" -#: services/js_i18n_strings.py:916 +#: services/js_i18n_strings.py:946 #, python-brace-format msgid "{title} ({count} versions)" msgstr "" -#: services/js_i18n_strings.py:917 +#: services/js_i18n_strings.py:947 msgid "⏳ Searching..." msgstr "" -#: services/js_i18n_strings.py:918 +#: services/js_i18n_strings.py:948 #, python-brace-format msgid "⚠️ Cancel {job}" msgstr "" -#: services/js_i18n_strings.py:919 +#: services/js_i18n_strings.py:949 msgid "⚠️ Confirm" msgstr "" -#: services/js_i18n_strings.py:920 +#: services/js_i18n_strings.py:950 msgid "⚡ via" msgstr "" -#: services/js_i18n_strings.py:921 +#: services/js_i18n_strings.py:951 msgid "✕ Clear" msgstr "" -#: services/js_i18n_strings.py:922 +#: services/js_i18n_strings.py:952 msgid "🏆 Refresh RetroAchievements" msgstr "" -#: services/js_i18n_strings.py:923 +#: services/js_i18n_strings.py:953 msgid "📋 Clear CLZ Imports" msgstr "" -#: services/js_i18n_strings.py:924 +#: services/js_i18n_strings.py:954 msgid "🔄 Restart Server" msgstr "" -#: services/js_i18n_strings.py:925 +#: services/js_i18n_strings.py:955 msgid "🔍 Lookup HLTB" msgstr "" -#: services/js_i18n_strings.py:926 +#: services/js_i18n_strings.py:956 msgid "🗑️ Clear HLTB Data" msgstr "" -#: services/js_i18n_strings.py:927 +#: services/js_i18n_strings.py:957 msgid "🗑️ Clear RetroAchievements Data" msgstr "" -#: services/js_i18n_strings.py:928 +#: services/js_i18n_strings.py:958 msgid "🗑️ Reset Metadata" msgstr "" -#: services/js_i18n_strings.py:929 +#: services/js_i18n_strings.py:959 msgid "🛑 Shut Down Server" msgstr "" -#: services/js_i18n_strings.py:930 +#: services/js_i18n_strings.py:960 msgid "🧹 Clean Missing ROMs" msgstr "" @@ -3614,8 +3687,8 @@ msgid "Only fields changed from \"Don't change\" will be updated." msgstr "" #: templates/_bulk_edit_modal.html:26 templates/achievements_system.html:171 -#: templates/analytics.html:1664 templates/base.html:1010 -#: templates/dashboard.html:491 templates/game_detail.html:219 +#: templates/analytics.html:1664 templates/base.html:1011 +#: templates/dashboard.html:491 templates/game_detail.html:254 #: templates/steam_achievements.html:78 templates/xbox_achievements.html:78 msgid "Not Started" msgstr "" @@ -3624,9 +3697,9 @@ msgstr "" #: templates/_settings_tabs/data.html:83 templates/_settings_tabs/data.html:156 #: templates/achievement_game.html:241 templates/all_games.html:72 #: templates/analytics.html:481 templates/analytics.html:2428 -#: templates/base.html:940 templates/base.html:1148 +#: templates/base.html:940 templates/base.html:1149 #: templates/compare_games.html:73 templates/dashboard.html:381 -#: templates/game_detail.html:265 templates/local_trophy_detail.html:99 +#: templates/game_detail.html:300 templates/local_trophy_detail.html:99 #: templates/psn_trophy_detail.html:94 templates/psn_trophy_detail.html:258 #: templates/steam_achievement_game.html:178 templates/system_games.html:63 #: templates/xbox_achievement_game.html:171 @@ -3643,9 +3716,9 @@ msgstr "" #: templates/_bulk_edit_modal.html:53 templates/_modals/edit_modal.html:94 #: templates/achievement_game.html:81 templates/all_games.html:78 #: templates/analytics.html:483 templates/analytics.html:2428 -#: templates/base.html:939 templates/base.html:1134 -#: templates/compare_games.html:71 templates/game_detail.html:241 -#: templates/game_detail.html:712 templates/local_trophy_detail.html:87 +#: templates/base.html:939 templates/base.html:1135 +#: templates/compare_games.html:71 templates/game_detail.html:276 +#: templates/game_detail.html:758 templates/local_trophy_detail.html:87 #: templates/psn_trophy_detail.html:82 templates/steam_achievement_game.html:78 #: templates/system_games.html:69 templates/xbox_achievement_game.html:67 msgid "Publisher" @@ -3660,8 +3733,8 @@ msgstr "" #: templates/_bulk_edit_modal.html:67 templates/_modals/edit_modal.html:99 #: templates/achievement_game.html:87 templates/all_games.html:81 #: templates/analytics.html:482 templates/analytics.html:2428 -#: templates/base.html:938 templates/base.html:1138 -#: templates/compare_games.html:70 templates/game_detail.html:253 +#: templates/base.html:938 templates/base.html:1139 +#: templates/compare_games.html:70 templates/game_detail.html:288 #: templates/local_trophy_detail.html:93 templates/psn_trophy_detail.html:88 #: templates/steam_achievement_game.html:84 templates/system_games.html:72 #: templates/xbox_achievement_game.html:73 @@ -3673,23 +3746,23 @@ msgid "ESRB Rating" msgstr "" #: templates/_bulk_edit_modal.html:84 templates/_modals/edit_modal.html:240 -#: templates/base.html:1251 +#: templates/base.html:1252 msgid "E - Everyone" msgstr "" #: templates/_bulk_edit_modal.html:85 templates/_modals/edit_modal.html:241 -#: templates/base.html:1252 +#: templates/base.html:1253 msgid "E10+ - Everyone 10+" msgstr "" #: templates/_bulk_edit_modal.html:86 templates/_modals/edit_modal.html:242 -#: templates/base.html:1253 +#: templates/base.html:1254 msgid "T - Teen" msgstr "" #: templates/_bulk_edit_modal.html:87 templates/_modals/edit_modal.html:243 -#: templates/_modals/edit_modal.html:287 templates/base.html:1254 -#: templates/base.html:1298 +#: templates/_modals/edit_modal.html:287 templates/base.html:1255 +#: templates/base.html:1299 msgid "M - Mature" msgstr "" @@ -3719,33 +3792,33 @@ msgstr "" #: templates/_bulk_edit_modal.html:106 templates/_modals/edit_modal.html:85 #: templates/analytics.html:492 templates/analytics.html:2428 -#: templates/base.html:1125 templates/compare_games.html:75 -#: templates/game_detail.html:709 templates/reports.html:444 +#: templates/base.html:1126 templates/compare_games.html:75 +#: templates/game_detail.html:755 templates/reports.html:444 msgid "Region" msgstr "" #: templates/_bulk_edit_modal.html:112 templates/_modals/edit_modal.html:132 -#: templates/all_games.html:84 templates/base.html:1164 -#: templates/compare_games.html:76 templates/game_detail.html:201 +#: templates/all_games.html:84 templates/base.html:1165 +#: templates/compare_games.html:76 templates/game_detail.html:215 #: templates/system_games.html:75 msgid "Players" msgstr "" #: templates/_bulk_edit_modal.html:118 templates/_modals/edit_modal.html:149 -#: templates/_settings_tabs/data.html:93 templates/base.html:1168 +#: templates/_settings_tabs/data.html:93 templates/base.html:1169 msgid "Game Structure" msgstr "" #: templates/_bulk_edit_modal.html:135 templates/_modals/edit_modal.html:162 #: templates/_settings_tabs/data.html:103 templates/all_games.html:87 -#: templates/analytics.html:199 templates/base.html:1176 +#: templates/analytics.html:199 templates/base.html:1177 #: templates/compare_games.html:78 templates/system_games.html:78 msgid "Perspective" msgstr "" #: templates/_bulk_edit_modal.html:151 templates/_modals/edit_modal.html:173 #: templates/_settings_tabs/data.html:113 templates/all_games.html:90 -#: templates/analytics.html:207 templates/base.html:1184 +#: templates/analytics.html:207 templates/base.html:1185 #: templates/compare_games.html:79 templates/system_games.html:81 msgid "Dimension" msgstr "" @@ -3794,7 +3867,7 @@ msgstr "" #: templates/achievement_game.html:17 templates/achievement_game.html:37 #: templates/achievements_system.html:15 templates/game_detail.html:21 -#: templates/game_detail.html:296 templates/game_detail.html:717 +#: templates/game_detail.html:342 templates/game_detail.html:763 #: templates/systems.html:43 msgid "RetroAchievements" msgstr "" @@ -3810,7 +3883,7 @@ msgid "Refresh RA" msgstr "" #: templates/achievement_game.html:93 templates/all_games.html:75 -#: templates/game_detail.html:277 templates/system_games.html:66 +#: templates/game_detail.html:312 templates/system_games.html:66 msgid "Series" msgstr "" @@ -3837,23 +3910,23 @@ msgstr "" msgid "Check your RetroAchievements credentials in Settings." msgstr "" -#: templates/achievement_game.html:146 templates/game_detail.html:486 +#: templates/achievement_game.html:146 templates/game_detail.html:532 #: templates/local_trophy_detail.html:158 templates/psn_trophy_detail.html:149 #: templates/steam_achievement_game.html:123 #: templates/xbox_achievement_game.html:116 msgid "How Long to Beat" msgstr "" -#: templates/achievement_game.html:153 templates/base.html:1023 -#: templates/game_detail.html:493 templates/psn_trophy_detail.html:157 +#: templates/achievement_game.html:153 templates/base.html:1024 +#: templates/game_detail.html:539 templates/psn_trophy_detail.html:157 #: templates/psn_trophy_detail.html:317 #: templates/steam_achievement_game.html:126 #: templates/xbox_achievement_game.html:119 msgid "Search game title..." msgstr "" -#: templates/achievement_game.html:156 templates/base.html:1026 -#: templates/game_detail.html:496 templates/local_trophy_detail.html:193 +#: templates/achievement_game.html:156 templates/base.html:1027 +#: templates/game_detail.html:542 templates/local_trophy_detail.html:193 #: templates/psn_trophy_detail.html:160 #: templates/steam_achievement_game.html:127 #: templates/xbox_achievement_game.html:120 @@ -3861,8 +3934,8 @@ msgid "Lookup HLTB" msgstr "" #: templates/_settings_tabs/account.html:73 templates/achievement_game.html:177 -#: templates/achievement_game.html:253 templates/base.html:1054 -#: templates/game_detail.html:517 templates/local_trophy_detail.html:223 +#: templates/achievement_game.html:253 templates/base.html:1055 +#: templates/game_detail.html:563 templates/local_trophy_detail.html:223 #: templates/psn_trophy_detail.html:181 templates/psn_trophy_detail.html:270 #: templates/settings_emulators.html:36 templates/settings_emulators.html:187 #: templates/steam_achievement_game.html:137 @@ -3871,8 +3944,8 @@ msgid "Save" msgstr "" #: templates/achievement_game.html:217 templates/all_games.html:108 -#: templates/base.html:1067 templates/duplicate_finder.html:301 -#: templates/game_detail.html:101 templates/game_detail.html:557 +#: templates/base.html:1068 templates/duplicate_finder.html:301 +#: templates/game_detail.html:115 templates/game_detail.html:603 #: templates/local_trophy_detail.html:185 templates/psn_trophy_detail.html:214 #: templates/reports.html:277 templates/screenshot_dedup.html:325 #: templates/steam_achievement_game.html:163 templates/system_games.html:99 @@ -3880,7 +3953,7 @@ msgstr "" msgid "Clear" msgstr "" -#: templates/achievement_game.html:225 templates/game_detail.html:565 +#: templates/achievement_game.html:225 templates/game_detail.html:611 #: templates/local_trophy_detail.html:203 templates/psn_trophy_detail.html:234 #: templates/steam_achievement_game.html:170 #: templates/xbox_achievement_game.html:163 @@ -4248,7 +4321,7 @@ msgstr "" msgid "Select Unscraped" msgstr "" -#: templates/all_games.html:204 templates/game_detail.html:783 +#: templates/all_games.html:204 templates/game_detail.html:829 #: templates/system_games.html:184 msgid "Full Re-scrape" msgstr "" @@ -4257,7 +4330,7 @@ msgstr "" msgid "Toggle between full re-scrape and fill missing mode" msgstr "" -#: templates/all_games.html:209 templates/game_detail.html:783 +#: templates/all_games.html:209 templates/game_detail.html:829 #: templates/system_games.html:189 msgid "Fill Missing" msgstr "" @@ -4293,7 +4366,7 @@ msgid "Developers" msgstr "" #: templates/_modals/edit_modal.html:107 templates/analytics.html:13 -#: templates/base.html:998 templates/base.html:1145 +#: templates/base.html:999 templates/base.html:1146 msgid "Gameplay" msgstr "" @@ -4561,7 +4634,7 @@ msgstr "" #: templates/_settings_tabs/customization.html:193 #: templates/_settings_tabs/customization.html:259 templates/analytics.html:479 #: templates/analytics.html:2428 templates/base.html:266 -#: templates/base.html:1352 templates/compare_games.html:97 +#: templates/base.html:1353 templates/compare_games.html:97 #: templates/settings_emulators.html:170 msgid "Description" msgstr "" @@ -4575,7 +4648,7 @@ msgstr "" #: templates/_modals/edit_modal.html:72 templates/_modals/filter_modal.html:14 #: templates/analytics.html:484 templates/analytics.html:2428 -#: templates/base.html:1117 templates/compare_games.html:72 +#: templates/base.html:1118 templates/compare_games.html:72 msgid "Release Date" msgstr "" @@ -4588,12 +4661,12 @@ msgid "Fanart" msgstr "" #: templates/analytics.html:487 templates/analytics.html:2428 -#: templates/game_detail.html:439 +#: templates/game_detail.html:485 msgid "Video" msgstr "" #: templates/_modals/manual_reader_modal.html:9 templates/analytics.html:488 -#: templates/analytics.html:2428 templates/game_detail.html:95 +#: templates/analytics.html:2428 templates/game_detail.html:109 msgid "Manual" msgstr "" @@ -5009,7 +5082,7 @@ msgstr "" msgid "Log Viewer" msgstr "" -#: templates/base.html:198 templates/base.html:1000 +#: templates/base.html:198 templates/base.html:1001 msgid "Info" msgstr "" @@ -5109,269 +5182,273 @@ msgstr "" msgid "Loading game details..." msgstr "" -#: templates/base.html:953 templates/game_detail.html:443 +#: templates/base.html:942 templates/game_detail.html:318 +msgid "Release dates" +msgstr "" + +#: templates/base.html:954 templates/game_detail.html:489 msgid "Your browser does not support the video tag." msgstr "" -#: templates/base.html:960 +#: templates/base.html:961 msgid "View Full Page" msgstr "" -#: templates/base.html:962 templates/game_detail.html:109 +#: templates/base.html:963 templates/game_detail.html:123 msgid "Fill missing metadata with AI" msgstr "" -#: templates/base.html:963 templates/game_detail.html:111 +#: templates/base.html:964 templates/game_detail.html:125 msgid "AI Fill" msgstr "" -#: templates/base.html:966 templates/game_detail.html:116 +#: templates/base.html:967 templates/game_detail.html:130 msgid "Scrape" msgstr "" -#: templates/_modals/screenshot_modal.html:2 templates/base.html:978 +#: templates/_modals/screenshot_modal.html:2 templates/base.html:979 msgid "Screenshot viewer" msgstr "" -#: templates/_modals/screenshot_modal.html:3 templates/base.html:979 +#: templates/_modals/screenshot_modal.html:3 templates/base.html:980 msgid "Close screenshot viewer" msgstr "" -#: templates/_modals/screenshot_modal.html:4 templates/base.html:980 +#: templates/_modals/screenshot_modal.html:4 templates/base.html:981 msgid "Previous screenshot" msgstr "" -#: templates/base.html:981 +#: templates/base.html:982 msgid "Screenshot" msgstr "" -#: templates/_modals/screenshot_modal.html:6 templates/base.html:982 +#: templates/_modals/screenshot_modal.html:6 templates/base.html:983 msgid "Next screenshot" msgstr "" -#: templates/base.html:983 +#: templates/base.html:984 msgid "Use ← → arrow keys to navigate • ESC to close" msgstr "" -#: templates/_modals/edit_modal.html:7 templates/base.html:990 +#: templates/_modals/edit_modal.html:7 templates/base.html:991 msgid "Close edit modal" msgstr "" -#: templates/base.html:993 +#: templates/base.html:994 msgid "Edit Game" msgstr "" -#: templates/base.html:994 +#: templates/base.html:995 msgid "Edit game sections" msgstr "" -#: templates/base.html:995 +#: templates/base.html:996 msgid "Quick" msgstr "" -#: templates/_modals/edit_modal.html:25 templates/base.html:996 -#: templates/base.html:1074 +#: templates/_modals/edit_modal.html:25 templates/base.html:997 +#: templates/base.html:1075 msgid "Identity" msgstr "" -#: templates/base.html:997 +#: templates/base.html:998 msgid "Release" msgstr "" -#: templates/_modals/edit_modal.html:187 templates/base.html:999 -#: templates/base.html:1206 +#: templates/_modals/edit_modal.html:187 templates/base.html:1000 +#: templates/base.html:1207 msgid "Technical" msgstr "" -#: templates/base.html:1006 +#: templates/base.html:1007 msgid "Quick edit" msgstr "" -#: templates/base.html:1019 +#: templates/base.html:1020 msgid "HowLongToBeat" msgstr "" -#: templates/base.html:1033 +#: templates/base.html:1034 msgid "Searching HowLongToBeat..." msgstr "" -#: templates/base.html:1064 templates/local_trophy_detail.html:189 +#: templates/base.html:1065 templates/local_trophy_detail.html:189 msgid "No playtime data" msgstr "" #: templates/_modals/edit_modal.html:28 templates/_modals/filter_modal.html:13 -#: templates/base.html:1077 templates/dashboard.html:320 +#: templates/base.html:1078 templates/dashboard.html:320 #: templates/dashboard.html:379 templates/dashboard.html:456 -#: templates/dashboard.html:522 templates/game_detail.html:710 +#: templates/dashboard.html:522 templates/game_detail.html:756 msgid "Title" msgstr "" -#: templates/_modals/edit_modal.html:32 templates/base.html:1081 +#: templates/_modals/edit_modal.html:32 templates/base.html:1082 msgid "Keep this title when scraping" msgstr "" -#: templates/_modals/edit_modal.html:36 templates/base.html:1085 +#: templates/_modals/edit_modal.html:36 templates/base.html:1086 msgid "Sort Title" msgstr "" -#: templates/_modals/edit_modal.html:38 templates/base.html:1087 +#: templates/_modals/edit_modal.html:38 templates/base.html:1088 msgid "Auto-generated from title" msgstr "" -#: templates/_modals/edit_modal.html:44 templates/base.html:1092 +#: templates/_modals/edit_modal.html:44 templates/base.html:1093 msgid "Franchise / Series" msgstr "" -#: templates/_modals/edit_modal.html:47 templates/base.html:1095 +#: templates/_modals/edit_modal.html:47 templates/base.html:1096 msgid "Toggle franchise status" msgstr "" -#: templates/_modals/edit_modal.html:52 templates/base.html:1100 +#: templates/_modals/edit_modal.html:52 templates/base.html:1101 msgid "e.g., Final Fantasy, Crash Bandicoot" msgstr "" -#: templates/_modals/edit_modal.html:56 templates/base.html:1103 -#: templates/compare_games.html:96 templates/game_detail.html:471 +#: templates/_modals/edit_modal.html:56 templates/base.html:1104 +#: templates/compare_games.html:96 templates/game_detail.html:517 msgid "Similar Games" msgstr "" -#: templates/_modals/edit_modal.html:57 templates/base.html:1104 +#: templates/_modals/edit_modal.html:57 templates/base.html:1105 msgid "Game 1, Game 2, Game 3" msgstr "" -#: templates/_modals/edit_modal.html:61 templates/base.html:1107 +#: templates/_modals/edit_modal.html:61 templates/base.html:1108 msgid "Edition" msgstr "" -#: templates/base.html:1108 +#: templates/base.html:1109 msgid "Standard, Collector's, GOTY" msgstr "" -#: templates/base.html:1114 +#: templates/base.html:1115 msgid "Release info" msgstr "" -#: templates/base.html:1119 +#: templates/base.html:1120 msgid "YYYY-MM-DD" msgstr "" -#: templates/_modals/edit_modal.html:80 templates/base.html:1121 +#: templates/_modals/edit_modal.html:80 templates/base.html:1122 msgid "Pick date" msgstr "" -#: templates/_modals/edit_modal.html:87 templates/base.html:1127 +#: templates/_modals/edit_modal.html:87 templates/base.html:1128 msgid "— Select —" msgstr "" -#: templates/_modals/edit_modal.html:95 templates/base.html:1135 +#: templates/_modals/edit_modal.html:95 templates/base.html:1136 msgid "Sony, Nintendo" msgstr "" -#: templates/base.html:1139 +#: templates/base.html:1140 msgid "Naughty Dog, Insomniac" msgstr "" -#: templates/_modals/edit_modal.html:114 templates/base.html:1151 +#: templates/_modals/edit_modal.html:114 templates/base.html:1152 msgid "Add genre" msgstr "" -#: templates/_modals/edit_modal.html:115 templates/base.html:1152 +#: templates/_modals/edit_modal.html:115 templates/base.html:1153 msgid "+ Add genre..." msgstr "" #: templates/_modals/edit_modal.html:121 templates/_settings_tabs/data.html:123 -#: templates/base.html:1156 +#: templates/base.html:1157 msgid "Play Modes" msgstr "" -#: templates/_modals/edit_modal.html:125 templates/base.html:1159 +#: templates/_modals/edit_modal.html:125 templates/base.html:1160 msgid "Add play mode" msgstr "" -#: templates/_modals/edit_modal.html:126 templates/base.html:1160 +#: templates/_modals/edit_modal.html:126 templates/base.html:1161 msgid "+ Add mode..." msgstr "" -#: templates/_modals/edit_modal.html:155 templates/base.html:1171 +#: templates/_modals/edit_modal.html:155 templates/base.html:1172 msgid "Add game structure" msgstr "" -#: templates/_modals/edit_modal.html:156 templates/base.html:1172 +#: templates/_modals/edit_modal.html:156 templates/base.html:1173 msgid "+ Add structure..." msgstr "" -#: templates/_modals/edit_modal.html:166 templates/base.html:1179 +#: templates/_modals/edit_modal.html:166 templates/base.html:1180 msgid "Add perspective" msgstr "" -#: templates/_modals/edit_modal.html:167 templates/base.html:1180 +#: templates/_modals/edit_modal.html:167 templates/base.html:1181 msgid "+ Add perspective..." msgstr "" -#: templates/_modals/edit_modal.html:177 templates/base.html:1187 +#: templates/_modals/edit_modal.html:177 templates/base.html:1188 msgid "Add dimension" msgstr "" -#: templates/_modals/edit_modal.html:178 templates/base.html:1188 +#: templates/_modals/edit_modal.html:178 templates/base.html:1189 msgid "+ Add dimension..." msgstr "" -#: templates/_modals/edit_modal.html:136 templates/base.html:1192 +#: templates/_modals/edit_modal.html:136 templates/base.html:1193 msgid "Campaign Mode" msgstr "" -#: templates/_modals/edit_modal.html:140 templates/base.html:1195 +#: templates/_modals/edit_modal.html:140 templates/base.html:1196 msgid "Toggle campaign mode" msgstr "" -#: templates/_modals/edit_modal.html:190 templates/base.html:1209 +#: templates/_modals/edit_modal.html:190 templates/base.html:1210 #: templates/compare_games.html:91 msgid "Controller Support" msgstr "" -#: templates/_modals/edit_modal.html:194 templates/base.html:1213 +#: templates/_modals/edit_modal.html:194 templates/base.html:1214 msgid "Add controller" msgstr "" -#: templates/_modals/edit_modal.html:195 templates/base.html:1214 +#: templates/_modals/edit_modal.html:195 templates/base.html:1215 msgid "+ Add controller..." msgstr "" -#: templates/_modals/edit_modal.html:199 templates/base.html:1216 +#: templates/_modals/edit_modal.html:199 templates/base.html:1217 msgid "Custom controller name" msgstr "" -#: templates/base.html:1216 +#: templates/base.html:1217 msgid "Or type custom..." msgstr "" #: templates/_modals/edit_modal.html:206 templates/_settings_tabs/data.html:133 -#: templates/base.html:1220 templates/compare_games.html:92 +#: templates/base.html:1221 templates/compare_games.html:92 msgid "Save Type" msgstr "" -#: templates/_modals/edit_modal.html:210 templates/base.html:1223 +#: templates/_modals/edit_modal.html:210 templates/base.html:1224 msgid "Add save type" msgstr "" -#: templates/_modals/edit_modal.html:211 templates/base.html:1224 +#: templates/_modals/edit_modal.html:211 templates/base.html:1225 msgid "+ Add save type..." msgstr "" -#: templates/_modals/edit_modal.html:217 templates/base.html:1228 +#: templates/_modals/edit_modal.html:217 templates/base.html:1229 msgid "Platform Availability" msgstr "" -#: templates/_modals/edit_modal.html:220 templates/base.html:1238 +#: templates/_modals/edit_modal.html:220 templates/base.html:1239 msgid "Toggle platform exclusivity" msgstr "" -#: templates/base.html:1243 +#: templates/base.html:1244 msgid "Xbox 360, PC, Switch" msgstr "" #: templates/_modals/edit_modal.html:236 -#: templates/_settings_tabs/library.html:152 templates/base.html:1247 +#: templates/_settings_tabs/library.html:152 templates/base.html:1248 msgid "ESRB (North America)" msgstr "" @@ -5379,142 +5456,142 @@ msgstr "" #: templates/_modals/edit_modal.html:262 templates/_modals/edit_modal.html:273 #: templates/_modals/edit_modal.html:284 templates/_modals/edit_modal.html:295 #: templates/_modals/edit_modal.html:309 templates/_modals/edit_modal.html:319 -#: templates/_modals/edit_modal.html:331 templates/base.html:1249 -#: templates/base.html:1262 templates/base.html:1273 templates/base.html:1284 -#: templates/base.html:1295 templates/base.html:1306 templates/base.html:1320 -#: templates/base.html:1330 templates/base.html:1342 +#: templates/_modals/edit_modal.html:331 templates/base.html:1250 +#: templates/base.html:1263 templates/base.html:1274 templates/base.html:1285 +#: templates/base.html:1296 templates/base.html:1307 templates/base.html:1321 +#: templates/base.html:1331 templates/base.html:1343 msgid "-- None --" msgstr "" -#: templates/_modals/edit_modal.html:239 templates/base.html:1250 +#: templates/_modals/edit_modal.html:239 templates/base.html:1251 msgid "EC - Early Childhood" msgstr "" -#: templates/_modals/edit_modal.html:244 templates/base.html:1255 +#: templates/_modals/edit_modal.html:244 templates/base.html:1256 msgid "AO - Adults Only" msgstr "" -#: templates/_modals/edit_modal.html:245 templates/base.html:1256 +#: templates/_modals/edit_modal.html:245 templates/base.html:1257 msgid "RP - Rating Pending" msgstr "" #: templates/_modals/edit_modal.html:249 -#: templates/_settings_tabs/library.html:153 templates/base.html:1260 +#: templates/_settings_tabs/library.html:153 templates/base.html:1261 msgid "PEGI (Europe)" msgstr "" #: templates/_modals/edit_modal.html:260 -#: templates/_settings_tabs/library.html:154 templates/base.html:1271 +#: templates/_settings_tabs/library.html:154 templates/base.html:1272 msgid "CERO (Japan)" msgstr "" #: templates/_modals/edit_modal.html:263 templates/_modals/edit_modal.html:296 -#: templates/base.html:1274 templates/base.html:1307 +#: templates/base.html:1275 templates/base.html:1308 msgid "A - All Ages" msgstr "" -#: templates/_modals/edit_modal.html:264 templates/base.html:1275 +#: templates/_modals/edit_modal.html:264 templates/base.html:1276 msgid "B - Ages 12+" msgstr "" -#: templates/_modals/edit_modal.html:265 templates/base.html:1276 +#: templates/_modals/edit_modal.html:265 templates/base.html:1277 msgid "C - Ages 15+" msgstr "" -#: templates/_modals/edit_modal.html:266 templates/base.html:1277 +#: templates/_modals/edit_modal.html:266 templates/base.html:1278 msgid "D - Ages 17+" msgstr "" -#: templates/_modals/edit_modal.html:267 templates/base.html:1278 +#: templates/_modals/edit_modal.html:267 templates/base.html:1279 msgid "Z - Ages 18+" msgstr "" #: templates/_modals/edit_modal.html:271 -#: templates/_settings_tabs/library.html:155 templates/base.html:1282 +#: templates/_settings_tabs/library.html:155 templates/base.html:1283 msgid "USK (Germany)" msgstr "" #: templates/_modals/edit_modal.html:282 -#: templates/_settings_tabs/library.html:156 templates/base.html:1293 +#: templates/_settings_tabs/library.html:156 templates/base.html:1294 msgid "ACB (Australia)" msgstr "" -#: templates/_modals/edit_modal.html:285 templates/base.html:1296 +#: templates/_modals/edit_modal.html:285 templates/base.html:1297 msgid "G - General" msgstr "" #: templates/_modals/edit_modal.html:286 templates/_modals/edit_modal.html:297 -#: templates/base.html:1297 +#: templates/base.html:1298 msgid "PG - Parental Guidance" msgstr "" -#: templates/_modals/edit_modal.html:288 templates/base.html:1299 +#: templates/_modals/edit_modal.html:288 templates/base.html:1300 msgid "MA15+ - Restricted" msgstr "" -#: templates/_modals/edit_modal.html:289 templates/base.html:1300 +#: templates/_modals/edit_modal.html:289 templates/base.html:1301 msgid "R18+ - Restricted" msgstr "" #: templates/_modals/edit_modal.html:293 -#: templates/_settings_tabs/library.html:157 templates/base.html:1304 +#: templates/_settings_tabs/library.html:157 templates/base.html:1305 msgid "FPB (South Africa)" msgstr "" #: templates/_modals/edit_modal.html:307 -#: templates/_settings_tabs/library.html:158 templates/base.html:1318 +#: templates/_settings_tabs/library.html:158 templates/base.html:1319 msgid "GRAC (South Korea)" msgstr "" -#: templates/_modals/edit_modal.html:310 templates/base.html:1321 +#: templates/_modals/edit_modal.html:310 templates/base.html:1322 msgid "ALL - All Ages" msgstr "" #: templates/_modals/edit_modal.html:317 -#: templates/_settings_tabs/library.html:159 templates/base.html:1328 +#: templates/_settings_tabs/library.html:159 templates/base.html:1329 msgid "ClassInd (Brazil)" msgstr "" -#: templates/_modals/edit_modal.html:320 templates/base.html:1331 +#: templates/_modals/edit_modal.html:320 templates/base.html:1332 msgid "L - General" msgstr "" #: templates/_modals/edit_modal.html:329 -#: templates/_settings_tabs/library.html:160 templates/base.html:1340 +#: templates/_settings_tabs/library.html:160 templates/base.html:1341 msgid "CADPA (China)" msgstr "" -#: templates/_modals/edit_modal.html:332 templates/base.html:1343 +#: templates/_modals/edit_modal.html:332 templates/base.html:1344 msgid "8+ - Ages 8 and up" msgstr "" -#: templates/_modals/edit_modal.html:333 templates/base.html:1344 +#: templates/_modals/edit_modal.html:333 templates/base.html:1345 msgid "12+ - Ages 12 and up" msgstr "" -#: templates/_modals/edit_modal.html:334 templates/base.html:1345 +#: templates/_modals/edit_modal.html:334 templates/base.html:1346 msgid "16+ - Ages 16 and up" msgstr "" -#: templates/_modals/edit_modal.html:343 templates/base.html:1354 +#: templates/_modals/edit_modal.html:343 templates/base.html:1355 msgid "Game Description" msgstr "" -#: templates/base.html:1355 +#: templates/base.html:1356 msgid "Enter game description..." msgstr "" -#: templates/base.html:1362 +#: templates/base.html:1363 msgid "Images/Video" msgstr "" #: templates/_modals/edit_modal.html:15 -#: templates/_settings_tabs/customization.html:267 templates/base.html:1367 +#: templates/_settings_tabs/customization.html:267 templates/base.html:1368 #: templates/wishlist.html:700 msgid "Save Changes" msgstr "" -#: templates/base.html:1403 +#: templates/base.html:1404 msgid "running" msgstr "" @@ -6221,363 +6298,396 @@ msgstr "" msgid "ROM Reports" msgstr "" -#: templates/game_detail.html:49 +#: templates/game_detail.html:34 +msgid "Browse games" +msgstr "" + +#: templates/_modals/manual_reader_modal.html:15 templates/game_detail.html:37 +msgid "Previous" +msgstr "" + +#: templates/_modals/manual_reader_modal.html:17 templates/game_detail.html:39 +msgid "Next" +msgstr "" + +#: templates/game_detail.html:63 msgid "CLZ Import - No ROM file" msgstr "" -#: templates/game_detail.html:49 templates/game_imports.html:167 +#: templates/game_detail.html:63 templates/game_imports.html:167 msgid "CLZ Import" msgstr "" -#: templates/game_detail.html:51 +#: templates/game_detail.html:65 msgid "Steam Import - No ROM file" msgstr "" -#: templates/game_detail.html:51 +#: templates/game_detail.html:65 msgid "Steam Import" msgstr "" -#: templates/game_detail.html:53 +#: templates/game_detail.html:67 msgid "Xbox Import - No ROM file" msgstr "" -#: templates/game_detail.html:53 +#: templates/game_detail.html:67 msgid "Xbox Import" msgstr "" -#: templates/game_detail.html:55 +#: templates/game_detail.html:69 msgid "PSN Import - No ROM file" msgstr "" -#: templates/game_detail.html:55 +#: templates/game_detail.html:69 msgid "PSN Import" msgstr "" -#: templates/game_detail.html:58 +#: templates/game_detail.html:72 msgid "This is a bonus/extra disc" msgstr "" -#: templates/game_detail.html:58 +#: templates/game_detail.html:72 msgid "Bonus Disc" msgstr "" -#: templates/game_detail.html:63 +#: templates/game_detail.html:77 msgid "Part of:" msgstr "" -#: templates/game_detail.html:74 +#: templates/game_detail.html:88 msgid "Regional versions" msgstr "" -#: templates/game_detail.html:75 +#: templates/game_detail.html:89 msgid "Versions:" msgstr "" -#: templates/game_detail.html:77 templates/game_detail.html:724 +#: templates/game_detail.html:91 templates/game_detail.html:770 msgid "Unknown region" msgstr "" -#: templates/game_detail.html:83 +#: templates/game_detail.html:97 msgid "Launch this game in the configured emulator" msgstr "" -#: templates/game_detail.html:89 +#: templates/game_detail.html:103 msgid "Keyboard game: press Scroll Lock for RetroArch keys" msgstr "" -#: templates/game_detail.html:93 +#: templates/game_detail.html:107 msgid "View Manual (PDF)" msgstr "" -#: templates/game_detail.html:118 +#: templates/game_detail.html:132 msgid "Delete game from database" msgstr "" -#: templates/_settings_tabs/account.html:320 templates/game_detail.html:120 +#: templates/_settings_tabs/account.html:320 templates/game_detail.html:134 #: templates/logs.html:735 templates/settings.html:70 #: templates/wishlist.html:642 msgid "Delete" msgstr "" -#: templates/game_detail.html:154 templates/game_detail.html:166 +#: templates/game_detail.html:168 templates/game_detail.html:180 msgid "Click to zoom" msgstr "" -#: templates/game_detail.html:171 +#: templates/game_detail.html:185 msgid "Switch between 2D and 3D boxart" msgstr "" -#: templates/game_detail.html:190 +#: templates/game_detail.html:204 msgid "" "Sharper images for this game are still being made. They'll appear on " "their own." msgstr "" -#: templates/game_detail.html:201 +#: templates/game_detail.html:215 msgid "Player" msgstr "" -#: templates/game_detail.html:206 +#: templates/game_detail.html:220 #, python-format msgid "Critic Score (%(count)s reviews)" msgstr "" -#: templates/game_detail.html:211 +#: templates/game_detail.html:225 #, python-format msgid "User Score (%(count)s ratings)" msgstr "" -#: templates/game_detail.html:218 -msgid "Completion status" +#: templates/game_detail.html:230 +#, python-format +msgid "Copies sold on %(system)s, from published sales figures" +msgstr "" + +#: templates/game_detail.html:231 +#, python-format +msgid "%(count)s sold" +msgstr "" + +#: templates/game_detail.html:242 +#, python-format +msgid "Last tested %(date)s." +msgstr "" + +#: templates/game_detail.html:246 +msgid "" +"RetroDB reads PS3 compatibility from your RPCS3. In RPCS3, choose Manage " +"> Databases > Download Compatibility Database." +msgstr "" + +#: templates/game_detail.html:247 +msgid "RPCS3: no compatibility list" msgstr "" -#: templates/game_detail.html:233 +#: templates/game_detail.html:268 msgid "No description available. Click \"Scrape\" to fetch metadata." msgstr "" -#: templates/game_detail.html:304 +#: templates/game_detail.html:350 msgid "View on RetroAchievements.org" msgstr "" -#: templates/game_detail.html:306 +#: templates/game_detail.html:352 msgid "RA Site" msgstr "" -#: templates/game_detail.html:318 templates/steam_achievement_game.html:17 +#: templates/game_detail.html:364 templates/steam_achievement_game.html:17 #: templates/steam_achievements.html:7 templates/steam_achievements.html:15 #: templates/steam_achievements.html:21 msgid "Steam Achievements" msgstr "" -#: templates/game_detail.html:334 templates/xbox_achievement_game.html:17 +#: templates/game_detail.html:380 templates/xbox_achievement_game.html:17 #: templates/xbox_achievements.html:7 templates/xbox_achievements.html:15 #: templates/xbox_achievements.html:21 msgid "Xbox Achievements" msgstr "" -#: templates/game_detail.html:349 templates/psn_trophies.html:6 +#: templates/game_detail.html:395 templates/psn_trophies.html:6 #: templates/psn_trophies.html:15 templates/psn_trophy_detail.html:17 msgid "PSN Trophies" msgstr "" -#: templates/game_detail.html:374 templates/local_trophy_detail.html:16 +#: templates/game_detail.html:420 templates/local_trophy_detail.html:16 msgid "RPCS3 Trophies" msgstr "" -#: templates/game_detail.html:407 +#: templates/game_detail.html:453 msgid "(cross-mapped)" msgstr "" -#: templates/game_detail.html:420 +#: templates/game_detail.html:466 #, python-format msgid "Screenshots (%(count)s)" msgstr "" -#: templates/game_detail.html:422 +#: templates/game_detail.html:468 msgid "Previous screenshots" msgstr "" -#: templates/game_detail.html:428 +#: templates/game_detail.html:474 msgid "Delete screenshot" msgstr "" -#: templates/game_detail.html:432 +#: templates/game_detail.html:478 msgid "Next screenshots" msgstr "" -#: templates/game_detail.html:451 +#: templates/game_detail.html:497 msgid "Also Known As" msgstr "" -#: templates/game_detail.html:455 +#: templates/game_detail.html:501 #, python-format msgid "Source: %(source)s" msgstr "" -#: templates/game_detail.html:480 +#: templates/game_detail.html:526 msgid "Loading recommendations..." msgstr "" -#: templates/game_detail.html:528 +#: templates/game_detail.html:574 #, python-format msgid "%(pct)s%% match" msgstr "" -#: templates/game_detail.html:569 +#: templates/game_detail.html:615 msgid "Source:" msgstr "" -#: templates/game_detail.html:572 +#: templates/game_detail.html:618 msgid "CLZ Games Import (No ROM file)" msgstr "" -#: templates/game_detail.html:574 +#: templates/game_detail.html:620 msgid "Steam Import (No ROM file)" msgstr "" -#: templates/game_detail.html:576 +#: templates/game_detail.html:622 msgid "Xbox Import (No ROM file)" msgstr "" -#: templates/game_detail.html:578 +#: templates/game_detail.html:624 msgid "PSN Import (No ROM file)" msgstr "" -#: templates/game_detail.html:584 +#: templates/game_detail.html:630 msgid "Original Title:" msgstr "" -#: templates/game_detail.html:590 +#: templates/game_detail.html:636 msgid "File:" msgstr "" -#: templates/game_detail.html:592 +#: templates/game_detail.html:638 msgid "Rename ROM file" msgstr "" -#: templates/game_detail.html:595 +#: templates/game_detail.html:641 msgid "Path:" msgstr "" -#: templates/game_detail.html:601 +#: templates/game_detail.html:647 msgid "Edition:" msgstr "" -#: templates/game_detail.html:608 +#: templates/game_detail.html:654 msgid "Exclusive:" msgstr "" -#: templates/game_detail.html:611 +#: templates/game_detail.html:657 msgid "Also On:" msgstr "" -#: templates/game_detail.html:618 +#: templates/game_detail.html:664 msgid "Modes:" msgstr "" -#: templates/game_detail.html:624 +#: templates/game_detail.html:670 msgid "Players:" msgstr "" -#: templates/game_detail.html:630 +#: templates/game_detail.html:676 msgid "Campaign:" msgstr "" -#: templates/game_detail.html:636 +#: templates/game_detail.html:682 msgid "Structure:" msgstr "" -#: templates/game_detail.html:642 +#: templates/game_detail.html:688 msgid "Perspective:" msgstr "" -#: templates/game_detail.html:648 +#: templates/game_detail.html:694 msgid "Dimension:" msgstr "" -#: templates/game_detail.html:654 +#: templates/game_detail.html:700 msgid "Save:" msgstr "" -#: templates/game_detail.html:660 +#: templates/game_detail.html:706 msgid "Input:" msgstr "" -#: templates/game_detail.html:665 +#: templates/game_detail.html:711 msgid "Scraped:" msgstr "" -#: templates/game_detail.html:674 +#: templates/game_detail.html:720 msgid "Bonus Content" msgstr "" -#: templates/game_detail.html:693 +#: templates/game_detail.html:739 msgid "Emulator settings" msgstr "" -#: templates/_macros/nav_tabs.html:40 templates/game_detail.html:703 +#: templates/_macros/nav_tabs.html:40 templates/game_detail.html:749 #: templates/rom_tools_hub.html:193 templates/versions_review.html:5 #: templates/versions_review.html:80 msgid "Regional Versions" msgstr "" -#: templates/game_detail.html:711 templates/museum_system.html:100 +#: templates/game_detail.html:757 templates/museum_system.html:100 msgid "Released" msgstr "" -#: templates/game_detail.html:713 +#: templates/game_detail.html:759 msgid "File size" msgstr "" -#: templates/game_detail.html:714 +#: templates/game_detail.html:760 msgid "Revision" msgstr "" -#: templates/game_detail.html:715 +#: templates/game_detail.html:761 msgid "Languages" msgstr "" -#: templates/game_detail.html:716 +#: templates/game_detail.html:762 msgid "Scores" msgstr "" -#: templates/game_detail.html:725 +#: templates/game_detail.html:771 msgid "(this page)" msgstr "" -#: templates/game_detail.html:743 +#: templates/game_detail.html:789 msgid "Known differences" msgstr "" -#: templates/game_detail.html:743 +#: templates/game_detail.html:789 msgid "AI-written" msgstr "" -#: templates/game_detail.html:744 +#: templates/game_detail.html:790 msgid "Written by AI from what it knows about these releases. It can be wrong." msgstr "" -#: templates/game_detail.html:746 +#: templates/game_detail.html:792 msgid "The AI knows of no differences between these versions." msgstr "" -#: templates/game_detail.html:749 +#: templates/game_detail.html:795 msgid "No other regional versions are linked to this game." msgstr "" -#: templates/game_detail.html:754 +#: templates/game_detail.html:800 msgid "Unlink this version" msgstr "" -#: templates/game_detail.html:755 +#: templates/game_detail.html:801 msgid "Summarise differences with AI" msgstr "" -#: templates/game_detail.html:757 +#: templates/game_detail.html:803 msgid "Link a version" msgstr "" -#: templates/game_detail.html:758 +#: templates/game_detail.html:804 msgid "Link a version: search this system" msgstr "" -#: templates/game_detail.html:771 +#: templates/game_detail.html:817 msgid "Scrape History" msgstr "" -#: templates/game_detail.html:797 +#: templates/game_detail.html:843 msgid "Primary scraper" msgstr "" -#: templates/game_detail.html:799 +#: templates/game_detail.html:845 msgid "Fallback scraper" msgstr "" -#: templates/game_detail.html:806 +#: templates/game_detail.html:852 msgid "Fields filled:" msgstr "" -#: templates/game_detail.html:819 +#: templates/game_detail.html:865 msgid "Still missing:" msgstr "" @@ -8807,14 +8917,6 @@ msgstr "" msgid "Manual page. Use the arrow keys to turn pages." msgstr "" -#: templates/_modals/manual_reader_modal.html:15 -msgid "Previous" -msgstr "" - -#: templates/_modals/manual_reader_modal.html:17 -msgid "Next" -msgstr "" - #: templates/_modals/rename_modal.html:4 msgid "Rename ROM File" msgstr "" diff --git a/retrodb.spec b/retrodb.spec index 1efa1d32..fb96c5cf 100644 --- a/retrodb.spec +++ b/retrodb.spec @@ -53,6 +53,8 @@ _MIGRATION_SCRIPTS = [ 'services.migrations.scripts.020_player', 'services.migrations.scripts.021_official_names', 'services.migrations.scripts.022_player_name', + 'services.migrations.scripts.023_games_units_sold', + 'services.migrations.scripts.024_games_release_dates', ] # Some pip packages have their own runtime imports that PyInstaller's diff --git a/routes/games.py b/routes/games.py index 60d07483..671064f4 100644 --- a/routes/games.py +++ b/routes/games.py @@ -5,7 +5,7 @@ # ============================================================================= from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash, g, make_response, abort -from flask_babel import gettext as _ +from flask_babel import get_locale, gettext as _ import hashlib import os import json @@ -30,13 +30,14 @@ invalidate_filter_cache, VERSION_COLUMNS, MULTI_FILTER_FIELDS, ) from services.analytics import invalidate_analytics_cache +from services.release_dates import display_release_dates from services.launch_resolver import keyboard_core, launch_checker, rom_launch_problem from services.game_metadata_service import ( build_game_card, apply_hybrid_metadata_to_game, normalize_game_edit, cross_map_ratings, edit_title_lock, ) -from services import apply_progress, upscale_queue +from services import apply_progress, rpcs3_compat, upscale_queue # Single source of truth for the completion-status whitelist, shared by the # single-edit endpoint (api_update_completion) and bulk-edit (Pass 48.5 — bulk @@ -49,7 +50,7 @@ ) from services.game_media_service import ( ALLOWED_IMAGE_EXT, ALLOWED_VIDEO_EXT, image_dir, - remove_media_file, save_upload, save_screenshots, try_standardize, + remove_media_file, save_upload, save_screenshots, ) logger = logging.getLogger(__name__) @@ -197,6 +198,8 @@ def api_games(): for row in rows ] _add_can_launch(games, rows) + if any(row['disc_serial'] is None and rpcs3_compat.applies_to(row) for row in rows): + rpcs3_compat.fill_missing_serials() return success( games=games, @@ -230,6 +233,44 @@ def api_games_ids(): return success(ids=ids, total=len(ids)) +@bp.route('/api/games/neighbors') +@login_required +@handle_api_errors +def api_games_neighbors(): + """The games before and after `id` in a Library or console list, under + that list's filters and sort (the same params as /api/games), for the + game page's Previous / Next links. A version the list folds into its + group's card is placed at that card. `found` is False when the game is + not in the list at all.""" + game_id = request.args.get('id', type=int) + if not game_id: + return error(_('No IDs provided'), 400) + ids_sql, ids_vals = _build_games_query(_filter_params(extra=('sort',)), ids_only=True) + # Pass 25.8 — the same cap as /api/games/ids. + ids_sql += f" LIMIT {20 * int(getattr(config, 'MAX_LIST_ROWS', 500))}" + ids = [r['id'] for r in query(ids_sql, tuple(ids_vals))] + + position = {gid: i for i, gid in enumerate(ids)} + index = position.get(game_id) + if index is None: + siblings = query(""" + SELECT id FROM games + WHERE version_group_id = (SELECT version_group_id FROM games WHERE id = ?) + """, (game_id,)) + index = min((position[r['id']] for r in siblings if r['id'] in position), default=None) + if index is None: + return success(found=False, total=len(ids)) + + def _neighbor(i): + if not 0 <= i < len(ids): + return None + row = query("SELECT id, title FROM games WHERE id = ?", (ids[i],), one=True) + return {'id': row['id'], 'title': row['title']} if row else None + + return success(found=True, position=index + 1, total=len(ids), + prev=_neighbor(index - 1), next=_neighbor(index + 1)) + + @bp.route('/api/games/card-data') @login_required @handle_api_errors @@ -262,7 +303,8 @@ def api_games_card_data(): # includes per-user PSN + achievement progress (joins below), so a # globally-keyed ETag lets one user's browser serve another user's # progress as a 304. CWE-524. - etag_payload = f"cd:{g.user['id']}:{','.join(str(i) for i in sorted_ids)}:{max_updated}" + etag_payload = (f"cd:{g.user['id']}:{','.join(str(i) for i in sorted_ids)}:{max_updated}" + f":{rpcs3_compat.stamp()}") etag = f'W/"{hashlib.md5(etag_payload.encode(), usedforsecurity=False).hexdigest()}"' if request.headers.get('If-None-Match') == etag: resp = make_response('', 304) @@ -280,7 +322,7 @@ def api_games_card_data(): g.grac_rating, g.classind_rating, g.china_rating, g.critic_score, g.critic_score_count, g.user_score, g.user_score_count, g.completion_status, g.scraped, g.has_retroachievements, - g.is_bonus_disc, g.rom_path, g.ra_achievement_count, + g.is_bonus_disc, g.rom_path, g.disc_serial, g.ra_achievement_count, s.name AS system_name, s.folder AS system_folder, s.system_type, COALESCE(bc.bonus_count, 0) AS bonus_count, {VERSION_COLUMNS}, @@ -482,19 +524,24 @@ def game_detail(game_id): remove_media_file(video_filename, 'video') video_filename = '' - uploaded = save_upload(request.files.get('custom_boxart'), image_dir('boxart'), game_id, 'custom', ALLOWED_IMAGE_EXT) - if uploaded: - boxart_filename = uploaded - try_standardize(os.path.join(image_dir('boxart'), uploaded), 'boxart') - - uploaded = save_upload(request.files.get('custom_boxart_3d'), image_dir('boxart_3d'), game_id, 'custom_3d', ALLOWED_IMAGE_EXT) - if uploaded: - boxart_3d_filename = uploaded - try_standardize(os.path.join(image_dir('boxart_3d'), uploaded), 'boxart_3d') - - uploaded = save_upload(request.files.get('custom_fanart'), image_dir('fanart'), game_id, 'custom_fanart', ALLOWED_IMAGE_EXT) - if uploaded: - fanart_filename = uploaded + # Uploads go through the scraper's image pipeline + # (format, size, responsive variants). The file an + # upload replaces is removed when no other game uses it. + def _upload(field, media_type, prefix, current): + uploaded = save_upload(request.files.get(field), image_dir(media_type), + game_id, prefix, ALLOWED_IMAGE_EXT, image_type=media_type) + if not uploaded: + return current + if current and current != uploaded and not query( + f"SELECT 1 FROM games WHERE " + f"{safe_column(media_type, ('boxart', 'boxart_3d', 'fanart'))} = ? AND id != ?", + (current, game_id), one=True): + remove_media_file(current, media_type) + return uploaded + + boxart_filename = _upload('custom_boxart', 'boxart', 'custom', boxart_filename) + boxart_3d_filename = _upload('custom_boxart_3d', 'boxart_3d', 'custom_3d', boxart_3d_filename) + fanart_filename = _upload('custom_fanart', 'fanart', 'custom_fanart', fanart_filename) video_upload = request.files.get('custom_video') uploaded = save_upload(video_upload, os.path.join(config.STATIC_PATH, 'videos'), game_id, 'custom', ALLOWED_VIDEO_EXT) @@ -505,11 +552,14 @@ def game_detail(game_id): "of up to %(mb)s MB.", mb=config.MAX_VIDEO_SIZE // (1024 * 1024)), "error") - screenshots = save_screenshots( + screenshots, duplicate_shots = save_screenshots( request.files.getlist('custom_screenshots'), game_id, screenshots, ) + if duplicate_shots: + flash(_("Screenshots not added because they match ones the game " + "already has: %(count)s", count=duplicate_shots), "warning") # Update database execute(""" @@ -603,7 +653,8 @@ def game_detail(game_id): region = NULL, franchise = NULL, similar_games = NULL, playtime_estimate = NULL, controller_support = NULL, save_type = NULL, scrape_history = NULL, critic_score = NULL, critic_score_count = NULL, - user_score = NULL, user_score_count = NULL, + user_score = NULL, user_score_count = NULL, units_sold = NULL, + release_dates = NULL, hltb_match_name = NULL, hltb_match_platform = NULL, hltb_match_confidence = NULL, scraped = 0, title_locked = 0 WHERE id = ? @@ -635,6 +686,10 @@ def game_detail(game_id): return redirect(url_for('games.all_games')) game_dict = dict(game) + rpcs3_compat.ensure_serial(game_dict) + game_dict['rpcs3_compat'] = rpcs3_compat.status_for_game(game_dict) + game_dict['rpcs3_compat_missing'] = (rpcs3_compat.applies_to(game_dict) + and not rpcs3_compat.available()) if game_dict.get('scrape_history'): try: game_dict['scrape_history'] = json.loads(game_dict['scrape_history']) @@ -739,12 +794,12 @@ def get(self, key, default=None): # language, and list region-tagged names first, grouped by region. from scraper.metadata_normalizer import merge_alt_titles from services.i18n import region_display_name - from flask_babel import get_locale alternate_titles = merge_alt_titles(game_dict.get('alternate_titles'), []) for alt in alternate_titles: if alt.get('region'): alt['region_label'] = region_display_name(alt['region'], get_locale()) alternate_titles.sort(key=lambda a: (not a.get('region_label'), a.get('region_label') or '')) + release_dates = display_release_dates(game_dict.get('release_dates'), get_locale()) # PASS-59-106 §4.4 — the other regional versions this game is linked to. from scraper.metadata_normalizer import alt_title_region @@ -766,11 +821,12 @@ def get(self, key, default=None): versions=versions, version_ai_summary=version_ai_summary, alternate_titles=alternate_titles, + release_dates=release_dates, pref_rating={'value': pref_rating_val, 'image': pref_rating_img, 'crossmapped': pref_rating_crossmapped, 'name': pref_rating_name, 'region': pref_rating_region, 'system': pref_sys}, all_ratings=all_ratings, - retroachievements=get_retroachievements_info(game['title'], game['system_folder']), + retroachievements=get_retroachievements_info(game), trophy_info=get_trophy_info_for_game(game['title'], game['system_name']), achievement_progress=achievement_progress, psn_progress=psn_progress, @@ -833,6 +889,7 @@ def api_game_detail(game_id): rpcs3_info = lookup_rpcs3_info(game, build_rpcs3_trophy_map([game])) except Exception: rpcs3_info = None + rpcs3_compat.ensure_serial(game) screenshots = [] if game['screenshots']: @@ -888,6 +945,9 @@ def api_game_detail(game_id): 'china_rating': game.get('china_rating'), 'critic_score': game['critic_score'], 'user_score': game['user_score'], + 'units_sold': game['units_sold'], + 'release_dates': display_release_dates(game['release_dates'], get_locale()), + 'rpcs3_compat': rpcs3_compat.status_for_game(game), 'players': game['players'], 'completion_status': game['completion_status'], 'has_retroachievements': game['has_retroachievements'], diff --git a/routes/games_ai.py b/routes/games_ai.py index f421c1a9..72e4bd48 100644 --- a/routes/games_ai.py +++ b/routes/games_ai.py @@ -45,20 +45,14 @@ def api_game_ai_fill(game_id): if not game: return error(_('Game not found'), 404) - from scraper.scrape_ai import get_game_details as ai_get_details, VALIDATE_FIELDS + from scraper.scrape_ai import ( + AI_FILLABLE_FIELDS, VALIDATE_FIELDS, get_game_details as ai_get_details, + ) from scraper.hybrid_scraper import should_use_default_controller, get_system_default_controller_name - ai_fields = [ - 'genre', 'description', 'developer', 'publisher', 'release_date', - 'players', 'modes', 'esrb_rating', 'pegi_rating', 'cero_rating', - 'usk_rating', 'acb_rating', 'fpb_rating', 'grac_rating', 'classind_rating', - 'china_rating', - 'region', 'franchise', 'similar_games', 'controller_support', 'save_type', - 'game_structure', 'perspective', 'dimension', 'campaign', - 'other_platforms', 'edition', - 'critic_score', 'critic_score_count', 'user_score', 'user_score_count', - ] - _int_fields = {'critic_score', 'critic_score_count', 'user_score', 'user_score_count', 'players'} + ai_fields = AI_FILLABLE_FIELDS + _int_fields = {'critic_score', 'critic_score_count', 'user_score', 'user_score_count', + 'units_sold', 'players'} existing_metadata = {} for field in ai_fields: existing_metadata[field] = str(game.get(field, '') or '') or '' @@ -158,7 +152,7 @@ def api_game_ai_fill(game_id): for k in RATING_SYSTEM_KEYS ) if not has_any_rating: - infer_data = {f: pending.get(f) or (game.get(f, '') or '').strip() for f in ai_fields} + infer_data = {f: pending.get(f) or str(game.get(f, '') or '').strip() for f in ai_fields} infer_data['system_folder'] = game.get('system_folder', '') inferred = infer_rating_from_content(infer_data) if inferred: diff --git a/routes/systems.py b/routes/systems.py index 38b574cf..ea0c49e1 100644 --- a/routes/systems.py +++ b/routes/systems.py @@ -183,7 +183,10 @@ def systems(): systems_list = query(""" SELECT s.*, COUNT(g.id) AS rom_count, - SUM(CASE WHEN g.scraped = 0 THEN 1 ELSE 0 END) AS missing_count + -- Bonus discs are left out, as on the console page and in + -- its Unscraped filter, which never list them. + SUM(CASE WHEN g.scraped = 0 AND (g.is_bonus_disc = 0 OR g.is_bonus_disc IS NULL) + THEN 1 ELSE 0 END) AS missing_count FROM systems s LEFT JOIN games g ON s.id = g.system_id GROUP BY s.id diff --git a/scraper/hybrid_scraper.py b/scraper/hybrid_scraper.py index 9e7ca784..bda429f7 100644 --- a/scraper/hybrid_scraper.py +++ b/scraper/hybrid_scraper.py @@ -767,6 +767,8 @@ def _save_game_row(c, metadata, scrape_history_json, db_game_id): critic_score_count = COALESCE(?, critic_score_count), user_score = COALESCE(?, user_score), user_score_count = COALESCE(?, user_score_count), + units_sold = COALESCE(?, units_sold), + release_dates = COALESCE(?, release_dates), alternate_titles = COALESCE(?, alternate_titles), scrape_history = ?, scraped = 1 @@ -812,6 +814,8 @@ def _save_game_row(c, metadata, scrape_history_json, db_game_id): metadata.get('critic_score_count'), metadata.get('user_score'), metadata.get('user_score_count'), + metadata.get('units_sold'), + metadata.get('release_dates'), alt_titles_json, scrape_history_json, db_game_id @@ -1254,6 +1258,8 @@ def apply_hybrid_metadata(db_game_id, primary_source, primary_id, system_folder, 'critic_score_count': None, 'user_score': None, 'user_score_count': None, + 'units_sold': None, + 'release_dates': None, 'alternate_titles': None, } diff --git a/scraper/metadata_merger.py b/scraper/metadata_merger.py index 8cc4ed0a..92d92e53 100644 --- a/scraper/metadata_merger.py +++ b/scraper/metadata_merger.py @@ -1274,7 +1274,8 @@ def _should_apply(field): result['filled_fields'].append(f'{field} (AI)') # Score fields — convert to int before storing - score_fields = ['critic_score', 'critic_score_count', 'user_score', 'user_score_count'] + score_fields = ['critic_score', 'critic_score_count', 'user_score', 'user_score_count', + 'units_sold'] for field in score_fields: val = ai_data.get(field) if val and _should_apply(field): @@ -1283,3 +1284,8 @@ def _should_apply(field): result['filled_fields'].append(f'{field} (AI)') except (ValueError, TypeError): pass + + # Release dates by region — already JSON text from scrape_ai's validation. + if ai_data.get('release_dates') and _should_apply('release_dates'): + metadata['release_dates'] = ai_data['release_dates'] + result['filled_fields'].append('release_dates (AI)') diff --git a/scraper/scrape_ai.py b/scraper/scrape_ai.py index 79a85fd0..86b4819c 100644 --- a/scraper/scrape_ai.py +++ b/scraper/scrape_ai.py @@ -37,6 +37,7 @@ 'game_structure', 'perspective', 'dimension', 'campaign', 'other_platforms', 'edition', 'critic_score', 'critic_score_count', 'user_score', 'user_score_count', + 'units_sold', 'release_dates', ] # Schema constraints for validation (values AI must pick from) @@ -478,6 +479,18 @@ def _build_prompt(title, system_name, missing_fields, existing_values=None): field_instructions.append( '"release_date": release date for THIS specific platform in YYYY-MM-DD format' ) + elif field == 'release_dates': + field_instructions.append( + '"release_dates": a JSON object giving the date this game was released on ' + + system_name + ' in each region where it came out, keyed by region code: ' + 'NA (North America), EU (Europe and other PAL regions), JP (Japan), ' + 'AU (Australia), KR (South Korea), CN (China), TW (Taiwan), BR (Brazil), ' + 'AS (Asia, only when no single country is known), WW (one worldwide release). ' + 'Dates in YYYY-MM-DD format, or YYYY-MM / YYYY when only that much is ' + 'known. Leave out regions where it was not released. Example: ' + '{"JP": "2002-11-17", "NA": "2002-11-18", "EU": "2003-03-21"}. ' + 'Return {} if unknown.' + ) elif field == 'players': field_instructions.append( '"players": max player count as a string (e.g., "1", "2", "4", "1-2", "1-4")' @@ -647,6 +660,20 @@ def _build_prompt(title, system_name, missing_fields, existing_values=None): '"user_score_count": number of user ratings contributing to the user score ' '(from the same source as user_score). Return null if unknown.' ) + elif field == 'units_sold': + field_instructions.append( + '"units_sold": lifetime number of copies this game sold worldwide on ' + + system_name + ' (physical and digital), as a whole number with no ' + 'commas or words (e.g., 2400000 for "2.4 million"). Use figures that are ' + 'free to read online, in priority order: the publisher\'s or developer\'s ' + 'own reports (annual reports, investor briefings, press releases), ' + 'platform-holder best-seller lists (Nintendo, Sony, Microsoft), Famitsu ' + 'and Media Create sales charts (Japan), Circana/NPD figures as reported in ' + 'the press (USA), Wikipedia\'s list of best-selling games for the platform ' + 'and the game\'s own Wikipedia article, then VGChartz. When only a ' + 'total across several platforms is published, return null rather than ' + 'that total. Return null if no figure is published.' + ) # Add validation prefix for fields with existing values final_instructions = [] @@ -683,6 +710,7 @@ def _build_prompt(title, system_name, missing_fields, existing_values=None): - Return ONLY valid JSON. No markdown, no explanation, no code fences. - Use empty string "" for any field you are genuinely unsure about. Do not guess. - For comma-separated fields (genre, modes, perspective, dimension, game_structure), separate values with ", " (comma-space). +- For units_sold, give a published sales figure, never an estimate of your own. - For review score fields (critic_score, user_score), search online for the most accurate aggregated scores. Prefer Metacritic, OpenCritic, and GameFAQs as primary sources. For classic/retro games where those sites have no coverage, MobyGames and AtariAge are excellent alternative sources. - Be accurate. Wrong data is worse than empty data.{validation_rule} """ @@ -904,6 +932,43 @@ def _parse_ai_response(raw_text): return {} +_SALES_SCALE = {'thousand': 1_000, 'k': 1_000, 'million': 1_000_000, + 'm': 1_000_000, 'mil': 1_000_000, 'billion': 1_000_000_000} +_SALES_RE = re.compile(r'^~?\s*([\d.,]+)\s*([a-z]*)', re.IGNORECASE) +# No single game has sold more than about 300 million copies on one platform. +_MAX_UNITS_SOLD = 500_000_000 + + +def parse_units_sold(value): + """A sales figure as a whole number of copies, or None. + + Takes what an AI writes despite being asked for a bare integer: + 2400000, "2,400,000", "2.4 million", "2.4M", "~850k", "over 1 million". + """ + if value is None or isinstance(value, bool): + return None + if isinstance(value, (int, float)): + number, scale = float(value), 1 + else: + text = re.sub(r'^(over|about|around|approximately|approx\.?|more than|nearly)\s+', + '', str(value).strip().lower()) + m = _SALES_RE.match(text) + if not m: + return None + digits, unit = m.group(1), m.group(2) + scale = _SALES_SCALE.get(unit, 1) if unit else 1 + if unit and unit not in _SALES_SCALE and unit not in ('copies', 'units'): + return None + # "2,400,000" groups thousands; "2.4" is a decimal. + digits = digits.replace(',', '') if scale == 1 or digits.count(',') > 1 else digits.replace(',', '.') + try: + number = float(digits) + except ValueError: + return None + count = int(round(number * scale)) + return count if 0 < count <= _MAX_UNITS_SOLD else None + + def _validate_all_fields(data): """Validate all fields in the parsed AI response. @@ -928,6 +993,11 @@ def _validate_field(field_name, value): if value is None: return '' + # An object, not text: stored as JSON, one date per region. + if field_name == 'release_dates': + from services.release_dates import release_dates_json + return release_dates_json(value) or '' + # Convert to string value = str(value).strip() @@ -988,6 +1058,9 @@ def _validate_field(field_name, value): return str(count) if count > 0 else '' except (ValueError, TypeError): return '' + if field_name == 'units_sold': + count = parse_units_sold(value) + return str(count) if count else '' # Free-text fields — basic sanitization # Truncate extremely long values diff --git a/services/disc_serial.py b/services/disc_serial.py index ed5c4679..e125510a 100644 --- a/services/disc_serial.py +++ b/services/disc_serial.py @@ -182,6 +182,51 @@ def _sfb_title_id(data: bytes) -> str: return '' +def _sfo_title_id(data: bytes) -> str: + """PARAM.SFO's TITLE_ID (`BLUS30443`) as `BLUS-30443`, or ''. A PSN or + installed game folder has no PS3_DISC.SFB, only this file.""" + if data[:4] != b'\x00PSF' or len(data) < 0x14: + return '' + key_start, data_start, count = struct.unpack_from(' len(data): + break + key_off, _fmt, length, _max, data_off = struct.unpack_from(' str: + """`BLUS30443` or `BLUS-30443` -> `BLUS-30443`; '' when neither.""" + m = _PS3_ID_RE.fullmatch(title_id.strip().upper()) + return f'{m.group(1)}-{m.group(2)}' if m else '' + + +def _ps3_folder_id(path: Path) -> str: + sfb = path / 'PS3_DISC.SFB' + if sfb.is_file(): + return _sfb_title_id(sfb.read_bytes()) + for sfo in (path / 'PS3_GAME' / 'PARAM.SFO', path / 'PARAM.SFO'): + if sfo.is_file(): + return _sfo_title_id(sfo.read_bytes()) + return '' + + +def ps3_id_from_name(name: str) -> str: + """A PS3 serial written in a file or folder name, as many dump sets do + (`Demon's Souls [BLUS30443].iso`), as `BLUS-30443`; else ''.""" + m = _PS3_ID_RE.search(Path(name).name.upper()) + return f'{m.group(1)}-{m.group(2)}' if m else '' + + # ----------------------------------------------------------------------------- # Public # ----------------------------------------------------------------------------- @@ -192,8 +237,7 @@ def read_disc_ids(rom_path, system_folder: str) -> tuple: try: path = Path(rom_path) if path.is_dir(): - sfb = path / 'PS3_DISC.SFB' - return (_sfb_title_id(sfb.read_bytes()) if sfb.is_file() else ''), None + return (_ps3_folder_id(path) or ps3_id_from_name(path.name)), None ext = path.suffix.lower() if ext == '.m3u': first = next((ln.strip() for ln in path.read_text(errors='replace').splitlines() @@ -211,7 +255,7 @@ def read_disc_ids(rom_path, system_folder: str) -> tuple: return '', None if system_folder == 'ps3': sfb = _read_file(reader, 'PS3_DISC.SFB') - return (_sfb_title_id(sfb) if sfb else ''), None + return ((_sfb_title_id(sfb) if sfb else '') or ps3_id_from_name(path.name)), None return _from_iso(reader, system_folder) except Exception as e: # noqa: BLE001 — the contract is "raises nothing" logger.info(f"disc serial: cannot read {rom_path}: {e}") diff --git a/services/emulator_adapters.py b/services/emulator_adapters.py index ac971f06..509380bd 100644 --- a/services/emulator_adapters.py +++ b/services/emulator_adapters.py @@ -82,10 +82,16 @@ def _pcsx2_dirs(binary: Path) -> list: def _rpcs3_dirs(binary: Path) -> list: + # RPCS3's fs::get_config_dir(): a `portable` folder beside the program + # wins; Windows keeps everything beside rpcs3.exe; macOS uses + # Application Support; Linux XDG_CONFIG_HOME (or ~/.config). home = _home_of(binary) - dirs = [home / '.config' / 'rpcs3'] if home else [] + dirs = [binary.parent / 'portable', binary.parent] + if home: + dirs.append(home / '.config' / 'rpcs3') return dirs + [_xdg('XDG_CONFIG_HOME', '.config') / 'rpcs3', - Path.home() / '.var/app/net.rpcs3.RPCS3/config/rpcs3'] + Path.home() / '.var/app/net.rpcs3.RPCS3/config/rpcs3', + Path.home() / 'Library' / 'Application Support' / 'rpcs3'] def find_data_dir(adapter: EmulatorAdapter, binary: Path) -> Optional[Path]: diff --git a/services/game_cleanup.py b/services/game_cleanup.py index 2d7d0c2d..99a1d336 100644 --- a/services/game_cleanup.py +++ b/services/game_cleanup.py @@ -31,7 +31,7 @@ 'grac_rating', 'classind_rating', 'china_rating', 'region', 'franchise', 'similar_games', 'playtime_estimate', 'controller_support', 'save_type', 'critic_score', 'critic_score_count', 'user_score', - 'user_score_count', 'boxart', 'boxart_3d', 'screenshots', 'fanart', + 'user_score_count', 'units_sold', 'release_dates', 'boxart', 'boxart_3d', 'screenshots', 'fanart', 'video', 'manual', ) diff --git a/services/game_media_service.py b/services/game_media_service.py index 4c83cf2b..45a04993 100644 --- a/services/game_media_service.py +++ b/services/game_media_service.py @@ -125,19 +125,26 @@ def resolve_media_path(filename, media_type): def remove_media_file(filename, media_type): - """Delete a stored media file from disk; log-and-swallow OS errors.""" + """Delete a stored media file from disk, with the `-sm` / `-md` variants + a boxart-family file carries; log-and-swallow OS errors.""" if not filename: return path = resolve_media_path(filename, media_type) - if not path or not os.path.exists(path): + if not path: return - try: - os.remove(path) - except OSError as e: - logger.warning(f"Could not delete {media_type} {path}: {e}") + from services.image_utils import _RESPONSIVE_VARIANTS, _variant_path + paths = [path] + [_variant_path(path, suffix) + for suffix, _w in _RESPONSIVE_VARIANTS.get(media_type, [])] + for p in paths: + if not os.path.exists(p): + continue + try: + os.remove(p) + except OSError as e: + logger.warning(f"Could not delete {media_type} {p}: {e}") -def save_upload(file_storage, dest_dir, game_id, prefix, allowed_ext): +def save_upload(file_storage, dest_dir, game_id, prefix, allowed_ext, image_type=None): """Persist an uploaded werkzeug FileStorage if present and valid. Validates extension, per-type size ceiling (MAX_IMAGE_SIZE for image @@ -150,6 +157,10 @@ def save_upload(file_storage, dest_dir, game_id, prefix, allowed_ext): game_id: game row id, used to namespace the output filename. prefix: filename discriminator (e.g. 'custom', 'custom_3d'). allowed_ext: iterable of allowed lowercase extensions (no dots). + image_type: media folder name ('boxart', 'boxart_3d', 'fanart'). An + image upload then goes through the scraper's pipeline: saved in + the configured image format, resized, and given its `-sm` / `-md` + versions (`finalize_upload`). Returns: str or None: saved filename (basename) on success, else None. @@ -171,10 +182,12 @@ def save_upload(file_storage, dest_dir, game_id, prefix, allowed_ext): if not _validate_image_bytes(raw): return None os.makedirs(dest_dir, exist_ok=True) - new_filename = f"{game_id}_{prefix}.{ext}" + new_filename = f"{game_id}_{prefix}{_upload_extension(image_type, ext)}" # Pass 32.9: write via tmp + os.replace. On a mid-write OOM or I/O # error the destination path never exists in a truncated state. _atomic_write_bytes(os.path.join(dest_dir, new_filename), raw) + if image_type: + finalize_upload(os.path.join(dest_dir, new_filename), image_type) else: # Video branch — Pass 25.6. Reject oversize videos before # file_storage.save() writes them to disk. Prefer Content-Length @@ -228,6 +241,27 @@ def save_upload(file_storage, dest_dir, game_id, prefix, allowed_ext): return new_filename +def _upload_extension(image_type, ext): + """The extension an uploaded image is stored under: the one a scraped + image of `image_type` gets (config.IMAGE_FORMAT), so the pipeline + re-encodes it the same way. No type keeps the upload's own.""" + if not image_type: + return f'.{ext}' + from services.image_utils import preferred_image_extension + return preferred_image_extension(image_type, f'.{ext}') + + +def finalize_upload(path, image_type): + """Run an uploaded image through the scraper's post-download pipeline: + format matching its extension, size standardization and the responsive + variants (services.image_utils.finalize_downloaded_image).""" + try: + from services.image_utils import finalize_downloaded_image + finalize_downloaded_image(path, image_type) + except Exception as e: + logger.warning(f"Processing uploaded {image_type} {path} failed: {e}") + + def _atomic_write_bytes(path, raw): """Write `raw` bytes to `path` atomically. @@ -243,15 +277,26 @@ def save_screenshots(file_storages, game_id, existing_csv): """Append uploaded screenshots to the existing CSV list, skipping invalid extensions. Indexing continues from where the existing list left off. - Returns the new comma-separated filename list (including prior entries). + Each one goes through the scraper's pipeline (format, size), then the + same perceptual-hash duplicate check scraped screenshots get: a shot + that looks like one the game already has, or an earlier one in the same + upload, is deleted and not listed. + + Returns (csv, duplicates): the new comma-separated filename list + (including prior entries) and how many uploads were dropped as + duplicates. """ existing = [s.strip() for s in (existing_csv or '').split(',') if s.strip()] valid = [f for f in (file_storages or []) if f and f.filename] if not valid: - return ','.join(existing) + return ','.join(existing), 0 + + from scraper.image_dedup import get_existing_screenshot_hashes, keep_screenshot_if_unique ss_dir = image_dir('screenshots') os.makedirs(ss_dir, exist_ok=True) + existing_hashes = get_existing_screenshot_hashes(existing) + duplicates = 0 # Derive the next index from the HIGHEST existing index, not the list # length. A gapped CSV (e.g. after stale-ref pruning removed `_ss2`) # would make len()+1 reuse an in-use index, overwriting a screenshot on @@ -273,25 +318,16 @@ def save_screenshots(file_storages, game_id, existing_csv): continue if not _validate_image_bytes(raw): continue - ss_filename = f"{game_id}_ss{next_idx}.{ext}" + ss_filename = f"{game_id}_ss{next_idx}{_upload_extension('screenshots', ext)}" + ss_path = os.path.join(ss_dir, ss_filename) # Pass 32.9: atomic write so a mid-save failure never leaves a # truncated screenshot visible at its final path. - _atomic_write_bytes(os.path.join(ss_dir, ss_filename), raw) + _atomic_write_bytes(ss_path, raw) + finalize_upload(ss_path, 'screenshots') + if not keep_screenshot_if_unique(ss_path, ss_filename, existing_hashes, 'uploaded'): + duplicates += 1 + continue existing.append(ss_filename) next_idx += 1 logger.info(f"Saved screenshot: {ss_filename}") - return ','.join(existing) - - -def try_standardize(path, media_type): - """Run the full post-download pipeline (format-matches-extension check, - size standardization, responsive variants). Pass 32.10 — what was - `standardize_downloaded_image` alone now goes through - `finalize_downloaded_image` so admin-uploaded PNG bytes inside a - `.webp` file get re-encoded to real WebP before they persist. - """ - try: - from services.image_utils import finalize_downloaded_image - finalize_downloaded_image(path, media_type) - except Exception as e: - logger.warning(f"Auto-resize {media_type} failed: {e}") + return ','.join(existing), duplicates diff --git a/services/game_metadata_service.py b/services/game_metadata_service.py index b632ed1f..3fb7e248 100644 --- a/services/game_metadata_service.py +++ b/services/game_metadata_service.py @@ -15,7 +15,7 @@ import logging from datetime import datetime -from services import apply_progress +from services import apply_progress, rpcs3_compat from services.game_utils import ( map_rating, RATING_SYSTEM_KEYS, @@ -239,6 +239,7 @@ def build_game_card(row, rpcs3_info=None, include_source_flag=False): 'psn_total': row['psn_total'], 'rpcs3_earned': rpcs3_info['earned'] if rpcs3_info else None, 'rpcs3_total': rpcs3_info['total'] if rpcs3_info else None, + 'rpcs3_compat': rpcs3_compat.status_for_game(row), }) # Pass FU.2 — emit pre-computed `srcset` strings so the JS card renderer diff --git a/services/game_query.py b/services/game_query.py index ed6e22f4..7cf7ad8d 100644 --- a/services/game_query.py +++ b/services/game_query.py @@ -35,14 +35,30 @@ def _as_list(value): return [v for v in value if v] if isinstance(value, (list, tuple)) else [value] -def get_retroachievements_info(game_title, system_folder): - """Get RetroAchievements info for a game.""" - try: - from scraper.retroachievements import check_retroachievements - return check_retroachievements(game_title, system_folder) - except ImportError: - logger.debug("RetroAchievements module not available") +def get_retroachievements_info(game): + """The RetroAchievements set a game is linked to, for its game page. + + Read from the link the RA refresh or a scrape stored (`ra_game_id` and + its counts), the same link the Library's RA badge shows. A fresh title + search here missed games the refresh had matched by ROM name, so a game + with the badge had no RA card (Metroid Prime on GameCube). RA is asked + only when the stored link carries no achievement count. + """ + ra_id = game.get('ra_game_id') + if not ra_id: return None + count = game.get('ra_achievement_count') or 0 + if count: + return { + 'id': ra_id, + 'achievement_count': count, + 'points': game.get('ra_points') or 0, + 'has_achievements': True, + 'url': f"https://retroachievements.org/game/{ra_id}", + } + try: + from scraper.retroachievements import get_game_info + return get_game_info(ra_id) except Exception as e: logger.debug(f"RetroAchievements lookup failed: {e}") return None @@ -322,7 +338,7 @@ def _build_games_query(params, count_only=False, ids_only=False, user_id=None, g.grac_rating, g.classind_rating, g.china_rating, g.critic_score, g.critic_score_count, g.user_score, g.user_score_count, g.completion_status, g.scraped, g.has_retroachievements, - g.is_bonus_disc, g.rom_path, g.ra_achievement_count, + g.is_bonus_disc, g.rom_path, g.disc_serial, g.ra_achievement_count, s.name AS system_name, s.folder AS system_folder, s.system_type, COALESCE(bc.bonus_count, 0) AS bonus_count, {VERSION_COLUMNS}, diff --git a/services/js_i18n_strings.py b/services/js_i18n_strings.py index d07c1559..7b6698e5 100644 --- a/services/js_i18n_strings.py +++ b/services/js_i18n_strings.py @@ -54,6 +54,7 @@ 'Bulk scraping complete!', 'CLZ Imports', 'CRT screen', + 'Can be completed with playable performance and no game-breaking glitches.', 'Cancel', 'Cancel Bulk Scrape', 'Cancel failed', @@ -80,6 +81,7 @@ 'Complete!', 'Completed', 'Completion Status', + 'Completion status', 'Completion status updated', 'Completionist', 'Confirm Action', @@ -107,6 +109,7 @@ 'Could not stop the running instance.', 'Could not unlink this version.', 'Could not write the summary.', + 'Critic Score ({count} reviews)', 'Currently set', 'Default', 'Default emulator for {system}', @@ -114,6 +117,7 @@ 'Detect failed: {error}', 'Detected path', 'Detecting…', + 'Does not start, or crashes the emulator.', 'Done', 'Done. Games asked: {total}. Names suggested: {proposals}.', 'Downloading 3D box art…', @@ -183,6 +187,7 @@ 'Generation complete: {generated} generated, {skipped} skipped, {failed} failed', 'Generation failed', 'Generation request failed', + 'Gets in game, but cannot be finished, has serious glitches or runs too slowly.', 'Go to Analytics', 'Go to Changelog', 'Go to Dashboard', @@ -209,6 +214,7 @@ 'Just synced', 'Keyboard Shortcuts', 'Kill', + 'Last tested {date}.', 'Launch failed: {error}', 'Launched (pid {pid})', 'Launched (replaced previous instance)', @@ -295,6 +301,11 @@ 'RA Data Cleared', 'ROM Files', 'ROM Tools', + 'RPCS3: Ingame', + 'RPCS3: Intro', + 'RPCS3: Loadable', + 'RPCS3: Nothing', + 'RPCS3: Playable', 'Ready', 'Refresh RetroAchievements', 'Refresh: {system}', @@ -368,6 +379,8 @@ 'Settings', 'Show keyboard shortcuts', 'Showing Bonus Discs', + 'Shows a black screen with a frame rate in the window title.', + 'Shows an image but does not get past the menus.', 'Shutting down server...', 'Standalone', 'Starting...', @@ -420,6 +433,7 @@ 'Uploading…', 'Use the system setting', 'Used for every {system} game unless the game sets its own. Applied when you press Play.', + 'User Score ({count} ratings)', 'Version unlinked.', 'Versions linked.', 'Will change?', @@ -436,6 +450,7 @@ '{confidence}% match', '{count} Achievements', '{count} regional versions', + '{count} sold', '{count} suggested groups. Untick any game that does not belong, then link.', '{fetched} fetched, {skipped} skipped, {failed} failed', '{job} Queued (#{position})', @@ -516,6 +531,7 @@ _('Bulk scraping complete!'), _('CLZ Imports'), _('CRT screen'), + _('Can be completed with playable performance and no game-breaking glitches.'), _('Cancel'), _('Cancel Bulk Scrape'), _('Cancel failed'), @@ -542,6 +558,7 @@ _('Complete!'), _('Completed'), _('Completion Status'), + _('Completion status'), _('Completion status updated'), _('Completionist'), _('Confirm Action'), @@ -569,6 +586,7 @@ _('Could not stop the running instance.'), _('Could not unlink this version.'), _('Could not write the summary.'), + _('Critic Score ({count} reviews)'), _('Currently set'), _('Default'), _('Default emulator for {system}'), @@ -576,6 +594,7 @@ _('Detect failed: {error}'), _('Detected path'), _('Detecting…'), + _('Does not start, or crashes the emulator.'), _('Done'), _('Done. Games asked: {total}. Names suggested: {proposals}.'), _('Downloading 3D box art…'), @@ -645,6 +664,7 @@ _('Generation complete: {generated} generated, {skipped} skipped, {failed} failed'), _('Generation failed'), _('Generation request failed'), + _('Gets in game, but cannot be finished, has serious glitches or runs too slowly.'), _('Go to Analytics'), _('Go to Changelog'), _('Go to Dashboard'), @@ -671,6 +691,7 @@ _('Just synced'), _('Keyboard Shortcuts'), _('Kill'), + _('Last tested {date}.'), _('Launch failed: {error}'), _('Launched (pid {pid})'), _('Launched (replaced previous instance)'), @@ -757,6 +778,11 @@ _('RA Data Cleared'), _('ROM Files'), _('ROM Tools'), + _('RPCS3: Ingame'), + _('RPCS3: Intro'), + _('RPCS3: Loadable'), + _('RPCS3: Nothing'), + _('RPCS3: Playable'), _('Ready'), _('Refresh RetroAchievements'), _('Refresh: {system}'), @@ -830,6 +856,8 @@ _('Settings'), _('Show keyboard shortcuts'), _('Showing Bonus Discs'), + _('Shows a black screen with a frame rate in the window title.'), + _('Shows an image but does not get past the menus.'), _('Shutting down server...'), _('Standalone'), _('Starting...'), @@ -882,6 +910,7 @@ _('Uploading…'), _('Use the system setting'), _('Used for every {system} game unless the game sets its own. Applied when you press Play.'), + _('User Score ({count} ratings)'), _('Version unlinked.'), _('Versions linked.'), _('Will change?'), @@ -898,6 +927,7 @@ _('{confidence}% match'), _('{count} Achievements'), _('{count} regional versions'), + _('{count} sold'), _('{count} suggested groups. Untick any game that does not belong, then link.'), _('{fetched} fetched, {skipped} skipped, {failed} failed'), _('{job} Queued (#{position})'), diff --git a/services/migrations/__init__.py b/services/migrations/__init__.py index 7d03dc5f..f7991cb9 100644 --- a/services/migrations/__init__.py +++ b/services/migrations/__init__.py @@ -47,6 +47,8 @@ '020_player', '021_official_names', '022_player_name', + '023_games_units_sold', + '024_games_release_dates', ] diff --git a/services/migrations/scripts/023_games_units_sold.py b/services/migrations/scripts/023_games_units_sold.py new file mode 100644 index 00000000..44735522 --- /dev/null +++ b/services/migrations/scripts/023_games_units_sold.py @@ -0,0 +1,15 @@ +# ============================================================================= +# Migration 023 — add games.units_sold (lifetime sales, filled by the AI) +# ============================================================================= +# Additive. The number of copies a game sold worldwide on the platform of its +# row, as an integer. NULL = not known. Filled by the AI during a scrape and by +# AI Fill; no other scraper supplies it, so nothing is backfilled here. +# ============================================================================= + +from services.migrations._helpers import _add_column_if_missing, _table_exists + + +def apply(conn): + cursor = conn.cursor() + if _table_exists(cursor, 'games'): + _add_column_if_missing(cursor, 'games', 'units_sold', 'INTEGER') diff --git a/services/migrations/scripts/024_games_release_dates.py b/services/migrations/scripts/024_games_release_dates.py new file mode 100644 index 00000000..f9f21455 --- /dev/null +++ b/services/migrations/scripts/024_games_release_dates.py @@ -0,0 +1,15 @@ +# ============================================================================= +# Migration 024 — add games.release_dates (release date in each region) +# ============================================================================= +# Additive. A JSON object keyed by region code, {"JP": "2002-11-17", ...}; see +# services/release_dates.py. NULL = not known. Filled by the AI during a scrape +# and by AI Fill; nothing is backfilled here. +# ============================================================================= + +from services.migrations._helpers import _add_column_if_missing, _table_exists + + +def apply(conn): + cursor = conn.cursor() + if _table_exists(cursor, 'games'): + _add_column_if_missing(cursor, 'games', 'release_dates', 'TEXT') diff --git a/services/release_dates.py b/services/release_dates.py new file mode 100644 index 00000000..be48f8f0 --- /dev/null +++ b/services/release_dates.py @@ -0,0 +1,134 @@ +# ============================================================================= +# RETRODB - Release dates by region +# ============================================================================= +# `games.release_dates` (migration 024) holds the date a game came out in each +# region, as a JSON object keyed by region code: +# {"JP": "2002-11-17", "NA": "2002-11-18", "EU": "2003-03-21"} +# A date is YYYY-MM-DD, or YYYY-MM / YYYY when only that much is known (as +# `release_date`, PASS-59-164). Only the AI fills it, in a scrape's gap-fill and +# in AI Fill. `release_date` stays the date of the row's own release. +# ============================================================================= + +import json +import re +from datetime import date, datetime + +# Region code -> CLDR territory, which names the region in the user's language +# (services.i18n.region_display_name). The order is the AI prompt's. +REGIONS = { + 'NA': '003', # North America + 'EU': '150', # Europe (PAL) + 'JP': 'JP', + 'AU': 'AU', + 'KR': 'KR', + 'CN': 'CN', + 'TW': 'TW', + 'BR': 'BR', + 'AS': '142', # Asia, when no single country is given + 'WW': '001', # one worldwide release +} + +# What a model writes instead of the code it was asked for. +_ALIASES = { + 'north america': 'NA', 'usa': 'NA', 'us': 'NA', 'america': 'NA', 'ntsc-u': 'NA', + 'europe': 'EU', 'pal': 'EU', 'eur': 'EU', + 'japan': 'JP', 'jpn': 'JP', 'ntsc-j': 'JP', + 'australia': 'AU', 'aus': 'AU', + 'korea': 'KR', 'south korea': 'KR', 'kor': 'KR', + 'china': 'CN', 'chn': 'CN', + 'taiwan': 'TW', + 'brazil': 'BR', 'bra': 'BR', + 'asia': 'AS', + 'world': 'WW', 'worldwide': 'WW', 'global': 'WW', +} + +_DATE_FORMATS = {4: '%Y', 7: '%Y-%m', 10: '%Y-%m-%d'} +_EARLIEST_YEAR = 1950 + + +def _region_code(name): + key = str(name or '').strip() + if key.upper() in REGIONS: + return key.upper() + return _ALIASES.get(key.lower()) + + +def _clean_date(value): + """A YYYY, YYYY-MM or YYYY-MM-DD date that exists and is not far in the + future, or None.""" + text = str(value or '').strip().replace('/', '-') + fmt = _DATE_FORMATS.get(len(text)) + if not fmt or not re.fullmatch(r'\d{4}(-\d{2}){0,2}', text): + return None + try: + parsed = datetime.strptime(text, fmt) + except ValueError: + return None + if not _EARLIEST_YEAR <= parsed.year <= date.today().year + 2: + return None + return text + + +def parse_release_dates(value): + """Regional release dates as {code: date}, in REGIONS order; {} when + nothing usable is given. + + Takes the stored JSON text, or what an AI returns: an object keyed by + region code or name, or a list of {"region", "date"} objects. Unknown + regions and impossible dates are dropped. + """ + if not value: + return {} + if isinstance(value, str): + try: + value = json.loads(value) + except ValueError: + return {} + if isinstance(value, list): + pairs = [(item.get('region'), item.get('date')) + for item in value if isinstance(item, dict)] + elif isinstance(value, dict): + pairs = list(value.items()) + else: + return {} + found = {} + for region, when in pairs: + code, cleaned = _region_code(region), _clean_date(when) + if code and cleaned and code not in found: + found[code] = cleaned + return {code: found[code] for code in REGIONS if code in found} + + +def release_dates_json(value): + """parse_release_dates() as the text stored in `games.release_dates`, + or None when there is nothing to store.""" + dates = parse_release_dates(value) + return json.dumps(dates, separators=(',', ':')) if dates else None + + +def _format_date(text, locale): + from babel.dates import format_date, format_skeleton + parsed = datetime.strptime(text, _DATE_FORMATS[len(text)]).date() + if len(text) == 10: + return format_date(parsed, 'medium', locale=locale) + if len(text) == 7: + return format_skeleton('yMMM', parsed, locale=locale) + return text + + +def display_release_dates(value, locale): + """The stored dates for a page, earliest first: a list of + {'code', 'region', 'date', 'iso'} with the region and date written for + `locale`.""" + from services.i18n import region_display_name + dates = parse_release_dates(value) + order = list(REGIONS) + rows = [] + for code, iso in sorted(dates.items(), key=lambda kv: (kv[1], order.index(kv[0]))): + try: + shown = _format_date(iso, str(locale or 'en')) + except Exception: + shown = iso + rows.append({'code': code, 'region': region_display_name(REGIONS[code], locale), + 'date': shown, 'iso': iso}) + return rows diff --git a/services/rpcs3_compat.py b/services/rpcs3_compat.py new file mode 100644 index 00000000..b036e91d --- /dev/null +++ b/services/rpcs3_compat.py @@ -0,0 +1,186 @@ +# ============================================================================= +# RETRODB - RPCS3 compatibility status for PS3 games +# ============================================================================= +# RPCS3 keeps the rpcs3.net compatibility list it downloads (Game List > +# right-click > Download Compatibility Database) as +# `/GuiConfigs/compat_database.dat`: the raw JSON of +# https://rpcs3.net/compatibility?api=v1&export, keyed by title ID without a +# dash (`BLUS30443`). RetroDB reads that file from the local RPCS3 install and +# never downloads it. Only PS3 games with a local ROM are looked up; imported +# games (PSN, Steam, Xbox, CLZ) have no disc to read a serial from. +# +# The status is not stored: the file is read again only when RPCS3 rewrites +# it, so a newer list shows at once. +# ============================================================================= + +from __future__ import annotations + +import json +import logging +import threading +import time +from pathlib import Path +from typing import Optional + +from services.database import get_db_with_context, query + +logger = logging.getLogger(__name__) + +# rpcs3.net's statuses, best first. +STATUSES = ('playable', 'ingame', 'intro', 'loadable', 'nothing') + +COMPAT_FILE = Path('GuiConfigs') / 'compat_database.dat' +_PATH_TTL = 60.0 + +_lock = threading.Lock() +_path_cache = {'at': 0.0, 'path': None} +_db_cache = {'key': None, 'results': {}} +_serial_fill = {'running': False} + + +def _candidate_dirs() -> list: + """RPCS3 config folders to try: those of each RPCS3 emulator row first, + then the usual places for a program found on no row.""" + from services.emulator_adapters import ADAPTERS + from services.launch_resolver import LaunchResolutionError, _resolve_binary + adapter = ADAPTERS['rpcs3'] + dirs = [] + rows = query("SELECT * FROM emulators WHERE binary_name = 'rpcs3' ORDER BY id") + for row in rows or []: + try: + dirs += adapter.data_dirs(Path(_resolve_binary(dict(row), None))) + except (LaunchResolutionError, OSError): + continue + return dirs + adapter.data_dirs(Path('rpcs3')) + + +def compat_file() -> Optional[Path]: + """The local RPCS3's compat_database.dat, or None. Looked for at most + once a minute.""" + now = time.monotonic() + with _lock: + if now - _path_cache['at'] < _PATH_TTL: + return _path_cache['path'] + found = None + try: + found = next((d / COMPAT_FILE for d in _candidate_dirs() + if (d / COMPAT_FILE).is_file()), None) + except Exception as e: # noqa: BLE001 — a page must render without it + logger.info(f"RPCS3 compatibility: cannot look for the list: {e}") + with _lock: + _path_cache.update(at=now, path=found) + return found + + +def _results() -> dict: + """The list's `results`, re-read only when the file changes.""" + path = compat_file() + if path is None: + return {} + try: + stat = path.stat() + except OSError: + return {} + key = (str(path), stat.st_mtime_ns, stat.st_size) + with _lock: + if _db_cache['key'] == key: + return _db_cache['results'] + results = {} + try: + data = json.loads(path.read_text(encoding='utf-8', errors='replace')) + if isinstance(data, dict) and int(data.get('return_code', -255)) >= 0 \ + and isinstance(data.get('results'), dict): + results = data['results'] + else: + logger.info(f"RPCS3 compatibility: {path} holds no results") + except (OSError, ValueError, TypeError) as e: + logger.info(f"RPCS3 compatibility: cannot read {path}: {e}") + with _lock: + _db_cache.update(key=key, results=results) + return results + + +def stamp() -> str: + """Changes when the list does: for ETags of responses that carry a status.""" + return str(_db_cache['key'] or '') if _results() else '' + + +def available() -> bool: + """True when a local RPCS3 has downloaded its compatibility list.""" + return bool(_results()) + + +def status_for_serial(serial) -> Optional[dict]: + """{'status': 'playable', 'date': '2024-05-01'} for a serial written + `BLUS-30443` or `BLUS30443`; None when it is not on the list.""" + if not serial: + return None + entry = _results().get(str(serial).replace('-', '').upper()) + if not isinstance(entry, dict): + return None + status = str(entry.get('status', '')).strip().lower() + if status not in STATUSES: + return None + return {'status': status, 'date': str(entry.get('date') or '')} + + +def applies_to(game) -> bool: + """A PS3 game with a local ROM. Imported games have a virtual rom_path.""" + from services.game_metadata_service import import_source_for_rom_path + keys = game.keys() if hasattr(game, 'keys') else () + folder = game['system_folder'] if 'system_folder' in keys else None + rom_path = game['rom_path'] if 'rom_path' in keys else None + return folder == 'ps3' and bool(rom_path) and import_source_for_rom_path(rom_path) is None + + +def status_for_game(game) -> Optional[dict]: + """The status of a games row (with system_folder, rom_path and + disc_serial), or None. Reads nothing from disc.""" + if not applies_to(game): + return None + return status_for_serial(game['disc_serial'] if 'disc_serial' in game.keys() else None) + + +def ensure_serial(game) -> None: + """Read and store the serial of one PS3 game that has none yet (the game + page). A list page calls fill_missing_serials instead.""" + if not applies_to(game) or game.get('disc_serial') is not None: + return + from services.standalone_settings import disc_ids + serial, _crc = disc_ids(game) + game['disc_serial'] = serial + + +def fill_missing_serials() -> None: + """Read, in the background, the serial of every local PS3 game not read + yet, so list pages show their status from the next load. Does nothing + when RPCS3 has no list, or a fill is already running.""" + if not available(): + return + with _lock: + if _serial_fill['running']: + return + _serial_fill['running'] = True + threading.Thread(target=_fill_serials, name='rpcs3-serials', daemon=True).start() + + +def _fill_serials() -> None: + from services.standalone_settings import disc_ids + try: + # Its own connection: the request one is not for other threads. + with get_db_with_context() as conn: + rows = conn.execute(""" + SELECT g.id, g.rom_path, g.disc_serial, g.disc_crc, s.folder AS system_folder + FROM games g JOIN systems s ON s.id = g.system_id + WHERE s.folder = 'ps3' AND g.disc_serial IS NULL + AND COALESCE(g.rom_path, '') != '' + """).fetchall() + for row in rows: + game = dict(row) + if applies_to(game): + disc_ids(game) + except Exception as e: # noqa: BLE001 — a background helper must not raise + logger.warning(f"RPCS3 compatibility: reading PS3 serials failed: {e}") + finally: + with _lock: + _serial_fill['running'] = False diff --git a/services/standalone_settings.py b/services/standalone_settings.py index 8a27f2f8..b7e0d2e6 100644 --- a/services/standalone_settings.py +++ b/services/standalone_settings.py @@ -228,7 +228,7 @@ def write_value(lines, spec, raw) -> list: # Which file (§4.6 "Which file") # ----------------------------------------------------------------------------- -def _disc_ids(game) -> tuple: +def disc_ids(game) -> tuple: """(serial, crc), read from the disc and stored the first time.""" if game.get('disc_serial') is not None: return game['disc_serial'], game.get('disc_crc') @@ -315,7 +315,7 @@ def _context(game_id) -> dict: ctx['reason'] = _('Run %(name)s once so it creates its settings, ' 'then play again.', name=emu['name']) return ctx - serial, crc = _disc_ids(game) + serial, crc = disc_ids(game) ctx['serial'] = serial ctx.update(_target(emu['binary_name'], data_dir, serial, crc)) return ctx diff --git a/static/asset_manifest.json b/static/asset_manifest.json index a7205ea1..e5b44ce7 100644 --- a/static/asset_manifest.json +++ b/static/asset_manifest.json @@ -1,7 +1,7 @@ { "css/core/fonts.css": "87796dac", - "css/main.min.css": "eeeccda7", + "css/main.min.css": "daf79d01", "js/core.bundle.js": "5d24a88d", - "js/games.bundle.js": "72c5ea2e", + "js/games.bundle.js": "6f44c831", "js/vendor/chart.umd.min.js": "48444a82" } \ No newline at end of file diff --git a/static/css/components/badges.css b/static/css/components/badges.css index 55382242..c14baa37 100644 --- a/static/css/components/badges.css +++ b/static/css/components/badges.css @@ -245,6 +245,26 @@ color: #3b82f6; } +/* Copies sold */ +.game-badge.sales-badge { + background: var(--cyan-bg); + border-color: var(--neon-cyan); + color: var(--neon-cyan); +} + +/* RPCS3 compatibility (PS3 games). The status class picks the colour. */ +.game-badge.rpcs3-compat { + --compat-color: var(--rpcs3-nothing); + background: color-mix(in srgb, var(--compat-color) 15%, transparent); + border-color: var(--compat-color); + color: var(--compat-color); +} + +.game-badge.rpcs3-compat.compat-playable { --compat-color: var(--rpcs3-playable); } +.game-badge.rpcs3-compat.compat-ingame { --compat-color: var(--rpcs3-ingame); } +.game-badge.rpcs3-compat.compat-intro { --compat-color: var(--rpcs3-intro); } +.game-badge.rpcs3-compat.compat-loadable { --compat-color: var(--rpcs3-loadable); } + /* ============================================================================= COMPLETION STATUS BADGES ============================================================================= */ diff --git a/static/css/core/variables.css b/static/css/core/variables.css index 3d4a8496..e5f30394 100644 --- a/static/css/core/variables.css +++ b/static/css/core/variables.css @@ -123,6 +123,13 @@ --purple-bg: rgba(168, 85, 247, 0.15); --magenta-bg: rgba(247, 37, 133, 0.15); + /* RPCS3 compatibility statuses, in rpcs3.net's own colours */ + --rpcs3-playable: #1ebc61; + --rpcs3-ingame: #f9b32f; + --rpcs3-intro: #e08a1e; + --rpcs3-loadable: #e74c3c; + --rpcs3-nothing: #8a9a9b; + /* PSN Trophy Variables */ --trophy-platinum: #7bb8d4; --trophy-gold: #cd9a2a; diff --git a/static/css/features/achievements.css b/static/css/features/achievements.css index fe2746fb..f0d3e141 100644 --- a/static/css/features/achievements.css +++ b/static/css/features/achievements.css @@ -50,7 +50,9 @@ } /* --- Games Grid --- */ -/* PASS-59-104 — the page caps at 4 columns of this grid. */ +/* PASS-59-104 — the page caps at 4 columns of this grid. Every tab of the + Achievements strip (RetroAchievements, RPCS3, PSN, Steam, Xbox) carries this + class and takes its card tokens from here, so the tabs share one width. */ .achievements-page { --card-min: 280px; --card-gap: var(--spacing-md); diff --git a/static/css/main.min.css b/static/css/main.min.css index 6e861ffd..7bd866d0 100644 --- a/static/css/main.min.css +++ b/static/css/main.min.css @@ -1 +1 @@ -:root{--primary-cyan:#4cc9f0;--primary-cyan-dark:#3a9fc2;--primary-cyan-light:#7dd8f5;--primary-cyan-glow:rgba(76,201,240,0.5);--secondary-magenta:#f72585;--secondary-magenta-dark:#c41d6a;--secondary-magenta-light:#f95fa3;--secondary-magenta-glow:rgba(247,37,133,0.5);--accent-purple:#7209b7;--accent-blue:#3a0ca3;--accent-violet:#560bad;--bg-darkest:#0a0e17;--bg-dark:#0f1419;--bg-darker:#0d1117;--bg-medium:#151b24;--bg-light:#1a222d;--bg-lighter:#212b38;--card-bg:#141a23;--card-bg-hover:#1a222d;--card-border:#2a3542;--card-border-hover:#3a4552;--text-primary:#e8eaed;--text-secondary:#9aa0a6;--text-muted:#6e7378;--text-accent:var(--primary-cyan);--border-color:#2a3542;--status-success:#00e676;--status-warning:#ffab00;--status-error:#ff5252;--status-info:var(--primary-cyan);--status-online:#00e676;--status-offline:#ff5252;--status-connected:var(--primary-cyan);--shadow-sm:0 2px 4px rgba(0,0,0,0.3);--shadow-md:0 4px 8px rgba(0,0,0,0.4);--shadow-lg:0 8px 16px rgba(0,0,0,0.5);--shadow-xl:0 12px 24px rgba(0,0,0,0.6);--shadow-glow-cyan:0 0 20px rgba(76,201,240,0.3);--shadow-glow-magenta:0 0 20px rgba(247,37,133,0.3);--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-full:9999px;--transition-fast:0.15s ease;--transition-normal:0.25s ease;--transition-slow:0.4s ease;--font-primary:'Rajdhani','Orbitron','Share Tech Mono','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',sans-serif;--font-heading:'Orbitron','Rajdhani','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',sans-serif;--font-mono:'Share Tech Mono','JetBrains Mono','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',monospace;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:16px;--spacing-lg:24px;--spacing-xl:32px;--spacing-2xl:48px;--sidebar-width:260px;--sidebar-collapsed-width:70px;--mobile-header-height:56px;--mobile-bar-height:60px;--sticky-top:0px;--neon-cyan:#4cc9f0;--neon-green:#22c55e;--neon-orange:#f59e0b;--neon-red:#ef4444;--neon-purple:#a855f7;--neon-gray:#64748b;--neon-blue:#3b82f6;--text-on-color:#fff;--font-display:var(--font-heading);--accent-cyan:var(--primary-cyan);--cyan-bg:rgba(76,201,240,0.15);--cyan-bg-light:rgba(76,201,240,0.1);--cyan-bg-strong:rgba(76,201,240,0.2);--green-bg:rgba(34,197,94,0.15);--red-bg:rgba(239,68,68,0.15);--orange-bg:rgba(245,158,11,0.15);--purple-bg:rgba(168,85,247,0.15);--magenta-bg:rgba(247,37,133,0.15);--trophy-platinum:#7bb8d4;--trophy-gold:#cd9a2a;--trophy-silver:#a8a8a8;--trophy-bronze:#cd7f32;--trophy-display-platinum:#a8c8dc;--trophy-display-gold:#ffd700;--trophy-display-silver:#c0c0c0;--trophy-display-bronze:#cd7f32;--trophy-display-text:#111;--rating-esrb:#64b5f6;--rating-esrb-border:rgba(25,118,210,0.3);--rating-esrb-bg:rgba(25,118,210,0.1);--rating-pegi:#ce93d8;--rating-pegi-border:rgba(123,31,162,0.3);--rating-pegi-bg:rgba(123,31,162,0.1);--completion-gold:#fbbf24}*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;scroll-behavior:smooth}body{font-family:var(--font-primary);background-color:var(--bg-darkest);color:var(--text-primary);line-height:1.6;min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul,ol{list-style:none}img,picture,video,canvas,svg{display:block;max-width:100%}button{font:inherit;cursor:pointer}input,textarea,select{font:inherit}input,button,textarea,select{font-family:inherit}a{color:inherit;text-decoration:none}@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Rajdhani:wght@300;400;500;600;700&family=Share+Tech+Mono&display=swap');h1,.h1{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);letter-spacing:1px;text-transform:uppercase}h2,.h2{font-family:var(--font-heading);font-size:1.5rem;font-weight:600;color:var(--text-primary);letter-spacing:0.5px}h3,.h3{font-family:var(--font-heading);font-size:1.25rem;font-weight:600;color:var(--text-primary)}h4,.h4{font-family:var(--font-primary);font-size:1.1rem;font-weight:600;color:var(--text-primary)}h5,.h5{font-family:var(--font-primary);font-size:1rem;font-weight:600;color:var(--text-secondary)}h6,.h6{font-family:var(--font-primary);font-size:0.9rem;font-weight:500;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px}.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.page-header h1{margin-bottom:var(--spacing-sm)}.page-header p{color:var(--text-secondary);font-size:1rem}.page-title{font-family:var(--font-heading);font-size:2.5rem;font-weight:700;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:4px;text-shadow:0 0 10px var(--primary-cyan-glow),0 0 20px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow);animation:glow-pulse 3s ease-in-out infinite}.page-subtitle{color:var(--text-secondary);font-size:1rem;margin-top:var(--spacing-xs)}.page-actions{display:flex;gap:var(--spacing-md)}.section-title{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:2px;display:flex;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.section-title::before{content:'';width:4px;height:20px;background:var(--primary-cyan);border-radius:2px}.breadcrumb{display:flex;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);font-size:0.9rem}.breadcrumb a{color:var(--text-secondary);text-decoration:none;transition:color var(--transition-fast)}.breadcrumb a:hover{color:var(--primary-cyan)}.breadcrumb-separator{color:var(--text-muted)}.breadcrumb-current{color:var(--text-primary)}code,pre,.mono{font-family:var(--font-mono)}code{padding:0.2em 0.4em;background:rgba(76,201,240,0.1);border-radius:var(--radius-sm);font-size:0.9em;color:var(--primary-cyan)}pre{padding:var(--spacing-md);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow-x:auto}pre code{padding:0;background:none}[data-theme="matrix"]{--primary-cyan:#00ff41;--primary-cyan-dark:#00cc33;--primary-cyan-light:#66ff88;--primary-cyan-glow:rgba(0,255,65,0.5);--secondary-magenta:#008f11;--secondary-magenta-dark:#006b0d;--secondary-magenta-light:#33b833;--secondary-magenta-glow:rgba(0,143,17,0.5);--accent-purple:#00aa22;--accent-blue:#004d00;--accent-violet:#009a1a;--bg-darkest:#000000;--bg-dark:#050505;--bg-darker:#0a0a0a;--bg-medium:#111111;--bg-light:#161616;--bg-lighter:#1a1a1a;--card-bg:#0a0a0a;--card-bg-hover:#111111;--card-border:#1a3a1a;--card-border-hover:#2a5a2a;--text-primary:#c0ffc0;--text-secondary:#80c080;--text-muted:#508050;--text-accent:var(--primary-cyan);--border-color:#1a3a1a;--status-success:#00ff41;--status-warning:#a0e000;--status-error:#ff3333;--status-info:var(--primary-cyan);--status-online:#00ff41;--status-offline:#ff3333;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(0,255,65,0.3);--shadow-glow-magenta:0 0 20px rgba(0,143,17,0.3);--neon-cyan:#00ff41;--neon-green:#00ff41;--neon-orange:#a0e000;--neon-red:#ff3333;--neon-purple:#33cc66;--neon-gray:#4a6a4a;--trophy-platinum:#66ffaa;--trophy-gold:#a0e060;--trophy-silver:#88bb88;--trophy-bronze:#77aa55;--toast-bg-from:rgba(0,10,0,0.95);--toast-bg-to:rgba(0,20,5,0.95);--toast-queued-bg-from:rgba(0,12,0,0.90);--toast-queued-bg-to:rgba(0,22,5,0.90)}[data-theme="matrix"] body::before{background:linear-gradient( to top right,rgba(0,255,65,0.06) 0%,rgba(0,143,17,0.04) 25%,rgba(0,80,0,0.03) 50%,rgba(0,0,0,0.02) 75%,transparent 100% )}[data-theme="matrix"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,255,65,0.02) 2px,rgba(0,255,65,0.02) 4px )}[data-theme="amber"]{--primary-cyan:#ffb000;--primary-cyan-dark:#cc8c00;--primary-cyan-light:#ffc944;--primary-cyan-glow:rgba(255,176,0,0.5);--secondary-magenta:#ff6600;--secondary-magenta-dark:#cc5200;--secondary-magenta-light:#ff8833;--secondary-magenta-glow:rgba(255,102,0,0.5);--accent-purple:#cc7700;--accent-blue:#995500;--accent-violet:#bb6600;--bg-darkest:#0a0800;--bg-dark:#0d0a02;--bg-darker:#0f0c04;--bg-medium:#151008;--bg-light:#1a150c;--bg-lighter:#211c12;--card-bg:#120e04;--card-bg-hover:#181208;--card-border:#3a2a10;--card-border-hover:#4a3a1a;--text-primary:#ffe0a0;--text-secondary:#c09050;--text-muted:#806030;--text-accent:var(--primary-cyan);--border-color:#3a2a10;--status-success:#ffcc00;--status-warning:#ff9900;--status-error:#ff4422;--status-info:var(--primary-cyan);--status-online:#ffcc00;--status-offline:#ff4422;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(255,176,0,0.3);--shadow-glow-magenta:0 0 20px rgba(255,102,0,0.3);--neon-cyan:#ffb000;--neon-green:#ddaa00;--neon-orange:#ff7700;--neon-red:#ff4422;--neon-purple:#ff8844;--neon-gray:#7a6a4a;--trophy-platinum:#ffe8aa;--trophy-gold:#ffcc44;--trophy-silver:#ccaa77;--trophy-bronze:#aa7733;--toast-bg-from:rgba(18,14,4,0.95);--toast-bg-to:rgba(28,20,8,0.95);--toast-queued-bg-from:rgba(20,16,4,0.90);--toast-queued-bg-to:rgba(30,22,8,0.90)}[data-theme="amber"] body::before{background:radial-gradient(ellipse 30% 20% at 50% 100%,rgba(255,230,100,0.2) 0%,rgba(255,200,50,0.12) 40%,transparent 100%),radial-gradient(ellipse 60% 40% at 50% 100%,rgba(255,160,0,0.1) 0%,rgba(255,100,0,0.05) 50%,transparent 100%),linear-gradient(to top,rgba(255,120,0,0.08) 0%,rgba(255,80,0,0.04) 20%,rgba(120,50,0,0.02) 45%,transparent 70%)}[data-theme="amber"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(255,176,0,0.03) 2px,rgba(255,176,0,0.03) 4px )}[data-theme="ocean"]{--primary-cyan:#4dabf7;--primary-cyan-dark:#3d8bd4;--primary-cyan-light:#74c0fa;--primary-cyan-glow:rgba(77,171,247,0.5);--secondary-magenta:#20c997;--secondary-magenta-dark:#1aaa7f;--secondary-magenta-light:#4dd8ad;--secondary-magenta-glow:rgba(32,201,151,0.5);--accent-purple:#5c7cfa;--accent-blue:#3b5bdb;--accent-violet:#4c6ef5;--bg-darkest:#040810;--bg-dark:#050a14;--bg-darker:#060c18;--bg-medium:#0a1220;--bg-light:#0e1828;--bg-lighter:#142030;--card-bg:#081018;--card-bg-hover:#0c1620;--card-border:#1a2a40;--card-border-hover:#2a3a55;--text-primary:#dce4f0;--text-secondary:#8899b0;--text-muted:#5a6a80;--text-accent:var(--primary-cyan);--border-color:#1a2a40;--status-success:#40c057;--status-warning:#fab005;--status-error:#fa5252;--status-info:var(--primary-cyan);--status-online:#40c057;--status-offline:#fa5252;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(77,171,247,0.3);--shadow-glow-magenta:0 0 20px rgba(32,201,151,0.3);--neon-cyan:#4dabf7;--neon-green:#40c057;--neon-orange:#fab005;--neon-red:#fa5252;--neon-purple:#7950f2;--neon-gray:#5a6a80;--trophy-platinum:#a5d8ff;--trophy-gold:#ffe066;--trophy-silver:#adb5bd;--trophy-bronze:#e8a44a;--toast-bg-from:rgba(6,12,22,0.95);--toast-bg-to:rgba(10,18,32,0.95);--toast-queued-bg-from:rgba(8,14,24,0.90);--toast-queued-bg-to:rgba(12,20,34,0.90)}[data-theme="ocean"] body::before{background:radial-gradient(circle at 70% 10%,rgba(220,235,255,0.4) 0%,rgba(200,220,250,0.2) 1.5%,rgba(150,190,240,0.08) 4%,transparent 10%),radial-gradient(circle at 70% 10%,rgba(77,171,247,0.08) 0%,transparent 18%),radial-gradient(ellipse 80% 50% at 50% 90%,rgba(32,201,151,0.04) 0%,rgba(77,171,247,0.05) 40%,transparent 100%),linear-gradient(to bottom,rgba(10,20,40,0.02) 0%,rgba(77,171,247,0.03) 55%,rgba(32,201,151,0.02) 100%)}[data-theme="ocean"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(77,171,247,0.02) 2px,rgba(77,171,247,0.02) 4px )}[data-theme="christian"]{--primary-cyan:#d4a843;--primary-cyan-dark:#b08a30;--primary-cyan-light:#e8c86a;--primary-cyan-glow:rgba(212,168,67,0.5);--secondary-magenta:#7b4db0;--secondary-magenta-dark:#5f3a8a;--secondary-magenta-light:#9b6dd0;--secondary-magenta-glow:rgba(123,77,176,0.5);--accent-purple:#6a3d9a;--accent-blue:#4a2d7a;--accent-violet:#7b4db0;--bg-darkest:#0c0810;--bg-dark:#0e0a14;--bg-darker:#110d18;--bg-medium:#16101f;--bg-light:#1c1528;--bg-lighter:#231c30;--card-bg:#120e1a;--card-bg-hover:#181222;--card-border:#2a1f3a;--card-border-hover:#3a2d4e;--text-primary:#f0e8d8;--text-secondary:#b0a090;--text-muted:#706050;--text-accent:var(--primary-cyan);--border-color:#2a1f3a;--status-success:#d4a843;--status-warning:#e8a030;--status-error:#c04040;--status-info:var(--primary-cyan);--status-online:#d4a843;--status-offline:#c04040;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(212,168,67,0.3);--shadow-glow-magenta:0 0 20px rgba(123,77,176,0.3);--neon-cyan:#d4a843;--neon-green:#c0a040;--neon-orange:#e8a030;--neon-red:#c04040;--neon-purple:#9b6dd0;--neon-gray:#6a5a50;--trophy-platinum:#f0e0c0;--trophy-gold:#d4a843;--trophy-silver:#b0a090;--trophy-bronze:#8a6a40;--toast-bg-from:rgba(14,10,20,0.95);--toast-bg-to:rgba(22,16,30,0.95);--toast-queued-bg-from:rgba(16,12,22,0.90);--toast-queued-bg-to:rgba(24,18,32,0.90)}[data-theme="christian"] body::before{background:radial-gradient(ellipse 40% 60% at 50% 0%,rgba(212,168,67,0.15) 0%,rgba(212,168,67,0.06) 30%,transparent 70%),radial-gradient(ellipse 70% 50% at 50% 10%,rgba(180,140,50,0.06) 0%,rgba(123,77,176,0.03) 50%,transparent 100%),linear-gradient(to top,rgba(100,60,20,0.04) 0%,rgba(80,40,120,0.02) 40%,transparent 70%)}[data-theme="christian"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(212,168,67,0.015) 2px,rgba(212,168,67,0.015) 4px )}[data-theme="bladerunner"]{--primary-cyan:#1a9fff;--primary-cyan-dark:#1480d4;--primary-cyan-light:#55b8ff;--primary-cyan-glow:rgba(26,159,255,0.5);--secondary-magenta:#ff2d7c;--secondary-magenta-dark:#d42060;--secondary-magenta-light:#ff5a9a;--secondary-magenta-glow:rgba(255,45,124,0.5);--accent-purple:#b040e0;--accent-blue:#1060c0;--accent-violet:#8830c0;--bg-darkest:#06080e;--bg-dark:#080a12;--bg-darker:#0a0d16;--bg-medium:#0e121c;--bg-light:#131824;--bg-lighter:#1a1f2e;--card-bg:#0a0e16;--card-bg-hover:#10141e;--card-border:#1a2236;--card-border-hover:#2a3248;--text-primary:#d8dce8;--text-secondary:#8892a8;--text-muted:#78809a;--text-accent:var(--primary-cyan);--border-color:#1a2236;--status-success:#22cc88;--status-warning:#ff8c30;--status-error:#ff2d5e;--status-info:var(--primary-cyan);--status-online:#22cc88;--status-offline:#ff2d5e;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(26,159,255,0.3);--shadow-glow-magenta:0 0 20px rgba(255,45,124,0.3);--neon-cyan:#1a9fff;--neon-green:#22cc88;--neon-orange:#ff8c30;--neon-red:#ff2d5e;--neon-purple:#b040e0;--neon-gray:#4a5268;--trophy-platinum:#c0d8ff;--trophy-gold:#ffd066;--trophy-silver:#a0aab8;--trophy-bronze:#d08a40;--toast-bg-from:rgba(8,10,18,0.95);--toast-bg-to:rgba(14,18,28,0.95);--toast-queued-bg-from:rgba(10,12,20,0.90);--toast-queued-bg-to:rgba(16,20,30,0.90)}[data-theme="bladerunner"] body::before{background:radial-gradient(ellipse 40% 30% at 20% 95%,rgba(255,45,124,0.08) 0%,rgba(255,45,124,0.03) 50%,transparent 100%),radial-gradient(ellipse 40% 30% at 80% 90%,rgba(26,159,255,0.08) 0%,rgba(26,159,255,0.03) 50%,transparent 100%),radial-gradient(ellipse 60% 40% at 50% 70%,rgba(130,40,180,0.04) 0%,transparent 80%),linear-gradient(to bottom,rgba(6,8,14,0.02) 0%,rgba(20,15,30,0.04) 60%,rgba(255,45,124,0.02) 100%)}[data-theme="bladerunner"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(26,159,255,0.02) 2px,rgba(26,159,255,0.02) 4px )}[data-theme="elite"]{--primary-cyan:#00ff00;--primary-cyan-dark:#00cc00;--primary-cyan-light:#66ff66;--primary-cyan-glow:rgba(0,255,0,0.5);--secondary-magenta:#cccccc;--secondary-magenta-dark:#999999;--secondary-magenta-light:#e0e0e0;--secondary-magenta-glow:rgba(204,204,204,0.5);--accent-purple:#00cc66;--accent-blue:#008844;--accent-violet:#00aa55;--bg-darkest:#000000;--bg-dark:#020202;--bg-darker:#040404;--bg-medium:#080808;--bg-light:#0c0c0c;--bg-lighter:#111111;--card-bg:#050505;--card-bg-hover:#0a0a0a;--card-border:#1a331a;--card-border-hover:#2a552a;--text-primary:#d0ffd0;--text-secondary:#80cc80;--text-muted:#508050;--text-accent:var(--primary-cyan);--border-color:#1a331a;--status-success:#00ff00;--status-warning:#ffff00;--status-error:#ff0000;--status-info:var(--primary-cyan);--status-online:#00ff00;--status-offline:#ff0000;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(0,255,0,0.3);--shadow-glow-magenta:0 0 20px rgba(204,204,204,0.3);--neon-cyan:#00ff00;--neon-green:#00ff00;--neon-orange:#ffff00;--neon-red:#ff0000;--neon-purple:#00cc66;--neon-gray:#4a6a4a;--trophy-platinum:#ccffcc;--trophy-gold:#ffff66;--trophy-silver:#aaccaa;--trophy-bronze:#88aa44;--toast-bg-from:rgba(0,5,0,0.95);--toast-bg-to:rgba(0,12,0,0.95);--toast-queued-bg-from:rgba(0,8,0,0.90);--toast-queued-bg-to:rgba(0,15,0,0.90)}[data-theme="elite"] body::before{background:radial-gradient(circle at 30% 20%,rgba(255,255,255,0.03) 0%,transparent 8%),radial-gradient(circle at 70% 15%,rgba(255,255,255,0.02) 0%,transparent 5%),radial-gradient(ellipse 40% 25% at 50% 95%,rgba(0,255,0,0.04) 0%,transparent 80%),linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,20,0,0.02) 100%)}[data-theme="elite"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,255,0,0.025) 2px,rgba(0,255,0,0.025) 4px )}.app-container{display:flex;min-height:100vh;position:relative;z-index:2}.main-content{flex:1;margin-left:var(--sidebar-width);padding:var(--spacing-xl);transition:margin-left var(--transition-normal);display:flex;justify-content:center;position:relative}.main-content.sidebar-collapsed{margin-left:var(--sidebar-collapsed-width)}.content-wrapper{width:65%;max-width:1400px;min-width:600px;position:relative;z-index:1}.content-wrapper.adaptive{width:100%;max-width:none;min-width:0}.main-content.adaptive{min-width:0}.page-box{max-width:var(--page-max,none);margin-inline:auto}.page-box.grid-4{--page-max:calc(5 * var(--card-min) + 4 * var(--card-gap) - 1px)}.page-box.data-page{--page-max:1400px}.page-box.form-page{--page-max:1100px}.page-box.detail-page{--page-max:1600px}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg)}.section-link{color:var(--primary-cyan);text-decoration:none;font-size:0.85rem;font-weight:500;text-transform:uppercase;letter-spacing:1px;transition:all var(--transition-fast)}.section-link:hover{text-shadow:0 0 10px var(--primary-cyan-glow)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--spacing-lg)}.systems-section{margin-bottom:var(--spacing-xl)}.sidebar{position:fixed;left:0;top:0;width:var(--sidebar-width);height:100vh;background:linear-gradient(180deg,color-mix(in srgb,var(--bg-medium) 88%,transparent) 0%,color-mix(in srgb,var(--bg-dark) 88%,transparent) 100% );backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-right:1px solid var(--card-border);display:flex;flex-direction:column;z-index:1000;transition:width var(--transition-normal)}.sidebar-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.logo{display:flex;align-items:center;gap:var(--spacing-md);text-decoration:none}.logo-icon{width:40px;height:40px;background:linear-gradient(135deg,var(--primary-cyan) 0%,var(--secondary-magenta) 100%);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:var(--shadow-glow-cyan)}.logo-text{font-family:var(--font-heading);font-size:1.5rem;font-weight:700;color:var(--text-primary);letter-spacing:2px}.logo-text span{color:var(--primary-cyan);text-shadow:0 0 10px var(--primary-cyan-glow)}.sidebar-nav{flex:1;padding:var(--spacing-md);overflow-y:auto}.nav-section{margin-bottom:var(--spacing-lg)}.nav-section-title{font-size:0.7rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;padding:var(--spacing-sm) var(--spacing-md);margin-bottom:var(--spacing-xs)}.nav-item{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);border-radius:var(--radius-md);color:var(--text-secondary);text-decoration:none;transition:all var(--transition-fast);margin-bottom:var(--spacing-xs);position:relative;overflow:hidden}.nav-item::before{content:'';position:absolute;left:0;top:0;height:100%;width:3px;background:var(--primary-cyan);transform:scaleY(0);transition:transform var(--transition-fast)}.nav-item:hover{background:var(--card-bg-hover);color:var(--text-primary)}.nav-item:hover::before{transform:scaleY(1)}.nav-item.active{background:linear-gradient(90deg,rgba(76,201,240,0.15) 0%,transparent 100%);color:var(--primary-cyan);border-left:3px solid var(--primary-cyan)}.nav-item.active::before{transform:scaleY(1)}.nav-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:1.1rem}.nav-text{font-weight:500;font-size:0.95rem}.sidebar-footer{padding:var(--spacing-md);border-top:1px solid var(--card-border)}.version-info{font-size:0.75rem;color:var(--primary-cyan);text-align:center;font-family:var(--font-mono);opacity:0.8;text-shadow:0 0 8px rgba(76,201,240,0.4);display:flex;align-items:center;justify-content:center;gap:0.5rem;transition:all var(--transition-fast);padding:0.5rem;border-radius:var(--radius-md);background:transparent;border:none;width:100%;cursor:pointer}.version-info:hover{background:rgba(76,201,240,0.1);color:var(--primary-cyan)}.version-icon{font-size:1rem}.upscale-status{display:flex;align-items:center;gap:0.5rem;margin-bottom:var(--spacing-sm);padding:0.5rem;font-size:0.75rem;color:var(--text-secondary);border:1px solid var(--card-border);border-radius:var(--radius-md)}.upscale-status[hidden]{display:none}.upscale-status-icon{font-size:1rem;color:var(--primary-cyan)}.user-indicator{display:flex;align-items:center;justify-content:space-between;gap:0.5rem;padding:0.75rem;background:rgba(76,201,240,0.05);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-top:var(--spacing-md)}.user-indicator-info{display:flex;align-items:center;gap:0.75rem;min-width:0;flex:1}.user-indicator-avatar{font-size:1.25rem;flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center}.user-avatar-img{width:100%;height:100%;border-radius:50%;object-fit:cover}.user-indicator-details{display:flex;flex-direction:column;min-width:0}.user-indicator-name{font-size:0.85rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.user-indicator-role{font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;padding:0.1rem 0.4rem;border-radius:var(--radius-full);width:fit-content}.user-indicator-role.admin{background:rgba(168,85,247,0.2);color:var(--neon-purple)}.user-indicator-role.editor{background:rgba(34,197,94,0.2);color:var(--neon-green)}.user-indicator-role.viewer{background:rgba(245,158,11,0.2);color:var(--neon-orange)}.user-indicator-meta{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:2px}.user-indicator-rank{font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.4px;padding:2px 8px;border-radius:var(--radius-full);text-decoration:none;white-space:nowrap;border:1px solid transparent;transition:all var(--transition-fast);line-height:1;display:inline-flex;align-items:center;gap:3px}.user-indicator-rank:hover{transform:translateY(-1px);filter:brightness(1.15)}.user-indicator-rank.rank-tier-0{background:rgba(110,115,120,0.18);color:var(--text-muted);border-color:rgba(110,115,120,0.35)}.user-indicator-rank.rank-tier-1{background:rgba(154,160,166,0.18);color:var(--text-secondary);border-color:rgba(154,160,166,0.4)}.user-indicator-rank.rank-tier-2{background:rgba(76,201,240,0.12);color:var(--primary-cyan);border-color:rgba(76,201,240,0.3)}.user-indicator-rank.rank-tier-3{background:rgba(76,201,240,0.18);color:var(--primary-cyan);border-color:rgba(76,201,240,0.45)}.user-indicator-rank.rank-tier-4{background:rgba(34,197,94,0.15);color:var(--neon-green);border-color:rgba(34,197,94,0.4)}.user-indicator-rank.rank-tier-5{background:rgba(245,158,11,0.15);color:var(--neon-orange);border-color:rgba(245,158,11,0.45)}.user-indicator-rank.rank-tier-6{background:rgba(168,85,247,0.15);color:var(--neon-purple);border-color:rgba(168,85,247,0.4)}.user-indicator-rank.rank-tier-7{background:rgba(168,85,247,0.22);color:var(--neon-purple);border-color:rgba(168,85,247,0.55)}.user-indicator-rank.rank-tier-8{background:rgba(247,37,133,0.18);color:var(--primary-magenta);border-color:rgba(247,37,133,0.5)}.user-indicator-rank.rank-tier-9{background:linear-gradient(135deg,rgba(247,37,133,0.3),rgba(168,85,247,0.3));color:var(--text-primary);border-color:var(--primary-magenta);box-shadow:0 0 8px rgba(247,37,133,0.35)}.user-logout-btn{font-size:1.1rem;padding:0.25rem;opacity:0.6;transition:all var(--transition-fast);text-decoration:none;flex-shrink:0}.user-logout-btn:hover{opacity:1;transform:scale(1.1)}.skip-link{position:absolute;top:0;left:0;padding:0.75rem 1.25rem;background:var(--primary-cyan);color:var(--bg-darkest);font-family:var(--font-heading);font-weight:700;text-decoration:none;border-radius:0 0 6px 0;z-index:10000;transform:translateY(-150%);transition:transform 0.15s ease-out}.skip-link:focus{transform:translateY(0);outline:2px solid var(--primary-magenta);outline-offset:2px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:var(--spacing-md) var(--spacing-lg);font-family:var(--font-primary);font-size:0.9rem;font-weight:600;text-transform:uppercase;letter-spacing:1px;border:2px solid;border-radius:var(--radius-md);cursor:pointer;transition:all 0.3s ease;text-decoration:none;position:relative;overflow:hidden}.btn-primary{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan);box-shadow:0 0 15px rgba(76,201,240,0.5),0 0 30px rgba(76,201,240,0.3),inset 0 0 20px rgba(76,201,240,0.1);text-shadow:0 0 10px rgba(76,201,240,0.8)}.btn-primary:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 20px rgba(76,201,240,0.7),0 0 40px rgba(76,201,240,0.4),inset 0 0 25px rgba(76,201,240,0.15);transform:translateY(-2px)}.btn-secondary{background:rgba(148,163,184,0.1);border-color:var(--neon-gray);color:#94a3b8;box-shadow:0 0 10px rgba(100,116,139,0.3),0 0 20px rgba(100,116,139,0.2)}.btn-secondary:hover{background:rgba(148,163,184,0.2);border-color:#94a3b8;color:#cbd5e1;box-shadow:0 0 15px rgba(148,163,184,0.5),0 0 30px rgba(148,163,184,0.3)}.btn-success{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green);box-shadow:0 0 15px rgba(34,197,94,0.5),0 0 30px rgba(34,197,94,0.3),inset 0 0 20px rgba(34,197,94,0.1);text-shadow:0 0 10px rgba(34,197,94,0.8)}.btn-success:hover{background:rgba(34,197,94,0.25);box-shadow:0 0 20px rgba(34,197,94,0.7),0 0 40px rgba(34,197,94,0.4),inset 0 0 25px rgba(34,197,94,0.15);transform:translateY(-2px)}.btn-warning{background:rgba(245,158,11,0.15);border-color:var(--neon-orange);color:var(--neon-orange);box-shadow:0 0 15px rgba(245,158,11,0.5),0 0 30px rgba(245,158,11,0.3),inset 0 0 20px rgba(245,158,11,0.1);text-shadow:0 0 10px rgba(245,158,11,0.8)}.btn-warning:hover{background:rgba(245,158,11,0.25);box-shadow:0 0 20px rgba(245,158,11,0.7),0 0 40px rgba(245,158,11,0.4),inset 0 0 25px rgba(245,158,11,0.15);transform:translateY(-2px)}.btn-danger{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red);box-shadow:0 0 15px rgba(239,68,68,0.5),0 0 30px rgba(239,68,68,0.3),inset 0 0 20px rgba(239,68,68,0.1);text-shadow:0 0 10px rgba(239,68,68,0.8)}.btn-danger:hover{background:rgba(239,68,68,0.25);box-shadow:0 0 20px rgba(239,68,68,0.7),0 0 40px rgba(239,68,68,0.4),inset 0 0 25px rgba(239,68,68,0.15);transform:translateY(-2px)}.btn-sm{padding:var(--spacing-sm) var(--spacing-md);font-size:0.8rem}.btn-xs{padding:3px 8px;font-size:0.7rem;line-height:1.2}.btn-lg{padding:var(--spacing-lg) var(--spacing-xl);font-size:1rem}.btn-icon{font-size:1.1em}.btn:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(76,201,240,0.5),0 0 15px rgba(76,201,240,0.3)}.btn:disabled,.btn[disabled]{opacity:0.4;cursor:not-allowed;pointer-events:none;box-shadow:none}input[type="file"]::file-selector-button{padding:8px 16px;margin-right:12px;background:rgba(76,201,240,0.15);border:2px solid var(--neon-cyan);border-radius:var(--radius-md);color:var(--neon-cyan);cursor:pointer;font-weight:600;font-family:var(--font-primary);text-transform:uppercase;letter-spacing:0.5px;font-size:0.85rem;box-shadow:0 0 15px rgba(76,201,240,0.5),0 0 30px rgba(76,201,240,0.3),inset 0 0 20px rgba(76,201,240,0.1);text-shadow:0 0 10px rgba(76,201,240,0.8);transition:all 0.3s ease}input[type="file"]::file-selector-button:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 20px rgba(76,201,240,0.7),0 0 40px rgba(76,201,240,0.4),inset 0 0 25px rgba(76,201,240,0.15)}.pagination{display:flex;justify-content:center;align-items:center;gap:var(--spacing-sm);margin-top:var(--spacing-xl)}.pagination-btn{padding:var(--spacing-sm) var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);cursor:pointer;transition:all var(--transition-fast)}.pagination-btn:hover{border-color:var(--primary-cyan);color:var(--primary-cyan)}.pagination-btn.active{background:var(--primary-cyan);border-color:var(--primary-cyan);color:var(--bg-darkest)}.pagination-btn:disabled{opacity:0.5;cursor:not-allowed}.back-to-top-btn{position:fixed;bottom:30px;right:30px;width:50px;height:50px;border-radius:50%;background:rgba(20,26,35,0.95);border:2px solid var(--neon-cyan);color:var(--neon-cyan);font-size:1.5rem;cursor:pointer;opacity:0;visibility:hidden;transform:translateY(20px);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s ease,background 0.3s ease,box-shadow 0.3s ease;z-index:9998;display:flex;align-items:center;justify-content:center;box-shadow:0 0 15px rgba(76,201,240,0.3),inset 0 0 10px rgba(76,201,240,0.1)}.back-to-top-btn.visible{opacity:1;visibility:visible;transform:translateY(0)}.back-to-top-btn:hover{background:rgba(76,201,240,0.2);box-shadow:0 0 25px rgba(76,201,240,0.5),inset 0 0 15px rgba(76,201,240,0.2);transform:translateY(-3px)}.back-to-top-btn:active{transform:translateY(0)}.back-to-top-icon{font-weight:bold;text-shadow:0 0 10px rgba(76,201,240,0.8)}.btn-group{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-md)}.btn-danger-outline{background:rgba(239,68,68,0.1);border:2px solid var(--neon-red);color:var(--neon-red);box-shadow:0 0 10px rgba(239,68,68,0.3),0 0 20px rgba(239,68,68,0.2);text-shadow:0 0 8px rgba(239,68,68,0.6)}.btn-danger-outline:hover{background:rgba(239,68,68,0.2);box-shadow:0 0 15px rgba(239,68,68,0.5),0 0 30px rgba(239,68,68,0.3)}.form-input,input[type="text"],input[type="number"],input[type="email"],input[type="password"],input[type="search"],input[type="date"],textarea{width:100%;padding:0.75rem 1rem;background:var(--bg-darker,#0d1117);border:1px solid var(--border-color,#2a3542);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:border-color 0.2s ease,box-shadow 0.2s ease}.form-input:focus,input[type="text"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="search"]:focus,input[type="date"]:focus,textarea:focus{outline:none;border-color:var(--neon-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15),0 0 15px rgba(76,201,240,0.2)}.form-input::placeholder,input::placeholder,textarea::placeholder{color:var(--text-muted)}select,.form-select{width:100%;padding:0.75rem 2.5rem 0.75rem 1rem;background:var(--bg-darker,#0d1117);border:1px solid var(--border-color,#2a3542);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 1rem center;transition:border-color 0.2s ease,box-shadow 0.2s ease}select:focus,.form-select:focus{outline:none;border-color:var(--neon-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15),0 0 15px rgba(76,201,240,0.2)}select:hover,.form-select:hover{border-color:var(--neon-cyan)}select option{background:var(--bg-medium);color:var(--text-primary);padding:0.5rem}.styled-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;padding:0.75rem 2.5rem 0.75rem 1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 1rem center;transition:border-color 0.2s ease,box-shadow 0.2s ease}.styled-select:hover{border-color:var(--primary-cyan)}.styled-select:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.toggle-switch{position:relative;display:inline-block;width:52px;height:28px;flex-shrink:0}.toggle-switch input{opacity:0;width:0;height:0}.toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:var(--neon-red);border-radius:28px;transition:all 0.3s ease;box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(239,68,68,0.3)}.toggle-slider:before{position:absolute;content:"";height:22px;width:22px;left:3px;bottom:3px;background:white;border-radius:50%;transition:all 0.3s ease;box-shadow:0 2px 4px rgba(0,0,0,0.3)}.toggle-switch input:checked + .toggle-slider{background:var(--neon-green);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 15px rgba(34,197,94,0.5)}.toggle-switch input:checked + .toggle-slider:before{transform:translateX(24px)}.toggle-switch input:focus + .toggle-slider{box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 0 2px rgba(76,201,240,0.3)}.toggle-item{display:flex;align-items:center;gap:0.75rem}.toggle-label{color:var(--text-secondary);font-size:0.9rem;cursor:pointer;user-select:none;transition:color 0.2s ease}.toggle-label.active{color:var(--text-primary)}.toggle-field{display:flex;align-items:center;gap:var(--spacing-md)}.toggle-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-sm)}.toggle-switch-wrapper .toggle-label-text{font-size:0.85rem;color:var(--text-secondary)}.form-group{margin-bottom:var(--spacing-md)}.form-label{display:block;font-size:0.85rem;font-weight:600;color:var(--text-secondary);margin-bottom:var(--spacing-xs)}.filter-select{padding:var(--spacing-md) var(--spacing-lg);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;transition:all var(--transition-fast);min-width:150px}.filter-select:focus{outline:none;border-color:var(--primary-cyan)}.locked-path-wrapper{display:flex;align-items:center;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);overflow:hidden;flex:1}.locked-path-wrapper:focus-within{border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.locked-path-prefix{padding:0.75rem 0 0.75rem 1rem;color:var(--text-muted);font-family:var(--font-mono);font-size:0.9rem;background:rgba(76,201,240,0.05);border-right:1px solid var(--border-color);white-space:nowrap;user-select:none}.locked-path-wrapper input{flex:1;padding:0.75rem 1rem;background:transparent;border:none;color:var(--text-primary);font-family:var(--font-mono);font-size:0.9rem;outline:none}.locked-path-wrapper input::placeholder{color:var(--text-muted)}.edit-section{margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.edit-section:last-child{border-bottom:none;margin-bottom:0}.edit-section-title{color:var(--primary-cyan);font-family:var(--font-heading);font-size:1rem;font-weight:600;margin-bottom:var(--spacing-lg);padding-bottom:var(--spacing-sm);border-bottom:1px solid rgba(76,201,240,0.3)}.edit-field{margin-bottom:var(--spacing-md)}.edit-field label{display:block;color:var(--text-primary);font-weight:500;font-size:0.9rem;margin-bottom:6px}.edit-field .field-tip{display:block;font-size:0.75rem;color:var(--text-muted);margin-top:4px;font-style:italic}.edit-field .title-lock-check,.gem-field .title-lock-check{display:flex;align-items:center;gap:var(--spacing-xs);margin:var(--spacing-xs) 0 0;font-size:0.8rem;font-weight:normal;color:var(--text-muted);text-transform:none;letter-spacing:normal;cursor:pointer}.edit-field .title-lock-check input,.gem-field .title-lock-check input{width:auto;padding:0;margin:0}.edit-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md) var(--spacing-lg)}.edit-grid .full-width{grid-column:1 / -1}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-md)}.form-help{font-size:0.8rem;color:var(--text-muted);margin-top:var(--spacing-xs)}.toggle-grid{display:flex;flex-wrap:wrap;gap:1.5rem}.card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);transition:all var(--transition-normal)}.card:hover{border-color:var(--card-border-hover);box-shadow:var(--shadow-lg)}.card + .card{margin-top:1.5rem}.card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-md);border-bottom:1px solid var(--card-border)}.card-title{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:0.5rem;text-transform:uppercase;letter-spacing:1px}.card-body{color:var(--text-secondary)}.glass-card,.card.glass,.glass-panel{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(76,201,240,0.2)}.glass-light,.glass-panel-light{background:rgba(20,26,35,0.6) !important;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.glass-medium,.glass-panel-medium{background:rgba(20,26,35,0.8) !important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.glass-dark,.glass-panel-dark{background:rgba(20,26,35,0.9) !important;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.glass-panel-hover:hover{background:rgba(20,26,35,0.85);border-color:var(--card-border-hover);box-shadow:var(--shadow-lg),0 0 20px rgba(76,201,240,0.1)}.system-card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);text-decoration:none;transition:all var(--transition-normal);display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;overflow:hidden}.system-card::before{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(180deg,transparent 60%,rgba(76,201,240,0.05) 100%);opacity:0;transition:opacity var(--transition-normal)}.system-card:hover{border-color:var(--primary-cyan);transform:translateY(-4px);box-shadow:var(--shadow-lg),0 0 20px rgba(76,201,240,0.1)}.system-card:hover::before{opacity:1}.system-logo{width:80px;height:80px;margin-bottom:var(--spacing-md);display:flex;align-items:center;justify-content:center}.system-logo img{max-width:100%;max-height:100%;object-fit:contain;filter:brightness(0.9);transition:all var(--transition-normal)}.system-card:hover .system-logo img{filter:brightness(1) drop-shadow(0 0 8px var(--primary-cyan-glow));transform:scale(1.05)}.system-logo-placeholder{width:80px;height:80px;background:var(--bg-lighter);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:2.5rem;color:var(--text-muted)}.system-name{font-family:var(--font-heading);font-size:1rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-xs);text-transform:uppercase;letter-spacing:1px}.system-info{display:flex;align-items:center;gap:var(--spacing-xs);font-size:0.8rem;color:var(--text-secondary)}.system-info-icon{color:var(--primary-cyan)}.section{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.status-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.status-item{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md) 0;border-bottom:1px solid var(--card-border)}.status-item:last-child{border-bottom:none}.status-info{display:flex;align-items:center;gap:var(--spacing-md)}.status-indicator{width:10px;height:10px;border-radius:50%;animation:pulse-status 2s ease-in-out infinite}.status-indicator.online{background:var(--status-online);box-shadow:0 0 10px var(--status-online)}.status-indicator.offline{background:var(--status-offline);box-shadow:0 0 10px var(--status-offline);animation:none}.status-name{color:var(--text-primary);font-weight:500}.status-badge{padding:var(--spacing-xs) var(--spacing-md);border-radius:var(--radius-full);font-size:0.7rem;font-weight:600;text-transform:uppercase;letter-spacing:1px}.status-badge.online{background:rgba(0,230,118,0.15);color:var(--status-online);border:1px solid rgba(0,230,118,0.3)}.status-badge.connected{background:rgba(76,201,240,0.15);color:var(--primary-cyan);border:1px solid rgba(76,201,240,0.3)}.status-badge.offline{background:rgba(255,82,82,0.15);color:var(--status-offline);border:1px solid rgba(255,82,82,0.3)}.scraper-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-xl);margin-bottom:var(--spacing-xl)}.scraper-section h2{font-family:var(--font-heading);font-size:1.2rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-lg);padding-bottom:var(--spacing-md);border-bottom:1px solid var(--card-border)}.scraper-form{margin-bottom:var(--spacing-lg)}.system-controller-card{transition:border-color 0.2s ease,box-shadow 0.2s ease}.system-controller-card:hover{border-color:rgba(76,201,240,0.3);box-shadow:0 0 10px rgba(76,201,240,0.1)}.controller-item{transition:background 0.2s ease,transform 0.15s ease}.controller-item:hover{background:rgba(76,201,240,0.1) !important;transform:translateX(4px)}.controller-details{border-left:2px solid var(--neon-cyan)}.add-controller-form{border:1px solid rgba(34,197,94,0.2)}.add-controller-form:hover{border-color:rgba(34,197,94,0.3)}.badge{display:inline-flex;align-items:center;gap:0.5rem;padding:0.35rem 0.75rem;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-full);font-size:0.8rem;font-weight:500;color:var(--neon-cyan);white-space:nowrap}.badge-success{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green)}.badge-warning{background:rgba(245,158,11,0.15);border-color:var(--neon-orange);color:var(--neon-orange)}.badge-danger{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red)}.badge-purple{background:rgba(168,85,247,0.15);border-color:var(--neon-purple);color:var(--neon-purple)}.badge-secondary{background:rgba(100,116,139,0.15);border-color:var(--neon-gray);color:#94a3b8}.badge-removable{padding-right:0.5rem}.badge-remove{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:0.25rem;background:transparent;border:none;border-radius:50%;color:inherit;cursor:pointer;font-size:1rem;line-height:1;opacity:0.7;transition:all 0.2s ease}.badge-remove:hover{opacity:1;background:rgba(255,255,255,0.1)}.game-badge{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:0 10px;border-radius:4px;font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;border:1px solid;white-space:nowrap;line-height:1;height:28px;box-sizing:border-box}.game-badge.esrb-e{background:rgba(32,178,170,0.15);border-color:#20b2aa;color:#20b2aa}.game-badge.esrb-e10{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.esrb-t{background:rgba(234,179,8,0.15);border-color:#eab308;color:#eab308}.game-badge.esrb-m{background:rgba(249,115,22,0.15);border-color:#f97316;color:#f97316}.game-badge.esrb-ao{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}.game-badge.esrb-rp{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.pegi-3{background:rgba(32,178,170,0.15);border-color:#20b2aa;color:#20b2aa}.game-badge.pegi-7{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.pegi-12{background:rgba(234,179,8,0.15);border-color:#eab308;color:#eab308}.game-badge.pegi-16{background:rgba(249,115,22,0.15);border-color:#f97316;color:#f97316}.game-badge.pegi-18{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}.game-badge.age-rating-badge{background:transparent;border-color:transparent;color:var(--text-secondary);padding:0}.game-badge.age-rating-badge .age-rating-img{height:28px;width:auto;display:block;filter:drop-shadow(0 1px 3px rgba(0,0,0,0.4))}.game-badge.age-rating-badge.crossmapped{opacity:0.75}.game-badge.genre{background:rgba(168,85,247,0.15);border-color:#a855f7;color:#a855f7}.game-badge.year{background:rgba(76,201,240,0.15);border-color:#4cc9f0;color:#4cc9f0}.game-badge.platform{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.region{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.players{background:rgba(59,130,246,0.15);border-color:#3b82f6;color:#3b82f6}.game-badge.completion{font-size:0.7rem}.game-badge.completion.not-started{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.completion.in-progress{background:rgba(245,158,11,0.15);border-color:#f59e0b;color:#f59e0b}.game-badge.completion.played{background:rgba(59,130,246,0.15);border-color:#3b82f6;color:#3b82f6}.game-badge.completion.completed{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.completion.\31 00-percent,.game-badge.completion.hundred-percent{background:linear-gradient(135deg,rgba(255,215,0,0.25) 0%,rgba(255,170,0,0.25) 100%);border-color:#ffd700;color:#ffd700;text-shadow:0 0 8px rgba(255,215,0,0.5);box-shadow:0 0 10px rgba(255,215,0,0.3),inset 0 0 10px rgba(255,215,0,0.1)}.game-badge.score-badge{font-weight:700;min-width:50px}.game-badge.score-badge.score-good{background:rgba(34,197,94,0.2);border-color:#22c55e;color:#22c55e}.game-badge.score-badge.score-mixed{background:rgba(234,179,8,0.2);border-color:#eab308;color:#fcd34d}.game-badge.score-badge.score-bad{background:rgba(239,68,68,0.2);border-color:#ef4444;color:#f87171}.meta-badge{padding:var(--spacing-xs) var(--spacing-md);border-radius:var(--radius-full);font-size:0.8rem;font-weight:600}.meta-badge.system{background:linear-gradient(135deg,var(--primary-cyan),var(--primary-cyan-dark));color:var(--bg-darkest)}.meta-badge.year{background:var(--bg-lighter);color:var(--text-primary);border:1px solid var(--card-border)}.meta-badge.genre{background:rgba(114,9,183,0.2);color:#b388ff;border:1px solid rgba(114,9,183,0.3)}.metadata-status{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs) var(--spacing-sm);border-radius:var(--radius-full);font-size:0.7rem;font-weight:600;text-transform:uppercase}.metadata-status.complete{background:rgba(0,230,118,0.15);color:var(--status-success)}.metadata-status.missing{background:rgba(255,171,0,0.15);color:var(--status-warning)}.source-badge{font-weight:600;text-transform:uppercase}.source-badge.thegamesdb{background:rgba(76,175,80,0.2);color:#81c784}.source-badge.igdb{background:rgba(33,150,243,0.2);color:#64b5f6}.source-badge.primary-source{border:2px solid currentColor;position:relative}.source-badge.primary-source::before{content:"★ ";font-size:0.8em}.source-badge.fallback-source{opacity:0.75;border:1px dashed currentColor}.psn-platform-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--radius-sm);font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;border:1px solid;width:fit-content}.platform-ps3{background:rgba(205,154,42,0.15);color:#cd9a2a;border-color:rgba(205,154,42,0.3)}.platform-ps4{background:rgba(0,112,209,0.15);color:#4da6ff;border-color:rgba(0,112,209,0.3)}.platform-ps5{background:rgba(76,201,240,0.15);color:#4cc9f0;border-color:rgba(76,201,240,0.3)}.platform-psvita,.platform-vita{background:rgba(168,85,247,0.15);color:#a855f7;border-color:rgba(168,85,247,0.3)}.data-table{width:100%;border-collapse:collapse}@media (max-width:600px){.page-box .data-table{display:block;overflow-x:auto}}@media (max-width:600px){.page-box .data-table.stack-narrow,.data-table.stack-narrow tbody,.data-table.stack-narrow tr,.data-table.stack-narrow td{display:block;overflow-x:visible}.data-table.stack-narrow thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.data-table.stack-narrow tr{margin-bottom:var(--spacing-sm);padding:var(--spacing-xs) 0;border:1px solid var(--card-border);border-radius:var(--radius-md)}.data-table.stack-narrow tr[hidden]{display:none}.data-table.stack-narrow td{padding:var(--spacing-xs) var(--spacing-sm);border-bottom:none;overflow-wrap:anywhere}.data-table.stack-narrow td[data-label]{display:flex;gap:var(--spacing-sm)}.data-table.stack-narrow td[data-label]::before{content:attr(data-label);flex:0 0 35%;color:var(--text-secondary);font-size:0.8rem;font-weight:600}}.data-table th{background:var(--bg-darker,#0d1117);color:var(--text-secondary);font-weight:600;text-align:left;padding:0.75rem 1rem;font-size:0.8rem;text-transform:uppercase;letter-spacing:0.5px;border-bottom:2px solid var(--card-border);position:sticky;top:0;z-index:10}.data-table td{padding:0.75rem 1rem;border-bottom:1px solid var(--card-border);color:var(--text-primary);font-size:0.9rem}.data-table tbody tr:hover{background:rgba(76,201,240,0.05)}.games-table-container{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);overflow:hidden}.games-table{width:100%;border-collapse:collapse}.games-table thead{background:linear-gradient(90deg,var(--bg-medium) 0%,var(--bg-light) 100%)}.games-table th{padding:var(--spacing-md) var(--spacing-lg);text-align:left;font-family:var(--font-heading);font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;border-bottom:1px solid var(--card-border)}.games-table tbody tr{border-bottom:1px solid var(--card-border);transition:background-color var(--transition-fast)}.games-table tbody tr:hover{background:var(--card-bg-hover)}.games-table tbody tr:last-child{border-bottom:none}.games-table td{padding:var(--spacing-md) var(--spacing-lg);vertical-align:middle}.game-row-boxart{width:60px;height:80px;object-fit:cover;border-radius:var(--radius-sm);border:1px solid var(--card-border)}.game-row-boxart.boxart-3d{object-fit:contain;background:var(--bg-medium)}.game-row-boxart-placeholder{width:60px;height:80px;background:var(--bg-lighter);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:1.5rem}.game-row-title{color:var(--text-primary);font-weight:600;text-decoration:none;transition:color var(--transition-fast)}.game-row-title:hover{color:var(--primary-cyan)}.game-row-system{color:var(--text-secondary);font-size:0.85rem}.game-row-genre{color:var(--text-secondary);font-size:0.85rem}.game-row-year{color:var(--text-secondary);font-size:0.85rem;font-family:var(--font-mono)}.score-list-section{background:rgba(0,0,0,0.2);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.score-list-section h4{color:var(--text-primary);font-family:var(--font-heading);font-size:1.1rem;margin-bottom:var(--spacing-md)}.score-list{display:flex;flex-direction:column;gap:var(--spacing-sm)}.score-list-item{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);text-decoration:none;transition:var(--transition-fast)}.score-list-item:hover{border-color:var(--primary-cyan);background:var(--card-bg-hover)}.score-game-info{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.score-game-title{color:var(--text-primary);font-weight:600;font-size:0.95rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.score-game-system{color:var(--text-muted);font-size:0.8rem}.score-game-scores{display:flex;gap:var(--spacing-sm);flex-shrink:0}.score-lists-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-xl);margin-top:var(--spacing-xl)}.bulk-filter-section{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.bulk-action-section{padding:var(--spacing-lg);background:var(--bg-dark);border-radius:var(--radius-md);border:1px solid var(--card-border)}.bulk-preview-count{font-size:1.5rem;font-weight:700;color:var(--primary-cyan);margin-bottom:var(--spacing-sm)}.bulk-game-list{max-height:300px;overflow-y:auto;margin-top:var(--spacing-md)}.bulk-game-item{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.bulk-game-item:last-child{border-bottom:none}.tool-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.tool-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.tool-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.tool-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.tool-tab .tab-icon{font-size:1.1rem}.tool-tab .tab-label{}.system-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.system-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.625rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-align:center;white-space:nowrap}.system-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.system-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan);font-weight:600}.system-tab .tab-count{font-size:0.75rem;padding:0.15rem 0.5rem;background:rgba(0,0,0,0.3);border-radius:var(--radius-full);color:var(--text-secondary)}.system-tab.active .tab-count{background:var(--primary-cyan);color:var(--bg-darkest)}@media (max-width:768px){.tool-tabs,.system-tabs{gap:var(--spacing-xs);padding:var(--spacing-xs)}.tool-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.tool-tab .tab-label{display:none}.tool-tab .tab-icon{font-size:1.25rem}.system-tab{padding:0.5rem 0.75rem;font-size:0.8rem}}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:1000;opacity:0;visibility:hidden;transition:all 0.3s ease}.modal-overlay.active{opacity:1;visibility:visible}.modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);max-width:90%;max-height:90vh;overflow:auto;box-shadow:0 0 50px rgba(76,201,240,0.2)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.modal-header h2{font-family:var(--font-heading);font-size:1.25rem;color:var(--neon-cyan)}.modal-close,.custom-modal-close,.about-modal-close,.game-detail-modal-close,.game-edit-modal-close,.modal-close-btn,.archive-modal-close,.modal .modal-close{background:none;border:none;font-size:1.75rem;font-weight:normal;color:var(--text-secondary);cursor:pointer;padding:0;line-height:1;transition:color 0.2s}.modal-close:hover,.custom-modal-close:hover,.about-modal-close:hover,.game-detail-modal-close:hover,.game-edit-modal-close:hover,.modal-close-btn:hover,.archive-modal-close:hover,.modal .modal-close:hover{color:var(--neon-red)}.modal-close:focus-visible,.custom-modal-close:focus-visible,.about-modal-close:focus-visible,.game-detail-modal-close:focus-visible,.game-edit-modal-close:focus-visible,.modal-close-btn:focus-visible,.archive-modal-close:focus-visible,.modal .modal-close:focus-visible{outline:2px solid rgba(76,201,240,0.5);outline-offset:2px}.modal-body{padding:var(--spacing-lg)}.modal-footer{display:flex;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg);border-top:1px solid var(--card-border)}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:10000;align-items:center;justify-content:center}.modal.active{display:flex}.modal-content{max-width:90%;max-height:90%;border-radius:var(--radius-lg)}.modal-nav{position:absolute;top:50%;transform:translateY(-50%);width:50px;height:50px;background:var(--card-bg);border:none;border-radius:50%;color:var(--text-primary);font-size:1.5rem;cursor:pointer;transition:all var(--transition-fast)}.modal-nav:hover{background:var(--primary-cyan);color:var(--bg-darkest)}.modal-nav.prev{left:var(--spacing-lg)}.modal-nav.next{right:var(--spacing-lg)}.custom-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:10100;align-items:center;justify-content:center}.custom-modal.active{display:flex}.custom-modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);width:90%;max-width:500px;box-shadow:0 20px 60px rgba(0,0,0,0.5)}.custom-modal-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);display:flex;justify-content:space-between;align-items:center}.custom-modal-header h3{margin:0;font-family:var(--font-heading);color:var(--primary-cyan);font-size:1.1rem}.custom-modal-body{padding:var(--spacing-lg);color:var(--text-secondary);line-height:1.6}.custom-modal-body code{word-break:break-all;overflow-wrap:break-word;display:inline-block;max-width:100%}.custom-modal-body ul{max-width:100%;overflow:hidden}.custom-modal-body li{word-break:break-word;overflow-wrap:break-word}.custom-modal-footer{padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border);display:flex;justify-content:flex-end;gap:var(--spacing-sm)}.about-modal-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.8);z-index:10000;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.about-modal-overlay.active{display:flex}.about-modal{background:linear-gradient(135deg,rgba(20,26,35,0.98),rgba(15,20,25,0.98));border:1px solid var(--primary-cyan);border-radius:var(--radius-lg);max-width:560px;width:90%;max-height:90vh;overflow-y:auto;box-shadow:0 0 40px rgba(76,201,240,0.3),0 20px 40px rgba(0,0,0,0.5);animation:modalSlideIn 0.3s ease}.about-modal-header{display:flex;justify-content:space-between;align-items:center;padding:1.25rem 1.5rem;border-bottom:1px solid var(--card-border)}.about-modal-header h2{margin:0;font-size:1.25rem;font-family:var(--font-heading);color:var(--primary-cyan)}.about-modal-body{padding:1.5rem}.about-logo{display:flex;align-items:center;justify-content:center;gap:1rem;margin-bottom:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid var(--card-border)}.about-logo-icon{font-size:3rem;filter:drop-shadow(0 0 10px rgba(76,201,240,0.5))}.about-logo-text{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);letter-spacing:2px}.about-logo-text span{color:var(--primary-cyan);text-shadow:0 0 10px var(--primary-cyan-glow)}.about-details{display:flex;flex-direction:column;gap:0.75rem;margin-bottom:1.5rem}.about-item{display:flex;justify-content:space-between;align-items:center;padding:0.75rem;background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.about-item.about-description{flex-direction:column;align-items:flex-start;gap:0.5rem}.about-label{color:var(--text-secondary);font-size:0.85rem;font-weight:500}.about-value{color:var(--text-primary);font-weight:600}.about-description .about-value{font-weight:400;font-size:0.9rem;line-height:1.5}.about-links{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0.75rem}.folder-item{display:flex;align-items:center;gap:0.75rem;padding:0.75rem 1rem;border-radius:var(--radius-md);cursor:pointer;transition:background 0.15s ease;background:transparent;border:none;width:100%;text-align:left;font:inherit;color:inherit;appearance:none;-webkit-appearance:none}.folder-item:hover{background:rgba(76,201,240,0.1)}.folder-item.folder-parent{border-bottom:1px solid var(--border-color);margin-bottom:0.5rem;padding-bottom:0.75rem;border-radius:0}.folder-item .folder-icon{font-size:1.25rem}.folder-item .folder-name{flex:1;color:var(--text-primary);font-weight:500}.folder-item .folder-info{color:var(--text-muted);font-size:0.8rem}@keyframes toast-slide-in{from{transform:translateX(-100%);opacity:0}to{transform:translateX(0);opacity:1}}@keyframes toast-slide-out{from{transform:translateX(0);opacity:1}to{transform:translateX(-100%);opacity:0}}.unified-toast-container{position:fixed;bottom:24px;left:280px;z-index:10000;display:flex;flex-direction:column-reverse;gap:10px;width:360px}.sidebar-collapsed ~ .main-content .unified-toast-container,body:has(.sidebar.collapsed) .unified-toast-container{left:calc(var(--sidebar-collapsed-width, 70px) + 20px)}@media (max-width:768px){.unified-toast-container{left:16px;right:16px;width:auto;max-width:none}}.unified-toast{animation:toast-slide-in 0.3s ease-out forwards;animation-iteration-count:1;width:100%;max-width:360px;border-radius:var(--radius-lg);overflow:hidden;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.unified-toast.removing{animation:toast-slide-out 0.3s ease-out forwards}.toast-content{display:flex;align-items:stretch;background:linear-gradient(135deg,var(--toast-bg-from,rgba(20,26,35,0.95)),var(--toast-bg-to,rgba(30,40,55,0.95)));border:2px solid var(--toast-color,var(--primary-cyan));border-radius:var(--radius-lg);box-shadow:0 0 20px rgba(var(--toast-color-rgb,76,201,240),0.3),0 4px 20px rgba(0,0,0,0.4);backdrop-filter:blur(10px)}.active-toast .toast-content{background:linear-gradient(135deg,var(--toast-bg-from,rgba(20,26,35,0.95)),var(--toast-bg-to,rgba(30,40,55,0.95)))}.queued-toast .toast-content{background:linear-gradient(135deg,var(--toast-queued-bg-from,rgba(25,30,40,0.90)),var(--toast-queued-bg-to,rgba(35,45,60,0.90)));opacity:0.9}.toast-main{flex:1;display:flex;align-items:center;gap:12px;padding:12px 16px;cursor:pointer;transition:background var(--transition-fast);min-width:0}.toast-main:hover{background:rgba(var(--toast-color-rgb,76,201,240),0.08)}.toast-icon{font-size:1.5rem;flex-shrink:0}.toast-icon:not(.paused){animation:pulse 1.5s ease-in-out infinite}.toast-icon.paused{animation:none;opacity:0.7}.toast-info{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}.toast-title{font-weight:600;font-size:0.9rem;color:var(--toast-color,var(--primary-cyan))}.toast-title.paused{color:var(--neon-orange)}.toast-system-name{color:var(--neon-purple);font-size:0.75rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toast-subtitle{color:var(--text-primary);font-size:0.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toast-progress-bar{height:4px;background:rgba(var(--toast-color-rgb,76,201,240),0.2);border-radius:2px;overflow:hidden;margin:4px 0}.toast-progress-fill{height:100%;background:linear-gradient(90deg,var(--toast-color,var(--primary-cyan)),var(--secondary-magenta,#f72585));border-radius:2px;transition:width var(--transition-normal)}.toast-progress-text{display:flex;justify-content:space-between;font-size:0.75rem;color:var(--text-secondary);font-family:var(--font-mono)}.toast-stats{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:0.85rem;font-family:var(--font-mono);margin-top:2px}.toast-stats .stat{display:flex;align-items:center;gap:3px;white-space:nowrap}.toast-stats .stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700;color:var(--text-primary)}.toast-stats .stat.success,.toast-stats .stat-success{color:var(--neon-green)}.toast-stats .stat.failed,.toast-stats .stat-failed{color:var(--neon-red)}.toast-stats .stat.skipped,.toast-stats .stat-skipped{color:var(--neon-orange)}.toast-meta{font-size:0.75rem;color:var(--text-muted)}.toast-queued-info{font-size:0.75rem;color:var(--text-muted)}.toast-controls{display:flex;flex-direction:column;border-left:1px solid rgba(var(--toast-color-rgb,76,201,240),0.2);flex-shrink:0}.toast-btn,.toast-ctrl-btn{flex:1;padding:8px 10px;border:none;background:transparent;cursor:pointer;font-size:0.9rem;transition:all var(--transition-fast);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;min-width:36px}.toast-btn:hover,.toast-ctrl-btn:hover{background:rgba(var(--toast-color-rgb,76,201,240),0.15);color:var(--toast-color,var(--primary-cyan))}.toast-btn.pause:hover,.toast-btn.pause.is-paused,.toast-ctrl-btn.pause:hover{background:rgba(245,158,11,0.2);color:var(--neon-orange)}.toast-btn.pause.is-paused{background:rgba(34,197,94,0.15);color:var(--neon-green)}.toast-btn.cancel:hover,.toast-ctrl-btn.cancel:hover{background:rgba(239,68,68,0.2);color:var(--neon-red)}.bulk-scrape-toast{--toast-color:var(--primary-cyan);--toast-color-rgb:76,201,240}.ra-sync-toast{--toast-color:var(--neon-orange);--toast-color-rgb:245,158,11}.ra-sync-toast .toast-main:hover{background:rgba(245,158,11,0.08)}.ra-refresh-toast{--toast-color:var(--neon-purple);--toast-color-rgb:168,85,247}.ra-refresh-toast .toast-main:hover{background:rgba(168,85,247,0.08)}.bulk-scrape-queued-toast{--toast-color:var(--primary-cyan-dark);--toast-color-rgb:58,159,194}.ra-sync-queued-toast{--toast-color:#c47d09;--toast-color-rgb:196,125,9}.ra-refresh-queued-toast{--toast-color:#8b46d4;--toast-color-rgb:139,70,212}.image-resize-toast{--toast-color:var(--neon-green);--toast-color-rgb:34,197,94}.image-resize-toast .toast-main:hover{background:rgba(34,197,94,0.08)}.toast-completed{--toast-color:var(--neon-green);--toast-color-rgb:34,197,94}.queued-toast{width:360px}.queued-toast .toast-content{padding:0}.queued-toast .toast-main{padding:10px 14px}.queued-toast .toast-icon{font-size:1.2rem}.queued-toast .toast-title{font-size:0.85rem}.queued-toast .toast-controls{flex-direction:row;min-width:36px}.queued-toast .toast-btn{min-width:36px;min-height:36px}.notification-container{position:fixed;top:20px;right:20px;z-index:10002;display:flex;flex-direction:column;gap:10px;max-width:400px}.notification{display:flex;align-items:center;gap:12px;padding:14px 18px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);box-shadow:var(--shadow-lg),0 0 20px rgba(0,0,0,0.3);transform:translateX(120%);opacity:0;transition:all var(--transition-normal)}.notification.show{transform:translateX(0);opacity:1}.notification-icon{font-size:1.2rem;flex-shrink:0}.notification-message{flex:1;color:var(--text-primary);font-size:0.9rem;line-height:1.4}.notification-close{background:none;border:none;color:var(--text-muted);font-size:1.2rem;cursor:pointer;padding:0 4px;opacity:0.6;transition:opacity 0.2s}.notification-close:hover{opacity:1;color:var(--text-primary)}.notification-success{border-color:var(--neon-green);box-shadow:var(--shadow-lg),0 0 15px rgba(34,197,94,0.2)}.notification-error{border-color:var(--neon-red);box-shadow:var(--shadow-lg),0 0 15px rgba(239,68,68,0.2)}.notification-warning{border-color:var(--neon-orange);box-shadow:var(--shadow-lg),0 0 15px rgba(245,158,11,0.2)}.notification-info{border-color:var(--primary-cyan);box-shadow:var(--shadow-lg),0 0 15px rgba(76,201,240,0.2)}.progress-container{background:#1a1a3e;border:2px solid var(--neon-cyan);border-radius:var(--radius-md);height:36px;overflow:hidden;position:relative;box-shadow:0 0 15px rgba(76,201,240,0.3)}.progress-bar{height:100%;background:linear-gradient(90deg,var(--neon-cyan),#7209b3);border-radius:calc(var(--radius-md) - 2px);display:flex;align-items:center;justify-content:center;color:white;font-size:0.9rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7);transition:width 0.3s ease;min-width:0}.progress-bar[data-percent="0"]{min-width:0;width:0%}.progress-bar:not([data-percent="0"]){min-width:60px}.progress-stats{display:flex;justify-content:space-between;color:var(--text-secondary);font-size:0.85rem;margin-top:0.5rem}.progress-container-sm{height:24px;border-width:1px}.progress-container-sm .progress-bar{font-size:0.75rem}.loading{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2xl)}.loading-spinner{width:40px;height:40px;border:3px solid var(--card-border);border-top-color:var(--primary-cyan);border-radius:50%;animation:spin 1s linear infinite}.loading-spinner-small{width:20px;height:20px;border:2px solid var(--card-border);border-top-color:var(--primary-cyan);border-radius:50%;animation:spin 1s linear infinite}.loading-text{margin-left:var(--spacing-md);color:var(--text-secondary)}.loading-overlay{position:fixed;inset:0;background:rgba(10,14,23,0.85);display:flex;align-items:center;justify-content:center;z-index:10001;opacity:0;visibility:hidden;transition:all 0.3s ease}.loading-overlay.active{opacity:1;visibility:visible}.loading-content{display:flex;flex-direction:column;align-items:center;gap:16px}.loading-content .loading-text{color:var(--text-primary);font-size:1rem;font-family:var(--font-primary);margin-left:0}.empty-state{text-align:center;padding:var(--spacing-2xl);color:var(--text-secondary)}.empty-state-icon{font-size:4rem;margin-bottom:var(--spacing-md);opacity:0.5}.empty-state-title{font-size:1.2rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-sm)}.empty-state-text{margin-bottom:var(--spacing-lg)}.scan-log-container{background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);margin-top:var(--spacing-lg)}.scan-log-header{display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1rem;background:var(--bg-dark);border-bottom:1px solid var(--border-color)}.scan-log-header h4{margin:0;font-size:0.9rem;color:var(--text-secondary);display:flex;align-items:center;gap:0.5rem}.scan-log-content{height:200px;overflow-y:auto;padding:0.75rem 1rem;font-family:var(--font-mono);font-size:0.8rem;line-height:1.6}.scan-log-content .log-entry{color:var(--text-secondary);padding:0.15rem 0}.scan-log-content .log-entry.info{color:var(--primary-cyan)}.scan-log-content .log-entry.warning{color:var(--neon-orange)}.scan-log-content .log-entry.error{color:var(--neon-red)}.rate-limits-container{background:var(--bg-darker,#0d1117);border:1px solid var(--card-border);border-radius:var(--radius-md);padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-md)}.rate-limit-header{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-muted);font-size:0.8rem;padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.rate-limit-item{display:flex;flex-direction:column;gap:var(--spacing-xs);padding:var(--spacing-md);background:rgba(255,255,255,0.02);border-radius:var(--radius-sm);border-left:3px solid var(--text-muted)}.rate-limit-item.status-green{border-left-color:var(--neon-green)}.rate-limit-item.status-yellow{border-left-color:var(--neon-orange)}.rate-limit-item.status-red{border-left-color:var(--neon-red)}.rate-limit-item.status-gray{border-left-color:var(--text-muted)}.rate-limit-item strong{color:var(--text-primary);font-size:0.95rem}.rate-limit-value{color:var(--text-secondary);font-size:0.9rem}.rate-limit-bar{width:100%;height:6px;background:rgba(255,255,255,0.08);border-radius:3px;overflow:hidden;margin:4px 0}.rate-limit-bar-fill{height:100%;border-radius:3px;transition:width 0.6s ease}.rate-limit-detail{color:var(--text-muted);font-size:0.8rem;line-height:1.5}.progress-bar-container{background:#1a1a3e;border:2px solid var(--neon-cyan);border-radius:var(--radius-md);height:36px;overflow:hidden;margin-bottom:0.5rem;box-shadow:0 0 15px rgba(76,201,240,0.3)}.progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--neon-cyan),#7209b3);width:0%;transition:width 0.3s ease;display:flex;align-items:center;justify-content:center;color:white;font-size:0.9rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7);border-radius:calc(var(--radius-md) - 2px)}.progress-details{display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);margin-top:0.75rem}.progress-status{color:var(--text-secondary);font-size:0.9rem}.progress-status strong{color:var(--primary-cyan)}.scanner-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);padding:1.5rem;margin-bottom:1.5rem}.scanner-section h3{color:var(--text-primary);font-size:1rem;margin-bottom:1rem;display:flex;align-items:center;gap:0.5rem}.scanner-controls{display:flex;flex-wrap:wrap;gap:1rem;align-items:center}.quick-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.tag{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-full);font-size:0.85rem;color:var(--text-secondary);white-space:nowrap}.tag.tag-system{background:rgba(76,201,240,0.15);border-color:rgba(76,201,240,0.4);color:var(--primary-cyan)}.tag.tag-date{background:rgba(168,85,247,0.15);border-color:rgba(168,85,247,0.4);color:#c084fc}.tag.tag-genre{background:rgba(34,197,94,0.15);border-color:rgba(34,197,94,0.4);color:#4ade80}.genre-select-container{display:flex;flex-wrap:wrap;gap:8px;padding:10px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);min-height:44px}.genre-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);border-radius:var(--radius-full);color:var(--neon-purple);font-size:0.8rem;font-weight:500}.genre-tag .remove-genre{cursor:pointer;opacity:0.7;transition:opacity 0.15s}.genre-tag .remove-genre:hover{opacity:1}.genre-dropdown{position:relative}.genre-dropdown-menu{position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);z-index:100;display:none}.genre-dropdown-menu.active{display:block}.genre-option{padding:8px 12px;cursor:pointer;transition:background 0.15s}.genre-option:hover{background:rgba(76,201,240,0.1)}.genre-option.selected{background:rgba(168,85,247,0.2);color:var(--neon-purple)}.controller-select-container{display:flex;flex-wrap:wrap;gap:8px;padding:10px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);min-height:44px}.controller-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-full);color:var(--neon-cyan);font-size:0.8rem;font-weight:500}.controller-tag .remove-controller{cursor:pointer;opacity:0.7;transition:opacity 0.15s}.controller-tag .remove-controller:hover{opacity:1}.controller-dropdown{position:relative}.active-filter-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.active-filter-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(76,201,240,0.15);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-full);color:var(--primary-cyan);font-size:0.8rem;font-weight:500}.active-filter-tag .remove-filter{background:none;border:none;color:var(--primary-cyan);font-size:1rem;line-height:1;cursor:pointer;padding:0;opacity:0.7;transition:opacity 0.2s}.active-filter-tag .remove-filter:hover{opacity:1}.gem-tag-container{display:flex;flex-wrap:wrap;gap:var(--spacing-xs);min-height:36px;padding:var(--spacing-sm);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-xs)}.gem-tag-container:empty::before{content:'None selected';color:var(--text-muted);font-style:italic;font-size:0.85rem}.gem-tag-dropdown{width:100%;padding:8px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.9rem;cursor:pointer}.gem-tag-dropdown:hover,.gem-tag-dropdown:focus{border-color:var(--primary-cyan);outline:none}.gem-controller-inputs{display:flex;gap:var(--spacing-xs)}.gem-controller-inputs select{flex:1}.gem-custom-input{flex:1;padding:8px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.9rem}.gem-custom-input:hover,.gem-custom-input:focus{border-color:var(--primary-cyan);outline:none}#queueManagerModal .queue-item{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:10px;transition:all 0.2s ease}#queueManagerModal .queue-item:hover{border-color:var(--primary-cyan)}#queueManagerModal .queue-item.running{border-color:var(--primary-cyan);border-width:2px;background:rgba(76,201,240,0.05)}#queueManagerModal .queue-item.queued{border-color:var(--neon-orange)}#queueManagerModal .queue-item-icon{font-size:1.5rem;flex-shrink:0}#queueManagerModal .queue-item-info{flex:1;min-width:0}#queueManagerModal .queue-item-title{font-weight:600;color:var(--text-primary);margin-bottom:2px}#queueManagerModal .queue-item-system{font-size:0.85rem;color:var(--neon-purple)}#queueManagerModal .queue-item.queued .queue-item-system{color:var(--neon-orange)}#queueManagerModal .queue-item-games{font-size:0.8rem;color:var(--text-secondary)}#queueManagerModal .queue-item-progress{font-size:0.75rem;color:var(--primary-cyan);margin-top:4px}#queueManagerModal .queue-item-controls{display:flex;flex-direction:column;gap:4px}#queueManagerModal .queue-btn{padding:6px 10px;border:1px solid var(--card-border);border-radius:var(--radius-sm);background:var(--bg-medium);color:var(--text-secondary);cursor:pointer;font-size:0.8rem;transition:all 0.15s ease;white-space:nowrap}#queueManagerModal .queue-btn:hover{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}#queueManagerModal .queue-btn.danger:hover{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}#queueManagerModal .queue-btn:disabled{opacity:0.3;cursor:not-allowed}#queueManagerModal .queue-empty{text-align:center;color:var(--text-muted);padding:20px;font-style:italic}.launch-indicator{position:fixed;bottom:var(--spacing-lg);right:var(--spacing-lg);z-index:1000;display:inline-flex;align-items:center;gap:0.5rem;padding:0.4rem 0.9rem;background:var(--bg-card);border:1px solid var(--primary-cyan);border-radius:999px;box-shadow:0 0 12px rgba(76,201,240,0.35);color:var(--text-primary);font-family:var(--font-mono,monospace);font-size:0.85rem;cursor:pointer;transition:opacity 0.2s ease,transform 0.2s ease}.launch-indicator.hidden{opacity:0;pointer-events:none;transform:translateY(8px)}.launch-indicator-icon{color:var(--neon-green,#44ff66);font-weight:bold}.launch-indicator-count{font-weight:bold;color:var(--primary-cyan)}.launch-indicator-popover{position:fixed;bottom:calc(var(--spacing-lg) + 2.5rem);right:var(--spacing-lg);z-index:1001;min-width:280px;padding:var(--spacing-md);background:var(--bg-card);border:1px solid var(--primary-cyan);border-radius:var(--border-radius);box-shadow:0 0 20px rgba(76,201,240,0.4);color:var(--text-primary)}.launch-indicator-popover-title{font-family:var(--font-heading);font-size:0.95rem;color:var(--primary-cyan);margin-bottom:var(--spacing-sm);text-transform:uppercase;letter-spacing:0.05em}.launch-indicator-popover-row{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-sm);padding:0.4rem 0;border-top:1px solid var(--border-color,rgba(255,255,255,0.08));font-size:0.85rem}.launch-indicator-popover-row:first-of-type{border-top:none}.global-search{position:relative;margin-top:var(--spacing-md)}.global-search-input{width:100%;padding:var(--spacing-sm) var(--spacing-md);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.global-search-input::placeholder{color:var(--text-muted)}.global-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:var(--shadow-glow-cyan)}.global-search-list{position:absolute;top:calc(100% + var(--spacing-xs));left:0;right:0;z-index:1001;max-height:60vh;overflow-y:auto;background:var(--card-bg);border:1px solid var(--card-border-hover);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);padding:var(--spacing-xs) 0}.global-search-group{padding:var(--spacing-xs) var(--spacing-sm);color:var(--text-muted);font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em}.global-search-option{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-xs) var(--spacing-sm);color:var(--text-primary);text-decoration:none;min-height:36px}.global-search-option:hover,.global-search-option[aria-selected="true"]{background:var(--card-bg-hover);color:var(--text-accent)}.global-search-thumb{flex:0 0 24px;height:32px;display:flex;align-items:center;justify-content:center}.global-search-thumb img{max-width:24px;max-height:32px;object-fit:contain}.global-search-text{flex:1;min-width:0;display:flex;flex-direction:column}.global-search-label{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;line-height:1.25}.global-search-meta{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted);font-size:0.8rem}.global-search-empty{padding:var(--spacing-sm);color:var(--text-muted)}.game-card-wrapper{background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden;transition:all var(--transition-normal)}.game-card-wrapper:hover{border-color:var(--primary-cyan);box-shadow:0 0 20px rgba(76,201,240,0.2);transform:translateY(-2px)}.game-hero{display:grid;grid-template-columns:300px 1fr;gap:var(--spacing-xl);margin-bottom:var(--spacing-xl);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-xl);position:relative;overflow:hidden}.game-hero::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta),var(--primary-cyan))}.hero-boxart{width:300px;flex-shrink:0;position:relative}.hero-boxart .game-boxart-image{width:100%;height:auto;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);border:2px solid var(--card-border)}.hero-info{display:flex;flex-direction:column;min-width:0;flex:1}.game-boxart-container{position:relative}.game-boxart{width:100%;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);border:2px solid var(--card-border)}.game-boxart-placeholder{width:100%;aspect-ratio:3/4;background:var(--bg-lighter);border-radius:var(--radius-lg);display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-muted);font-size:4rem;border:2px dashed var(--card-border)}.game-boxart-placeholder span{font-size:1rem;margin-top:var(--spacing-md)}.game-info{display:flex;flex-direction:column}.game-title-section{margin-bottom:var(--spacing-lg)}.game-title{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);margin-bottom:var(--spacing-sm);line-height:1.2}.game-meta-badges{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.game-ratings{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.rating-display{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm) var(--spacing-md);background:var(--bg-lighter);border-radius:var(--radius-md);border:1px solid var(--card-border)}.rating-display img{height:32px}.rating-display .rating-text{font-weight:600;font-size:0.9rem}.rating-display.esrb{border-color:var(--rating-esrb-border);background:var(--rating-esrb-bg)}.rating-display.esrb .rating-text{color:var(--rating-esrb)}.rating-display.pegi{border-color:var(--rating-pegi-border);background:var(--rating-pegi-bg)}.rating-display.pegi .rating-text{color:var(--rating-pegi)}.game-details-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.detail-item{display:flex;flex-direction:column;gap:var(--spacing-xs)}.detail-label{font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px}.detail-value{color:var(--text-primary);font-size:0.95rem}.game-description{margin-bottom:var(--spacing-lg)}.game-description h3{font-family:var(--font-heading);font-size:1rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-sm)}.game-description p{color:var(--text-secondary);line-height:1.7}.screenshots-section{margin-bottom:var(--spacing-xl)}.screenshots-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:var(--spacing-md)}.screenshot-item{position:relative;aspect-ratio:16/9;border-radius:var(--radius-md);overflow:hidden;cursor:pointer;border:2px solid var(--card-border);transition:all var(--transition-fast)}.screenshot-item:hover{border-color:var(--primary-cyan);transform:scale(1.02)}.screenshot-item img{width:100%;height:100%;object-fit:cover}.screenshot-item::after{content:'🔍';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:2rem;opacity:0;transition:opacity var(--transition-fast)}.screenshot-item:hover::after{opacity:1}.search-results{margin-top:var(--spacing-lg)}.search-result{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm);transition:all var(--transition-fast)}.search-result:hover{border-color:var(--primary-cyan);background:var(--bg-light)}.search-result-info h4{color:var(--text-primary);font-weight:600;margin-bottom:var(--spacing-xs)}.search-result-meta{display:flex;gap:var(--spacing-sm);font-size:0.8rem}.search-result-meta span{padding:2px 8px;background:var(--bg-lighter);border-radius:var(--radius-sm);color:var(--text-secondary)}.search-result-alts{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--spacing-xs);font-size:0.78rem}.search-result-alts-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;font-size:0.7rem;font-weight:600}.game-detail-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.9);backdrop-filter:blur(4px);z-index:10000;align-items:center;justify-content:center;padding:var(--spacing-lg)}.game-detail-modal.active{display:flex}.game-detail-modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);width:100%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative;box-shadow:0 0 40px rgba(0,0,0,0.5),0 0 80px rgba(76,201,240,0.1)}.game-detail-modal-close{position:absolute;top:var(--spacing-md);right:var(--spacing-md);z-index:10}.game-detail-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--spacing-xl);min-height:300px;color:var(--text-secondary)}.game-detail-content{padding:var(--spacing-lg)}.gdm-header{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.gdm-boxart-container{flex-shrink:0;width:180px}.gdm-boxart{width:100%;border-radius:var(--radius-md);box-shadow:0 4px 20px rgba(0,0,0,0.3)}.gdm-info{flex:1}.gdm-title{font-family:var(--font-heading);font-size:1.5rem;color:var(--primary-cyan);margin:0 0 var(--spacing-sm) 0}.gdm-meta{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.gdm-system-badge{background:var(--primary-cyan);color:var(--bg-darkest);padding:2px 8px;border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600}.gdm-year,.gdm-players{background:rgba(var(--toast-color-rgb,76,201,240),0.15);color:var(--text-primary);padding:2px 8px;border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600;border:1px solid rgba(var(--toast-color-rgb,76,201,240),0.25)}.gdm-ratings{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.gdm-ratings .rating-badge{display:inline-block;padding:4px 8px;border-radius:var(--radius-sm);font-size:0.75rem;font-weight:600}.gdm-ratings .rating-badge.esrb{background:rgba(168,85,247,0.2);color:var(--neon-purple);border:1px solid rgba(168,85,247,0.3)}.gdm-ratings .rating-badge.score{background:rgba(34,197,94,0.2);color:var(--neon-green);border:1px solid rgba(34,197,94,0.3)}.gdm-completion .completion-badge{display:inline-block;padding:4px 10px;border-radius:var(--radius-sm);font-size:0.75rem;font-weight:600}.gdm-completion .status-in_progress,.gdm-completion .status-in-progress{background:rgba(76,201,240,0.2);color:var(--primary-cyan);border:1px solid rgba(76,201,240,0.3)}.gdm-completion .status-completed{background:rgba(34,197,94,0.2);color:var(--neon-green);border:1px solid rgba(34,197,94,0.3)}.gdm-completion .status-played{background:rgba(59,130,246,0.2);color:#3b82f6;border:1px solid rgba(59,130,246,0.3)}.gdm-completion .status-100_percent,.gdm-completion .status-100-percent{background:linear-gradient(135deg,rgba(245,158,11,0.2),rgba(247,37,133,0.2));color:var(--completion-gold);border:1px solid rgba(245,158,11,0.3)}.gdm-description{color:var(--text-secondary);line-height:1.6;margin-bottom:var(--spacing-lg);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.gdm-details-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.gdm-detail{display:flex;flex-direction:column}.gdm-detail .label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px}.gdm-detail span:last-child{color:var(--text-primary)}.gdm-achievements{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.ra-badge-modal{background:linear-gradient(135deg,var(--neon-orange),#d97706);color:white;padding:6px 12px;border-radius:var(--radius-md);font-weight:600}.gdm-achievements .game-badge.achievement-progress{font-size:0.75rem;padding:4px 10px}.gdm-achievements .ach-icon{width:16px;height:16px;vertical-align:middle}.gdm-achievements .ach-bar{display:inline-block;width:40px;height:4px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden;vertical-align:middle;margin-left:4px}.gdm-achievements .ach-fill{display:block;height:100%;border-radius:2px;transition:width 0.3s ease}.gdm-screenshots{display:flex;gap:var(--spacing-sm);overflow-x:auto;padding-bottom:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.gdm-screenshot{height:80px;border-radius:var(--radius-sm);cursor:pointer;transition:transform var(--transition-fast)}.gdm-screenshot:hover{transform:scale(1.05)}.gdm-video{margin-bottom:var(--spacing-lg)}.gdm-video video{width:100%;max-height:300px;border-radius:var(--radius-md);background:#000}.gdm-lightbox{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);z-index:10002;align-items:center;justify-content:center;flex-direction:column}.gdm-lightbox.active{display:flex}.gdm-lightbox-image{max-width:90%;max-height:80vh;object-fit:contain;border-radius:var(--radius-md)}.gdm-lightbox-close{position:absolute;top:20px;right:35px;font-size:2.5rem;color:var(--text-primary);cursor:pointer;z-index:10003;transition:color var(--transition-fast);background:none;border:none;line-height:1}.gdm-lightbox-close:hover{color:var(--primary-cyan)}.gdm-lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.5);border:2px solid var(--card-border);color:var(--text-primary);font-size:3rem;padding:10px 20px;cursor:pointer;border-radius:var(--radius-md);transition:all var(--transition-fast);z-index:10003}.gdm-lightbox-nav:hover{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.gdm-lightbox-nav.prev{left:20px}.gdm-lightbox-nav.next{right:20px}.gdm-lightbox-counter{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);color:var(--text-secondary);font-size:0.9rem;font-family:var(--font-mono)}.gdm-lightbox-hint{position:absolute;bottom:45px;left:50%;transform:translateX(-50%);color:var(--text-muted);font-size:0.75rem;white-space:nowrap}.gdm-actions{display:flex;gap:var(--spacing-md);padding-top:var(--spacing-md);border-top:1px solid var(--card-border)}.game-edit-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.95);backdrop-filter:blur(4px);z-index:10001;align-items:center;justify-content:center;padding:var(--spacing-lg)}.game-edit-modal.active{display:flex}.game-edit-modal-content{background:var(--bg-medium);border:1px solid var(--primary-cyan);border-radius:var(--radius-lg);width:100%;max-width:700px;max-height:90vh;overflow-y:auto;position:relative;box-shadow:0 0 40px rgba(0,0,0,0.5),0 0 60px rgba(76,201,240,0.15)}.game-edit-modal-close{position:absolute;top:var(--spacing-md);right:var(--spacing-md);z-index:10}.gem-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.gem-title{font-family:var(--font-heading);font-size:1.25rem;color:var(--primary-cyan);margin:0;padding-right:2rem}.gem-body{padding:var(--spacing-lg);display:grid}.gem-section{margin-bottom:var(--spacing-lg)}.gem-section:last-child{margin-bottom:0}.gem-label{display:block;font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-sm)}.gem-select{width:100%;padding:10px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:1rem;cursor:pointer;transition:border-color var(--transition-fast)}.gem-select:hover,.gem-select:focus{border-color:var(--primary-cyan);outline:none}.gem-hltb-display{padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.gem-hltb-empty{color:var(--text-muted);font-style:italic}.gem-hltb-current{color:var(--text-primary);display:block}.gem-hltb-match-info{display:block;font-size:0.8rem;color:var(--text-muted);margin-top:var(--spacing-xs)}.gem-hltb-actions{display:flex;gap:var(--spacing-sm)}.gem-hltb-results{margin-top:var(--spacing-md);padding:var(--spacing-md);background:rgba(76,201,240,0.05);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-md)}.gem-hltb-searching{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary)}.gem-hltb-match{margin-bottom:var(--spacing-md)}.gem-hltb-match-title{display:block;color:var(--text-primary);font-weight:600}.gem-hltb-match-confidence{font-size:0.8rem;color:var(--neon-green)}.gem-hltb-match-platform{display:block;font-size:0.8rem;color:var(--neon-purple);margin-top:2px}.gem-hltb-times{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.gem-hltb-time{text-align:center;padding:var(--spacing-sm);background:rgba(0,0,0,0.2);border-radius:var(--radius-sm)}.gem-hltb-time-label{display:block;font-size:0.7rem;color:var(--text-muted);text-transform:uppercase;margin-bottom:2px}.gem-hltb-time-value{display:block;color:var(--primary-cyan);font-weight:600}.gem-hltb-error{color:var(--neon-red);font-size:0.9rem}.gem-footer{display:flex;justify-content:space-between;gap:var(--spacing-md);padding:var(--spacing-lg);border-top:1px solid var(--card-border)}.gem-footer-right{display:flex;gap:var(--spacing-sm)}.gem-tabs{display:flex;gap:var(--spacing-xs);margin-top:var(--spacing-md);overflow-x:auto;padding-bottom:var(--spacing-xs)}.gem-tab{padding:8px 12px;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-size:0.85rem;cursor:pointer;transition:all var(--transition-fast);white-space:nowrap}.gem-tab:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.gem-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.gem-tab-content{grid-area:1 / 1;visibility:hidden;pointer-events:none}.gem-tab-content.active{visibility:visible;pointer-events:auto}.gem-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md)}.gem-field{display:flex;flex-direction:column}.gem-field label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-xs)}.gem-field input,.gem-field select,.gem-field textarea{padding:10px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.95rem;transition:border-color var(--transition-fast)}.gem-field input:hover,.gem-field input:focus,.gem-field select:hover,.gem-field select:focus,.gem-field textarea:hover,.gem-field textarea:focus{border-color:var(--primary-cyan);outline:none}.gem-field input::placeholder,.gem-field textarea::placeholder{color:var(--text-muted)}.gem-field-full{grid-column:1 / -1}.gem-field textarea{resize:vertical;min-height:120px}.gem-field-row{display:flex;gap:var(--spacing-xs)}.gem-field-row input{flex:1}.gem-field-row .btn{flex-shrink:0}.gem-toggle-row{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) 0}.gem-toggle-label{color:var(--text-secondary);font-size:0.9rem}.psn-profile-bar{display:flex;align-items:center;gap:var(--spacing-lg);padding:var(--spacing-md) var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-avatar{width:48px;height:48px;border-radius:50%;border:2px solid var(--primary-cyan);object-fit:cover}.psn-avatar-placeholder{width:48px;height:48px;border-radius:50%;border:2px solid var(--card-border);background:var(--bg-medium);display:flex;align-items:center;justify-content:center;font-size:1.5rem}.psn-profile-info{flex:1}.psn-username{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary)}.psn-level{font-size:0.8rem;color:var(--text-secondary)}.psn-level strong{color:var(--primary-cyan)}.psn-sync-status{font-size:0.75rem;color:var(--text-muted);display:flex;align-items:center;gap:6px}.psn-stats-bar{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.psn-stat-box{padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);text-align:center}.psn-stat-icon{font-size:1.5rem;margin-bottom:var(--spacing-xs)}.psn-stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700}.psn-stat-value.platinum{color:var(--trophy-platinum)}.psn-stat-value.gold{color:var(--trophy-gold)}.psn-stat-value.silver{color:var(--trophy-silver)}.psn-stat-value.bronze{color:var(--trophy-bronze)}.psn-stat-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.platform-filters{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);flex-wrap:wrap}.platform-filter-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast)}.platform-filter-btn:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.platform-filter-btn.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.filter-count{font-size:0.7rem;padding:2px 6px;background:rgba(255,255,255,0.1);border-radius:var(--radius-full)}.platform-filter-btn.active .filter-count{background:rgba(76,201,240,0.2)}.psn-search-sort-row{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg);align-items:center}.psn-search-wrapper{flex:1;position:relative}.psn-search-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-muted);pointer-events:none}.psn-search-input{width:100%;padding:10px 12px 10px 36px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem}.psn-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.psn-search-input::placeholder{color:var(--text-muted)}.psn-sort-controls{display:flex;align-items:center;gap:var(--spacing-sm)}.psn-sort-label{font-size:0.8rem;color:var(--text-muted);white-space:nowrap}.psn-sort-select{padding:8px 32px 8px 12px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.85rem;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center}.psn-sort-select:focus{outline:none;border-color:var(--primary-cyan)}.psn-games-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-md)}.psn-trophy-game-card{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);transition:all var(--transition-fast);text-decoration:none;color:inherit}.psn-trophy-game-card:hover{border-color:var(--primary-cyan);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.3)}.psn-trophy-game-thumb{width:64px;height:64px;border-radius:var(--radius-sm);object-fit:cover;border:1px solid var(--card-border);flex-shrink:0}.psn-trophy-game-thumb-placeholder{width:64px;height:64px;border-radius:var(--radius-sm);background:var(--bg-medium);border:1px solid var(--card-border);display:flex;align-items:center;justify-content:center;font-size:1.5rem;flex-shrink:0}.psn-trophy-game-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.psn-trophy-game-name{font-size:0.9rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.psn-trophy-game-npwr{font-family:var(--font-mono);font-size:0.65rem;color:var(--text-muted)}.psn-trophy-game-progress{display:flex;align-items:center;gap:var(--spacing-sm)}.psn-progress-track{flex:1;height:6px;background:var(--bg-dark);border-radius:3px;overflow:hidden}.psn-progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--neon-purple));border-radius:3px;transition:width var(--transition-normal)}.psn-progress-pct{font-family:var(--font-mono);font-size:0.7rem;color:var(--primary-cyan);min-width:32px;text-align:right}.psn-trophy-mini-badges{display:flex;gap:6px}.trophy-mini-badge{display:inline-flex;align-items:center;gap:3px;font-size:0.65rem;font-weight:600}.tmb-platinum{color:var(--trophy-platinum)}.tmb-gold{color:var(--trophy-gold)}.tmb-silver{color:var(--trophy-silver)}.tmb-bronze{color:var(--trophy-bronze)}.psn-game-info-card{display:flex;gap:var(--spacing-lg);padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-game-boxart{width:180px;flex-shrink:0}.psn-game-boxart img{width:100%;border-radius:var(--radius-md);border:1px solid var(--card-border)}.psn-game-boxart-placeholder{width:100%;aspect-ratio:1;background:var(--bg-medium);border-radius:var(--radius-md);border:1px solid var(--card-border);display:flex;align-items:center;justify-content:center;font-size:4rem}.psn-game-meta{flex:1}.psn-game-badges{display:flex;gap:var(--spacing-sm);flex-wrap:wrap;margin-bottom:var(--spacing-md)}.psn-game-description{font-size:0.85rem;color:var(--text-secondary);line-height:1.6;margin-bottom:var(--spacing-md);max-height:100px;overflow:hidden}.psn-meta-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-md)}.psn-meta-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);margin-bottom:2px}.psn-meta-value{font-size:0.85rem;color:var(--text-secondary)}.psn-trophy-progress-card{padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-trophy-progress-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-md)}.psn-trophy-progress-title{font-family:var(--font-heading);font-size:1rem;font-weight:600;display:flex;align-items:center;gap:8px}.psn-trophy-progress-fraction{font-family:var(--font-mono);font-size:1rem;color:var(--text-secondary)}.psn-trophy-progress-pct{font-family:var(--font-heading);font-size:1.2rem;font-weight:700;color:var(--primary-cyan)}.psn-trophy-progress-bar-container{height:28px;background:rgba(26,26,62,0.8);border:2px solid var(--primary-cyan);border-radius:var(--radius-md);overflow:hidden;margin-bottom:var(--spacing-md);box-shadow:0 0 15px rgba(76,201,240,0.2)}.psn-trophy-progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--neon-purple));border-radius:var(--radius-md);transition:width 0.8s ease;display:flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7)}.psn-trophy-type-breakdown{display:flex;justify-content:center;gap:var(--spacing-xl)}.psn-trophy-type-item{display:flex;align-items:center;gap:6px;font-size:0.85rem}.psn-trophy-type-icon{font-size:1.1rem}.psn-trophy-type-count{font-family:var(--font-mono)}.type-platinum .psn-trophy-type-count{color:var(--trophy-platinum)}.type-gold .psn-trophy-type-count{color:var(--trophy-gold)}.type-silver .psn-trophy-type-count{color:var(--trophy-silver)}.type-bronze .psn-trophy-type-count{color:var(--trophy-bronze)}.psn-info-panels-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.psn-info-panel{padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg)}.psn-info-panel-title{font-family:var(--font-heading);font-size:0.9rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-md);display:flex;align-items:center;gap:8px}.psn-detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-sm) var(--spacing-lg)}.psn-detail-label{font-size:0.75rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.psn-detail-value{font-size:0.85rem;color:var(--text-secondary)}.psn-mono{font-family:var(--font-mono);font-size:0.8rem}.psn-hltb-input{width:100%;padding:8px 12px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.85rem;margin-bottom:var(--spacing-sm)}.psn-hltb-input:focus{outline:none;border-color:var(--primary-cyan)}.psn-trophy-lists-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-lg)}.psn-trophy-list-panel{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);overflow:hidden}.psn-trophy-list-header{padding:var(--spacing-md) var(--spacing-lg);border-bottom:1px solid var(--card-border);font-family:var(--font-heading);font-size:0.9rem;font-weight:600;text-transform:uppercase;letter-spacing:1px;display:flex;align-items:center;gap:8px}.psn-trophy-list-header.header-unlocked{color:var(--neon-green)}.psn-trophy-list-header.header-locked{color:var(--neon-gray)}.psn-trophy-list-body{max-height:600px;overflow-y:auto;padding:var(--spacing-sm)}.psn-trophy-list-body::-webkit-scrollbar{width:6px}.psn-trophy-list-body::-webkit-scrollbar-track{background:var(--bg-dark)}.psn-trophy-list-body::-webkit-scrollbar-thumb{background:var(--card-border);border-radius:3px}.psn-trophy-list-body::-webkit-scrollbar-thumb:hover{background:var(--primary-cyan)}.psn-trophy-entry{display:flex;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--radius-md);transition:background var(--transition-fast)}.psn-trophy-entry:hover{background:rgba(76,201,240,0.03)}.psn-trophy-entry + .psn-trophy-entry{border-top:1px solid rgba(42,53,66,0.5)}.psn-trophy-icon-box{width:48px;height:48px;border-radius:var(--radius-sm);background:var(--bg-medium);display:flex;align-items:center;justify-content:center;font-size:1.4rem;flex-shrink:0;border:1px solid var(--card-border);overflow:hidden}.psn-trophy-icon-box.locked{opacity:0.5;filter:grayscale(0.5)}.psn-trophy-icon-img{width:100%;height:100%;object-fit:cover}.psn-trophy-details{flex:1;min-width:0}.psn-trophy-name{font-size:0.85rem;font-weight:600;color:var(--text-primary);margin-bottom:2px}.psn-trophy-description{font-size:0.75rem;color:var(--text-muted);line-height:1.4}.psn-trophy-earned-info{display:flex;align-items:center;gap:var(--spacing-sm);margin-top:4px;flex-wrap:wrap}.psn-trophy-type-badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:3px;font-size:0.65rem;font-weight:700;text-transform:uppercase;letter-spacing:0.5px}.psn-trophy-type-badge.ttb-platinum{background:rgba(123,184,212,0.2);color:var(--trophy-platinum);border:1px solid rgba(123,184,212,0.3)}.psn-trophy-type-badge.ttb-gold{background:rgba(205,154,42,0.2);color:var(--trophy-gold);border:1px solid rgba(205,154,42,0.3)}.psn-trophy-type-badge.ttb-silver{background:rgba(168,168,168,0.2);color:var(--trophy-silver);border:1px solid rgba(168,168,168,0.3)}.psn-trophy-type-badge.ttb-bronze{background:rgba(205,127,50,0.2);color:var(--trophy-bronze);border:1px solid rgba(205,127,50,0.3)}.psn-trophy-rarity{display:inline-flex;align-items:center;gap:3px;font-size:0.65rem;color:var(--text-muted);padding:2px 6px;border-radius:3px;background:rgba(255,255,255,0.03)}.psn-trophy-rarity.rarity-ultra-rare{color:var(--neon-cyan)}.psn-trophy-rarity.rarity-very-rare{color:var(--neon-purple)}.psn-trophy-rarity.rarity-rare{color:var(--neon-orange)}.psn-trophy-rarity.rarity-common{color:var(--neon-gray)}.psn-trophy-earned-date{font-family:var(--font-mono);font-size:0.65rem;color:var(--text-muted);display:flex;align-items:center;gap:4px}.psn-trophy-earned-date .date-icon{font-size:0.6rem}.psn-dlc-separator{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);margin-top:var(--spacing-sm)}.psn-dlc-separator-line{flex:1;height:1px;background:var(--card-border)}.psn-dlc-separator-label{font-size:0.7rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--neon-purple);white-space:nowrap}.psn-trophy-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--spacing-xl);color:var(--text-muted);text-align:center}.psn-trophy-empty-icon{font-size:2rem;margin-bottom:var(--spacing-sm)}.psn-trophy-complete{color:var(--neon-green)}.psn-not-configured{text-align:center;padding:var(--spacing-2xl)}.psn-not-configured-icon{font-size:4rem;margin-bottom:var(--spacing-lg)}.psn-not-configured-title{font-family:var(--font-heading);font-size:1.5rem;color:var(--text-primary);margin-bottom:var(--spacing-md)}.psn-not-configured-text{color:var(--text-secondary);margin-bottom:var(--spacing-lg);max-width:500px;margin-left:auto;margin-right:auto}.psn-no-games{text-align:center;padding:var(--spacing-2xl);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-lg)}.psn-no-games-icon{font-size:3rem;margin-bottom:var(--spacing-md)}.psn-no-games-title{font-family:var(--font-heading);font-size:1.2rem;color:var(--text-primary);margin-bottom:var(--spacing-sm)}.psn-no-games-text{color:var(--text-muted)}.psn-visible-count{text-align:center;padding:var(--spacing-md);color:var(--text-muted);font-size:0.85rem}.achievement-overview{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg)}.achievement-stat{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md)}.achievement-stat-icon{font-size:2rem}.achievement-stat-info{display:flex;flex-direction:column}.achievement-stat-label{font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.achievement-stat-value{font-size:1.3rem;font-weight:600;color:var(--neon-cyan)}.achievements-page{--card-min:280px;--card-gap:var(--spacing-md)}.achievement-games-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.achievement-game-card{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md);text-decoration:none;color:inherit;transition:all 0.2s ease}.achievement-game-card:hover{border-color:var(--neon-cyan);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.3)}.achievement-game-icon{flex-shrink:0;width:80px;height:80px;border-radius:var(--radius-sm);overflow:hidden;background:var(--bg-darker)}.achievement-game-icon img{width:100%;height:100%;object-fit:cover}.achievement-game-card .no-boxart{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:2rem;background:var(--bg-darker)}.achievement-game-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.achievement-game-title{font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.achievement-game-system{font-size:0.8rem;color:var(--text-muted)}.achievement-game-progress{margin-top:auto}.achievement-progress-bar{height:6px;background:var(--bg-darker);border-radius:3px;overflow:hidden;margin-bottom:4px}.achievement-progress-fill{height:100%;background:linear-gradient(90deg,var(--neon-cyan),var(--neon-purple));border-radius:3px;transition:width 0.3s ease}.achievement-progress-fill.xbox-fill{background:linear-gradient(90deg,var(--neon-green),#14b873)}.achievement-progress-text{font-size:0.75rem;color:var(--text-muted)}.no-boxart-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:4rem;background:var(--bg-darker);color:var(--text-muted)}.trophy-type-label.gamerscore-earned{background:#ffd700;color:#111}.trophy-type-label.gamerscore-locked{background:var(--bg-lighter);color:var(--text-muted)}.stat-box{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);text-align:center;transition:all var(--transition-normal)}.stat-box:hover{border-color:var(--neon-cyan);box-shadow:0 0 20px rgba(76,201,240,0.2)}.stat-value{font-family:var(--font-heading);font-size:2.5rem;font-weight:700;color:var(--neon-cyan);line-height:1.2;text-shadow:0 0 20px rgba(76,201,240,0.5)}.stat-label{font-size:0.9rem;color:var(--text-secondary);margin-top:0.5rem;text-transform:uppercase;letter-spacing:0.5px}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.stat-card{background:linear-gradient(135deg,var(--card-bg) 0%,var(--bg-medium) 100%);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);position:relative;overflow:hidden;transition:all var(--transition-normal)}.stat-card::before{content:'';position:absolute;top:0;left:0;width:100%;height:3px;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta))}.stat-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-xl);border-color:var(--primary-cyan)}.stat-card-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:var(--spacing-md)}.stat-card .stat-label{font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:0}.stat-icon{width:40px;height:40px;background:linear-gradient(135deg,rgba(76,201,240,0.2) 0%,rgba(247,37,133,0.2) 100%);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:1.2rem;color:var(--primary-cyan)}.stat-card .stat-value{font-family:var(--font-heading);font-size:3rem;font-weight:700;color:var(--text-primary);line-height:1;margin-bottom:var(--spacing-xs);text-shadow:none}.stat-subtitle{font-size:0.85rem;color:var(--text-secondary)}.stat-card.warning .stat-icon{background:linear-gradient(135deg,rgba(255,171,0,0.2) 0%,rgba(255,143,0,0.2) 100%);color:var(--status-warning)}.stat-card.warning::before{background:var(--status-warning)}.stat-card.success .stat-icon{background:linear-gradient(135deg,rgba(0,230,118,0.2) 0%,rgba(0,200,83,0.2) 100%);color:var(--status-success)}.stat-card.success::before{background:var(--status-success)}.psn-stats-bar{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.psn-stat-box{padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);text-align:center}.psn-stat-icon{font-size:1.5rem;margin-bottom:var(--spacing-xs)}.psn-stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700}.psn-stat-value.platinum{color:var(--trophy-platinum)}.psn-stat-value.gold{color:var(--trophy-gold)}.psn-stat-value.silver{color:var(--trophy-silver)}.psn-stat-value.bronze{color:var(--trophy-bronze)}.psn-stat-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.stat-box.warning .stat-value{color:var(--neon-orange)}.stat-box.danger .stat-value{color:var(--neon-red)}.stat-box.success .stat-value{color:var(--neon-green)}.filter-bar{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-xl);flex-wrap:wrap}.search-box{--search-icon-offset:var(--spacing-md);--search-icon-size:1.15rem;--search-icon-width:2rem;--search-icon-gap:var(--spacing-sm);flex:1;min-width:250px;position:relative}.search-icon{position:absolute;left:var(--search-icon-offset);top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:var(--search-icon-size)}input.search-input{width:100%;padding:var(--spacing-md) var(--spacing-lg);padding-left:calc(var(--search-icon-offset) + var(--search-icon-width) + var(--search-icon-gap));background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:all var(--transition-fast)}input.search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 3px rgba(76,201,240,0.1)}input.search-input::placeholder{color:var(--text-muted)}.filter-select{padding:var(--spacing-md) var(--spacing-lg);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;transition:all var(--transition-fast);min-width:150px}.filter-select:focus{outline:none;border-color:var(--primary-cyan)}.platform-filters{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);flex-wrap:wrap}.platform-filter-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast)}.platform-filter-btn:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.platform-filter-btn.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.filter-count{font-size:0.7rem;padding:2px 6px;background:rgba(255,255,255,0.1);border-radius:var(--radius-full)}.platform-filter-btn.active .filter-count{background:rgba(76,201,240,0.2)}.bulk-filter-section{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.bulk-action-section{padding:var(--spacing-lg);background:var(--bg-dark);border-radius:var(--radius-md);border:1px solid var(--card-border)}.bulk-preview-count{font-size:1.5rem;font-weight:700;color:var(--primary-cyan);margin-bottom:var(--spacing-sm)}.bulk-game-list{max-height:300px;overflow-y:auto;margin-top:var(--spacing-md)}.bulk-game-item{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.bulk-game-item:last-child{border-bottom:none}.psn-search-sort-row{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg);align-items:center}.psn-search-wrapper{flex:1;position:relative}.psn-search-wrapper .search-input{width:100%;padding-left:calc(var(--search-icon-offset, var(--spacing-md)) + var(--search-icon-width, 2rem) + var(--search-icon-gap, var(--spacing-sm)))}.psn-sort-select{padding:var(--spacing-sm) var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;cursor:pointer}.psn-sort-select:focus{outline:none;border-color:var(--primary-cyan)}.hltb-card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-md)}.hltb-card-title{display:flex;align-items:center;gap:var(--spacing-sm);font-family:var(--font-primary);font-size:1rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-md)}.hltb-search-input{width:100%;padding:10px 14px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;margin-bottom:var(--spacing-sm)}.hltb-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.btn-hltb{width:100%;display:flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:10px 16px;background:rgba(76,201,240,0.1);border:1px solid var(--primary-cyan);border-radius:var(--radius-md);color:var(--primary-cyan);font-family:var(--font-primary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.btn-hltb:hover{background:rgba(76,201,240,0.2)}.btn-hltb:disabled{opacity:0.5;cursor:not-allowed}.hltb-results{margin-top:var(--spacing-md)}.hltb-match-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--spacing-xs);padding-bottom:var(--spacing-sm);margin-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.hltb-match-title{color:var(--primary-cyan);font-weight:600;font-size:0.9rem}.hltb-match-platform{color:var(--text-secondary);font-size:0.8rem}.hltb-match-year{color:var(--neon-orange);font-size:0.8rem;font-weight:500}.hltb-match-developer{color:var(--text-secondary);font-size:0.8rem;font-style:italic}.hltb-match-warning{color:var(--neon-orange);font-size:0.8rem;font-weight:600}.hltb-match-confidence{color:var(--accent-green);font-size:0.8rem;font-weight:500}.hltb-match-alt-notice{display:inline-flex;align-items:center;gap:0.25rem;color:var(--neon-orange);background:rgba(245,158,11,0.12);border:1px solid rgba(245,158,11,0.4);padding:2px 8px;border-radius:var(--radius-full);font-size:0.75rem;font-weight:600;cursor:help;white-space:nowrap}.hltb-time-row{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-sm) 0;border-bottom:1px solid var(--card-border)}.hltb-time-row:last-child{border-bottom:none}.hltb-label{color:var(--text-secondary);font-size:0.85rem}.hltb-value{color:var(--text-primary);font-size:0.85rem;font-weight:500}.hltb-value.highlight{color:var(--primary-cyan)}.hltb-actions{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-md)}.hltb-actions .btn{flex:1}.hltb-empty{color:var(--text-muted);font-size:0.9rem;padding:var(--spacing-sm) 0}.hltb-loading{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary);font-size:0.9rem;padding:var(--spacing-sm) 0}.hltb-compact .hltb-time-row{padding:var(--spacing-xs) 0}.hltb-compact .hltb-label,.hltb-compact .hltb-value{font-size:0.8rem}.manual-reader{display:flex;flex-direction:column;width:min(96vw,1400px);height:94vh;background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl)}.manual-reader-header{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);border-bottom:1px solid var(--card-border)}.manual-reader-header h2{flex:1;min-width:0;margin:0;font-family:var(--font-heading);font-size:1.1rem;color:var(--neon-cyan);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.manual-reader-stage{flex:1;min-height:0;display:grid;place-items:center;padding:var(--spacing-sm);overflow:hidden;perspective:2400px;touch-action:pan-y;user-select:none}.manual-reader-stage:focus-visible{outline:2px solid var(--primary-cyan);outline-offset:-2px}.manual-reader-sheet{grid-area:1 / 1;max-width:100%;max-height:100%;box-shadow:var(--shadow-lg);transform-origin:left center;backface-visibility:hidden}.manual-reader-sheet.turn-out{z-index:1;animation:manual-turn-out 0.6s ease-in-out forwards}.manual-reader-sheet.turn-in{z-index:1;animation:manual-turn-in 0.6s ease-in-out forwards}@keyframes manual-turn-out{from{transform:rotateY(0deg)}to{transform:rotateY(-180deg)}}@keyframes manual-turn-in{from{transform:rotateY(-180deg)}to{transform:rotateY(0deg)}}@media (prefers-reduced-motion:reduce){.manual-reader-sheet.turn-out,.manual-reader-sheet.turn-in{animation:none}}.manual-reader-message{grid-area:1 / 1;color:var(--text-secondary);text-align:center}.manual-reader-controls{display:flex;align-items:center;justify-content:center;gap:var(--spacing-lg);padding:var(--spacing-sm);border-top:1px solid var(--card-border)}.manual-reader-controls .btn{white-space:nowrap}.manual-reader-page{min-width:8em;text-align:center;color:var(--text-primary);font-family:var(--font-mono)}@media (max-width:600px){.manual-reader{width:100vw;height:100dvh;border-radius:0}.manual-reader-header .btn{font-size:0.75rem}}.score-badge.score-good{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green)}.score-badge.score-mixed{background:rgba(234,179,8,0.15);border-color:var(--neon-orange);color:var(--neon-orange)}.score-badge.score-bad{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red)}.bulk-option-label{display:flex;align-items:center;gap:6px;padding:6px 12px;background:rgba(168,85,247,0.1);border:1px solid var(--neon-purple);border-radius:var(--radius-md);color:var(--neon-purple);font-size:0.85rem;cursor:pointer;transition:all var(--transition-fast)}.bulk-option-label:hover{background:rgba(168,85,247,0.2)}.bulk-option-label input[type="checkbox"]{width:16px;height:16px;accent-color:var(--neon-purple);cursor:pointer}.bulk-mode-selector{display:flex;gap:8px;align-items:center}.scrape-mode-toggle{display:flex;align-items:center;gap:12px;padding:6px 16px;background:rgba(100,116,139,0.1);border:1px solid var(--neon-gray);border-radius:var(--radius-md)}.scrape-mode-toggle .mode-label{font-size:0.85rem;font-weight:500;transition:all var(--transition-fast)}.scrape-mode-toggle .mode-label.mode-off{color:var(--text-muted)}.scrape-mode-toggle .mode-label.mode-on{color:var(--text-muted)}.scrape-mode-toggle:has(input:not(:checked)) .mode-off{color:var(--neon-orange);text-shadow:0 0 8px rgba(245,158,11,0.5)}.scrape-mode-toggle:has(input:checked) .mode-on{color:var(--neon-cyan);text-shadow:0 0 8px rgba(76,201,240,0.5)}.keep-media-option{display:flex;align-items:center;gap:6px;font-size:0.85rem;color:var(--text-muted);cursor:pointer}.bulk-mode-selector:has(#scrapeModeToggle:checked) .keep-media-option{display:none}.scrape-mode-toggle .toggle-switch{position:relative;display:inline-block;width:44px;height:24px;flex-shrink:0}.scrape-mode-toggle .toggle-switch input{opacity:0;width:0;height:0}.scrape-mode-toggle .toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:var(--neon-orange);border-radius:24px;transition:all var(--transition-normal);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(245,158,11,0.3)}.scrape-mode-toggle .toggle-slider:before{position:absolute;content:"";height:18px;width:18px;left:3px;bottom:3px;background:white;border-radius:50%;transition:all var(--transition-normal);box-shadow:0 2px 4px rgba(0,0,0,0.3)}.scrape-mode-toggle .toggle-switch input:checked + .toggle-slider{background:var(--neon-cyan);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(76,201,240,0.3)}.scrape-mode-toggle .toggle-switch input:checked + .toggle-slider:before{transform:translateX(20px)}.fanart-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;background-size:cover;background-position:center;background-repeat:no-repeat;opacity:0;transition:opacity 0.5s ease,background-image 0.5s ease;pointer-events:none}.fanart-background::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient( to bottom,rgba(10,14,20,0.7) 0%,rgba(10,14,20,0.85) 50%,rgba(10,14,20,0.95) 100% )}.fanart-background.active{opacity:1}.glass-panel{background:rgba(20,28,40,0.7);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(76,201,240,0.2)}.glass-card{background:rgba(20,28,40,0.5);backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px)}.glitch-text{position:relative}.glitch-text::before,.glitch-text::after{content:attr(data-text);position:absolute;top:0;left:0;width:100%;height:100%}.glitch-text::before{left:2px;text-shadow:-2px 0 var(--primary-magenta);clip:rect(24px,550px,90px,0);animation:glitch-anim 3s infinite linear alternate-reverse}.glitch-text::after{left:-2px;text-shadow:-2px 0 var(--primary-cyan);clip:rect(85px,550px,140px,0);animation:glitch-anim2 3s infinite linear alternate-reverse}@keyframes glitch-anim{0%{clip:rect(20px,9999px,40px,0)}25%{clip:rect(60px,9999px,80px,0)}50%{clip:rect(10px,9999px,30px,0)}75%{clip:rect(70px,9999px,90px,0)}100%{clip:rect(30px,9999px,50px,0)}}@keyframes glitch-anim2{0%{clip:rect(80px,9999px,100px,0)}25%{clip:rect(20px,9999px,40px,0)}50%{clip:rect(50px,9999px,70px,0)}75%{clip:rect(10px,9999px,30px,0)}100%{clip:rect(60px,9999px,80px,0)}}.scan-lines{position:absolute;top:0;left:0;width:100%;height:100%;background:repeating-linear-gradient( 0deg,rgba(0,0,0,0.1),rgba(0,0,0,0.1) 1px,transparent 1px,transparent 2px );pointer-events:none;opacity:0;transition:opacity var(--transition-normal)}.game-card-new:hover .scan-lines{opacity:0.3}.card-glow{position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;background:linear-gradient(45deg,var(--primary-cyan),var(--primary-magenta),var(--primary-cyan));border-radius:calc(var(--radius-lg) + 2px);z-index:-1;opacity:0;filter:blur(8px);transition:opacity var(--transition-normal)}.game-card-new:hover .card-glow{opacity:0.5;animation:glow-pulse 2s ease-in-out infinite}@keyframes glow-pulse{0%,100%{opacity:0.3}50%{opacity:0.6}}.filter-bar-new{display:flex;gap:var(--spacing-md);align-items:center;flex-wrap:wrap;padding:var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);position:sticky;top:var(--sticky-top);z-index:100;background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.search-box-integrated{--search-icon-offset:12px;--search-icon-size:1rem;--search-icon-width:1.75rem;--search-icon-gap:var(--spacing-sm);position:relative;min-width:200px;max-width:350px;flex:1}.search-icon-inside{position:absolute;left:var(--search-icon-offset);top:50%;transform:translateY(-50%);font-size:var(--search-icon-size);pointer-events:none;z-index:1}.search-input-integrated{width:100%;padding:var(--spacing-sm) var(--spacing-md);padding-left:calc(var(--search-icon-offset) + var(--search-icon-width) + var(--search-icon-gap));padding-right:32px;background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.95rem;outline:none;transition:border-color var(--transition-fast)}.search-clear-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text-muted);font-size:1.2rem;cursor:pointer;padding:0 4px;line-height:1;opacity:0;transition:opacity 0.2s,color 0.2s;z-index:1}.search-clear-btn:hover{color:var(--primary-cyan)}.search-box-integrated:has(.search-input-integrated:not(:placeholder-shown)) .search-clear-btn{opacity:1}.search-input-integrated:focus{border-color:var(--primary-cyan)}.search-input-integrated::placeholder{color:var(--text-muted)}.filter-buttons{display:flex;gap:var(--spacing-sm);flex-wrap:wrap}.filter-btn{display:flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-sm) var(--spacing-md);background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast);font-size:0.85rem}.filter-btn:hover{border-color:var(--primary-cyan);color:var(--primary-cyan);background:rgba(76,201,240,0.1)}.filter-btn:disabled{opacity:0.35;cursor:default;pointer-events:none}.filter-btn.active{border-color:var(--primary-cyan);background:rgba(76,201,240,0.2);color:var(--primary-cyan)}.filter-btn.clear-btn{border-color:var(--status-error);color:var(--status-error)}.filter-btn.clear-btn:hover{background:rgba(255,107,107,0.2)}.filter-btn.ra-filter-btn{border-color:rgba(255,215,0,0.4);color:var(--trophy-display-gold)}.filter-btn.ra-filter-btn:hover{border-color:var(--trophy-display-gold);background:rgba(255,215,0,0.15)}.filter-btn.ra-filter-btn.active{border-color:var(--trophy-display-gold);background:rgba(255,215,0,0.25);color:var(--trophy-display-gold);box-shadow:0 0 10px rgba(255,215,0,0.2)}.filter-btn.bonus-filter-btn{border-color:rgba(245,158,11,0.4);color:var(--neon-orange)}.filter-btn.bonus-filter-btn:hover{border-color:var(--neon-orange);background:rgba(245,158,11,0.15)}.filter-btn.bonus-filter-btn.active{border-color:var(--neon-orange);background:rgba(245,158,11,0.25);color:var(--neon-orange);box-shadow:0 0 10px rgba(245,158,11,0.2)}.filter-btn.refresh-ra-btn{border-color:rgba(76,201,240,0.4);color:var(--primary-cyan)}.filter-btn.refresh-ra-btn:hover{border-color:var(--primary-cyan);background:rgba(76,201,240,0.15)}.filter-btn.refresh-ra-btn:disabled{opacity:0.6;cursor:not-allowed}.filter-tag.ra-tag{background:rgba(255,215,0,0.2);border-color:rgba(255,215,0,0.4);color:var(--trophy-display-gold)}.filter-bar-top{display:flex;align-items:center;gap:var(--spacing-md);width:100%}.bulk-controls{display:flex;gap:var(--spacing-sm);align-items:center;margin-left:auto;flex-wrap:wrap;justify-content:flex-end}.bulk-btn{border-color:rgba(147,51,234,0.4);color:var(--neon-purple)}.bulk-btn:hover{border-color:var(--neon-purple);background:rgba(147,51,234,0.15)}.bulk-btn.active{border-color:var(--neon-purple);background:rgba(147,51,234,0.25);color:var(--neon-purple)}.bulk-action-btn{border-color:rgba(59,130,246,0.4);color:var(--neon-blue)}.bulk-action-btn:hover{border-color:var(--neon-blue);background:rgba(59,130,246,0.15)}.bulk-scrape-btn{border-color:rgba(34,197,94,0.4);color:var(--neon-green)}.bulk-scrape-btn:hover{border-color:var(--neon-green);background:rgba(34,197,94,0.15)}.game-card-wrapper{position:relative}.bulk-select-checkbox{position:absolute;top:8px;right:8px;z-index:20}.bulk-select-checkbox input[type="checkbox"]{width:22px;height:22px;cursor:pointer;accent-color:var(--primary-cyan)}.game-card-wrapper.selected .game-card-new{border-color:var(--primary-cyan);box-shadow:0 0 15px rgba(76,201,240,0.4)}.bulk-scrape-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:3000;align-items:center;justify-content:center}.bulk-scrape-modal.active{display:flex}.bulk-scrape-content{width:90%;max-width:500px;border-radius:var(--radius-lg);overflow:hidden}.bulk-scrape-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);display:flex;justify-content:space-between;align-items:center}.bulk-scrape-header h2{margin:0;color:var(--primary-cyan)}.bulk-scrape-body{padding:var(--spacing-lg)}.bulk-progress-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md)}.bulk-progress-text{flex:1;overflow:hidden}#bulkCurrentGame{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text-primary);font-weight:500}.bulk-progress-numbers{color:var(--primary-cyan);font-family:var(--font-mono);font-size:1.1rem;margin-left:var(--spacing-md)}.bulk-progress-bar{width:100%;height:20px;background:var(--bg-medium);border-radius:10px;margin-bottom:var(--spacing-md);border:2px solid rgba(76,201,240,0.5);box-shadow:inset 0 2px 4px rgba(0,0,0,0.5);overflow:hidden;padding:2px}.bulk-progress-fill{display:block;height:100%;width:var(--progress,0%);min-width:0;max-width:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta));border-radius:8px;box-shadow:0 0 10px rgba(76,201,240,0.5)}.bulk-status{text-align:center;color:var(--text-secondary);font-size:0.9rem;margin-bottom:var(--spacing-md)}.bulk-controls-row{display:flex;justify-content:center;gap:var(--spacing-md);margin-top:var(--spacing-lg);margin-bottom:var(--spacing-md);padding-top:var(--spacing-sm)}.bulk-results{display:flex;justify-content:space-around;padding:var(--spacing-md);background:rgba(0,0,0,0.3);border-radius:var(--radius-md);margin-top:var(--spacing-md)}.bulk-result-item{display:flex;align-items:center;gap:var(--spacing-xs);font-size:0.9rem}.bulk-result-item.success{color:var(--neon-green)}.bulk-result-item.failed{color:var(--neon-red)}.bulk-result-item.skipped{color:var(--neon-orange)}.bulk-scrape-footer{padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border);display:flex;justify-content:center}.active-filter-tags{display:flex;gap:var(--spacing-sm);flex:1;min-width:0;overflow-x:auto;scrollbar-width:thin}.active-filter-tags .filter-tag{flex-shrink:0;white-space:nowrap}.filter-tag{display:flex;align-items:center;gap:var(--spacing-xs);padding:4px 10px;background:var(--primary-cyan);color:var(--bg-dark);border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600}.filter-tag-exclude{background:rgba(239,68,68,0.2);border:1px solid rgba(239,68,68,0.5);color:var(--neon-red)}.filter-tag-remove{cursor:pointer;opacity:0.7;transition:opacity 0.2s}.filter-tag-remove:hover{opacity:1}.alphabet-nav{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md) var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);position:sticky;top:calc(var(--sticky-top) + 80px);z-index:99}.alphabet-label{color:var(--text-muted);font-size:0.85rem;white-space:nowrap}.alphabet-letters{display:flex;flex-wrap:wrap;gap:4px}.alphabet-btn{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.alphabet-btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.active{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.disabled{opacity:0.3;cursor:not-allowed}.alphabet-btn.disabled:hover{background:rgba(76,201,240,0.1);border-color:rgba(76,201,240,0.3);color:var(--text-secondary)}.cards-page{--card-min:380px;--card-gap:var(--spacing-lg);max-width:calc(4 * 480px + 3 * var(--card-gap));margin-inline:auto}.games-grid-new{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--card-min,380px),1fr));grid-auto-rows:1px;column-gap:var(--spacing-lg)}.game-card-wrapper{grid-row-end:span 296;align-self:start;transition:transform var(--transition-normal);contain:layout style paint;content-visibility:auto;contain-intrinsic-size:auto 380px 296px}.game-card-new{position:relative;display:flex;flex-direction:row;border-radius:var(--radius-lg);overflow:hidden;text-decoration:none;transition:all var(--transition-normal);border:1px solid transparent;min-height:240px}.game-card-wrapper:hover{transform:translateY(-4px)}.game-card-new:hover{box-shadow:0 8px 30px rgba(76,201,240,0.2)}.game-card-wrapper.hidden{display:none}.game-card-new.hidden{display:none}.card-boxart-section{flex-shrink:0;width:135px;align-self:stretch;background:var(--bg-medium);display:flex;align-items:center;justify-content:center;position:relative}.card-play-btn{position:absolute;top:var(--spacing-sm);right:var(--spacing-sm);z-index:5;width:28px;height:28px;border-radius:var(--radius-full);border:1px solid var(--primary-cyan);background:var(--bg-darkest);color:var(--primary-cyan);font-size:0.8rem;line-height:1;cursor:pointer;box-shadow:var(--shadow-md);transition:background var(--transition-fast),color var(--transition-fast),box-shadow var(--transition-fast)}.card-play-btn{display:flex;align-items:center;justify-content:center;padding:0}.card-play-btn svg{width:60%;height:60%;fill:currentColor}.card-play-btn:hover,.card-play-btn:focus-visible{background:var(--primary-cyan);color:var(--bg-darkest);box-shadow:var(--shadow-glow-cyan)}.card-play-btn:disabled{cursor:progress}.games-grid-new.bulk-mode .card-play-btn{display:none}.game-card-new.has-play .card-info-section >:first-child{padding-right:calc(28px + var(--spacing-sm))}.card-info-section{flex:1;padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-sm);background:rgba(0,0,0,0.4)}.title-row{display:flex;align-items:flex-start;gap:var(--spacing-sm)}.title-row .game-card-title{flex:1}.game-card-ra-badge{position:absolute;top:8px;left:8px;width:28px;height:28px;background:linear-gradient(135deg,var(--trophy-display-gold) 0%,#ffaa00 100%);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.9rem;z-index:10;box-shadow:0 2px 8px rgba(255,215,0,0.4);border:2px solid rgba(255,255,255,0.3)}.game-card-rating-badge{position:absolute;top:8px;right:8px;z-index:10}.rating-text-badge{font-size:0.65rem;padding:2px 6px;border-radius:var(--radius-sm);font-weight:700;text-transform:uppercase}.rating-text-badge.esrb{background:rgba(0,0,0,0.7);color:var(--text-on-color);border:1px solid var(--text-on-color)}.rating-text-badge.pegi{background:rgba(141,198,63,0.9);color:var(--text-on-color);border:1px solid var(--text-on-color)}.system-badge-row{display:flex;align-items:center;gap:var(--spacing-sm)}.system-badge{padding:3px 8px;background:rgba(76,201,240,0.2);border:1px solid var(--primary-cyan);border-radius:var(--radius-sm);font-size:0.65rem;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:0.5px}.system-badge.nes{background:rgba(198,40,40,0.25);border-color:#e53935;color:#e53935}.system-badge.snes{background:rgba(229,57,53,0.2);border-color:#ef5350;color:#ef5350}.system-badge.n64{background:rgba(244,67,54,0.2);border-color:#f44336;color:#f44336}.system-badge.gc,.system-badge.gamecube{background:rgba(103,58,183,0.2);border-color:#9575cd;color:#9575cd}.system-badge.wii{background:rgba(255,255,255,0.15);border-color:#e0e0e0;color:#e0e0e0}.system-badge.wiiu{background:rgba(33,150,243,0.2);border-color:#42a5f5;color:#42a5f5}.system-badge.switch{background:rgba(229,57,53,0.2);border-color:#ef5350;color:#ef5350}.system-badge.gb{background:rgba(139,195,74,0.25);border-color:#9ccc65;color:#9ccc65}.system-badge.gbc{background:rgba(156,39,176,0.2);border-color:#ba68c8;color:#ba68c8}.system-badge.gba{background:rgba(103,58,183,0.2);border-color:#7e57c2;color:#7e57c2}.system-badge.nds{background:rgba(158,158,158,0.2);border-color:#bdbdbd;color:#bdbdbd}.system-badge.3ds{background:rgba(229,57,53,0.2);border-color:#e57373;color:#e57373}.system-badge.virtualboy,.system-badge.vb{background:rgba(183,28,28,0.3);border-color:#c62828;color:#ef5350}.system-badge.fds{background:rgba(255,193,7,0.2);border-color:#ffc107;color:#ffc107}.system-badge.psx,.system-badge.ps1{background:rgba(144,202,249,0.25);border-color:#90caf9;color:#90caf9}.system-badge.ps2{background:rgba(33,150,243,0.2);border-color:#42a5f5;color:#42a5f5}.system-badge.ps3{background:rgba(21,101,192,0.25);border-color:#1976d2;color:#64b5f6}.system-badge.ps4{background:rgba(13,71,161,0.3);border-color:#1565c0;color:#42a5f5}.system-badge.psp{background:rgba(100,181,246,0.2);border-color:#64b5f6;color:#64b5f6}.system-badge.psvita,.system-badge.vita{background:rgba(30,136,229,0.2);border-color:#1e88e5;color:#42a5f5}.system-badge.sg-1000,.system-badge.sg1000{background:rgba(0,77,64,0.3);border-color:#00897b;color:#4db6ac}.system-badge.mastersystem,.system-badge.sms{background:rgba(0,121,107,0.25);border-color:#00897b;color:#4db6ac}.system-badge.genesis,.system-badge.megadrive{background:rgba(0,150,136,0.2);border-color:#26a69a;color:#26a69a}.system-badge.segacd,.system-badge.megacd{background:rgba(0,137,123,0.25);border-color:#00897b;color:#4db6ac}.system-badge.sega32x,.system-badge.32x{background:rgba(0,105,92,0.3);border-color:#00796b;color:#4db6ac}.system-badge.saturn{background:rgba(128,203,196,0.2);border-color:#80cbc4;color:#80cbc4}.system-badge.dreamcast,.system-badge.dc{background:rgba(255,152,0,0.2);border-color:#ffb74d;color:#ffb74d}.system-badge.gamegear,.system-badge.gg{background:rgba(0,188,212,0.2);border-color:#4dd0e1;color:#4dd0e1}.system-badge.atari2600{background:rgba(230,81,0,0.25);border-color:#ff6d00;color:#ffab40}.system-badge.atari5200{background:rgba(255,111,0,0.2);border-color:#ff9100;color:#ffab40}.system-badge.atari7800{background:rgba(255,143,0,0.2);border-color:#ffa000;color:#ffca28}.system-badge.atarilynx,.system-badge.lynx{background:rgba(255,193,7,0.2);border-color:#ffc107;color:#ffc107}.system-badge.atarijaguar,.system-badge.jaguar{background:rgba(255,87,34,0.2);border-color:#ff7043;color:#ff7043}.system-badge.atarist,.system-badge.st{background:rgba(121,85,72,0.25);border-color:#a1887f;color:#bcaaa4}.system-badge.arcade{background:rgba(233,30,99,0.2);border-color:#ec407a;color:#ec407a}.system-badge.mame{background:rgba(233,30,99,0.2);border-color:#ec407a;color:#ec407a}.system-badge.fbneo{background:rgba(233,30,99,0.2);border-color:#f06292;color:#f06292}.system-badge.cps1,.system-badge.cps2,.system-badge.cps3{background:rgba(216,27,96,0.2);border-color:#e91e63;color:#f48fb1}.system-badge.naomi{background:rgba(194,24,91,0.25);border-color:#d81b60;color:#f48fb1}.system-badge.neogeo{background:rgba(255,193,7,0.25);border-color:#ffc107;color:#ffc107}.system-badge.neogeocd{background:rgba(255,179,0,0.2);border-color:#ffb300;color:#ffc107}.system-badge.ngp{background:rgba(255,213,79,0.2);border-color:#ffd54f;color:#ffd54f}.system-badge.ngpc{background:rgba(255,224,130,0.2);border-color:#ffe082;color:#ffe082}.system-badge.pcengine,.system-badge.tg16{background:rgba(156,39,176,0.2);border-color:#ab47bc;color:#ce93d8}.system-badge.pcenginecd,.system-badge.tg-cd{background:rgba(142,36,170,0.2);border-color:#9c27b0;color:#ce93d8}.system-badge.supergrafx{background:rgba(171,71,188,0.2);border-color:#ba68c8;color:#ce93d8}.system-badge.pcfx{background:rgba(123,31,162,0.25);border-color:#8e24aa;color:#ce93d8}.system-badge.c64{background:rgba(121,85,72,0.25);border-color:#8d6e63;color:#bcaaa4}.system-badge.amiga{background:rgba(141,110,99,0.2);border-color:#a1887f;color:#d7ccc8}.system-badge.vic20{background:rgba(93,64,55,0.3);border-color:#6d4c41;color:#a1887f}.system-badge.xbox{background:rgba(76,175,80,0.2);border-color:#66bb6a;color:#81c784}.system-badge.xbox360{background:rgba(56,142,60,0.25);border-color:#43a047;color:#81c784}.system-badge.3do{background:rgba(255,215,0,0.2);border-color:#ffd700;color:#ffd700}.system-badge.philipscdi,.system-badge.cdi{background:rgba(255,193,7,0.15);border-color:#ffca28;color:#ffca28}.system-badge.dos{background:rgba(96,125,139,0.2);border-color:#78909c;color:#90a4ae}.system-badge.pc{background:rgba(69,90,100,0.25);border-color:#607d8b;color:#90a4ae}.system-badge.pc88,.system-badge.pc98{background:rgba(84,110,122,0.2);border-color:#78909c;color:#b0bec5}.system-badge.msx{background:rgba(0,188,212,0.2);border-color:#00bcd4;color:#4dd0e1}.system-badge.msx2{background:rgba(0,172,193,0.2);border-color:#00acc1;color:#4dd0e1}.system-badge.wonderswan,.system-badge.ws{background:rgba(63,81,181,0.2);border-color:#5c6bc0;color:#7986cb}.system-badge.wonderswancolor,.system-badge.wsc{background:rgba(48,63,159,0.2);border-color:#3f51b5;color:#7986cb}.system-badge.pokemini{background:rgba(255,235,59,0.25);border-color:#ffee58;color:#fff176}.system-badge.nggage,.system-badge.ngage{background:rgba(255,112,67,0.2);border-color:#ff7043;color:#ff8a65}.system-badge.colecovision{background:rgba(183,28,28,0.2);border-color:#c62828;color:#ef5350}.system-badge.intellivision{background:rgba(255,152,0,0.2);border-color:#ff9800;color:#ffb74d}.system-badge.vectrex{background:rgba(255,255,255,0.2);border-color:#eeeeee;color:#eeeeee}.system-badge.scummvm{background:rgba(139,195,74,0.2);border-color:#8bc34a;color:#aed581}.system-badge.daphne{background:rgba(103,58,183,0.2);border-color:#7e57c2;color:#b39ddb}.title-badges{display:flex;gap:4px;flex-shrink:0}.title-badges .ra-badge{width:14px;height:14px;flex-shrink:0;object-fit:contain;filter:drop-shadow(0 0 4px rgba(255,193,7,0.5))}.import-badge.clz-badge{filter:drop-shadow(0 0 4px rgba(168,85,247,0.5))}.import-badge{width:14px;height:14px;flex-shrink:0;object-fit:contain}.import-badge.steam-badge{filter:drop-shadow(0 0 4px rgba(102,192,244,0.5))}.import-badge.xbox-badge{filter:drop-shadow(0 0 4px rgba(16,124,16,0.5))}.import-badge.psn-badge{filter:drop-shadow(0 0 4px rgba(0,55,145,0.5))}.bonus-badge{font-size:0.9rem;filter:drop-shadow(0 0 4px rgba(245,158,11,0.5));flex-shrink:0}.has-bonus-badge{font-size:0.75rem;background:rgba(168,85,247,0.2);border:1px solid var(--neon-purple);color:var(--neon-purple);padding:1px 4px;border-radius:4px;filter:drop-shadow(0 0 4px rgba(168,85,247,0.4));flex-shrink:0}.versions-badge{font-size:0.75rem;background:color-mix(in srgb,var(--neon-cyan) 20%,transparent);border:1px solid var(--neon-cyan);color:var(--neon-cyan);padding:1px 4px;border-radius:4px;flex-shrink:0}.game-card-wrapper.bonus-disc .game-card-new{border-left:3px solid var(--neon-orange);opacity:0.85}.game-card-wrapper.bonus-disc .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(245,158,11,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.clz-import .game-card-new{border-left:3px solid var(--neon-purple)}.game-card-wrapper.clz-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(168,85,247,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.steam-import .game-card-new{border-left:3px solid #66c0f4}.game-card-wrapper.steam-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(102,192,244,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.xbox-import .game-card-new{border-left:3px solid #107c10}.game-card-wrapper.xbox-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(16,124,16,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.psn-import .game-card-new{border-left:3px solid #0070d1}.game-card-wrapper.psn-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(0,112,209,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-title{font-size:0.95rem;font-weight:600;color:var(--text-primary);margin:0;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 12px;flex:1}.meta-item{display:flex;flex-direction:column;gap:1px}.meta-label{font-size:0.6rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.meta-value{font-size:0.75rem;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meta-value.meta-clickable{cursor:pointer;padding:2px 6px;margin:-2px -6px;border-radius:var(--radius-sm);transition:all var(--transition-fast);display:inline-block}.meta-value.meta-clickable:hover{background:rgba(76,201,240,0.15);color:var(--neon-cyan)}.rating-badges{display:flex;gap:var(--spacing-sm);flex-wrap:wrap;margin-top:auto}.rating-badge{padding:3px 8px;border-radius:var(--radius-sm);font-size:0.65rem;font-weight:700}.rating-badge.esrb{background:linear-gradient(135deg,var(--bg-medium),#16213e);color:#fff;border:1px solid #4a4a6a}.rating-badge.pegi{background:linear-gradient(135deg,#0f3460,#16213e);color:var(--primary-cyan);border:1px solid var(--primary-cyan)}.game-badge.achievement-progress{gap:4px;font-size:0.65rem;font-weight:700;font-family:var(--font-mono)}.ach-icon{width:14px;height:14px;object-fit:contain;flex-shrink:0;filter:drop-shadow(0 0 3px rgba(255,255,255,0.3))}.game-badge.achievement-progress.ra{background:linear-gradient(135deg,rgba(74,144,217,0.12) 0%,rgba(212,160,23,0.12) 100%);border-image:linear-gradient(135deg,#4A90D9,#D4A017) 1;color:#D4A017}.game-badge.achievement-progress.ra.ach-complete{background:linear-gradient(135deg,rgba(74,144,217,0.2) 0%,rgba(212,160,23,0.2) 100%);color:#e8b820}.game-badge.achievement-progress.steam{background:linear-gradient(135deg,rgba(27,40,56,0.35) 0%,rgba(102,192,244,0.12) 100%);border-image:linear-gradient(135deg,#2A6DAB,#66C0F4) 1;color:#66C0F4}.game-badge.achievement-progress.steam.ach-complete{background:linear-gradient(135deg,rgba(27,40,56,0.45) 0%,rgba(102,192,244,0.2) 100%);color:#88d4f8}.game-badge.achievement-progress.xbox{background:linear-gradient(135deg,rgba(16,124,16,0.12) 0%,rgba(82,176,67,0.12) 100%);border-image:linear-gradient(135deg,#107C10,#52B043) 1;color:#52B043}.game-badge.achievement-progress.xbox.ach-complete{background:linear-gradient(135deg,rgba(16,124,16,0.2) 0%,rgba(82,176,67,0.2) 100%);color:#6dcc5e}.game-badge.achievement-progress.psn{background:linear-gradient(135deg,rgba(0,48,135,0.15) 0%,rgba(108,168,217,0.12) 100%);border-image:linear-gradient(135deg,#0070D1,#6CA8D9) 1;color:#6CA8D9}.game-badge.achievement-progress.psn.ach-complete{background:linear-gradient(135deg,rgba(0,48,135,0.25) 0%,rgba(108,168,217,0.2) 100%);color:#8dc0e8}.game-badge.achievement-progress.rpcs3{background:linear-gradient(135deg,rgba(74,122,140,0.12) 0%,rgba(168,208,224,0.1) 100%);border-image:linear-gradient(135deg,#4A7A8C,#A8D0E0) 1;color:#A8D0E0}.game-badge.achievement-progress.rpcs3.ach-complete{background:linear-gradient(135deg,rgba(74,122,140,0.2) 0%,rgba(168,208,224,0.18) 100%);color:#c0e0ec}.game-badge.achievement-progress.ach-low{opacity:0.7}.ach-bar{display:inline-block;width:30px;height:4px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden;vertical-align:middle}.ach-fill{display:block;height:100%;border-radius:2px;transition:width 0.3s ease}.ra .ach-fill{background:linear-gradient(90deg,#4A90D9,#D4A017)}.ra.ach-complete .ach-fill{box-shadow:0 0 4px #D4A017}.steam .ach-fill{background:linear-gradient(90deg,#2A6DAB,#66C0F4)}.steam.ach-complete .ach-fill{box-shadow:0 0 4px #66C0F4}.xbox .ach-fill{background:linear-gradient(90deg,#107C10,#52B043)}.xbox.ach-complete .ach-fill{box-shadow:0 0 4px #52B043}.psn .ach-fill{background:linear-gradient(90deg,#0070D1,#6CA8D9)}.psn.ach-complete .ach-fill{box-shadow:0 0 4px #6CA8D9}.rpcs3 .ach-fill{background:linear-gradient(90deg,#4A7A8C,#A8D0E0)}.rpcs3.ach-complete .ach-fill{box-shadow:0 0 4px #A8D0E0}.game-card-image{width:100%;height:100%;overflow:hidden;background:var(--bg-medium);display:flex;align-items:center;justify-content:center;position:absolute;top:0;left:0;right:0;bottom:0}.game-boxart-img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;transition:transform var(--transition-normal)}.game-boxart-img.boxart-3d{object-fit:contain;padding:4px}.game-card-new:hover .game-boxart-img{transform:scale(1.05)}.game-boxart-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--bg-medium),var(--bg-dark))}.placeholder-icon{font-size:3rem;opacity:0.3}.games-count-bar{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-md);min-height:3rem;margin-bottom:var(--spacing-lg);padding:var(--spacing-sm) 0;border-bottom:1px solid var(--card-border)}.showing-text{flex-shrink:0;font-size:0.85rem;color:var(--text-muted);letter-spacing:1px}.showing-text span{color:var(--primary-cyan);font-weight:600}.sort-control{display:flex;align-items:center;gap:var(--spacing-sm)}.sort-label{font-size:0.85rem;color:var(--text-muted);white-space:nowrap}.sort-select{padding:var(--spacing-xs) var(--spacing-sm);background-color:var(--bg-light);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.85rem;cursor:pointer}.sort-select:focus-visible{outline:2px solid var(--primary-cyan);outline-offset:1px}#gamesEmpty .clear-btn{display:inline-flex;margin-top:var(--spacing-md)}.alphabet-nav.is-disabled{opacity:0.35}.bulk-action-bar{position:sticky;bottom:var(--spacing-md);z-index:101;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:var(--spacing-md);margin-top:var(--spacing-lg);border-radius:var(--radius-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.bulk-action-bar[hidden]{display:none}.filters-toggle-btn{display:none}.filter-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:2000;align-items:center;justify-content:center}.filter-modal.active{display:flex}.filter-modal-content{width:90%;max-width:500px;max-height:70vh;border-radius:var(--radius-lg);overflow:hidden;display:flex;flex-direction:column}.filter-modal-header{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);gap:var(--spacing-md)}.filter-modal-header h2{color:var(--primary-cyan);font-family:var(--font-heading);margin:0}.filter-mode-toggle{display:flex;border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden}.filter-mode-btn{padding:4px 12px;background:transparent;border:none;color:var(--text-secondary);cursor:pointer;font-size:0.85rem;font-weight:600;transition:all var(--transition-fast)}.filter-mode-btn:hover{color:var(--text-primary)}.filter-mode-btn.active[data-mode="include"]{background:rgba(76,201,240,0.2);color:var(--primary-cyan)}.filter-mode-btn.active[data-mode="exclude"]{background:rgba(239,68,68,0.2);color:var(--neon-red)}.filter-modal-body{padding:var(--spacing-lg);overflow-y:auto;flex:1}.filter-options{display:flex;flex-direction:column;gap:var(--spacing-sm)}.filter-option{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);cursor:pointer;transition:all var(--transition-fast)}.filter-option:hover{border-color:var(--primary-cyan);background:rgba(76,201,240,0.1)}.filter-modal.exclude-mode .filter-option:hover{border-color:var(--neon-red);background:rgba(239,68,68,0.1)}.filter-option-name{color:var(--text-primary)}.filter-option-count{color:var(--text-muted);font-size:0.85rem}button.filter-option{width:100%;font:inherit;color:inherit;text-align:left}.filter-option[aria-checked]::before{content:'';flex:0 0 auto;width:1.1rem;height:1.1rem;margin-right:var(--spacing-sm);border:1px solid var(--card-border);border-radius:var(--radius-sm);line-height:1.1rem;text-align:center}.filter-option[aria-checked] .filter-option-name{flex:1}.filter-option[aria-checked="true"]{border-color:var(--primary-cyan);background:rgba(76,201,240,0.15)}.filter-option[aria-checked="true"]::before{content:'✓';color:var(--primary-cyan);border-color:var(--primary-cyan)}.filter-modal.exclude-mode .filter-option[aria-checked="true"]{border-color:var(--neon-red);background:rgba(239,68,68,0.15)}.filter-modal.exclude-mode .filter-option[aria-checked="true"]::before{content:'✕';color:var(--neon-red);border-color:var(--neon-red)}.filter-modal-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-md);padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border)}.filter-modal-footer[hidden]{display:none}.filter-modal-hint{color:var(--text-muted);font-size:0.85rem}.filter-modal .hint-exclude,.filter-modal.exclude-mode .hint-include{display:none}.filter-modal.exclude-mode .hint-exclude{display:inline}@media (max-width:768px){.games-grid-new{grid-template-columns:1fr;grid-auto-rows:auto;row-gap:var(--spacing-lg)}.game-card-wrapper{grid-row-end:auto}.game-card-new{min-height:180px}.card-boxart-section{width:100px}.meta-grid{grid-template-columns:1fr}.filter-bar-top{flex-wrap:wrap}.filter-buttons{width:100%;justify-content:flex-start}.filters-toggle-btn{display:flex}.filter-bar-new .filter-buttons{display:none;position:absolute;top:100%;left:0;right:0;margin-top:var(--spacing-xs);padding:var(--spacing-md);border:1px solid var(--card-border);border-radius:var(--radius-lg);background:var(--bg-medium);box-shadow:0 8px 24px rgba(0,0,0,0.5)}.filter-bar-new.filters-open .filter-buttons{display:flex}.games-count-bar{flex-wrap:wrap}.games-count-bar .active-filter-tags{order:3;flex-basis:100%;min-height:1.75rem}.filter-btn{flex:1;min-width:80px;justify-content:center}.bulk-controls{width:100%;justify-content:flex-start}.glitch-text::before,.glitch-text::after{display:none}}.game-page-header{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg);position:relative;z-index:1}.game-page-title{font-family:var(--font-display);font-size:2rem;color:var(--primary-cyan);text-shadow:0 0 30px rgba(76,201,240,0.3);margin:0}.header-buttons{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.play-key-hint{align-self:center;font-size:0.8rem;color:var(--text-secondary)}.last-scan-indicator{display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-md);font-size:0.8rem;color:var(--text-secondary)}.last-scan-indicator .scan-icon{font-size:1rem}.last-scan-indicator .scan-time{color:var(--primary-cyan);font-weight:500}.report-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.report-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1.25rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.report-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.report-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.report-tab .tab-count{font-size:0.75rem;padding:0.15rem 0.5rem;background:rgba(0,0,0,0.3);border-radius:var(--radius-full);color:var(--text-secondary)}.report-tab.active .tab-count{background:var(--primary-cyan);color:var(--bg-darkest)}.report-tab .tab-icon{font-size:1.1rem}.report-panel{display:none}.report-panel.active{display:block;animation:fadeIn 0.3s ease}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.results-table-container{background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden}.settings-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.settings-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.settings-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.settings-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.settings-tab .tab-icon{font-size:1.1rem}.settings-tab-panel{display:none}.settings-tab-panel.active{display:block}.naming-tag-option{padding:0.25rem 0.6rem;border-radius:4px;font-size:0.8rem;font-weight:600;background:rgba(100,116,139,0.15);border:1px solid rgba(100,116,139,0.3);color:var(--text-secondary);cursor:pointer;transition:all 0.15s ease}.naming-tag-option:hover{border-color:var(--neon-green);color:var(--neon-green)}.parent-option{padding:var(--spacing-sm);border-bottom:1px solid var(--card-border);cursor:pointer}.parent-option:hover{background:rgba(76,201,240,0.1)}.tab-subnav{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:rgba(0,0,0,0.75);border-radius:var(--radius-md);position:sticky;top:calc(var(--sticky-top) + 54px);z-index:99;backdrop-filter:blur(10px)}.subnav-link{padding:var(--spacing-xs) var(--spacing-md);color:var(--text-secondary);text-decoration:none;font-size:0.85rem;border-radius:var(--radius-sm);transition:all var(--transition-fast)}.subnav-link:hover{background:rgba(76,201,240,0.1);color:var(--text-primary)}.subnav-link.active{background:rgba(76,201,240,0.15);color:var(--primary-cyan)}.achievement-overview{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg)}.achievement-stat{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md)}.achievement-stat-icon{font-size:2rem}.achievement-stat-info{display:flex;flex-direction:column}.achievement-stat-label{font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.achievement-stat-value{font-size:1.3rem;font-weight:600;color:var(--neon-cyan)}.dashboard-page{--card-min:280px;--card-gap:var(--spacing-lg)}.systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min,280px),100%),1fr));gap:var(--card-gap,var(--spacing-lg))}.system-card{display:flex;flex-direction:column;padding:var(--spacing-lg);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-lg);text-decoration:none;color:inherit;transition:all var(--transition-fast)}.system-card:hover{border-color:var(--neon-cyan);transform:translateY(-3px);box-shadow:0 8px 24px rgba(0,0,0,0.3),0 0 15px rgba(76,201,240,0.15)}.system-card-header{margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.system-name{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);margin:0}.system-card-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.system-stat{display:flex;flex-direction:column;align-items:center;text-align:center}.system-stat-value{font-size:1.2rem;font-weight:600;color:var(--neon-cyan)}.system-stat-label{font-size:0.7rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.3px}.system-card-action{margin-top:auto;padding-top:var(--spacing-md);text-align:center;color:var(--neon-cyan);font-size:0.9rem;font-weight:500;opacity:0.7;transition:opacity 0.2s}.system-card:hover .system-card-action{opacity:1}.filter-group{display:flex;align-items:center;gap:0.5rem}.filter-group label{color:var(--text-secondary);font-size:0.85rem;white-space:nowrap}.filter-group select,.filter-group input{padding:0.5rem 0.75rem;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-sm);color:var(--text-primary);font-size:0.85rem}.filter-group select:focus,.filter-group input:focus{outline:none;border-color:var(--primary-cyan)}.alert{padding:var(--spacing-md) var(--spacing-lg);border-radius:var(--radius-md);margin-bottom:var(--spacing-md)}.alert-warning{background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);color:var(--neon-orange)}.alert-danger{background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);color:var(--neon-red)}.alert-success{background:rgba(34,197,94,0.1);border:1px solid rgba(34,197,94,0.3);color:var(--neon-green)}.alert-info{background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);color:var(--primary-cyan)}.alert a{color:inherit;text-decoration:underline}.field-tip{display:block;margin-top:var(--spacing-xs);font-size:0.8rem;color:var(--text-muted)}.changelog-entry{margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-xl);border-bottom:1px solid var(--card-border)}.changelog-entry:last-child{border-bottom:none}.changelog-version{font-family:var(--font-heading);font-size:1.5rem;color:var(--primary-cyan);margin-bottom:var(--spacing-xs)}.changelog-date{font-size:0.85rem;color:var(--text-muted);margin-bottom:var(--spacing-md)}.changelog-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-xs);margin-bottom:var(--spacing-md)}.changelog-tag{padding:4px 12px;border-radius:var(--radius-full);font-size:0.75rem;font-weight:600;text-transform:uppercase}.tag-feature{background:rgba(76,201,240,0.15);border:1px solid var(--primary-cyan);color:var(--primary-cyan)}.tag-minor{background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);color:var(--neon-purple)}.tag-patch{background:rgba(245,158,11,0.15);border:1px solid var(--neon-orange);color:var(--neon-orange)}.tag-fix{background:rgba(239,68,68,0.15);border:1px solid var(--neon-red);color:var(--neon-red)}.tag-major{background:rgba(247,37,133,0.15);border:1px solid var(--secondary-magenta);color:var(--secondary-magenta)}.tag-ui{background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);color:var(--neon-purple)}.tag-improvement{background:rgba(76,201,240,0.15);border:1px solid var(--primary-cyan);color:var(--primary-cyan)}.tag-enhancement{background:rgba(34,197,94,0.15);border:1px solid var(--neon-green);color:var(--neon-green)}.tag-initial{background:rgba(247,37,133,0.15);border:1px solid var(--secondary-magenta);color:var(--secondary-magenta)}.changelog-list{list-style:none;padding:0;margin:0}.changelog-list li{padding:var(--spacing-xs) 0;padding-left:var(--spacing-lg);position:relative;color:var(--text-secondary)}.changelog-list li::before{content:'•';position:absolute;left:0;color:var(--primary-cyan)}.login-container{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:var(--spacing-lg)}.login-box{width:100%;max-width:400px;padding:var(--spacing-xl);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);box-shadow:0 0 40px rgba(76,201,240,0.1)}.login-header{text-align:center;margin-bottom:var(--spacing-xl)}.login-logo{font-family:var(--font-heading);font-size:2rem;color:var(--primary-cyan);margin-bottom:var(--spacing-xs)}.login-subtitle{color:var(--text-secondary);font-size:0.9rem}.help-page{--help-text-max:calc(75ch + 2 * var(--spacing-lg));--help-nav-width:220px;--page-max:var(--help-text-max)}.help-page > .help-nav{flex-wrap:nowrap;overflow-x:auto}@media (max-width:600px){.help-page code{overflow-wrap:anywhere}}@media (min-width:1300px){.help-page{--page-max:calc(var(--help-nav-width) + var(--spacing-xl) + var(--help-text-max));display:grid;grid-template-columns:var(--help-nav-width) minmax(0,1fr);column-gap:var(--spacing-xl);align-items:start}.help-page > *{grid-column:2}.help-page > .page-header{grid-column:1 / -1}.help-page > .help-nav{grid-column:1;grid-row:2 / span 100;flex-direction:column;flex-wrap:nowrap;margin:0;max-height:100vh;overflow-x:hidden;overflow-y:auto}.help-page .help-nav-item{white-space:normal}}.help-section{margin-bottom:var(--spacing-xl)}.help-section h2{font-family:var(--font-heading);font-size:1.25rem;color:var(--primary-cyan);margin-bottom:var(--spacing-md)}.help-item{padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.help-item h3{color:var(--text-primary);font-size:1rem;margin-bottom:var(--spacing-xs)}.help-item p{color:var(--text-secondary);font-size:0.9rem;margin:0}.tool-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg)}.tool-description{color:var(--text-secondary);font-size:0.95rem;margin-bottom:var(--spacing-lg);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);border-left:3px solid var(--primary-cyan)}.tool-results{margin-top:var(--spacing-lg)}.tool-result-item{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.tool-result-item:hover{border-color:var(--primary-cyan)}.tool-result-icon{font-size:1.5rem}.tool-result-info{flex:1}.tool-result-name{color:var(--text-primary);font-weight:500}.tool-result-detail{color:var(--text-muted);font-size:0.85rem}@media (max-width:768px){.settings-tabs,.report-tabs{gap:var(--spacing-xs);padding:var(--spacing-xs)}.settings-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.settings-tab .tab-text{display:none}.settings-tab .tab-icon{font-size:1.25rem}.report-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.report-tab .tab-label{display:none}.report-tab .tab-icon{font-size:1.25rem}}.game-fanart-bg{position:fixed;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;opacity:0;z-index:-1;transition:opacity 0.5s ease}.game-fanart-bg.active{opacity:0.15}.game-fanart-bg::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,transparent 0%,var(--bg-primary) 100%)}.game-compact-layout{max-width:100%}.game-hero{display:flex;gap:var(--spacing-xl);margin-bottom:var(--spacing-xl);background:color-mix(in srgb,var(--bg-medium) 85%,transparent);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.hero-boxart{position:relative;flex-shrink:0;width:200px}.hero-boxart-frame{position:relative}.upscale-game-note{margin:var(--spacing-sm) 0 0;font-size:0.75rem;color:var(--text-secondary)}.game-boxart-image{width:100%;height:auto;border-radius:var(--radius-md);box-shadow:0 8px 32px rgba(0,0,0,0.4)}.hero-age-ratings{display:flex;flex-wrap:wrap;gap:5px;margin-top:auto;padding-top:var(--spacing-sm)}.hero-info{flex:1;min-width:0;display:flex;flex-direction:column}.quick-tags{display:flex;flex-wrap:wrap;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.tag{padding:4px 10px;border-radius:4px;font-size:0.8rem;font-weight:500;background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.1)}.quick-tags .tag,.quick-tags .game-badge{height:32px;box-sizing:border-box;line-height:1}.quick-tags .game-badge.age-rating-badge .age-rating-img{height:32px}.tag-system{color:var(--primary-cyan);border-color:var(--primary-cyan)}.hero-description{flex:1;font-size:0.9rem;line-height:1.6;color:var(--text-secondary);margin-bottom:var(--spacing-md);overflow-y:auto;max-height:120px}.key-details{display:flex;flex-wrap:wrap;gap:var(--spacing-lg);padding-top:var(--spacing-md);border-top:1px solid var(--card-border)}.kd-item{display:flex;flex-direction:column;gap:2px}.kd-label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase}.kd-value{font-size:0.9rem;color:var(--text-primary)}.trophy-summary{text-align:center}.trophy-summary-header{display:flex;justify-content:center;align-items:center;gap:var(--spacing-lg);margin-bottom:var(--spacing-md)}.trophy-summary-title{font-size:1.1rem;font-weight:600}.trophy-summary-total{font-size:1.3rem;font-weight:bold;color:var(--primary-cyan)}.trophy-summary-progress{display:flex;align-items:center;gap:var(--spacing-md);max-width:500px;margin:0 auto var(--spacing-lg)}.trophy-progress-bar.large{flex:1;height:12px;background:rgba(255,255,255,0.1);border-radius:6px;overflow:hidden}.trophy-progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),#00ddff);border-radius:6px}.trophy-progress-percent{font-size:1.2rem;font-weight:bold;color:var(--primary-cyan);min-width:60px}.trophy-summary-counts{display:flex;justify-content:center;gap:var(--spacing-xl);flex-wrap:wrap}.trophy-count{display:flex;align-items:center;gap:var(--spacing-sm);font-size:1.1rem;font-weight:600}.trophy-count img{width:28px;height:28px}.trophy-count.platinum{color:var(--trophy-display-platinum)}.trophy-count.gold{color:var(--trophy-display-gold)}.trophy-count.silver{color:var(--trophy-display-silver)}.trophy-count.bronze{color:var(--trophy-display-bronze)}.bottom-row{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.bottom-section{flex:1;background:color-mix(in srgb,var(--bg-medium) 85%,transparent);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-md)}.section-title-sm{font-size:0.9rem;color:var(--text-secondary);margin-bottom:var(--spacing-md)}.hltb-section{display:flex;flex-direction:column;gap:var(--spacing-sm)}.more-details{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-sm)}.md-item{display:flex;flex-direction:column}.md-item.md-inline{flex-direction:row;align-items:baseline;gap:var(--spacing-sm);grid-column:span 2}.md-label{font-size:0.7rem;color:var(--text-muted);text-transform:uppercase}.md-value{font-size:0.85rem;color:var(--text-primary)}.trophy-columns{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:650px;gap:var(--spacing-xl)}.trophy-columns > .card{height:100%;display:flex;flex-direction:column;overflow:hidden}.trophy-columns > .card + .card{margin-top:0}.trophy-columns > .card .card-header{flex-shrink:0}.trophy-column .card-body{flex:1;overflow-y:auto;min-height:0}.trophy-list{display:flex;flex-direction:column;gap:var(--spacing-md)}.trophy-item{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.trophy-item.locked{opacity:0.5}.trophy-item.locked .trophy-item-icon img:first-child{filter:grayscale(1) brightness(0.5)}.trophy-item-icon{position:relative;flex-shrink:0}.trophy-item-icon > img:first-child{width:60px;height:60px;border-radius:var(--radius-sm);background:rgba(0,0,0,0.3);object-fit:cover}.trophy-item-icon > img.trophy-type-badge{position:absolute;bottom:-4px;right:-4px;width:24px;height:24px}.trophy-item-icon .badge-placeholder{width:60px;height:60px;border-radius:var(--radius-sm);background:rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;font-size:1.5rem}.trophy-item-info{flex:1;min-width:0}.trophy-item-name{font-weight:600;font-size:0.95rem;margin-bottom:4px}.trophy-item-desc{font-size:0.85rem;color:var(--text-secondary);margin-bottom:8px}.trophy-item-meta{display:flex;gap:var(--spacing-md);align-items:center;flex-wrap:wrap;font-size:0.8rem}.trophy-type-label{padding:2px 8px;border-radius:4px;font-weight:600;text-transform:uppercase;font-size:0.7rem}.trophy-type-label.p{background:var(--trophy-display-platinum);color:var(--trophy-display-text)}.trophy-type-label.g{background:var(--trophy-display-gold);color:var(--trophy-display-text)}.trophy-type-label.s{background:var(--trophy-display-silver);color:var(--trophy-display-text)}.trophy-type-label.b{background:var(--trophy-display-bronze);color:var(--trophy-display-text)}.trophy-timestamp{color:var(--primary-cyan)}.trophy-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted);font-style:italic}.hltb-search-input{width:100%;padding:10px 14px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;margin-bottom:var(--spacing-sm)}.hltb-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.btn-hltb{width:100%;display:flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:10px 16px;background:rgba(76,201,240,0.1);border:1px solid var(--primary-cyan);border-radius:var(--radius-md);color:var(--primary-cyan);font-family:var(--font-primary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.btn-hltb:hover{background:rgba(76,201,240,0.2)}.btn-hltb:disabled{opacity:0.5;cursor:not-allowed}@media (max-width:900px){.trophy-columns{grid-template-columns:1fr;grid-template-rows:auto}.trophy-columns > .card{height:auto;max-height:500px}.game-hero{flex-direction:column;align-items:center}.hero-boxart{width:150px}.bottom-row{flex-direction:column}}.alphabet-nav{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);position:sticky;top:var(--sticky-top);z-index:99}.alphabet-label{color:var(--text-muted);font-size:0.8rem;white-space:nowrap}.alphabet-letters{display:flex;flex-wrap:wrap;gap:3px}.alphabet-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:rgba(76,201,240,0.08);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:0.75rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast);padding:0}.alphabet-btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.active{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.disabled{opacity:0.25;cursor:not-allowed}.alphabet-btn.disabled:hover{background:rgba(76,201,240,0.08);border-color:rgba(76,201,240,0.2);color:var(--text-secondary)}@media (max-width:768px){.alphabet-nav{padding:var(--spacing-xs) var(--spacing-md)}.alphabet-btn{width:24px;height:24px;font-size:0.65rem}.alphabet-label{display:none}}.warning-banner{background:linear-gradient(135deg,rgba(255,193,7,0.1),rgba(247,37,133,0.1));border:1px solid rgba(255,193,7,0.3);border-radius:var(--radius-md);padding:1rem 1.25rem;margin-bottom:1.5rem;display:flex;align-items:flex-start;gap:1rem}.warning-banner .warning-icon{font-size:1.5rem}.warning-banner .warning-text h4{color:var(--neon-orange);margin-bottom:0.25rem}.warning-banner .warning-text p{color:var(--text-secondary);font-size:0.9rem;margin:0}.info-banner{background:linear-gradient(135deg,rgba(76,201,240,0.1),rgba(114,9,183,0.1));border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-md);padding:1rem 1.25rem;margin-bottom:1.5rem;display:flex;align-items:flex-start;gap:1rem}.info-banner .info-icon{font-size:1.5rem}.info-banner .info-text h4{color:var(--text-primary);margin-bottom:0.25rem}.info-banner .info-text p{color:var(--text-secondary);font-size:0.9rem;margin:0}.file-list{background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);max-height:400px;overflow-y:auto}.file-list-header{display:grid;gap:1rem;padding:0.75rem 1rem;background:var(--bg-medium);border-bottom:1px solid var(--border-color);font-size:0.75rem;text-transform:uppercase;letter-spacing:1px;color:var(--text-secondary);position:sticky;top:0;z-index:10}.file-list-item{display:grid;gap:1rem;padding:0.75rem 1rem;border-bottom:1px solid var(--border-color);font-size:0.9rem;align-items:center}.file-list-item:hover{background:rgba(76,201,240,0.05)}.file-list-item:last-child{border-bottom:none}.file-list-item input[type="checkbox"]{width:18px;height:18px;accent-color:var(--neon-cyan);cursor:pointer}.file-list-item .filename{color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-list-item .filesize{color:var(--text-secondary)}.action-bar{display:flex;gap:1rem;margin-top:1rem;padding:1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md)}.action-btn{padding:0.4rem 0.75rem;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-sm);color:var(--neon-cyan);cursor:pointer;font-size:0.8rem;transition:all var(--transition-fast)}.action-btn:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 10px rgba(76,201,240,0.3)}.theme-selector{display:flex;gap:var(--spacing-md);flex-wrap:wrap}.theme-option{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-md);background:var(--card-bg);border:2px solid var(--card-border);border-radius:var(--radius-md);cursor:pointer;transition:border-color var(--transition-fast),box-shadow var(--transition-fast);min-width:110px}.theme-option:hover{border-color:var(--card-border-hover)}.theme-option.active{border-color:var(--primary-cyan);box-shadow:0 0 12px var(--primary-cyan-glow)}.theme-preview{display:flex;gap:4px;padding:4px;background:rgba(0,0,0,0.3);border-radius:var(--radius-sm)}.theme-swatch{width:24px;height:24px;border-radius:3px}.theme-name{font-family:var(--font-primary);font-size:0.85rem;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px}.theme-option.active .theme-name{color:var(--primary-cyan)}.section-title{font-family:var(--font-display);font-size:1rem;color:var(--text-primary);margin-bottom:1rem;padding-bottom:0.5rem;border-bottom:1px solid var(--border-color);display:flex;align-items:center;gap:0.5rem}.emu-folder-list{list-style:none;margin:0 0 var(--spacing-md);padding:0}.emu-folder-list li{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-sm);padding:var(--spacing-sm) 0;border-bottom:1px solid var(--border-color)}.emu-folder-list code{overflow-wrap:anywhere}.emu-folder-add{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.emu-folder-add .form-input{flex:1 1 16rem}.museum-progress{padding:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.museum-progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-sm)}.museum-progress-title{font-family:var(--font-heading);font-size:0.95rem;color:var(--primary-cyan)}.museum-progress-stats{font-size:0.85rem;color:var(--text-secondary);margin-top:var(--spacing-sm)}.museum-progress-stats .text-success{color:var(--status-success)}.museum-progress-stats .text-warning{color:var(--status-warning)}.museum-progress-stats .text-error{color:var(--status-error)}.museum-timeline{display:flex;flex-direction:column;gap:var(--spacing-xl)}.museum-era{position:relative}.museum-era-header{display:flex;align-items:center;gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.museum-era-line{width:4px;height:48px;background:linear-gradient(180deg,var(--primary-cyan),transparent);border-radius:2px;flex-shrink:0}.museum-era-info{display:flex;flex-direction:column;gap:2px}.museum-era-title{font-family:var(--font-heading);font-size:1.3rem;color:var(--primary-cyan);margin:0;text-shadow:0 0 20px rgba(76,201,240,0.3)}.museum-era-date{font-family:var(--font-mono);font-size:0.8rem;color:var(--text-muted)}.museum-era-count{font-size:0.8rem;color:var(--text-secondary)}.museum-page{--card-min:260px;--card-gap:var(--spacing-md)}.museum-systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.museum-system-card{display:flex;flex-direction:column;position:relative;border-radius:var(--radius-lg);overflow:hidden;text-decoration:none;color:inherit;transition:transform var(--transition-normal),box-shadow var(--transition-normal)}.museum-system-card:hover{transform:translateY(-3px);box-shadow:0 0 20px rgba(76,201,240,0.15);border-color:var(--card-border-hover)}.museum-card-image{height:140px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.2);padding:var(--spacing-md)}.museum-card-image img{max-height:100%;max-width:100%;object-fit:contain}.museum-card-placeholder{font-size:3rem;opacity:0.3}.museum-card-body{padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-xs);flex:1}.museum-card-title{font-family:var(--font-heading);font-size:0.9rem;color:var(--text-primary);margin:0;line-height:1.3}.museum-card-summary{font-size:0.78rem;color:var(--text-secondary);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.4}.museum-card-meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:auto}.museum-meta-chip{display:inline-block;padding:2px 8px;font-size:0.7rem;font-family:var(--font-mono);color:var(--text-secondary);background:rgba(76,201,240,0.08);border:1px solid rgba(76,201,240,0.15);border-radius:var(--radius-full);white-space:nowrap}.museum-content-indicator{position:absolute;top:var(--spacing-sm);right:var(--spacing-sm);width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-full);background:rgba(34,197,94,0.2);color:var(--neon-green);font-size:0.7rem}.museum-system-header{margin-bottom:var(--spacing-xl)}.museum-hero-layout{display:flex;gap:var(--spacing-xl);align-items:flex-start}.museum-hero-image{flex-shrink:0;width:240px;height:200px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.2);border-radius:var(--radius-lg);border:1px solid var(--card-border);padding:var(--spacing-md)}.museum-hero-image img{max-height:100%;max-width:100%;object-fit:contain}.museum-hero-placeholder{font-size:4rem;opacity:0.3}.museum-hero-info{flex:1;display:flex;flex-direction:column;gap:var(--spacing-sm)}.museum-hero-title{font-family:var(--font-heading);font-size:2rem;color:var(--primary-cyan);margin:0;text-shadow:0 0 30px rgba(76,201,240,0.3)}.museum-hero-summary{font-size:1rem;color:var(--text-secondary);line-height:1.5;margin:0}.museum-hero-meta{display:flex;flex-wrap:wrap;gap:var(--spacing-xs)}.museum-hero-actions{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-sm);flex-wrap:wrap}.museum-nav{display:flex;gap:0;border-bottom:2px solid var(--border-color);margin-bottom:var(--spacing-lg);position:sticky;background:var(--bg-darkest);z-index:10;padding-top:var(--spacing-xs)}.museum-nav-item{padding:var(--spacing-sm) var(--spacing-lg);font-family:var(--font-heading);font-size:0.85rem;color:var(--text-secondary);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-2px;transition:color var(--transition-fast),border-color var(--transition-fast);white-space:nowrap}.museum-nav-item:hover{color:var(--text-primary)}.museum-nav-item.active{color:var(--primary-cyan);border-bottom-color:var(--primary-cyan)}.museum-section{margin-bottom:var(--spacing-xl)}.museum-section-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--spacing-sm)}.museum-section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md);flex-wrap:wrap;gap:var(--spacing-sm)}.museum-section-title{font-family:var(--font-heading);font-size:1.2rem;color:var(--primary-cyan);margin:0}.museum-history-content{max-width:calc(75ch + 2 * var(--spacing-lg));padding:var(--spacing-lg);line-height:1.75;color:var(--text-primary);font-size:0.95rem}.museum-history-content p{margin:0 0 var(--spacing-md) 0}.museum-history-content p:last-child{margin-bottom:0}.museum-placeholder{padding:var(--spacing-xl);text-align:center}.museum-placeholder .text-muted{color:var(--text-muted)}.museum-generated-badge{font-size:0.75rem;font-family:var(--font-mono);color:var(--text-muted);padding:4px 10px;background:rgba(76,201,240,0.06);border:1px solid rgba(76,201,240,0.12);border-radius:var(--radius-full)}.museum-ai-badge{display:inline-block;padding:2px 8px;font-size:0.7rem;font-family:var(--font-mono);color:var(--neon-purple);background:rgba(168,85,247,0.1);border:1px solid rgba(168,85,247,0.2);border-radius:var(--radius-full)}.museum-system-page{--card-min:280px;--card-gap:var(--spacing-md)}.museum-specs-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.museum-spec-item{padding:var(--spacing-md);display:flex;flex-direction:column;gap:4px}.museum-spec-label{font-family:var(--font-heading);font-size:0.75rem;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:0.5px}.museum-spec-value{font-size:0.9rem;color:var(--text-primary);font-family:var(--font-mono);word-break:break-word}.museum-top-games-list{display:flex;flex-direction:column;gap:var(--spacing-sm)}.museum-top-game{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);transition:border-color var(--transition-fast)}.museum-top-game:hover{border-color:var(--card-border-hover)}.museum-rank{font-family:var(--font-heading);font-size:1.3rem;color:var(--primary-cyan);min-width:40px;text-align:center;flex-shrink:0}.museum-top-game-art{width:48px;height:64px;flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);overflow:hidden;background:rgba(0,0,0,0.2)}.museum-top-game-art img{max-height:100%;max-width:100%;object-fit:cover}.museum-top-game-placeholder{font-size:1.5rem;opacity:0.3}.museum-top-game-info{flex:1;display:flex;flex-direction:column;gap:2px}.museum-top-game-title{font-family:var(--font-primary);font-weight:600;font-size:0.95rem;color:var(--text-primary);text-decoration:none}a.museum-top-game-title:hover{color:var(--primary-cyan)}.museum-top-game-meta{display:flex;gap:var(--spacing-xs);align-items:center}.museum-controllers-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:var(--spacing-md)}.museum-controller-card{display:flex;flex-direction:column;overflow:hidden;border-radius:var(--radius-lg)}.museum-controller-image{height:180px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.15);padding:var(--spacing-md);position:relative}.museum-controller-image img{max-height:100%;max-width:100%;object-fit:contain;cursor:zoom-in}.museum-controller-image-actions{display:flex;gap:4px}.museum-controller-image-actions .btn{padding:2px 6px;font-size:0.65rem;line-height:1.2;letter-spacing:0.5px;backdrop-filter:blur(8px);background:rgba(0,0,0,0.6);border-color:rgba(76,201,240,0.4);color:var(--primary-cyan);box-shadow:none;text-shadow:none}.museum-controller-image-actions .btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan)}.museum-controller-image > .museum-controller-image-actions{position:absolute;bottom:var(--spacing-xs);right:var(--spacing-xs);opacity:0;transition:opacity var(--transition-fast)}.museum-controller-image:hover > .museum-controller-image-actions{opacity:1}.museum-controller-upload-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-sm);background:rgba(10,14,23,0.85);z-index:5;border-radius:inherit}.museum-controller-upload-text{font-family:var(--font-primary);font-size:0.75rem;color:var(--text-secondary);letter-spacing:0.5px}.museum-lightbox{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);z-index:10002;align-items:center;justify-content:center;flex-direction:column}.museum-lightbox.active{display:flex}.museum-lightbox-image{max-width:90%;max-height:80vh;object-fit:contain;border-radius:var(--radius-md);position:relative;z-index:1}.museum-lightbox::before{content:"";position:absolute;top:45%;left:50%;transform:translate(-50%,-50%);width:90vmin;height:90vmin;background:radial-gradient(circle at center,rgba(76,201,240,0.19) 0%,rgba(76,201,240,0.09) 25%,rgba(76,201,240,0.03) 45%,transparent 40%);border-radius:50%;pointer-events:none;z-index:0}.museum-lightbox-close{position:absolute;top:20px;right:35px;font-size:2.5rem;color:var(--text-primary);cursor:pointer;z-index:10003;transition:color var(--transition-fast);background:none;border:none;line-height:1}.museum-lightbox-close:hover{color:var(--primary-cyan)}.museum-lightbox-title{position:absolute;bottom:30px;left:50%;transform:translateX(-50%);color:var(--text-secondary);font-family:var(--font-heading);font-size:0.9rem;white-space:nowrap}.museum-lightbox-hint{position:absolute;bottom:12px;left:50%;transform:translateX(-50%);color:var(--text-muted);font-size:0.75rem;white-space:nowrap}.museum-lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.5);border:2px solid var(--card-border);color:var(--text-primary);font-size:3rem;padding:10px 20px;cursor:pointer;border-radius:var(--radius-md);transition:all var(--transition-fast);z-index:10003}.museum-lightbox-nav:hover{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.museum-lightbox-nav.prev{left:20px}.museum-lightbox-nav.next{right:20px}.museum-controller-placeholder{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-sm);color:var(--text-muted);font-size:2.5rem}.museum-controller-info{padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-xs)}.museum-controller-name{font-family:var(--font-heading);font-size:0.85rem;color:var(--text-primary);margin:0}.museum-controller-meta{display:flex;flex-wrap:wrap;gap:4px}.museum-controller-desc{font-size:0.8rem;color:var(--text-secondary);margin:var(--spacing-xs) 0 0 0;line-height:1.4}.museum-controller-buttons{margin-top:var(--spacing-xs);font-size:0.8rem;color:var(--text-secondary)}.museum-controller-buttons summary{cursor:pointer;color:var(--primary-cyan);font-family:var(--font-heading);font-size:0.75rem}.museum-controller-buttons p{margin:var(--spacing-xs) 0 0 0;font-family:var(--font-mono);font-size:0.75rem;line-height:1.5}@media (max-width:768px){.museum-hero-layout{flex-direction:column;align-items:center;text-align:center}.museum-hero-image{width:180px;height:150px}.museum-hero-meta,.museum-hero-actions{justify-content:center}.museum-systems-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.museum-specs-grid{grid-template-columns:1fr}.museum-controllers-grid{grid-template-columns:1fr}.museum-nav{overflow-x:auto;-webkit-overflow-scrolling:touch}}body::before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:linear-gradient( to top right,rgba(76,201,240,0.08) 0%,rgba(114,9,183,0.06) 25%,rgba(247,37,133,0.04) 50%,rgba(20,26,35,0.02) 75%,transparent 100% );pointer-events:none;z-index:-2}body::after{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,0,0,0.03) 2px,rgba(0,0,0,0.03) 4px );pointer-events:none;z-index:9999}@media (prefers-reduced-motion:reduce){body::after{display:none}}@keyframes glow-pulse{0%,100%{text-shadow:0 0 10px var(--primary-cyan-glow),0 0 20px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow)}50%{text-shadow:0 0 15px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow),0 0 45px var(--primary-cyan-glow)}}@keyframes pulse-status{0%,100%{opacity:1}50%{opacity:0.5}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes highlight-pulse{0%,100%{box-shadow:0 0 0 0 rgba(76,201,240,0)}50%{box-shadow:0 0 20px 5px rgba(76,201,240,0.4)}}.highlight-jump{animation:highlight-pulse 0.75s ease-in-out 2}@keyframes modalSlideIn{from{opacity:0;transform:scale(0.9) translateY(-20px)}to{opacity:1;transform:scale(1) translateY(0)}}@media (prefers-reduced-motion:reduce){.highlight-jump{animation:none}}.mt-0{margin-top:0}.mt-xs{margin-top:var(--spacing-xs)}.mt-sm{margin-top:var(--spacing-sm)}.mt-md{margin-top:var(--spacing-md)}.mt-lg{margin-top:var(--spacing-lg)}.mt-xl{margin-top:var(--spacing-xl)}.mt-2xl{margin-top:var(--spacing-2xl)}.mb-0{margin-bottom:0}.mb-xs{margin-bottom:var(--spacing-xs)}.mb-sm{margin-bottom:var(--spacing-sm)}.mb-md{margin-bottom:var(--spacing-md)}.mb-lg{margin-bottom:var(--spacing-lg)}.mb-xl{margin-bottom:var(--spacing-xl)}.mb-2xl{margin-bottom:var(--spacing-2xl)}.ml-0{margin-left:0}.ml-xs{margin-left:var(--spacing-xs)}.ml-sm{margin-left:var(--spacing-sm)}.ml-md{margin-left:var(--spacing-md)}.ml-lg{margin-left:var(--spacing-lg)}.ml-xl{margin-left:var(--spacing-xl)}.mr-0{margin-right:0}.mr-xs{margin-right:var(--spacing-xs)}.mr-sm{margin-right:var(--spacing-sm)}.mr-md{margin-right:var(--spacing-md)}.mr-lg{margin-right:var(--spacing-lg)}.mr-xl{margin-right:var(--spacing-xl)}.mx-0{margin-left:0;margin-right:0}.mx-sm{margin-left:var(--spacing-sm);margin-right:var(--spacing-sm)}.mx-md{margin-left:var(--spacing-md);margin-right:var(--spacing-md)}.mx-lg{margin-left:var(--spacing-lg);margin-right:var(--spacing-lg)}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-sm{margin-top:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.my-md{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md)}.my-lg{margin-top:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.pt-0{padding-top:0}.pt-xs{padding-top:var(--spacing-xs)}.pt-sm{padding-top:var(--spacing-sm)}.pt-md{padding-top:var(--spacing-md)}.pt-lg{padding-top:var(--spacing-lg)}.pt-xl{padding-top:var(--spacing-xl)}.pb-0{padding-bottom:0}.pb-xs{padding-bottom:var(--spacing-xs)}.pb-sm{padding-bottom:var(--spacing-sm)}.pb-md{padding-bottom:var(--spacing-md)}.pb-lg{padding-bottom:var(--spacing-lg)}.pb-xl{padding-bottom:var(--spacing-xl)}.pl-0{padding-left:0}.pl-sm{padding-left:var(--spacing-sm)}.pl-md{padding-left:var(--spacing-md)}.pl-lg{padding-left:var(--spacing-lg)}.pr-0{padding-right:0}.pr-sm{padding-right:var(--spacing-sm)}.pr-md{padding-right:var(--spacing-md)}.pr-lg{padding-right:var(--spacing-lg)}.px-0{padding-left:0;padding-right:0}.px-sm{padding-left:var(--spacing-sm);padding-right:var(--spacing-sm)}.px-md{padding-left:var(--spacing-md);padding-right:var(--spacing-md)}.px-lg{padding-left:var(--spacing-lg);padding-right:var(--spacing-lg)}.py-0{padding-top:0;padding-bottom:0}.py-sm{padding-top:var(--spacing-sm);padding-bottom:var(--spacing-sm)}.py-md{padding-top:var(--spacing-md);padding-bottom:var(--spacing-md)}.py-lg{padding-top:var(--spacing-lg);padding-bottom:var(--spacing-lg)}.d-none{display:none}.d-block{display:block}.d-inline{display:inline}.d-inline-block{display:inline-block}.d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.align-start{align-items:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.align-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-0{gap:0}.gap-xs{gap:var(--spacing-xs)}.gap-sm{gap:var(--spacing-sm)}.gap-md{gap:var(--spacing-md)}.gap-lg{gap:var(--spacing-lg)}.gap-xl{gap:var(--spacing-xl)}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-primary{color:var(--text-primary)}.text-secondary{color:var(--text-secondary)}.text-muted{color:var(--text-muted)}.text-cyan{color:var(--primary-cyan)}.text-magenta{color:var(--secondary-magenta)}.text-success{color:var(--neon-green)}.text-warning{color:var(--neon-orange)}.text-danger{color:var(--neon-red)}.text-purple{color:var(--neon-purple)}.font-heading{font-family:var(--font-heading)}.font-mono{font-family:var(--font-mono)}.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-sm{font-size:0.85rem}.text-base{font-size:1rem}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.leading-normal{line-height:1.6}.leading-loose{line-height:2}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.break-word{word-wrap:break-word;overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.w-full{width:100%}.w-auto{width:auto}.h-full{height:100%}.h-auto{height:auto}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.border{border:1px solid var(--card-border)}.border-0{border:0}.border-t{border-top:1px solid var(--card-border)}.border-b{border-bottom:1px solid var(--card-border)}.border-l{border-left:1px solid var(--card-border)}.border-r{border-right:1px solid var(--card-border)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-md{border-radius:var(--radius-md)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-full{border-radius:var(--radius-full)}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.opacity-0{opacity:0}.opacity-25{opacity:0.25}.opacity-50{opacity:0.5}.opacity-75{opacity:0.75}.opacity-100{opacity:1}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.select-none{user-select:none}.select-text{user-select:text}.select-all{user-select:all}.mobile-header,.mobile-bottom-bar,.sidebar-scrim{display:none}@media (max-width:992px){:root{--sticky-top:var(--mobile-header-height)}.mobile-header{position:fixed;top:0;left:0;right:0;z-index:1001;height:var(--mobile-header-height);display:flex;align-items:center;gap:var(--spacing-sm);padding:0 var(--spacing-sm);background:var(--bg-medium);border-bottom:1px solid var(--card-border)}.mobile-header-logo{flex:1}.mobile-header-logo .logo-text{font-size:1.25rem}.mobile-menu-toggle,.mobile-search-toggle{display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:none;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer}.mobile-menu-toggle{flex-direction:column;gap:4px}.mobile-menu-toggle span{width:24px;height:3px;background:var(--text-primary);border-radius:2px}.mobile-search-toggle{font-size:1.25rem}.mobile-menu-toggle:focus-visible,.mobile-search-toggle:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-cyan)}.sidebar{top:var(--mobile-header-height);height:calc(100dvh - var(--mobile-header-height));width:min(var(--sidebar-width),85vw);background:linear-gradient(180deg,var(--bg-medium) 0%,var(--bg-dark) 100%);backdrop-filter:none;-webkit-backdrop-filter:none;visibility:hidden;transition:transform var(--transition-normal),visibility 0s var(--transition-normal)}.sidebar.open{visibility:visible;transition:transform var(--transition-normal)}.sidebar-header > .logo{display:none}.sidebar-header > .logo + *{margin-top:0}.global-search-list{max-height:50dvh}.sidebar-nav{overflow-y:auto}.sidebar-scrim{display:block;position:fixed;inset:0;z-index:999;background:rgba(0,0,0,0.6);opacity:0;pointer-events:none;transition:opacity var(--transition-normal)}body.mobile-menu-open .sidebar-scrim{opacity:1;pointer-events:auto}body.mobile-menu-open{overflow:hidden}.mobile-bottom-bar{position:fixed;left:0;right:0;bottom:0;z-index:998;display:flex;height:calc(var(--mobile-bar-height) + env(safe-area-inset-bottom, 0px));padding-bottom:env(safe-area-inset-bottom,0px);background:var(--bg-medium);border-top:1px solid var(--card-border)}.mobile-bottom-item{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-width:0;color:var(--text-secondary);text-decoration:none;font-size:0.75rem}.mobile-bottom-icon{font-size:1.25rem}.mobile-bottom-label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mobile-bottom-item.active{color:var(--primary-cyan);box-shadow:inset 0 2px 0 var(--primary-cyan)}.mobile-bottom-item:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--primary-cyan)}.main-content{padding-top:calc(var(--mobile-header-height) + var(--spacing-md));padding-bottom:calc(var(--mobile-bar-height) + env(safe-area-inset-bottom, 0px) + var(--spacing-md))}body:has(.mobile-bottom-bar) .back-to-top-btn{bottom:calc(var(--mobile-bar-height) + 16px)}body:has(.mobile-bottom-bar) .unified-toast-container{bottom:calc(var(--mobile-bar-height) + 12px)}body:has(.mobile-bottom-bar) .launch-indicator{bottom:calc(var(--mobile-bar-height) + var(--spacing-sm))}body:has(.mobile-bottom-bar) .launch-indicator-popover{bottom:calc(var(--mobile-bar-height) + var(--spacing-sm) + 2.5rem)}}@media (max-width:1600px){.content-wrapper{width:80%}}@media (max-width:1200px){.content-wrapper{width:95%;min-width:auto}.game-hero{grid-template-columns:250px 1fr}.game-details-grid{grid-template-columns:1fr}}@media (max-width:992px){.sidebar{transform:translateX(-100%)}.sidebar.open{transform:translateX(0)}.main-content{margin-left:0}.game-hero{grid-template-columns:1fr}.game-boxart-container{max-width:300px;margin:0 auto}.unified-toast-container{left:20px;right:20px;width:auto;max-width:calc(100% - 40px)}.system-controller-grid{grid-template-columns:1fr !important}}@media (max-width:900px){.score-lists-grid{grid-template-columns:1fr}.psn-trophy-lists-row{grid-template-columns:1fr}.psn-info-panels-row{grid-template-columns:1fr}.psn-game-info-card{flex-direction:column}.psn-game-boxart{width:140px}.psn-meta-grid{grid-template-columns:1fr 1fr}.psn-stats-bar{grid-template-columns:repeat(2,1fr)}}@media (max-width:768px){.page-header{flex-direction:column;align-items:flex-start;gap:var(--spacing-md)}.page-title{font-size:1.8rem}.stats-grid{grid-template-columns:1fr}.systems-grid{grid-template-columns:repeat(2,1fr)}.filter-bar{flex-direction:column}.search-box{min-width:100%}.game-meta-badges{flex-direction:column}.game-ratings{flex-direction:column}.game-hero{grid-template-columns:1fr}.hero-boxart{width:200px;margin:0 auto}.tool-tab{padding:0.75rem 0.5rem;font-size:0.8rem}.tool-tab .tab-label{display:none}.tool-tab .tab-icon{font-size:1.25rem}.edit-grid{grid-template-columns:1fr}.psn-games-grid{grid-template-columns:1fr}.psn-search-sort-row{flex-direction:column}.psn-sort-controls{width:100%}.psn-sort-select{flex:1}.psn-profile-bar{flex-direction:column;text-align:center}}@media (max-width:600px){.gdm-header{flex-direction:column;align-items:center;text-align:center}.gdm-boxart-container{width:140px}.gdm-details-grid{grid-template-columns:1fr}.gdm-meta{justify-content:center}.gdm-ratings{justify-content:center}.gdm-actions{flex-direction:column}.gdm-actions .btn{width:100%}.gem-hltb-times{grid-template-columns:1fr}.gem-footer{flex-direction:column}.gem-footer .btn{width:100%}.gem-tabs{flex-wrap:nowrap}.gem-tab{padding:6px 10px;font-size:0.75rem}.gem-grid{grid-template-columns:1fr}.gem-footer-right{flex-direction:column;width:100%}.psn-meta-grid{grid-template-columns:1fr}.psn-trophy-type-breakdown{flex-wrap:wrap;gap:var(--spacing-md)}.platform-filters{justify-content:center}}@media (max-width:480px){.systems-grid{grid-template-columns:1fr}.page-title{font-size:1.5rem;letter-spacing:2px}.stat-value{font-size:2.5rem}} \ No newline at end of file +:root{--primary-cyan:#4cc9f0;--primary-cyan-dark:#3a9fc2;--primary-cyan-light:#7dd8f5;--primary-cyan-glow:rgba(76,201,240,0.5);--secondary-magenta:#f72585;--secondary-magenta-dark:#c41d6a;--secondary-magenta-light:#f95fa3;--secondary-magenta-glow:rgba(247,37,133,0.5);--accent-purple:#7209b7;--accent-blue:#3a0ca3;--accent-violet:#560bad;--bg-darkest:#0a0e17;--bg-dark:#0f1419;--bg-darker:#0d1117;--bg-medium:#151b24;--bg-light:#1a222d;--bg-lighter:#212b38;--card-bg:#141a23;--card-bg-hover:#1a222d;--card-border:#2a3542;--card-border-hover:#3a4552;--text-primary:#e8eaed;--text-secondary:#9aa0a6;--text-muted:#6e7378;--text-accent:var(--primary-cyan);--border-color:#2a3542;--status-success:#00e676;--status-warning:#ffab00;--status-error:#ff5252;--status-info:var(--primary-cyan);--status-online:#00e676;--status-offline:#ff5252;--status-connected:var(--primary-cyan);--shadow-sm:0 2px 4px rgba(0,0,0,0.3);--shadow-md:0 4px 8px rgba(0,0,0,0.4);--shadow-lg:0 8px 16px rgba(0,0,0,0.5);--shadow-xl:0 12px 24px rgba(0,0,0,0.6);--shadow-glow-cyan:0 0 20px rgba(76,201,240,0.3);--shadow-glow-magenta:0 0 20px rgba(247,37,133,0.3);--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-full:9999px;--transition-fast:0.15s ease;--transition-normal:0.25s ease;--transition-slow:0.4s ease;--font-primary:'Rajdhani','Orbitron','Share Tech Mono','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',sans-serif;--font-heading:'Orbitron','Rajdhani','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',sans-serif;--font-mono:'Share Tech Mono','JetBrains Mono','Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',monospace;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:16px;--spacing-lg:24px;--spacing-xl:32px;--spacing-2xl:48px;--sidebar-width:260px;--sidebar-collapsed-width:70px;--mobile-header-height:56px;--mobile-bar-height:60px;--sticky-top:0px;--neon-cyan:#4cc9f0;--neon-green:#22c55e;--neon-orange:#f59e0b;--neon-red:#ef4444;--neon-purple:#a855f7;--neon-gray:#64748b;--neon-blue:#3b82f6;--text-on-color:#fff;--font-display:var(--font-heading);--accent-cyan:var(--primary-cyan);--cyan-bg:rgba(76,201,240,0.15);--cyan-bg-light:rgba(76,201,240,0.1);--cyan-bg-strong:rgba(76,201,240,0.2);--green-bg:rgba(34,197,94,0.15);--red-bg:rgba(239,68,68,0.15);--orange-bg:rgba(245,158,11,0.15);--purple-bg:rgba(168,85,247,0.15);--magenta-bg:rgba(247,37,133,0.15);--rpcs3-playable:#1ebc61;--rpcs3-ingame:#f9b32f;--rpcs3-intro:#e08a1e;--rpcs3-loadable:#e74c3c;--rpcs3-nothing:#8a9a9b;--trophy-platinum:#7bb8d4;--trophy-gold:#cd9a2a;--trophy-silver:#a8a8a8;--trophy-bronze:#cd7f32;--trophy-display-platinum:#a8c8dc;--trophy-display-gold:#ffd700;--trophy-display-silver:#c0c0c0;--trophy-display-bronze:#cd7f32;--trophy-display-text:#111;--rating-esrb:#64b5f6;--rating-esrb-border:rgba(25,118,210,0.3);--rating-esrb-bg:rgba(25,118,210,0.1);--rating-pegi:#ce93d8;--rating-pegi-border:rgba(123,31,162,0.3);--rating-pegi-bg:rgba(123,31,162,0.1);--completion-gold:#fbbf24}*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;scroll-behavior:smooth}body{font-family:var(--font-primary);background-color:var(--bg-darkest);color:var(--text-primary);line-height:1.6;min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul,ol{list-style:none}img,picture,video,canvas,svg{display:block;max-width:100%}button{font:inherit;cursor:pointer}input,textarea,select{font:inherit}input,button,textarea,select{font-family:inherit}a{color:inherit;text-decoration:none}@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Rajdhani:wght@300;400;500;600;700&family=Share+Tech+Mono&display=swap');h1,.h1{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);letter-spacing:1px;text-transform:uppercase}h2,.h2{font-family:var(--font-heading);font-size:1.5rem;font-weight:600;color:var(--text-primary);letter-spacing:0.5px}h3,.h3{font-family:var(--font-heading);font-size:1.25rem;font-weight:600;color:var(--text-primary)}h4,.h4{font-family:var(--font-primary);font-size:1.1rem;font-weight:600;color:var(--text-primary)}h5,.h5{font-family:var(--font-primary);font-size:1rem;font-weight:600;color:var(--text-secondary)}h6,.h6{font-family:var(--font-primary);font-size:0.9rem;font-weight:500;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px}.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.page-header h1{margin-bottom:var(--spacing-sm)}.page-header p{color:var(--text-secondary);font-size:1rem}.page-title{font-family:var(--font-heading);font-size:2.5rem;font-weight:700;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:4px;text-shadow:0 0 10px var(--primary-cyan-glow),0 0 20px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow);animation:glow-pulse 3s ease-in-out infinite}.page-subtitle{color:var(--text-secondary);font-size:1rem;margin-top:var(--spacing-xs)}.page-actions{display:flex;gap:var(--spacing-md)}.section-title{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:2px;display:flex;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.section-title::before{content:'';width:4px;height:20px;background:var(--primary-cyan);border-radius:2px}.breadcrumb{display:flex;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);font-size:0.9rem}.breadcrumb a{color:var(--text-secondary);text-decoration:none;transition:color var(--transition-fast)}.breadcrumb a:hover{color:var(--primary-cyan)}.breadcrumb-separator{color:var(--text-muted)}.breadcrumb-current{color:var(--text-primary)}code,pre,.mono{font-family:var(--font-mono)}code{padding:0.2em 0.4em;background:rgba(76,201,240,0.1);border-radius:var(--radius-sm);font-size:0.9em;color:var(--primary-cyan)}pre{padding:var(--spacing-md);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow-x:auto}pre code{padding:0;background:none}[data-theme="matrix"]{--primary-cyan:#00ff41;--primary-cyan-dark:#00cc33;--primary-cyan-light:#66ff88;--primary-cyan-glow:rgba(0,255,65,0.5);--secondary-magenta:#008f11;--secondary-magenta-dark:#006b0d;--secondary-magenta-light:#33b833;--secondary-magenta-glow:rgba(0,143,17,0.5);--accent-purple:#00aa22;--accent-blue:#004d00;--accent-violet:#009a1a;--bg-darkest:#000000;--bg-dark:#050505;--bg-darker:#0a0a0a;--bg-medium:#111111;--bg-light:#161616;--bg-lighter:#1a1a1a;--card-bg:#0a0a0a;--card-bg-hover:#111111;--card-border:#1a3a1a;--card-border-hover:#2a5a2a;--text-primary:#c0ffc0;--text-secondary:#80c080;--text-muted:#508050;--text-accent:var(--primary-cyan);--border-color:#1a3a1a;--status-success:#00ff41;--status-warning:#a0e000;--status-error:#ff3333;--status-info:var(--primary-cyan);--status-online:#00ff41;--status-offline:#ff3333;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(0,255,65,0.3);--shadow-glow-magenta:0 0 20px rgba(0,143,17,0.3);--neon-cyan:#00ff41;--neon-green:#00ff41;--neon-orange:#a0e000;--neon-red:#ff3333;--neon-purple:#33cc66;--neon-gray:#4a6a4a;--trophy-platinum:#66ffaa;--trophy-gold:#a0e060;--trophy-silver:#88bb88;--trophy-bronze:#77aa55;--toast-bg-from:rgba(0,10,0,0.95);--toast-bg-to:rgba(0,20,5,0.95);--toast-queued-bg-from:rgba(0,12,0,0.90);--toast-queued-bg-to:rgba(0,22,5,0.90)}[data-theme="matrix"] body::before{background:linear-gradient( to top right,rgba(0,255,65,0.06) 0%,rgba(0,143,17,0.04) 25%,rgba(0,80,0,0.03) 50%,rgba(0,0,0,0.02) 75%,transparent 100% )}[data-theme="matrix"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,255,65,0.02) 2px,rgba(0,255,65,0.02) 4px )}[data-theme="amber"]{--primary-cyan:#ffb000;--primary-cyan-dark:#cc8c00;--primary-cyan-light:#ffc944;--primary-cyan-glow:rgba(255,176,0,0.5);--secondary-magenta:#ff6600;--secondary-magenta-dark:#cc5200;--secondary-magenta-light:#ff8833;--secondary-magenta-glow:rgba(255,102,0,0.5);--accent-purple:#cc7700;--accent-blue:#995500;--accent-violet:#bb6600;--bg-darkest:#0a0800;--bg-dark:#0d0a02;--bg-darker:#0f0c04;--bg-medium:#151008;--bg-light:#1a150c;--bg-lighter:#211c12;--card-bg:#120e04;--card-bg-hover:#181208;--card-border:#3a2a10;--card-border-hover:#4a3a1a;--text-primary:#ffe0a0;--text-secondary:#c09050;--text-muted:#806030;--text-accent:var(--primary-cyan);--border-color:#3a2a10;--status-success:#ffcc00;--status-warning:#ff9900;--status-error:#ff4422;--status-info:var(--primary-cyan);--status-online:#ffcc00;--status-offline:#ff4422;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(255,176,0,0.3);--shadow-glow-magenta:0 0 20px rgba(255,102,0,0.3);--neon-cyan:#ffb000;--neon-green:#ddaa00;--neon-orange:#ff7700;--neon-red:#ff4422;--neon-purple:#ff8844;--neon-gray:#7a6a4a;--trophy-platinum:#ffe8aa;--trophy-gold:#ffcc44;--trophy-silver:#ccaa77;--trophy-bronze:#aa7733;--toast-bg-from:rgba(18,14,4,0.95);--toast-bg-to:rgba(28,20,8,0.95);--toast-queued-bg-from:rgba(20,16,4,0.90);--toast-queued-bg-to:rgba(30,22,8,0.90)}[data-theme="amber"] body::before{background:radial-gradient(ellipse 30% 20% at 50% 100%,rgba(255,230,100,0.2) 0%,rgba(255,200,50,0.12) 40%,transparent 100%),radial-gradient(ellipse 60% 40% at 50% 100%,rgba(255,160,0,0.1) 0%,rgba(255,100,0,0.05) 50%,transparent 100%),linear-gradient(to top,rgba(255,120,0,0.08) 0%,rgba(255,80,0,0.04) 20%,rgba(120,50,0,0.02) 45%,transparent 70%)}[data-theme="amber"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(255,176,0,0.03) 2px,rgba(255,176,0,0.03) 4px )}[data-theme="ocean"]{--primary-cyan:#4dabf7;--primary-cyan-dark:#3d8bd4;--primary-cyan-light:#74c0fa;--primary-cyan-glow:rgba(77,171,247,0.5);--secondary-magenta:#20c997;--secondary-magenta-dark:#1aaa7f;--secondary-magenta-light:#4dd8ad;--secondary-magenta-glow:rgba(32,201,151,0.5);--accent-purple:#5c7cfa;--accent-blue:#3b5bdb;--accent-violet:#4c6ef5;--bg-darkest:#040810;--bg-dark:#050a14;--bg-darker:#060c18;--bg-medium:#0a1220;--bg-light:#0e1828;--bg-lighter:#142030;--card-bg:#081018;--card-bg-hover:#0c1620;--card-border:#1a2a40;--card-border-hover:#2a3a55;--text-primary:#dce4f0;--text-secondary:#8899b0;--text-muted:#5a6a80;--text-accent:var(--primary-cyan);--border-color:#1a2a40;--status-success:#40c057;--status-warning:#fab005;--status-error:#fa5252;--status-info:var(--primary-cyan);--status-online:#40c057;--status-offline:#fa5252;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(77,171,247,0.3);--shadow-glow-magenta:0 0 20px rgba(32,201,151,0.3);--neon-cyan:#4dabf7;--neon-green:#40c057;--neon-orange:#fab005;--neon-red:#fa5252;--neon-purple:#7950f2;--neon-gray:#5a6a80;--trophy-platinum:#a5d8ff;--trophy-gold:#ffe066;--trophy-silver:#adb5bd;--trophy-bronze:#e8a44a;--toast-bg-from:rgba(6,12,22,0.95);--toast-bg-to:rgba(10,18,32,0.95);--toast-queued-bg-from:rgba(8,14,24,0.90);--toast-queued-bg-to:rgba(12,20,34,0.90)}[data-theme="ocean"] body::before{background:radial-gradient(circle at 70% 10%,rgba(220,235,255,0.4) 0%,rgba(200,220,250,0.2) 1.5%,rgba(150,190,240,0.08) 4%,transparent 10%),radial-gradient(circle at 70% 10%,rgba(77,171,247,0.08) 0%,transparent 18%),radial-gradient(ellipse 80% 50% at 50% 90%,rgba(32,201,151,0.04) 0%,rgba(77,171,247,0.05) 40%,transparent 100%),linear-gradient(to bottom,rgba(10,20,40,0.02) 0%,rgba(77,171,247,0.03) 55%,rgba(32,201,151,0.02) 100%)}[data-theme="ocean"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(77,171,247,0.02) 2px,rgba(77,171,247,0.02) 4px )}[data-theme="christian"]{--primary-cyan:#d4a843;--primary-cyan-dark:#b08a30;--primary-cyan-light:#e8c86a;--primary-cyan-glow:rgba(212,168,67,0.5);--secondary-magenta:#7b4db0;--secondary-magenta-dark:#5f3a8a;--secondary-magenta-light:#9b6dd0;--secondary-magenta-glow:rgba(123,77,176,0.5);--accent-purple:#6a3d9a;--accent-blue:#4a2d7a;--accent-violet:#7b4db0;--bg-darkest:#0c0810;--bg-dark:#0e0a14;--bg-darker:#110d18;--bg-medium:#16101f;--bg-light:#1c1528;--bg-lighter:#231c30;--card-bg:#120e1a;--card-bg-hover:#181222;--card-border:#2a1f3a;--card-border-hover:#3a2d4e;--text-primary:#f0e8d8;--text-secondary:#b0a090;--text-muted:#706050;--text-accent:var(--primary-cyan);--border-color:#2a1f3a;--status-success:#d4a843;--status-warning:#e8a030;--status-error:#c04040;--status-info:var(--primary-cyan);--status-online:#d4a843;--status-offline:#c04040;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(212,168,67,0.3);--shadow-glow-magenta:0 0 20px rgba(123,77,176,0.3);--neon-cyan:#d4a843;--neon-green:#c0a040;--neon-orange:#e8a030;--neon-red:#c04040;--neon-purple:#9b6dd0;--neon-gray:#6a5a50;--trophy-platinum:#f0e0c0;--trophy-gold:#d4a843;--trophy-silver:#b0a090;--trophy-bronze:#8a6a40;--toast-bg-from:rgba(14,10,20,0.95);--toast-bg-to:rgba(22,16,30,0.95);--toast-queued-bg-from:rgba(16,12,22,0.90);--toast-queued-bg-to:rgba(24,18,32,0.90)}[data-theme="christian"] body::before{background:radial-gradient(ellipse 40% 60% at 50% 0%,rgba(212,168,67,0.15) 0%,rgba(212,168,67,0.06) 30%,transparent 70%),radial-gradient(ellipse 70% 50% at 50% 10%,rgba(180,140,50,0.06) 0%,rgba(123,77,176,0.03) 50%,transparent 100%),linear-gradient(to top,rgba(100,60,20,0.04) 0%,rgba(80,40,120,0.02) 40%,transparent 70%)}[data-theme="christian"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(212,168,67,0.015) 2px,rgba(212,168,67,0.015) 4px )}[data-theme="bladerunner"]{--primary-cyan:#1a9fff;--primary-cyan-dark:#1480d4;--primary-cyan-light:#55b8ff;--primary-cyan-glow:rgba(26,159,255,0.5);--secondary-magenta:#ff2d7c;--secondary-magenta-dark:#d42060;--secondary-magenta-light:#ff5a9a;--secondary-magenta-glow:rgba(255,45,124,0.5);--accent-purple:#b040e0;--accent-blue:#1060c0;--accent-violet:#8830c0;--bg-darkest:#06080e;--bg-dark:#080a12;--bg-darker:#0a0d16;--bg-medium:#0e121c;--bg-light:#131824;--bg-lighter:#1a1f2e;--card-bg:#0a0e16;--card-bg-hover:#10141e;--card-border:#1a2236;--card-border-hover:#2a3248;--text-primary:#d8dce8;--text-secondary:#8892a8;--text-muted:#78809a;--text-accent:var(--primary-cyan);--border-color:#1a2236;--status-success:#22cc88;--status-warning:#ff8c30;--status-error:#ff2d5e;--status-info:var(--primary-cyan);--status-online:#22cc88;--status-offline:#ff2d5e;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(26,159,255,0.3);--shadow-glow-magenta:0 0 20px rgba(255,45,124,0.3);--neon-cyan:#1a9fff;--neon-green:#22cc88;--neon-orange:#ff8c30;--neon-red:#ff2d5e;--neon-purple:#b040e0;--neon-gray:#4a5268;--trophy-platinum:#c0d8ff;--trophy-gold:#ffd066;--trophy-silver:#a0aab8;--trophy-bronze:#d08a40;--toast-bg-from:rgba(8,10,18,0.95);--toast-bg-to:rgba(14,18,28,0.95);--toast-queued-bg-from:rgba(10,12,20,0.90);--toast-queued-bg-to:rgba(16,20,30,0.90)}[data-theme="bladerunner"] body::before{background:radial-gradient(ellipse 40% 30% at 20% 95%,rgba(255,45,124,0.08) 0%,rgba(255,45,124,0.03) 50%,transparent 100%),radial-gradient(ellipse 40% 30% at 80% 90%,rgba(26,159,255,0.08) 0%,rgba(26,159,255,0.03) 50%,transparent 100%),radial-gradient(ellipse 60% 40% at 50% 70%,rgba(130,40,180,0.04) 0%,transparent 80%),linear-gradient(to bottom,rgba(6,8,14,0.02) 0%,rgba(20,15,30,0.04) 60%,rgba(255,45,124,0.02) 100%)}[data-theme="bladerunner"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(26,159,255,0.02) 2px,rgba(26,159,255,0.02) 4px )}[data-theme="elite"]{--primary-cyan:#00ff00;--primary-cyan-dark:#00cc00;--primary-cyan-light:#66ff66;--primary-cyan-glow:rgba(0,255,0,0.5);--secondary-magenta:#cccccc;--secondary-magenta-dark:#999999;--secondary-magenta-light:#e0e0e0;--secondary-magenta-glow:rgba(204,204,204,0.5);--accent-purple:#00cc66;--accent-blue:#008844;--accent-violet:#00aa55;--bg-darkest:#000000;--bg-dark:#020202;--bg-darker:#040404;--bg-medium:#080808;--bg-light:#0c0c0c;--bg-lighter:#111111;--card-bg:#050505;--card-bg-hover:#0a0a0a;--card-border:#1a331a;--card-border-hover:#2a552a;--text-primary:#d0ffd0;--text-secondary:#80cc80;--text-muted:#508050;--text-accent:var(--primary-cyan);--border-color:#1a331a;--status-success:#00ff00;--status-warning:#ffff00;--status-error:#ff0000;--status-info:var(--primary-cyan);--status-online:#00ff00;--status-offline:#ff0000;--status-connected:var(--primary-cyan);--shadow-glow-cyan:0 0 20px rgba(0,255,0,0.3);--shadow-glow-magenta:0 0 20px rgba(204,204,204,0.3);--neon-cyan:#00ff00;--neon-green:#00ff00;--neon-orange:#ffff00;--neon-red:#ff0000;--neon-purple:#00cc66;--neon-gray:#4a6a4a;--trophy-platinum:#ccffcc;--trophy-gold:#ffff66;--trophy-silver:#aaccaa;--trophy-bronze:#88aa44;--toast-bg-from:rgba(0,5,0,0.95);--toast-bg-to:rgba(0,12,0,0.95);--toast-queued-bg-from:rgba(0,8,0,0.90);--toast-queued-bg-to:rgba(0,15,0,0.90)}[data-theme="elite"] body::before{background:radial-gradient(circle at 30% 20%,rgba(255,255,255,0.03) 0%,transparent 8%),radial-gradient(circle at 70% 15%,rgba(255,255,255,0.02) 0%,transparent 5%),radial-gradient(ellipse 40% 25% at 50% 95%,rgba(0,255,0,0.04) 0%,transparent 80%),linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,20,0,0.02) 100%)}[data-theme="elite"] body::after{background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,255,0,0.025) 2px,rgba(0,255,0,0.025) 4px )}.app-container{display:flex;min-height:100vh;position:relative;z-index:2}.main-content{flex:1;margin-left:var(--sidebar-width);padding:var(--spacing-xl);transition:margin-left var(--transition-normal);display:flex;justify-content:center;position:relative}.main-content.sidebar-collapsed{margin-left:var(--sidebar-collapsed-width)}.content-wrapper{width:65%;max-width:1400px;min-width:600px;position:relative;z-index:1}.content-wrapper.adaptive{width:100%;max-width:none;min-width:0}.main-content.adaptive{min-width:0}.page-box{max-width:var(--page-max,none);margin-inline:auto}.page-box.grid-4{--page-max:calc(5 * var(--card-min) + 4 * var(--card-gap) - 1px)}.page-box.data-page{--page-max:1400px}.page-box.form-page{--page-max:1100px}.page-box.detail-page{--page-max:1600px}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg)}.section-link{color:var(--primary-cyan);text-decoration:none;font-size:0.85rem;font-weight:500;text-transform:uppercase;letter-spacing:1px;transition:all var(--transition-fast)}.section-link:hover{text-shadow:0 0 10px var(--primary-cyan-glow)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--spacing-lg)}.systems-section{margin-bottom:var(--spacing-xl)}.sidebar{position:fixed;left:0;top:0;width:var(--sidebar-width);height:100vh;background:linear-gradient(180deg,color-mix(in srgb,var(--bg-medium) 88%,transparent) 0%,color-mix(in srgb,var(--bg-dark) 88%,transparent) 100% );backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-right:1px solid var(--card-border);display:flex;flex-direction:column;z-index:1000;transition:width var(--transition-normal)}.sidebar-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.logo{display:flex;align-items:center;gap:var(--spacing-md);text-decoration:none}.logo-icon{width:40px;height:40px;background:linear-gradient(135deg,var(--primary-cyan) 0%,var(--secondary-magenta) 100%);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:var(--shadow-glow-cyan)}.logo-text{font-family:var(--font-heading);font-size:1.5rem;font-weight:700;color:var(--text-primary);letter-spacing:2px}.logo-text span{color:var(--primary-cyan);text-shadow:0 0 10px var(--primary-cyan-glow)}.sidebar-nav{flex:1;padding:var(--spacing-md);overflow-y:auto}.nav-section{margin-bottom:var(--spacing-lg)}.nav-section-title{font-size:0.7rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;padding:var(--spacing-sm) var(--spacing-md);margin-bottom:var(--spacing-xs)}.nav-item{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);border-radius:var(--radius-md);color:var(--text-secondary);text-decoration:none;transition:all var(--transition-fast);margin-bottom:var(--spacing-xs);position:relative;overflow:hidden}.nav-item::before{content:'';position:absolute;left:0;top:0;height:100%;width:3px;background:var(--primary-cyan);transform:scaleY(0);transition:transform var(--transition-fast)}.nav-item:hover{background:var(--card-bg-hover);color:var(--text-primary)}.nav-item:hover::before{transform:scaleY(1)}.nav-item.active{background:linear-gradient(90deg,rgba(76,201,240,0.15) 0%,transparent 100%);color:var(--primary-cyan);border-left:3px solid var(--primary-cyan)}.nav-item.active::before{transform:scaleY(1)}.nav-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:1.1rem}.nav-text{font-weight:500;font-size:0.95rem}.sidebar-footer{padding:var(--spacing-md);border-top:1px solid var(--card-border)}.version-info{font-size:0.75rem;color:var(--primary-cyan);text-align:center;font-family:var(--font-mono);opacity:0.8;text-shadow:0 0 8px rgba(76,201,240,0.4);display:flex;align-items:center;justify-content:center;gap:0.5rem;transition:all var(--transition-fast);padding:0.5rem;border-radius:var(--radius-md);background:transparent;border:none;width:100%;cursor:pointer}.version-info:hover{background:rgba(76,201,240,0.1);color:var(--primary-cyan)}.version-icon{font-size:1rem}.upscale-status{display:flex;align-items:center;gap:0.5rem;margin-bottom:var(--spacing-sm);padding:0.5rem;font-size:0.75rem;color:var(--text-secondary);border:1px solid var(--card-border);border-radius:var(--radius-md)}.upscale-status[hidden]{display:none}.upscale-status-icon{font-size:1rem;color:var(--primary-cyan)}.user-indicator{display:flex;align-items:center;justify-content:space-between;gap:0.5rem;padding:0.75rem;background:rgba(76,201,240,0.05);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-top:var(--spacing-md)}.user-indicator-info{display:flex;align-items:center;gap:0.75rem;min-width:0;flex:1}.user-indicator-avatar{font-size:1.25rem;flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center}.user-avatar-img{width:100%;height:100%;border-radius:50%;object-fit:cover}.user-indicator-details{display:flex;flex-direction:column;min-width:0}.user-indicator-name{font-size:0.85rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.user-indicator-role{font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;padding:0.1rem 0.4rem;border-radius:var(--radius-full);width:fit-content}.user-indicator-role.admin{background:rgba(168,85,247,0.2);color:var(--neon-purple)}.user-indicator-role.editor{background:rgba(34,197,94,0.2);color:var(--neon-green)}.user-indicator-role.viewer{background:rgba(245,158,11,0.2);color:var(--neon-orange)}.user-indicator-meta{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:2px}.user-indicator-rank{font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.4px;padding:2px 8px;border-radius:var(--radius-full);text-decoration:none;white-space:nowrap;border:1px solid transparent;transition:all var(--transition-fast);line-height:1;display:inline-flex;align-items:center;gap:3px}.user-indicator-rank:hover{transform:translateY(-1px);filter:brightness(1.15)}.user-indicator-rank.rank-tier-0{background:rgba(110,115,120,0.18);color:var(--text-muted);border-color:rgba(110,115,120,0.35)}.user-indicator-rank.rank-tier-1{background:rgba(154,160,166,0.18);color:var(--text-secondary);border-color:rgba(154,160,166,0.4)}.user-indicator-rank.rank-tier-2{background:rgba(76,201,240,0.12);color:var(--primary-cyan);border-color:rgba(76,201,240,0.3)}.user-indicator-rank.rank-tier-3{background:rgba(76,201,240,0.18);color:var(--primary-cyan);border-color:rgba(76,201,240,0.45)}.user-indicator-rank.rank-tier-4{background:rgba(34,197,94,0.15);color:var(--neon-green);border-color:rgba(34,197,94,0.4)}.user-indicator-rank.rank-tier-5{background:rgba(245,158,11,0.15);color:var(--neon-orange);border-color:rgba(245,158,11,0.45)}.user-indicator-rank.rank-tier-6{background:rgba(168,85,247,0.15);color:var(--neon-purple);border-color:rgba(168,85,247,0.4)}.user-indicator-rank.rank-tier-7{background:rgba(168,85,247,0.22);color:var(--neon-purple);border-color:rgba(168,85,247,0.55)}.user-indicator-rank.rank-tier-8{background:rgba(247,37,133,0.18);color:var(--primary-magenta);border-color:rgba(247,37,133,0.5)}.user-indicator-rank.rank-tier-9{background:linear-gradient(135deg,rgba(247,37,133,0.3),rgba(168,85,247,0.3));color:var(--text-primary);border-color:var(--primary-magenta);box-shadow:0 0 8px rgba(247,37,133,0.35)}.user-logout-btn{font-size:1.1rem;padding:0.25rem;opacity:0.6;transition:all var(--transition-fast);text-decoration:none;flex-shrink:0}.user-logout-btn:hover{opacity:1;transform:scale(1.1)}.skip-link{position:absolute;top:0;left:0;padding:0.75rem 1.25rem;background:var(--primary-cyan);color:var(--bg-darkest);font-family:var(--font-heading);font-weight:700;text-decoration:none;border-radius:0 0 6px 0;z-index:10000;transform:translateY(-150%);transition:transform 0.15s ease-out}.skip-link:focus{transform:translateY(0);outline:2px solid var(--primary-magenta);outline-offset:2px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:var(--spacing-md) var(--spacing-lg);font-family:var(--font-primary);font-size:0.9rem;font-weight:600;text-transform:uppercase;letter-spacing:1px;border:2px solid;border-radius:var(--radius-md);cursor:pointer;transition:all 0.3s ease;text-decoration:none;position:relative;overflow:hidden}.btn-primary{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan);box-shadow:0 0 15px rgba(76,201,240,0.5),0 0 30px rgba(76,201,240,0.3),inset 0 0 20px rgba(76,201,240,0.1);text-shadow:0 0 10px rgba(76,201,240,0.8)}.btn-primary:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 20px rgba(76,201,240,0.7),0 0 40px rgba(76,201,240,0.4),inset 0 0 25px rgba(76,201,240,0.15);transform:translateY(-2px)}.btn-secondary{background:rgba(148,163,184,0.1);border-color:var(--neon-gray);color:#94a3b8;box-shadow:0 0 10px rgba(100,116,139,0.3),0 0 20px rgba(100,116,139,0.2)}.btn-secondary:hover{background:rgba(148,163,184,0.2);border-color:#94a3b8;color:#cbd5e1;box-shadow:0 0 15px rgba(148,163,184,0.5),0 0 30px rgba(148,163,184,0.3)}.btn-success{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green);box-shadow:0 0 15px rgba(34,197,94,0.5),0 0 30px rgba(34,197,94,0.3),inset 0 0 20px rgba(34,197,94,0.1);text-shadow:0 0 10px rgba(34,197,94,0.8)}.btn-success:hover{background:rgba(34,197,94,0.25);box-shadow:0 0 20px rgba(34,197,94,0.7),0 0 40px rgba(34,197,94,0.4),inset 0 0 25px rgba(34,197,94,0.15);transform:translateY(-2px)}.btn-warning{background:rgba(245,158,11,0.15);border-color:var(--neon-orange);color:var(--neon-orange);box-shadow:0 0 15px rgba(245,158,11,0.5),0 0 30px rgba(245,158,11,0.3),inset 0 0 20px rgba(245,158,11,0.1);text-shadow:0 0 10px rgba(245,158,11,0.8)}.btn-warning:hover{background:rgba(245,158,11,0.25);box-shadow:0 0 20px rgba(245,158,11,0.7),0 0 40px rgba(245,158,11,0.4),inset 0 0 25px rgba(245,158,11,0.15);transform:translateY(-2px)}.btn-danger{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red);box-shadow:0 0 15px rgba(239,68,68,0.5),0 0 30px rgba(239,68,68,0.3),inset 0 0 20px rgba(239,68,68,0.1);text-shadow:0 0 10px rgba(239,68,68,0.8)}.btn-danger:hover{background:rgba(239,68,68,0.25);box-shadow:0 0 20px rgba(239,68,68,0.7),0 0 40px rgba(239,68,68,0.4),inset 0 0 25px rgba(239,68,68,0.15);transform:translateY(-2px)}.btn-sm{padding:var(--spacing-sm) var(--spacing-md);font-size:0.8rem}.btn-xs{padding:3px 8px;font-size:0.7rem;line-height:1.2}.btn-lg{padding:var(--spacing-lg) var(--spacing-xl);font-size:1rem}.btn-icon{font-size:1.1em}.btn:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(76,201,240,0.5),0 0 15px rgba(76,201,240,0.3)}.btn:disabled,.btn[disabled]{opacity:0.4;cursor:not-allowed;pointer-events:none;box-shadow:none}input[type="file"]::file-selector-button{padding:8px 16px;margin-right:12px;background:rgba(76,201,240,0.15);border:2px solid var(--neon-cyan);border-radius:var(--radius-md);color:var(--neon-cyan);cursor:pointer;font-weight:600;font-family:var(--font-primary);text-transform:uppercase;letter-spacing:0.5px;font-size:0.85rem;box-shadow:0 0 15px rgba(76,201,240,0.5),0 0 30px rgba(76,201,240,0.3),inset 0 0 20px rgba(76,201,240,0.1);text-shadow:0 0 10px rgba(76,201,240,0.8);transition:all 0.3s ease}input[type="file"]::file-selector-button:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 20px rgba(76,201,240,0.7),0 0 40px rgba(76,201,240,0.4),inset 0 0 25px rgba(76,201,240,0.15)}.pagination{display:flex;justify-content:center;align-items:center;gap:var(--spacing-sm);margin-top:var(--spacing-xl)}.pagination-btn{padding:var(--spacing-sm) var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);cursor:pointer;transition:all var(--transition-fast)}.pagination-btn:hover{border-color:var(--primary-cyan);color:var(--primary-cyan)}.pagination-btn.active{background:var(--primary-cyan);border-color:var(--primary-cyan);color:var(--bg-darkest)}.pagination-btn:disabled{opacity:0.5;cursor:not-allowed}.back-to-top-btn{position:fixed;bottom:30px;right:30px;width:50px;height:50px;border-radius:50%;background:rgba(20,26,35,0.95);border:2px solid var(--neon-cyan);color:var(--neon-cyan);font-size:1.5rem;cursor:pointer;opacity:0;visibility:hidden;transform:translateY(20px);transition:opacity 0.3s ease,visibility 0.3s ease,transform 0.3s ease,background 0.3s ease,box-shadow 0.3s ease;z-index:9998;display:flex;align-items:center;justify-content:center;box-shadow:0 0 15px rgba(76,201,240,0.3),inset 0 0 10px rgba(76,201,240,0.1)}.back-to-top-btn.visible{opacity:1;visibility:visible;transform:translateY(0)}.back-to-top-btn:hover{background:rgba(76,201,240,0.2);box-shadow:0 0 25px rgba(76,201,240,0.5),inset 0 0 15px rgba(76,201,240,0.2);transform:translateY(-3px)}.back-to-top-btn:active{transform:translateY(0)}.back-to-top-icon{font-weight:bold;text-shadow:0 0 10px rgba(76,201,240,0.8)}.btn-group{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-md)}.btn-danger-outline{background:rgba(239,68,68,0.1);border:2px solid var(--neon-red);color:var(--neon-red);box-shadow:0 0 10px rgba(239,68,68,0.3),0 0 20px rgba(239,68,68,0.2);text-shadow:0 0 8px rgba(239,68,68,0.6)}.btn-danger-outline:hover{background:rgba(239,68,68,0.2);box-shadow:0 0 15px rgba(239,68,68,0.5),0 0 30px rgba(239,68,68,0.3)}.form-input,input[type="text"],input[type="number"],input[type="email"],input[type="password"],input[type="search"],input[type="date"],textarea{width:100%;padding:0.75rem 1rem;background:var(--bg-darker,#0d1117);border:1px solid var(--border-color,#2a3542);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:border-color 0.2s ease,box-shadow 0.2s ease}.form-input:focus,input[type="text"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="search"]:focus,input[type="date"]:focus,textarea:focus{outline:none;border-color:var(--neon-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15),0 0 15px rgba(76,201,240,0.2)}.form-input::placeholder,input::placeholder,textarea::placeholder{color:var(--text-muted)}select,.form-select{width:100%;padding:0.75rem 2.5rem 0.75rem 1rem;background:var(--bg-darker,#0d1117);border:1px solid var(--border-color,#2a3542);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 1rem center;transition:border-color 0.2s ease,box-shadow 0.2s ease}select:focus,.form-select:focus{outline:none;border-color:var(--neon-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15),0 0 15px rgba(76,201,240,0.2)}select:hover,.form-select:hover{border-color:var(--neon-cyan)}select option{background:var(--bg-medium);color:var(--text-primary);padding:0.5rem}.styled-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;padding:0.75rem 2.5rem 0.75rem 1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 1rem center;transition:border-color 0.2s ease,box-shadow 0.2s ease}.styled-select:hover{border-color:var(--primary-cyan)}.styled-select:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.toggle-switch{position:relative;display:inline-block;width:52px;height:28px;flex-shrink:0}.toggle-switch input{opacity:0;width:0;height:0}.toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:var(--neon-red);border-radius:28px;transition:all 0.3s ease;box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(239,68,68,0.3)}.toggle-slider:before{position:absolute;content:"";height:22px;width:22px;left:3px;bottom:3px;background:white;border-radius:50%;transition:all 0.3s ease;box-shadow:0 2px 4px rgba(0,0,0,0.3)}.toggle-switch input:checked + .toggle-slider{background:var(--neon-green);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 15px rgba(34,197,94,0.5)}.toggle-switch input:checked + .toggle-slider:before{transform:translateX(24px)}.toggle-switch input:focus + .toggle-slider{box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 0 2px rgba(76,201,240,0.3)}.toggle-item{display:flex;align-items:center;gap:0.75rem}.toggle-label{color:var(--text-secondary);font-size:0.9rem;cursor:pointer;user-select:none;transition:color 0.2s ease}.toggle-label.active{color:var(--text-primary)}.toggle-field{display:flex;align-items:center;gap:var(--spacing-md)}.toggle-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-sm)}.toggle-switch-wrapper .toggle-label-text{font-size:0.85rem;color:var(--text-secondary)}.form-group{margin-bottom:var(--spacing-md)}.form-label{display:block;font-size:0.85rem;font-weight:600;color:var(--text-secondary);margin-bottom:var(--spacing-xs)}.filter-select{padding:var(--spacing-md) var(--spacing-lg);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;transition:all var(--transition-fast);min-width:150px}.filter-select:focus{outline:none;border-color:var(--primary-cyan)}.locked-path-wrapper{display:flex;align-items:center;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);overflow:hidden;flex:1}.locked-path-wrapper:focus-within{border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.locked-path-prefix{padding:0.75rem 0 0.75rem 1rem;color:var(--text-muted);font-family:var(--font-mono);font-size:0.9rem;background:rgba(76,201,240,0.05);border-right:1px solid var(--border-color);white-space:nowrap;user-select:none}.locked-path-wrapper input{flex:1;padding:0.75rem 1rem;background:transparent;border:none;color:var(--text-primary);font-family:var(--font-mono);font-size:0.9rem;outline:none}.locked-path-wrapper input::placeholder{color:var(--text-muted)}.edit-section{margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.edit-section:last-child{border-bottom:none;margin-bottom:0}.edit-section-title{color:var(--primary-cyan);font-family:var(--font-heading);font-size:1rem;font-weight:600;margin-bottom:var(--spacing-lg);padding-bottom:var(--spacing-sm);border-bottom:1px solid rgba(76,201,240,0.3)}.edit-field{margin-bottom:var(--spacing-md)}.edit-field label{display:block;color:var(--text-primary);font-weight:500;font-size:0.9rem;margin-bottom:6px}.edit-field .field-tip{display:block;font-size:0.75rem;color:var(--text-muted);margin-top:4px;font-style:italic}.edit-field .title-lock-check,.gem-field .title-lock-check{display:flex;align-items:center;gap:var(--spacing-xs);margin:var(--spacing-xs) 0 0;font-size:0.8rem;font-weight:normal;color:var(--text-muted);text-transform:none;letter-spacing:normal;cursor:pointer}.edit-field .title-lock-check input,.gem-field .title-lock-check input{width:auto;padding:0;margin:0}.edit-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md) var(--spacing-lg)}.edit-grid .full-width{grid-column:1 / -1}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-md)}.form-help{font-size:0.8rem;color:var(--text-muted);margin-top:var(--spacing-xs)}.toggle-grid{display:flex;flex-wrap:wrap;gap:1.5rem}.card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);transition:all var(--transition-normal)}.card:hover{border-color:var(--card-border-hover);box-shadow:var(--shadow-lg)}.card + .card{margin-top:1.5rem}.card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-md);border-bottom:1px solid var(--card-border)}.card-title{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:0.5rem;text-transform:uppercase;letter-spacing:1px}.card-body{color:var(--text-secondary)}.glass-card,.card.glass,.glass-panel{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(76,201,240,0.2)}.glass-light,.glass-panel-light{background:rgba(20,26,35,0.6) !important;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.glass-medium,.glass-panel-medium{background:rgba(20,26,35,0.8) !important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.glass-dark,.glass-panel-dark{background:rgba(20,26,35,0.9) !important;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.glass-panel-hover:hover{background:rgba(20,26,35,0.85);border-color:var(--card-border-hover);box-shadow:var(--shadow-lg),0 0 20px rgba(76,201,240,0.1)}.system-card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);text-decoration:none;transition:all var(--transition-normal);display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;overflow:hidden}.system-card::before{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(180deg,transparent 60%,rgba(76,201,240,0.05) 100%);opacity:0;transition:opacity var(--transition-normal)}.system-card:hover{border-color:var(--primary-cyan);transform:translateY(-4px);box-shadow:var(--shadow-lg),0 0 20px rgba(76,201,240,0.1)}.system-card:hover::before{opacity:1}.system-logo{width:80px;height:80px;margin-bottom:var(--spacing-md);display:flex;align-items:center;justify-content:center}.system-logo img{max-width:100%;max-height:100%;object-fit:contain;filter:brightness(0.9);transition:all var(--transition-normal)}.system-card:hover .system-logo img{filter:brightness(1) drop-shadow(0 0 8px var(--primary-cyan-glow));transform:scale(1.05)}.system-logo-placeholder{width:80px;height:80px;background:var(--bg-lighter);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:2.5rem;color:var(--text-muted)}.system-name{font-family:var(--font-heading);font-size:1rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-xs);text-transform:uppercase;letter-spacing:1px}.system-info{display:flex;align-items:center;gap:var(--spacing-xs);font-size:0.8rem;color:var(--text-secondary)}.system-info-icon{color:var(--primary-cyan)}.section{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.status-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.status-item{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md) 0;border-bottom:1px solid var(--card-border)}.status-item:last-child{border-bottom:none}.status-info{display:flex;align-items:center;gap:var(--spacing-md)}.status-indicator{width:10px;height:10px;border-radius:50%;animation:pulse-status 2s ease-in-out infinite}.status-indicator.online{background:var(--status-online);box-shadow:0 0 10px var(--status-online)}.status-indicator.offline{background:var(--status-offline);box-shadow:0 0 10px var(--status-offline);animation:none}.status-name{color:var(--text-primary);font-weight:500}.status-badge{padding:var(--spacing-xs) var(--spacing-md);border-radius:var(--radius-full);font-size:0.7rem;font-weight:600;text-transform:uppercase;letter-spacing:1px}.status-badge.online{background:rgba(0,230,118,0.15);color:var(--status-online);border:1px solid rgba(0,230,118,0.3)}.status-badge.connected{background:rgba(76,201,240,0.15);color:var(--primary-cyan);border:1px solid rgba(76,201,240,0.3)}.status-badge.offline{background:rgba(255,82,82,0.15);color:var(--status-offline);border:1px solid rgba(255,82,82,0.3)}.scraper-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-xl);margin-bottom:var(--spacing-xl)}.scraper-section h2{font-family:var(--font-heading);font-size:1.2rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-lg);padding-bottom:var(--spacing-md);border-bottom:1px solid var(--card-border)}.scraper-form{margin-bottom:var(--spacing-lg)}.system-controller-card{transition:border-color 0.2s ease,box-shadow 0.2s ease}.system-controller-card:hover{border-color:rgba(76,201,240,0.3);box-shadow:0 0 10px rgba(76,201,240,0.1)}.controller-item{transition:background 0.2s ease,transform 0.15s ease}.controller-item:hover{background:rgba(76,201,240,0.1) !important;transform:translateX(4px)}.controller-details{border-left:2px solid var(--neon-cyan)}.add-controller-form{border:1px solid rgba(34,197,94,0.2)}.add-controller-form:hover{border-color:rgba(34,197,94,0.3)}.badge{display:inline-flex;align-items:center;gap:0.5rem;padding:0.35rem 0.75rem;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-full);font-size:0.8rem;font-weight:500;color:var(--neon-cyan);white-space:nowrap}.badge-success{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green)}.badge-warning{background:rgba(245,158,11,0.15);border-color:var(--neon-orange);color:var(--neon-orange)}.badge-danger{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red)}.badge-purple{background:rgba(168,85,247,0.15);border-color:var(--neon-purple);color:var(--neon-purple)}.badge-secondary{background:rgba(100,116,139,0.15);border-color:var(--neon-gray);color:#94a3b8}.badge-removable{padding-right:0.5rem}.badge-remove{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:0.25rem;background:transparent;border:none;border-radius:50%;color:inherit;cursor:pointer;font-size:1rem;line-height:1;opacity:0.7;transition:all 0.2s ease}.badge-remove:hover{opacity:1;background:rgba(255,255,255,0.1)}.game-badge{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:0 10px;border-radius:4px;font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;border:1px solid;white-space:nowrap;line-height:1;height:28px;box-sizing:border-box}.game-badge.esrb-e{background:rgba(32,178,170,0.15);border-color:#20b2aa;color:#20b2aa}.game-badge.esrb-e10{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.esrb-t{background:rgba(234,179,8,0.15);border-color:#eab308;color:#eab308}.game-badge.esrb-m{background:rgba(249,115,22,0.15);border-color:#f97316;color:#f97316}.game-badge.esrb-ao{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}.game-badge.esrb-rp{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.pegi-3{background:rgba(32,178,170,0.15);border-color:#20b2aa;color:#20b2aa}.game-badge.pegi-7{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.pegi-12{background:rgba(234,179,8,0.15);border-color:#eab308;color:#eab308}.game-badge.pegi-16{background:rgba(249,115,22,0.15);border-color:#f97316;color:#f97316}.game-badge.pegi-18{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}.game-badge.age-rating-badge{background:transparent;border-color:transparent;color:var(--text-secondary);padding:0}.game-badge.age-rating-badge .age-rating-img{height:28px;width:auto;display:block;filter:drop-shadow(0 1px 3px rgba(0,0,0,0.4))}.game-badge.age-rating-badge.crossmapped{opacity:0.75}.game-badge.genre{background:rgba(168,85,247,0.15);border-color:#a855f7;color:#a855f7}.game-badge.year{background:rgba(76,201,240,0.15);border-color:#4cc9f0;color:#4cc9f0}.game-badge.platform{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.region{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.players{background:rgba(59,130,246,0.15);border-color:#3b82f6;color:#3b82f6}.game-badge.sales-badge{background:var(--cyan-bg);border-color:var(--neon-cyan);color:var(--neon-cyan)}.game-badge.rpcs3-compat{--compat-color:var(--rpcs3-nothing);background:color-mix(in srgb,var(--compat-color) 15%,transparent);border-color:var(--compat-color);color:var(--compat-color)}.game-badge.rpcs3-compat.compat-playable{--compat-color:var(--rpcs3-playable)}.game-badge.rpcs3-compat.compat-ingame{--compat-color:var(--rpcs3-ingame)}.game-badge.rpcs3-compat.compat-intro{--compat-color:var(--rpcs3-intro)}.game-badge.rpcs3-compat.compat-loadable{--compat-color:var(--rpcs3-loadable)}.game-badge.completion{font-size:0.7rem}.game-badge.completion.not-started{background:rgba(100,116,139,0.15);border-color:#64748b;color:#94a3b8}.game-badge.completion.in-progress{background:rgba(245,158,11,0.15);border-color:#f59e0b;color:#f59e0b}.game-badge.completion.played{background:rgba(59,130,246,0.15);border-color:#3b82f6;color:#3b82f6}.game-badge.completion.completed{background:rgba(34,197,94,0.15);border-color:#22c55e;color:#22c55e}.game-badge.completion.\31 00-percent,.game-badge.completion.hundred-percent{background:linear-gradient(135deg,rgba(255,215,0,0.25) 0%,rgba(255,170,0,0.25) 100%);border-color:#ffd700;color:#ffd700;text-shadow:0 0 8px rgba(255,215,0,0.5);box-shadow:0 0 10px rgba(255,215,0,0.3),inset 0 0 10px rgba(255,215,0,0.1)}.game-badge.score-badge{font-weight:700;min-width:50px}.game-badge.score-badge.score-good{background:rgba(34,197,94,0.2);border-color:#22c55e;color:#22c55e}.game-badge.score-badge.score-mixed{background:rgba(234,179,8,0.2);border-color:#eab308;color:#fcd34d}.game-badge.score-badge.score-bad{background:rgba(239,68,68,0.2);border-color:#ef4444;color:#f87171}.meta-badge{padding:var(--spacing-xs) var(--spacing-md);border-radius:var(--radius-full);font-size:0.8rem;font-weight:600}.meta-badge.system{background:linear-gradient(135deg,var(--primary-cyan),var(--primary-cyan-dark));color:var(--bg-darkest)}.meta-badge.year{background:var(--bg-lighter);color:var(--text-primary);border:1px solid var(--card-border)}.meta-badge.genre{background:rgba(114,9,183,0.2);color:#b388ff;border:1px solid rgba(114,9,183,0.3)}.metadata-status{display:inline-flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-xs) var(--spacing-sm);border-radius:var(--radius-full);font-size:0.7rem;font-weight:600;text-transform:uppercase}.metadata-status.complete{background:rgba(0,230,118,0.15);color:var(--status-success)}.metadata-status.missing{background:rgba(255,171,0,0.15);color:var(--status-warning)}.source-badge{font-weight:600;text-transform:uppercase}.source-badge.thegamesdb{background:rgba(76,175,80,0.2);color:#81c784}.source-badge.igdb{background:rgba(33,150,243,0.2);color:#64b5f6}.source-badge.primary-source{border:2px solid currentColor;position:relative}.source-badge.primary-source::before{content:"★ ";font-size:0.8em}.source-badge.fallback-source{opacity:0.75;border:1px dashed currentColor}.psn-platform-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--radius-sm);font-size:0.65rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;border:1px solid;width:fit-content}.platform-ps3{background:rgba(205,154,42,0.15);color:#cd9a2a;border-color:rgba(205,154,42,0.3)}.platform-ps4{background:rgba(0,112,209,0.15);color:#4da6ff;border-color:rgba(0,112,209,0.3)}.platform-ps5{background:rgba(76,201,240,0.15);color:#4cc9f0;border-color:rgba(76,201,240,0.3)}.platform-psvita,.platform-vita{background:rgba(168,85,247,0.15);color:#a855f7;border-color:rgba(168,85,247,0.3)}.data-table{width:100%;border-collapse:collapse}@media (max-width:600px){.page-box .data-table{display:block;overflow-x:auto}}@media (max-width:600px){.page-box .data-table.stack-narrow,.data-table.stack-narrow tbody,.data-table.stack-narrow tr,.data-table.stack-narrow td{display:block;overflow-x:visible}.data-table.stack-narrow thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.data-table.stack-narrow tr{margin-bottom:var(--spacing-sm);padding:var(--spacing-xs) 0;border:1px solid var(--card-border);border-radius:var(--radius-md)}.data-table.stack-narrow tr[hidden]{display:none}.data-table.stack-narrow td{padding:var(--spacing-xs) var(--spacing-sm);border-bottom:none;overflow-wrap:anywhere}.data-table.stack-narrow td[data-label]{display:flex;gap:var(--spacing-sm)}.data-table.stack-narrow td[data-label]::before{content:attr(data-label);flex:0 0 35%;color:var(--text-secondary);font-size:0.8rem;font-weight:600}}.data-table th{background:var(--bg-darker,#0d1117);color:var(--text-secondary);font-weight:600;text-align:left;padding:0.75rem 1rem;font-size:0.8rem;text-transform:uppercase;letter-spacing:0.5px;border-bottom:2px solid var(--card-border);position:sticky;top:0;z-index:10}.data-table td{padding:0.75rem 1rem;border-bottom:1px solid var(--card-border);color:var(--text-primary);font-size:0.9rem}.data-table tbody tr:hover{background:rgba(76,201,240,0.05)}.games-table-container{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);overflow:hidden}.games-table{width:100%;border-collapse:collapse}.games-table thead{background:linear-gradient(90deg,var(--bg-medium) 0%,var(--bg-light) 100%)}.games-table th{padding:var(--spacing-md) var(--spacing-lg);text-align:left;font-family:var(--font-heading);font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;border-bottom:1px solid var(--card-border)}.games-table tbody tr{border-bottom:1px solid var(--card-border);transition:background-color var(--transition-fast)}.games-table tbody tr:hover{background:var(--card-bg-hover)}.games-table tbody tr:last-child{border-bottom:none}.games-table td{padding:var(--spacing-md) var(--spacing-lg);vertical-align:middle}.game-row-boxart{width:60px;height:80px;object-fit:cover;border-radius:var(--radius-sm);border:1px solid var(--card-border)}.game-row-boxart.boxart-3d{object-fit:contain;background:var(--bg-medium)}.game-row-boxart-placeholder{width:60px;height:80px;background:var(--bg-lighter);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:1.5rem}.game-row-title{color:var(--text-primary);font-weight:600;text-decoration:none;transition:color var(--transition-fast)}.game-row-title:hover{color:var(--primary-cyan)}.game-row-system{color:var(--text-secondary);font-size:0.85rem}.game-row-genre{color:var(--text-secondary);font-size:0.85rem}.game-row-year{color:var(--text-secondary);font-size:0.85rem;font-family:var(--font-mono)}.score-list-section{background:rgba(0,0,0,0.2);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.score-list-section h4{color:var(--text-primary);font-family:var(--font-heading);font-size:1.1rem;margin-bottom:var(--spacing-md)}.score-list{display:flex;flex-direction:column;gap:var(--spacing-sm)}.score-list-item{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);text-decoration:none;transition:var(--transition-fast)}.score-list-item:hover{border-color:var(--primary-cyan);background:var(--card-bg-hover)}.score-game-info{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.score-game-title{color:var(--text-primary);font-weight:600;font-size:0.95rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.score-game-system{color:var(--text-muted);font-size:0.8rem}.score-game-scores{display:flex;gap:var(--spacing-sm);flex-shrink:0}.score-lists-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-xl);margin-top:var(--spacing-xl)}.bulk-filter-section{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.bulk-action-section{padding:var(--spacing-lg);background:var(--bg-dark);border-radius:var(--radius-md);border:1px solid var(--card-border)}.bulk-preview-count{font-size:1.5rem;font-weight:700;color:var(--primary-cyan);margin-bottom:var(--spacing-sm)}.bulk-game-list{max-height:300px;overflow-y:auto;margin-top:var(--spacing-md)}.bulk-game-item{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.bulk-game-item:last-child{border-bottom:none}.tool-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.tool-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.tool-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.tool-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.tool-tab .tab-icon{font-size:1.1rem}.tool-tab .tab-label{}.system-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.system-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.625rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-align:center;white-space:nowrap}.system-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.system-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan);font-weight:600}.system-tab .tab-count{font-size:0.75rem;padding:0.15rem 0.5rem;background:rgba(0,0,0,0.3);border-radius:var(--radius-full);color:var(--text-secondary)}.system-tab.active .tab-count{background:var(--primary-cyan);color:var(--bg-darkest)}@media (max-width:768px){.tool-tabs,.system-tabs{gap:var(--spacing-xs);padding:var(--spacing-xs)}.tool-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.tool-tab .tab-label{display:none}.tool-tab .tab-icon{font-size:1.25rem}.system-tab{padding:0.5rem 0.75rem;font-size:0.8rem}}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:1000;opacity:0;visibility:hidden;transition:all 0.3s ease}.modal-overlay.active{opacity:1;visibility:visible}.modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);max-width:90%;max-height:90vh;overflow:auto;box-shadow:0 0 50px rgba(76,201,240,0.2)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.modal-header h2{font-family:var(--font-heading);font-size:1.25rem;color:var(--neon-cyan)}.modal-close,.custom-modal-close,.about-modal-close,.game-detail-modal-close,.game-edit-modal-close,.modal-close-btn,.archive-modal-close,.modal .modal-close{background:none;border:none;font-size:1.75rem;font-weight:normal;color:var(--text-secondary);cursor:pointer;padding:0;line-height:1;transition:color 0.2s}.modal-close:hover,.custom-modal-close:hover,.about-modal-close:hover,.game-detail-modal-close:hover,.game-edit-modal-close:hover,.modal-close-btn:hover,.archive-modal-close:hover,.modal .modal-close:hover{color:var(--neon-red)}.modal-close:focus-visible,.custom-modal-close:focus-visible,.about-modal-close:focus-visible,.game-detail-modal-close:focus-visible,.game-edit-modal-close:focus-visible,.modal-close-btn:focus-visible,.archive-modal-close:focus-visible,.modal .modal-close:focus-visible{outline:2px solid rgba(76,201,240,0.5);outline-offset:2px}.modal-body{padding:var(--spacing-lg)}.modal-footer{display:flex;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg);border-top:1px solid var(--card-border)}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:10000;align-items:center;justify-content:center}.modal.active{display:flex}.modal-content{max-width:90%;max-height:90%;border-radius:var(--radius-lg)}.modal-nav{position:absolute;top:50%;transform:translateY(-50%);width:50px;height:50px;background:var(--card-bg);border:none;border-radius:50%;color:var(--text-primary);font-size:1.5rem;cursor:pointer;transition:all var(--transition-fast)}.modal-nav:hover{background:var(--primary-cyan);color:var(--bg-darkest)}.modal-nav.prev{left:var(--spacing-lg)}.modal-nav.next{right:var(--spacing-lg)}.custom-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:10100;align-items:center;justify-content:center}.custom-modal.active{display:flex}.custom-modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);width:90%;max-width:500px;box-shadow:0 20px 60px rgba(0,0,0,0.5)}.custom-modal-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);display:flex;justify-content:space-between;align-items:center}.custom-modal-header h3{margin:0;font-family:var(--font-heading);color:var(--primary-cyan);font-size:1.1rem}.custom-modal-body{padding:var(--spacing-lg);color:var(--text-secondary);line-height:1.6}.custom-modal-body code{word-break:break-all;overflow-wrap:break-word;display:inline-block;max-width:100%}.custom-modal-body ul{max-width:100%;overflow:hidden}.custom-modal-body li{word-break:break-word;overflow-wrap:break-word}.custom-modal-footer{padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border);display:flex;justify-content:flex-end;gap:var(--spacing-sm)}.about-modal-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.8);z-index:10000;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.about-modal-overlay.active{display:flex}.about-modal{background:linear-gradient(135deg,rgba(20,26,35,0.98),rgba(15,20,25,0.98));border:1px solid var(--primary-cyan);border-radius:var(--radius-lg);max-width:560px;width:90%;max-height:90vh;overflow-y:auto;box-shadow:0 0 40px rgba(76,201,240,0.3),0 20px 40px rgba(0,0,0,0.5);animation:modalSlideIn 0.3s ease}.about-modal-header{display:flex;justify-content:space-between;align-items:center;padding:1.25rem 1.5rem;border-bottom:1px solid var(--card-border)}.about-modal-header h2{margin:0;font-size:1.25rem;font-family:var(--font-heading);color:var(--primary-cyan)}.about-modal-body{padding:1.5rem}.about-logo{display:flex;align-items:center;justify-content:center;gap:1rem;margin-bottom:1.5rem;padding-bottom:1.5rem;border-bottom:1px solid var(--card-border)}.about-logo-icon{font-size:3rem;filter:drop-shadow(0 0 10px rgba(76,201,240,0.5))}.about-logo-text{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);letter-spacing:2px}.about-logo-text span{color:var(--primary-cyan);text-shadow:0 0 10px var(--primary-cyan-glow)}.about-details{display:flex;flex-direction:column;gap:0.75rem;margin-bottom:1.5rem}.about-item{display:flex;justify-content:space-between;align-items:center;padding:0.75rem;background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.about-item.about-description{flex-direction:column;align-items:flex-start;gap:0.5rem}.about-label{color:var(--text-secondary);font-size:0.85rem;font-weight:500}.about-value{color:var(--text-primary);font-weight:600}.about-description .about-value{font-weight:400;font-size:0.9rem;line-height:1.5}.about-links{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0.75rem}.folder-item{display:flex;align-items:center;gap:0.75rem;padding:0.75rem 1rem;border-radius:var(--radius-md);cursor:pointer;transition:background 0.15s ease;background:transparent;border:none;width:100%;text-align:left;font:inherit;color:inherit;appearance:none;-webkit-appearance:none}.folder-item:hover{background:rgba(76,201,240,0.1)}.folder-item.folder-parent{border-bottom:1px solid var(--border-color);margin-bottom:0.5rem;padding-bottom:0.75rem;border-radius:0}.folder-item .folder-icon{font-size:1.25rem}.folder-item .folder-name{flex:1;color:var(--text-primary);font-weight:500}.folder-item .folder-info{color:var(--text-muted);font-size:0.8rem}@keyframes toast-slide-in{from{transform:translateX(-100%);opacity:0}to{transform:translateX(0);opacity:1}}@keyframes toast-slide-out{from{transform:translateX(0);opacity:1}to{transform:translateX(-100%);opacity:0}}.unified-toast-container{position:fixed;bottom:24px;left:280px;z-index:10000;display:flex;flex-direction:column-reverse;gap:10px;width:360px}.sidebar-collapsed ~ .main-content .unified-toast-container,body:has(.sidebar.collapsed) .unified-toast-container{left:calc(var(--sidebar-collapsed-width, 70px) + 20px)}@media (max-width:768px){.unified-toast-container{left:16px;right:16px;width:auto;max-width:none}}.unified-toast{animation:toast-slide-in 0.3s ease-out forwards;animation-iteration-count:1;width:100%;max-width:360px;border-radius:var(--radius-lg);overflow:hidden;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.unified-toast.removing{animation:toast-slide-out 0.3s ease-out forwards}.toast-content{display:flex;align-items:stretch;background:linear-gradient(135deg,var(--toast-bg-from,rgba(20,26,35,0.95)),var(--toast-bg-to,rgba(30,40,55,0.95)));border:2px solid var(--toast-color,var(--primary-cyan));border-radius:var(--radius-lg);box-shadow:0 0 20px rgba(var(--toast-color-rgb,76,201,240),0.3),0 4px 20px rgba(0,0,0,0.4);backdrop-filter:blur(10px)}.active-toast .toast-content{background:linear-gradient(135deg,var(--toast-bg-from,rgba(20,26,35,0.95)),var(--toast-bg-to,rgba(30,40,55,0.95)))}.queued-toast .toast-content{background:linear-gradient(135deg,var(--toast-queued-bg-from,rgba(25,30,40,0.90)),var(--toast-queued-bg-to,rgba(35,45,60,0.90)));opacity:0.9}.toast-main{flex:1;display:flex;align-items:center;gap:12px;padding:12px 16px;cursor:pointer;transition:background var(--transition-fast);min-width:0}.toast-main:hover{background:rgba(var(--toast-color-rgb,76,201,240),0.08)}.toast-icon{font-size:1.5rem;flex-shrink:0}.toast-icon:not(.paused){animation:pulse 1.5s ease-in-out infinite}.toast-icon.paused{animation:none;opacity:0.7}.toast-info{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}.toast-title{font-weight:600;font-size:0.9rem;color:var(--toast-color,var(--primary-cyan))}.toast-title.paused{color:var(--neon-orange)}.toast-system-name{color:var(--neon-purple);font-size:0.75rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toast-subtitle{color:var(--text-primary);font-size:0.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toast-progress-bar{height:4px;background:rgba(var(--toast-color-rgb,76,201,240),0.2);border-radius:2px;overflow:hidden;margin:4px 0}.toast-progress-fill{height:100%;background:linear-gradient(90deg,var(--toast-color,var(--primary-cyan)),var(--secondary-magenta,#f72585));border-radius:2px;transition:width var(--transition-normal)}.toast-progress-text{display:flex;justify-content:space-between;font-size:0.75rem;color:var(--text-secondary);font-family:var(--font-mono)}.toast-stats{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:0.85rem;font-family:var(--font-mono);margin-top:2px}.toast-stats .stat{display:flex;align-items:center;gap:3px;white-space:nowrap}.toast-stats .stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700;color:var(--text-primary)}.toast-stats .stat.success,.toast-stats .stat-success{color:var(--neon-green)}.toast-stats .stat.failed,.toast-stats .stat-failed{color:var(--neon-red)}.toast-stats .stat.skipped,.toast-stats .stat-skipped{color:var(--neon-orange)}.toast-meta{font-size:0.75rem;color:var(--text-muted)}.toast-queued-info{font-size:0.75rem;color:var(--text-muted)}.toast-controls{display:flex;flex-direction:column;border-left:1px solid rgba(var(--toast-color-rgb,76,201,240),0.2);flex-shrink:0}.toast-btn,.toast-ctrl-btn{flex:1;padding:8px 10px;border:none;background:transparent;cursor:pointer;font-size:0.9rem;transition:all var(--transition-fast);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;min-width:36px}.toast-btn:hover,.toast-ctrl-btn:hover{background:rgba(var(--toast-color-rgb,76,201,240),0.15);color:var(--toast-color,var(--primary-cyan))}.toast-btn.pause:hover,.toast-btn.pause.is-paused,.toast-ctrl-btn.pause:hover{background:rgba(245,158,11,0.2);color:var(--neon-orange)}.toast-btn.pause.is-paused{background:rgba(34,197,94,0.15);color:var(--neon-green)}.toast-btn.cancel:hover,.toast-ctrl-btn.cancel:hover{background:rgba(239,68,68,0.2);color:var(--neon-red)}.bulk-scrape-toast{--toast-color:var(--primary-cyan);--toast-color-rgb:76,201,240}.ra-sync-toast{--toast-color:var(--neon-orange);--toast-color-rgb:245,158,11}.ra-sync-toast .toast-main:hover{background:rgba(245,158,11,0.08)}.ra-refresh-toast{--toast-color:var(--neon-purple);--toast-color-rgb:168,85,247}.ra-refresh-toast .toast-main:hover{background:rgba(168,85,247,0.08)}.bulk-scrape-queued-toast{--toast-color:var(--primary-cyan-dark);--toast-color-rgb:58,159,194}.ra-sync-queued-toast{--toast-color:#c47d09;--toast-color-rgb:196,125,9}.ra-refresh-queued-toast{--toast-color:#8b46d4;--toast-color-rgb:139,70,212}.image-resize-toast{--toast-color:var(--neon-green);--toast-color-rgb:34,197,94}.image-resize-toast .toast-main:hover{background:rgba(34,197,94,0.08)}.toast-completed{--toast-color:var(--neon-green);--toast-color-rgb:34,197,94}.queued-toast{width:360px}.queued-toast .toast-content{padding:0}.queued-toast .toast-main{padding:10px 14px}.queued-toast .toast-icon{font-size:1.2rem}.queued-toast .toast-title{font-size:0.85rem}.queued-toast .toast-controls{flex-direction:row;min-width:36px}.queued-toast .toast-btn{min-width:36px;min-height:36px}.notification-container{position:fixed;top:20px;right:20px;z-index:10002;display:flex;flex-direction:column;gap:10px;max-width:400px}.notification{display:flex;align-items:center;gap:12px;padding:14px 18px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);box-shadow:var(--shadow-lg),0 0 20px rgba(0,0,0,0.3);transform:translateX(120%);opacity:0;transition:all var(--transition-normal)}.notification.show{transform:translateX(0);opacity:1}.notification-icon{font-size:1.2rem;flex-shrink:0}.notification-message{flex:1;color:var(--text-primary);font-size:0.9rem;line-height:1.4}.notification-close{background:none;border:none;color:var(--text-muted);font-size:1.2rem;cursor:pointer;padding:0 4px;opacity:0.6;transition:opacity 0.2s}.notification-close:hover{opacity:1;color:var(--text-primary)}.notification-success{border-color:var(--neon-green);box-shadow:var(--shadow-lg),0 0 15px rgba(34,197,94,0.2)}.notification-error{border-color:var(--neon-red);box-shadow:var(--shadow-lg),0 0 15px rgba(239,68,68,0.2)}.notification-warning{border-color:var(--neon-orange);box-shadow:var(--shadow-lg),0 0 15px rgba(245,158,11,0.2)}.notification-info{border-color:var(--primary-cyan);box-shadow:var(--shadow-lg),0 0 15px rgba(76,201,240,0.2)}.progress-container{background:#1a1a3e;border:2px solid var(--neon-cyan);border-radius:var(--radius-md);height:36px;overflow:hidden;position:relative;box-shadow:0 0 15px rgba(76,201,240,0.3)}.progress-bar{height:100%;background:linear-gradient(90deg,var(--neon-cyan),#7209b3);border-radius:calc(var(--radius-md) - 2px);display:flex;align-items:center;justify-content:center;color:white;font-size:0.9rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7);transition:width 0.3s ease;min-width:0}.progress-bar[data-percent="0"]{min-width:0;width:0%}.progress-bar:not([data-percent="0"]){min-width:60px}.progress-stats{display:flex;justify-content:space-between;color:var(--text-secondary);font-size:0.85rem;margin-top:0.5rem}.progress-container-sm{height:24px;border-width:1px}.progress-container-sm .progress-bar{font-size:0.75rem}.loading{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2xl)}.loading-spinner{width:40px;height:40px;border:3px solid var(--card-border);border-top-color:var(--primary-cyan);border-radius:50%;animation:spin 1s linear infinite}.loading-spinner-small{width:20px;height:20px;border:2px solid var(--card-border);border-top-color:var(--primary-cyan);border-radius:50%;animation:spin 1s linear infinite}.loading-text{margin-left:var(--spacing-md);color:var(--text-secondary)}.loading-overlay{position:fixed;inset:0;background:rgba(10,14,23,0.85);display:flex;align-items:center;justify-content:center;z-index:10001;opacity:0;visibility:hidden;transition:all 0.3s ease}.loading-overlay.active{opacity:1;visibility:visible}.loading-content{display:flex;flex-direction:column;align-items:center;gap:16px}.loading-content .loading-text{color:var(--text-primary);font-size:1rem;font-family:var(--font-primary);margin-left:0}.empty-state{text-align:center;padding:var(--spacing-2xl);color:var(--text-secondary)}.empty-state-icon{font-size:4rem;margin-bottom:var(--spacing-md);opacity:0.5}.empty-state-title{font-size:1.2rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-sm)}.empty-state-text{margin-bottom:var(--spacing-lg)}.scan-log-container{background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);margin-top:var(--spacing-lg)}.scan-log-header{display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1rem;background:var(--bg-dark);border-bottom:1px solid var(--border-color)}.scan-log-header h4{margin:0;font-size:0.9rem;color:var(--text-secondary);display:flex;align-items:center;gap:0.5rem}.scan-log-content{height:200px;overflow-y:auto;padding:0.75rem 1rem;font-family:var(--font-mono);font-size:0.8rem;line-height:1.6}.scan-log-content .log-entry{color:var(--text-secondary);padding:0.15rem 0}.scan-log-content .log-entry.info{color:var(--primary-cyan)}.scan-log-content .log-entry.warning{color:var(--neon-orange)}.scan-log-content .log-entry.error{color:var(--neon-red)}.rate-limits-container{background:var(--bg-darker,#0d1117);border:1px solid var(--card-border);border-radius:var(--radius-md);padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-md)}.rate-limit-header{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-muted);font-size:0.8rem;padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.rate-limit-item{display:flex;flex-direction:column;gap:var(--spacing-xs);padding:var(--spacing-md);background:rgba(255,255,255,0.02);border-radius:var(--radius-sm);border-left:3px solid var(--text-muted)}.rate-limit-item.status-green{border-left-color:var(--neon-green)}.rate-limit-item.status-yellow{border-left-color:var(--neon-orange)}.rate-limit-item.status-red{border-left-color:var(--neon-red)}.rate-limit-item.status-gray{border-left-color:var(--text-muted)}.rate-limit-item strong{color:var(--text-primary);font-size:0.95rem}.rate-limit-value{color:var(--text-secondary);font-size:0.9rem}.rate-limit-bar{width:100%;height:6px;background:rgba(255,255,255,0.08);border-radius:3px;overflow:hidden;margin:4px 0}.rate-limit-bar-fill{height:100%;border-radius:3px;transition:width 0.6s ease}.rate-limit-detail{color:var(--text-muted);font-size:0.8rem;line-height:1.5}.progress-bar-container{background:#1a1a3e;border:2px solid var(--neon-cyan);border-radius:var(--radius-md);height:36px;overflow:hidden;margin-bottom:0.5rem;box-shadow:0 0 15px rgba(76,201,240,0.3)}.progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--neon-cyan),#7209b3);width:0%;transition:width 0.3s ease;display:flex;align-items:center;justify-content:center;color:white;font-size:0.9rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7);border-radius:calc(var(--radius-md) - 2px)}.progress-details{display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);margin-top:0.75rem}.progress-status{color:var(--text-secondary);font-size:0.9rem}.progress-status strong{color:var(--primary-cyan)}.scanner-section{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);padding:1.5rem;margin-bottom:1.5rem}.scanner-section h3{color:var(--text-primary);font-size:1rem;margin-bottom:1rem;display:flex;align-items:center;gap:0.5rem}.scanner-controls{display:flex;flex-wrap:wrap;gap:1rem;align-items:center}.quick-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.tag{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-full);font-size:0.85rem;color:var(--text-secondary);white-space:nowrap}.tag.tag-system{background:rgba(76,201,240,0.15);border-color:rgba(76,201,240,0.4);color:var(--primary-cyan)}.tag.tag-date{background:rgba(168,85,247,0.15);border-color:rgba(168,85,247,0.4);color:#c084fc}.tag.tag-genre{background:rgba(34,197,94,0.15);border-color:rgba(34,197,94,0.4);color:#4ade80}.genre-select-container{display:flex;flex-wrap:wrap;gap:8px;padding:10px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);min-height:44px}.genre-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);border-radius:var(--radius-full);color:var(--neon-purple);font-size:0.8rem;font-weight:500}.genre-tag .remove-genre{cursor:pointer;opacity:0.7;transition:opacity 0.15s}.genre-tag .remove-genre:hover{opacity:1}.genre-dropdown{position:relative}.genre-dropdown-menu{position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);z-index:100;display:none}.genre-dropdown-menu.active{display:block}.genre-option{padding:8px 12px;cursor:pointer;transition:background 0.15s}.genre-option:hover{background:rgba(76,201,240,0.1)}.genre-option.selected{background:rgba(168,85,247,0.2);color:var(--neon-purple)}.controller-select-container{display:flex;flex-wrap:wrap;gap:8px;padding:10px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);min-height:44px}.controller-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-full);color:var(--neon-cyan);font-size:0.8rem;font-weight:500}.controller-tag .remove-controller{cursor:pointer;opacity:0.7;transition:opacity 0.15s}.controller-tag .remove-controller:hover{opacity:1}.controller-dropdown{position:relative}.active-filter-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.active-filter-tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(76,201,240,0.15);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-full);color:var(--primary-cyan);font-size:0.8rem;font-weight:500}.active-filter-tag .remove-filter{background:none;border:none;color:var(--primary-cyan);font-size:1rem;line-height:1;cursor:pointer;padding:0;opacity:0.7;transition:opacity 0.2s}.active-filter-tag .remove-filter:hover{opacity:1}.gem-tag-container{display:flex;flex-wrap:wrap;gap:var(--spacing-xs);min-height:36px;padding:var(--spacing-sm);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-xs)}.gem-tag-container:empty::before{content:'None selected';color:var(--text-muted);font-style:italic;font-size:0.85rem}.gem-tag-dropdown{width:100%;padding:8px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.9rem;cursor:pointer}.gem-tag-dropdown:hover,.gem-tag-dropdown:focus{border-color:var(--primary-cyan);outline:none}.gem-controller-inputs{display:flex;gap:var(--spacing-xs)}.gem-controller-inputs select{flex:1}.gem-custom-input{flex:1;padding:8px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.9rem}.gem-custom-input:hover,.gem-custom-input:focus{border-color:var(--primary-cyan);outline:none}#queueManagerModal .queue-item{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:10px;transition:all 0.2s ease}#queueManagerModal .queue-item:hover{border-color:var(--primary-cyan)}#queueManagerModal .queue-item.running{border-color:var(--primary-cyan);border-width:2px;background:rgba(76,201,240,0.05)}#queueManagerModal .queue-item.queued{border-color:var(--neon-orange)}#queueManagerModal .queue-item-icon{font-size:1.5rem;flex-shrink:0}#queueManagerModal .queue-item-info{flex:1;min-width:0}#queueManagerModal .queue-item-title{font-weight:600;color:var(--text-primary);margin-bottom:2px}#queueManagerModal .queue-item-system{font-size:0.85rem;color:var(--neon-purple)}#queueManagerModal .queue-item.queued .queue-item-system{color:var(--neon-orange)}#queueManagerModal .queue-item-games{font-size:0.8rem;color:var(--text-secondary)}#queueManagerModal .queue-item-progress{font-size:0.75rem;color:var(--primary-cyan);margin-top:4px}#queueManagerModal .queue-item-controls{display:flex;flex-direction:column;gap:4px}#queueManagerModal .queue-btn{padding:6px 10px;border:1px solid var(--card-border);border-radius:var(--radius-sm);background:var(--bg-medium);color:var(--text-secondary);cursor:pointer;font-size:0.8rem;transition:all 0.15s ease;white-space:nowrap}#queueManagerModal .queue-btn:hover{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}#queueManagerModal .queue-btn.danger:hover{background:rgba(239,68,68,0.15);border-color:#ef4444;color:#ef4444}#queueManagerModal .queue-btn:disabled{opacity:0.3;cursor:not-allowed}#queueManagerModal .queue-empty{text-align:center;color:var(--text-muted);padding:20px;font-style:italic}.launch-indicator{position:fixed;bottom:var(--spacing-lg);right:var(--spacing-lg);z-index:1000;display:inline-flex;align-items:center;gap:0.5rem;padding:0.4rem 0.9rem;background:var(--bg-card);border:1px solid var(--primary-cyan);border-radius:999px;box-shadow:0 0 12px rgba(76,201,240,0.35);color:var(--text-primary);font-family:var(--font-mono,monospace);font-size:0.85rem;cursor:pointer;transition:opacity 0.2s ease,transform 0.2s ease}.launch-indicator.hidden{opacity:0;pointer-events:none;transform:translateY(8px)}.launch-indicator-icon{color:var(--neon-green,#44ff66);font-weight:bold}.launch-indicator-count{font-weight:bold;color:var(--primary-cyan)}.launch-indicator-popover{position:fixed;bottom:calc(var(--spacing-lg) + 2.5rem);right:var(--spacing-lg);z-index:1001;min-width:280px;padding:var(--spacing-md);background:var(--bg-card);border:1px solid var(--primary-cyan);border-radius:var(--border-radius);box-shadow:0 0 20px rgba(76,201,240,0.4);color:var(--text-primary)}.launch-indicator-popover-title{font-family:var(--font-heading);font-size:0.95rem;color:var(--primary-cyan);margin-bottom:var(--spacing-sm);text-transform:uppercase;letter-spacing:0.05em}.launch-indicator-popover-row{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-sm);padding:0.4rem 0;border-top:1px solid var(--border-color,rgba(255,255,255,0.08));font-size:0.85rem}.launch-indicator-popover-row:first-of-type{border-top:none}.global-search{position:relative;margin-top:var(--spacing-md)}.global-search-input{width:100%;padding:var(--spacing-sm) var(--spacing-md);background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}.global-search-input::placeholder{color:var(--text-muted)}.global-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:var(--shadow-glow-cyan)}.global-search-list{position:absolute;top:calc(100% + var(--spacing-xs));left:0;right:0;z-index:1001;max-height:60vh;overflow-y:auto;background:var(--card-bg);border:1px solid var(--card-border-hover);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);padding:var(--spacing-xs) 0}.global-search-group{padding:var(--spacing-xs) var(--spacing-sm);color:var(--text-muted);font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em}.global-search-option{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-xs) var(--spacing-sm);color:var(--text-primary);text-decoration:none;min-height:36px}.global-search-option:hover,.global-search-option[aria-selected="true"]{background:var(--card-bg-hover);color:var(--text-accent)}.global-search-thumb{flex:0 0 24px;height:32px;display:flex;align-items:center;justify-content:center}.global-search-thumb img{max-width:24px;max-height:32px;object-fit:contain}.global-search-text{flex:1;min-width:0;display:flex;flex-direction:column}.global-search-label{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;line-height:1.25}.global-search-meta{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted);font-size:0.8rem}.global-search-empty{padding:var(--spacing-sm);color:var(--text-muted)}.game-card-wrapper{background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden;transition:all var(--transition-normal)}.game-card-wrapper:hover{border-color:var(--primary-cyan);box-shadow:0 0 20px rgba(76,201,240,0.2);transform:translateY(-2px)}.game-hero{display:grid;grid-template-columns:300px 1fr;gap:var(--spacing-xl);margin-bottom:var(--spacing-xl);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-xl);position:relative;overflow:hidden}.game-hero::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta),var(--primary-cyan))}.hero-boxart{width:300px;flex-shrink:0;position:relative}.hero-boxart .game-boxart-image{width:100%;height:auto;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);border:2px solid var(--card-border)}.hero-info{display:flex;flex-direction:column;min-width:0;flex:1}.game-boxart-container{position:relative}.game-boxart{width:100%;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);border:2px solid var(--card-border)}.game-boxart-placeholder{width:100%;aspect-ratio:3/4;background:var(--bg-lighter);border-radius:var(--radius-lg);display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-muted);font-size:4rem;border:2px dashed var(--card-border)}.game-boxart-placeholder span{font-size:1rem;margin-top:var(--spacing-md)}.game-info{display:flex;flex-direction:column}.game-title-section{margin-bottom:var(--spacing-lg)}.game-title{font-family:var(--font-heading);font-size:2rem;font-weight:700;color:var(--text-primary);margin-bottom:var(--spacing-sm);line-height:1.2}.game-meta-badges{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.game-ratings{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.rating-display{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm) var(--spacing-md);background:var(--bg-lighter);border-radius:var(--radius-md);border:1px solid var(--card-border)}.rating-display img{height:32px}.rating-display .rating-text{font-weight:600;font-size:0.9rem}.rating-display.esrb{border-color:var(--rating-esrb-border);background:var(--rating-esrb-bg)}.rating-display.esrb .rating-text{color:var(--rating-esrb)}.rating-display.pegi{border-color:var(--rating-pegi-border);background:var(--rating-pegi-bg)}.rating-display.pegi .rating-text{color:var(--rating-pegi)}.game-details-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.detail-item{display:flex;flex-direction:column;gap:var(--spacing-xs)}.detail-label{font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px}.detail-value{color:var(--text-primary);font-size:0.95rem}.game-description{margin-bottom:var(--spacing-lg)}.game-description h3{font-family:var(--font-heading);font-size:1rem;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-sm)}.game-description p{color:var(--text-secondary);line-height:1.7}.screenshots-section{margin-bottom:var(--spacing-xl)}.screenshots-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:var(--spacing-md)}.screenshot-item{position:relative;aspect-ratio:16/9;border-radius:var(--radius-md);overflow:hidden;cursor:pointer;border:2px solid var(--card-border);transition:all var(--transition-fast)}.screenshot-item:hover{border-color:var(--primary-cyan);transform:scale(1.02)}.screenshot-item img{width:100%;height:100%;object-fit:cover}.screenshot-item::after{content:'🔍';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:2rem;opacity:0;transition:opacity var(--transition-fast)}.screenshot-item:hover::after{opacity:1}.search-results{margin-top:var(--spacing-lg)}.search-result{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm);transition:all var(--transition-fast)}.search-result:hover{border-color:var(--primary-cyan);background:var(--bg-light)}.search-result-info h4{color:var(--text-primary);font-weight:600;margin-bottom:var(--spacing-xs)}.search-result-meta{display:flex;gap:var(--spacing-sm);font-size:0.8rem}.search-result-meta span{padding:2px 8px;background:var(--bg-lighter);border-radius:var(--radius-sm);color:var(--text-secondary)}.search-result-alts{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--spacing-xs);font-size:0.78rem}.search-result-alts-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;font-size:0.7rem;font-weight:600}.game-detail-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.9);backdrop-filter:blur(4px);z-index:10000;align-items:center;justify-content:center;padding:var(--spacing-lg)}.game-detail-modal.active{display:flex}.game-detail-modal-content{background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);width:100%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative;box-shadow:0 0 40px rgba(0,0,0,0.5),0 0 80px rgba(76,201,240,0.1)}.game-detail-modal-close{position:absolute;top:var(--spacing-md);right:var(--spacing-md);z-index:10}.game-detail-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--spacing-xl);min-height:300px;color:var(--text-secondary)}.game-detail-content{padding:var(--spacing-lg)}.gdm-header{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.gdm-boxart-container{flex-shrink:0;width:180px}.gdm-boxart{width:100%;border-radius:var(--radius-md);box-shadow:0 4px 20px rgba(0,0,0,0.3)}.gdm-info{flex:1}.gdm-title{font-family:var(--font-heading);font-size:1.5rem;color:var(--primary-cyan);margin:0 0 var(--spacing-sm) 0}.gdm-meta{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.gdm-system-badge{background:var(--primary-cyan);color:var(--bg-darkest);padding:2px 8px;border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600}.gdm-year,.gdm-players{background:rgba(var(--toast-color-rgb,76,201,240),0.15);color:var(--text-primary);padding:2px 8px;border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600;border:1px solid rgba(var(--toast-color-rgb,76,201,240),0.25)}.gdm-ratings{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.gdm-ratings .rating-badge{display:inline-block;padding:4px 8px;border-radius:var(--radius-sm);font-size:0.75rem;font-weight:600}.gdm-ratings .rating-badge.esrb{background:rgba(168,85,247,0.2);color:var(--neon-purple);border:1px solid rgba(168,85,247,0.3)}.gdm-ratings .rating-badge.score{background:rgba(34,197,94,0.2);color:var(--neon-green);border:1px solid rgba(34,197,94,0.3)}.gdm-completion .completion-badge{display:inline-block;padding:4px 10px;border-radius:var(--radius-sm);font-size:0.75rem;font-weight:600}.gdm-completion .status-in_progress,.gdm-completion .status-in-progress{background:rgba(76,201,240,0.2);color:var(--primary-cyan);border:1px solid rgba(76,201,240,0.3)}.gdm-completion .status-completed{background:rgba(34,197,94,0.2);color:var(--neon-green);border:1px solid rgba(34,197,94,0.3)}.gdm-completion .status-played{background:rgba(59,130,246,0.2);color:#3b82f6;border:1px solid rgba(59,130,246,0.3)}.gdm-completion .status-100_percent,.gdm-completion .status-100-percent{background:linear-gradient(135deg,rgba(245,158,11,0.2),rgba(247,37,133,0.2));color:var(--completion-gold);border:1px solid rgba(245,158,11,0.3)}.gdm-description{color:var(--text-secondary);line-height:1.6;margin-bottom:var(--spacing-lg);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.gdm-details-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.gdm-detail{display:flex;flex-direction:column}.gdm-detail .label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px}.gdm-detail span:last-child{color:var(--text-primary)}.gdm-achievements{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.ra-badge-modal{background:linear-gradient(135deg,var(--neon-orange),#d97706);color:white;padding:6px 12px;border-radius:var(--radius-md);font-weight:600}.gdm-achievements .game-badge.achievement-progress{font-size:0.75rem;padding:4px 10px}.gdm-achievements .ach-icon{width:16px;height:16px;vertical-align:middle}.gdm-achievements .ach-bar{display:inline-block;width:40px;height:4px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden;vertical-align:middle;margin-left:4px}.gdm-achievements .ach-fill{display:block;height:100%;border-radius:2px;transition:width 0.3s ease}.gdm-screenshots{display:flex;gap:var(--spacing-sm);overflow-x:auto;padding-bottom:var(--spacing-sm);margin-bottom:var(--spacing-lg)}.gdm-screenshot{height:80px;border-radius:var(--radius-sm);cursor:pointer;transition:transform var(--transition-fast)}.gdm-screenshot:hover{transform:scale(1.05)}.gdm-video{margin-bottom:var(--spacing-lg)}.gdm-video video{width:100%;max-height:300px;border-radius:var(--radius-md);background:#000}.gdm-lightbox{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);z-index:10002;align-items:center;justify-content:center;flex-direction:column}.gdm-lightbox.active{display:flex}.gdm-lightbox-image{max-width:90%;max-height:80vh;object-fit:contain;border-radius:var(--radius-md)}.gdm-lightbox-close{position:absolute;top:20px;right:35px;font-size:2.5rem;color:var(--text-primary);cursor:pointer;z-index:10003;transition:color var(--transition-fast);background:none;border:none;line-height:1}.gdm-lightbox-close:hover{color:var(--primary-cyan)}.gdm-lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.5);border:2px solid var(--card-border);color:var(--text-primary);font-size:3rem;padding:10px 20px;cursor:pointer;border-radius:var(--radius-md);transition:all var(--transition-fast);z-index:10003}.gdm-lightbox-nav:hover{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.gdm-lightbox-nav.prev{left:20px}.gdm-lightbox-nav.next{right:20px}.gdm-lightbox-counter{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);color:var(--text-secondary);font-size:0.9rem;font-family:var(--font-mono)}.gdm-lightbox-hint{position:absolute;bottom:45px;left:50%;transform:translateX(-50%);color:var(--text-muted);font-size:0.75rem;white-space:nowrap}.gdm-actions{display:flex;gap:var(--spacing-md);padding-top:var(--spacing-md);border-top:1px solid var(--card-border)}.game-edit-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(10,14,23,0.95);backdrop-filter:blur(4px);z-index:10001;align-items:center;justify-content:center;padding:var(--spacing-lg)}.game-edit-modal.active{display:flex}.game-edit-modal-content{background:var(--bg-medium);border:1px solid var(--primary-cyan);border-radius:var(--radius-lg);width:100%;max-width:700px;max-height:90vh;overflow-y:auto;position:relative;box-shadow:0 0 40px rgba(0,0,0,0.5),0 0 60px rgba(76,201,240,0.15)}.game-edit-modal-close{position:absolute;top:var(--spacing-md);right:var(--spacing-md);z-index:10}.gem-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border)}.gem-title{font-family:var(--font-heading);font-size:1.25rem;color:var(--primary-cyan);margin:0;padding-right:2rem}.gem-body{padding:var(--spacing-lg);display:grid}.gem-section{margin-bottom:var(--spacing-lg)}.gem-section:last-child{margin-bottom:0}.gem-label{display:block;font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-sm)}.gem-select{width:100%;padding:10px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:1rem;cursor:pointer;transition:border-color var(--transition-fast)}.gem-select:hover,.gem-select:focus{border-color:var(--primary-cyan);outline:none}.gem-hltb-display{padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.gem-hltb-empty{color:var(--text-muted);font-style:italic}.gem-hltb-current{color:var(--text-primary);display:block}.gem-hltb-match-info{display:block;font-size:0.8rem;color:var(--text-muted);margin-top:var(--spacing-xs)}.gem-hltb-actions{display:flex;gap:var(--spacing-sm)}.gem-hltb-results{margin-top:var(--spacing-md);padding:var(--spacing-md);background:rgba(76,201,240,0.05);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-md)}.gem-hltb-searching{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary)}.gem-hltb-match{margin-bottom:var(--spacing-md)}.gem-hltb-match-title{display:block;color:var(--text-primary);font-weight:600}.gem-hltb-match-confidence{font-size:0.8rem;color:var(--neon-green)}.gem-hltb-match-platform{display:block;font-size:0.8rem;color:var(--neon-purple);margin-top:2px}.gem-hltb-times{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.gem-hltb-time{text-align:center;padding:var(--spacing-sm);background:rgba(0,0,0,0.2);border-radius:var(--radius-sm)}.gem-hltb-time-label{display:block;font-size:0.7rem;color:var(--text-muted);text-transform:uppercase;margin-bottom:2px}.gem-hltb-time-value{display:block;color:var(--primary-cyan);font-weight:600}.gem-hltb-error{color:var(--neon-red);font-size:0.9rem}.gem-footer{display:flex;justify-content:space-between;gap:var(--spacing-md);padding:var(--spacing-lg);border-top:1px solid var(--card-border)}.gem-footer-right{display:flex;gap:var(--spacing-sm)}.gem-tabs{display:flex;gap:var(--spacing-xs);margin-top:var(--spacing-md);overflow-x:auto;padding-bottom:var(--spacing-xs)}.gem-tab{padding:8px 12px;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-size:0.85rem;cursor:pointer;transition:all var(--transition-fast);white-space:nowrap}.gem-tab:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.gem-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.gem-tab-content{grid-area:1 / 1;visibility:hidden;pointer-events:none}.gem-tab-content.active{visibility:visible;pointer-events:auto}.gem-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-md)}.gem-field{display:flex;flex-direction:column}.gem-field label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:var(--spacing-xs)}.gem-field input,.gem-field select,.gem-field textarea{padding:10px 12px;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.95rem;transition:border-color var(--transition-fast)}.gem-field input:hover,.gem-field input:focus,.gem-field select:hover,.gem-field select:focus,.gem-field textarea:hover,.gem-field textarea:focus{border-color:var(--primary-cyan);outline:none}.gem-field input::placeholder,.gem-field textarea::placeholder{color:var(--text-muted)}.gem-field-full{grid-column:1 / -1}.gem-field textarea{resize:vertical;min-height:120px}.gem-field-row{display:flex;gap:var(--spacing-xs)}.gem-field-row input{flex:1}.gem-field-row .btn{flex-shrink:0}.gem-toggle-row{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) 0}.gem-toggle-label{color:var(--text-secondary);font-size:0.9rem}.psn-profile-bar{display:flex;align-items:center;gap:var(--spacing-lg);padding:var(--spacing-md) var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-avatar{width:48px;height:48px;border-radius:50%;border:2px solid var(--primary-cyan);object-fit:cover}.psn-avatar-placeholder{width:48px;height:48px;border-radius:50%;border:2px solid var(--card-border);background:var(--bg-medium);display:flex;align-items:center;justify-content:center;font-size:1.5rem}.psn-profile-info{flex:1}.psn-username{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary)}.psn-level{font-size:0.8rem;color:var(--text-secondary)}.psn-level strong{color:var(--primary-cyan)}.psn-sync-status{font-size:0.75rem;color:var(--text-muted);display:flex;align-items:center;gap:6px}.psn-stats-bar{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.psn-stat-box{padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);text-align:center}.psn-stat-icon{font-size:1.5rem;margin-bottom:var(--spacing-xs)}.psn-stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700}.psn-stat-value.platinum{color:var(--trophy-platinum)}.psn-stat-value.gold{color:var(--trophy-gold)}.psn-stat-value.silver{color:var(--trophy-silver)}.psn-stat-value.bronze{color:var(--trophy-bronze)}.psn-stat-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.platform-filters{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);flex-wrap:wrap}.platform-filter-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast)}.platform-filter-btn:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.platform-filter-btn.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.filter-count{font-size:0.7rem;padding:2px 6px;background:rgba(255,255,255,0.1);border-radius:var(--radius-full)}.platform-filter-btn.active .filter-count{background:rgba(76,201,240,0.2)}.psn-search-sort-row{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg);align-items:center}.psn-search-wrapper{flex:1;position:relative}.psn-search-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-muted);pointer-events:none}.psn-search-input{width:100%;padding:10px 12px 10px 36px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem}.psn-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.psn-search-input::placeholder{color:var(--text-muted)}.psn-sort-controls{display:flex;align-items:center;gap:var(--spacing-sm)}.psn-sort-label{font-size:0.8rem;color:var(--text-muted);white-space:nowrap}.psn-sort-select{padding:8px 32px 8px 12px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.85rem;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%234cc9f0' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center}.psn-sort-select:focus{outline:none;border-color:var(--primary-cyan)}.psn-games-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-md)}.psn-trophy-game-card{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);transition:all var(--transition-fast);text-decoration:none;color:inherit}.psn-trophy-game-card:hover{border-color:var(--primary-cyan);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.3)}.psn-trophy-game-thumb{width:64px;height:64px;border-radius:var(--radius-sm);object-fit:cover;border:1px solid var(--card-border);flex-shrink:0}.psn-trophy-game-thumb-placeholder{width:64px;height:64px;border-radius:var(--radius-sm);background:var(--bg-medium);border:1px solid var(--card-border);display:flex;align-items:center;justify-content:center;font-size:1.5rem;flex-shrink:0}.psn-trophy-game-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.psn-trophy-game-name{font-size:0.9rem;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.psn-trophy-game-npwr{font-family:var(--font-mono);font-size:0.65rem;color:var(--text-muted)}.psn-trophy-game-progress{display:flex;align-items:center;gap:var(--spacing-sm)}.psn-progress-track{flex:1;height:6px;background:var(--bg-dark);border-radius:3px;overflow:hidden}.psn-progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--neon-purple));border-radius:3px;transition:width var(--transition-normal)}.psn-progress-pct{font-family:var(--font-mono);font-size:0.7rem;color:var(--primary-cyan);min-width:32px;text-align:right}.psn-trophy-mini-badges{display:flex;gap:6px}.trophy-mini-badge{display:inline-flex;align-items:center;gap:3px;font-size:0.65rem;font-weight:600}.tmb-platinum{color:var(--trophy-platinum)}.tmb-gold{color:var(--trophy-gold)}.tmb-silver{color:var(--trophy-silver)}.tmb-bronze{color:var(--trophy-bronze)}.psn-game-info-card{display:flex;gap:var(--spacing-lg);padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-game-boxart{width:180px;flex-shrink:0}.psn-game-boxart img{width:100%;border-radius:var(--radius-md);border:1px solid var(--card-border)}.psn-game-boxart-placeholder{width:100%;aspect-ratio:1;background:var(--bg-medium);border-radius:var(--radius-md);border:1px solid var(--card-border);display:flex;align-items:center;justify-content:center;font-size:4rem}.psn-game-meta{flex:1}.psn-game-badges{display:flex;gap:var(--spacing-sm);flex-wrap:wrap;margin-bottom:var(--spacing-md)}.psn-game-description{font-size:0.85rem;color:var(--text-secondary);line-height:1.6;margin-bottom:var(--spacing-md);max-height:100px;overflow:hidden}.psn-meta-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-md)}.psn-meta-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);margin-bottom:2px}.psn-meta-value{font-size:0.85rem;color:var(--text-secondary)}.psn-trophy-progress-card{padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg)}.psn-trophy-progress-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--spacing-md)}.psn-trophy-progress-title{font-family:var(--font-heading);font-size:1rem;font-weight:600;display:flex;align-items:center;gap:8px}.psn-trophy-progress-fraction{font-family:var(--font-mono);font-size:1rem;color:var(--text-secondary)}.psn-trophy-progress-pct{font-family:var(--font-heading);font-size:1.2rem;font-weight:700;color:var(--primary-cyan)}.psn-trophy-progress-bar-container{height:28px;background:rgba(26,26,62,0.8);border:2px solid var(--primary-cyan);border-radius:var(--radius-md);overflow:hidden;margin-bottom:var(--spacing-md);box-shadow:0 0 15px rgba(76,201,240,0.2)}.psn-trophy-progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--neon-purple));border-radius:var(--radius-md);transition:width 0.8s ease;display:flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.7)}.psn-trophy-type-breakdown{display:flex;justify-content:center;gap:var(--spacing-xl)}.psn-trophy-type-item{display:flex;align-items:center;gap:6px;font-size:0.85rem}.psn-trophy-type-icon{font-size:1.1rem}.psn-trophy-type-count{font-family:var(--font-mono)}.type-platinum .psn-trophy-type-count{color:var(--trophy-platinum)}.type-gold .psn-trophy-type-count{color:var(--trophy-gold)}.type-silver .psn-trophy-type-count{color:var(--trophy-silver)}.type-bronze .psn-trophy-type-count{color:var(--trophy-bronze)}.psn-info-panels-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.psn-info-panel{padding:var(--spacing-lg);background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg)}.psn-info-panel-title{font-family:var(--font-heading);font-size:0.9rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-md);display:flex;align-items:center;gap:8px}.psn-detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-sm) var(--spacing-lg)}.psn-detail-label{font-size:0.75rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.psn-detail-value{font-size:0.85rem;color:var(--text-secondary)}.psn-mono{font-family:var(--font-mono);font-size:0.8rem}.psn-hltb-input{width:100%;padding:8px 12px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.85rem;margin-bottom:var(--spacing-sm)}.psn-hltb-input:focus{outline:none;border-color:var(--primary-cyan)}.psn-trophy-lists-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-lg)}.psn-trophy-list-panel{background:rgba(20,26,35,0.8);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);overflow:hidden}.psn-trophy-list-header{padding:var(--spacing-md) var(--spacing-lg);border-bottom:1px solid var(--card-border);font-family:var(--font-heading);font-size:0.9rem;font-weight:600;text-transform:uppercase;letter-spacing:1px;display:flex;align-items:center;gap:8px}.psn-trophy-list-header.header-unlocked{color:var(--neon-green)}.psn-trophy-list-header.header-locked{color:var(--neon-gray)}.psn-trophy-list-body{max-height:600px;overflow-y:auto;padding:var(--spacing-sm)}.psn-trophy-list-body::-webkit-scrollbar{width:6px}.psn-trophy-list-body::-webkit-scrollbar-track{background:var(--bg-dark)}.psn-trophy-list-body::-webkit-scrollbar-thumb{background:var(--card-border);border-radius:3px}.psn-trophy-list-body::-webkit-scrollbar-thumb:hover{background:var(--primary-cyan)}.psn-trophy-entry{display:flex;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--radius-md);transition:background var(--transition-fast)}.psn-trophy-entry:hover{background:rgba(76,201,240,0.03)}.psn-trophy-entry + .psn-trophy-entry{border-top:1px solid rgba(42,53,66,0.5)}.psn-trophy-icon-box{width:48px;height:48px;border-radius:var(--radius-sm);background:var(--bg-medium);display:flex;align-items:center;justify-content:center;font-size:1.4rem;flex-shrink:0;border:1px solid var(--card-border);overflow:hidden}.psn-trophy-icon-box.locked{opacity:0.5;filter:grayscale(0.5)}.psn-trophy-icon-img{width:100%;height:100%;object-fit:cover}.psn-trophy-details{flex:1;min-width:0}.psn-trophy-name{font-size:0.85rem;font-weight:600;color:var(--text-primary);margin-bottom:2px}.psn-trophy-description{font-size:0.75rem;color:var(--text-muted);line-height:1.4}.psn-trophy-earned-info{display:flex;align-items:center;gap:var(--spacing-sm);margin-top:4px;flex-wrap:wrap}.psn-trophy-type-badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:3px;font-size:0.65rem;font-weight:700;text-transform:uppercase;letter-spacing:0.5px}.psn-trophy-type-badge.ttb-platinum{background:rgba(123,184,212,0.2);color:var(--trophy-platinum);border:1px solid rgba(123,184,212,0.3)}.psn-trophy-type-badge.ttb-gold{background:rgba(205,154,42,0.2);color:var(--trophy-gold);border:1px solid rgba(205,154,42,0.3)}.psn-trophy-type-badge.ttb-silver{background:rgba(168,168,168,0.2);color:var(--trophy-silver);border:1px solid rgba(168,168,168,0.3)}.psn-trophy-type-badge.ttb-bronze{background:rgba(205,127,50,0.2);color:var(--trophy-bronze);border:1px solid rgba(205,127,50,0.3)}.psn-trophy-rarity{display:inline-flex;align-items:center;gap:3px;font-size:0.65rem;color:var(--text-muted);padding:2px 6px;border-radius:3px;background:rgba(255,255,255,0.03)}.psn-trophy-rarity.rarity-ultra-rare{color:var(--neon-cyan)}.psn-trophy-rarity.rarity-very-rare{color:var(--neon-purple)}.psn-trophy-rarity.rarity-rare{color:var(--neon-orange)}.psn-trophy-rarity.rarity-common{color:var(--neon-gray)}.psn-trophy-earned-date{font-family:var(--font-mono);font-size:0.65rem;color:var(--text-muted);display:flex;align-items:center;gap:4px}.psn-trophy-earned-date .date-icon{font-size:0.6rem}.psn-dlc-separator{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);margin-top:var(--spacing-sm)}.psn-dlc-separator-line{flex:1;height:1px;background:var(--card-border)}.psn-dlc-separator-label{font-size:0.7rem;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--neon-purple);white-space:nowrap}.psn-trophy-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--spacing-xl);color:var(--text-muted);text-align:center}.psn-trophy-empty-icon{font-size:2rem;margin-bottom:var(--spacing-sm)}.psn-trophy-complete{color:var(--neon-green)}.psn-not-configured{text-align:center;padding:var(--spacing-2xl)}.psn-not-configured-icon{font-size:4rem;margin-bottom:var(--spacing-lg)}.psn-not-configured-title{font-family:var(--font-heading);font-size:1.5rem;color:var(--text-primary);margin-bottom:var(--spacing-md)}.psn-not-configured-text{color:var(--text-secondary);margin-bottom:var(--spacing-lg);max-width:500px;margin-left:auto;margin-right:auto}.psn-no-games{text-align:center;padding:var(--spacing-2xl);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-lg)}.psn-no-games-icon{font-size:3rem;margin-bottom:var(--spacing-md)}.psn-no-games-title{font-family:var(--font-heading);font-size:1.2rem;color:var(--text-primary);margin-bottom:var(--spacing-sm)}.psn-no-games-text{color:var(--text-muted)}.psn-visible-count{text-align:center;padding:var(--spacing-md);color:var(--text-muted);font-size:0.85rem}.achievement-overview{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg)}.achievement-stat{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md)}.achievement-stat-icon{font-size:2rem}.achievement-stat-info{display:flex;flex-direction:column}.achievement-stat-label{font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.achievement-stat-value{font-size:1.3rem;font-weight:600;color:var(--neon-cyan)}.achievements-page{--card-min:280px;--card-gap:var(--spacing-md)}.achievement-games-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.achievement-game-card{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md);text-decoration:none;color:inherit;transition:all 0.2s ease}.achievement-game-card:hover{border-color:var(--neon-cyan);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.3)}.achievement-game-icon{flex-shrink:0;width:80px;height:80px;border-radius:var(--radius-sm);overflow:hidden;background:var(--bg-darker)}.achievement-game-icon img{width:100%;height:100%;object-fit:cover}.achievement-game-card .no-boxart{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:2rem;background:var(--bg-darker)}.achievement-game-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.achievement-game-title{font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.achievement-game-system{font-size:0.8rem;color:var(--text-muted)}.achievement-game-progress{margin-top:auto}.achievement-progress-bar{height:6px;background:var(--bg-darker);border-radius:3px;overflow:hidden;margin-bottom:4px}.achievement-progress-fill{height:100%;background:linear-gradient(90deg,var(--neon-cyan),var(--neon-purple));border-radius:3px;transition:width 0.3s ease}.achievement-progress-fill.xbox-fill{background:linear-gradient(90deg,var(--neon-green),#14b873)}.achievement-progress-text{font-size:0.75rem;color:var(--text-muted)}.no-boxart-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:4rem;background:var(--bg-darker);color:var(--text-muted)}.trophy-type-label.gamerscore-earned{background:#ffd700;color:#111}.trophy-type-label.gamerscore-locked{background:var(--bg-lighter);color:var(--text-muted)}.stat-box{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);text-align:center;transition:all var(--transition-normal)}.stat-box:hover{border-color:var(--neon-cyan);box-shadow:0 0 20px rgba(76,201,240,0.2)}.stat-value{font-family:var(--font-heading);font-size:2.5rem;font-weight:700;color:var(--neon-cyan);line-height:1.2;text-shadow:0 0 20px rgba(76,201,240,0.5)}.stat-label{font-size:0.9rem;color:var(--text-secondary);margin-top:0.5rem;text-transform:uppercase;letter-spacing:0.5px}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.stat-card{background:linear-gradient(135deg,var(--card-bg) 0%,var(--bg-medium) 100%);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg);position:relative;overflow:hidden;transition:all var(--transition-normal)}.stat-card::before{content:'';position:absolute;top:0;left:0;width:100%;height:3px;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta))}.stat-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-xl);border-color:var(--primary-cyan)}.stat-card-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:var(--spacing-md)}.stat-card .stat-label{font-size:0.75rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:0}.stat-icon{width:40px;height:40px;background:linear-gradient(135deg,rgba(76,201,240,0.2) 0%,rgba(247,37,133,0.2) 100%);border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:1.2rem;color:var(--primary-cyan)}.stat-card .stat-value{font-family:var(--font-heading);font-size:3rem;font-weight:700;color:var(--text-primary);line-height:1;margin-bottom:var(--spacing-xs);text-shadow:none}.stat-subtitle{font-size:0.85rem;color:var(--text-secondary)}.stat-card.warning .stat-icon{background:linear-gradient(135deg,rgba(255,171,0,0.2) 0%,rgba(255,143,0,0.2) 100%);color:var(--status-warning)}.stat-card.warning::before{background:var(--status-warning)}.stat-card.success .stat-icon{background:linear-gradient(135deg,rgba(0,230,118,0.2) 0%,rgba(0,200,83,0.2) 100%);color:var(--status-success)}.stat-card.success::before{background:var(--status-success)}.psn-stats-bar{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.psn-stat-box{padding:var(--spacing-md);background:rgba(20,26,35,0.7);backdrop-filter:blur(8px);border:1px solid var(--card-border);border-radius:var(--radius-md);text-align:center}.psn-stat-icon{font-size:1.5rem;margin-bottom:var(--spacing-xs)}.psn-stat-value{font-family:var(--font-heading);font-size:1.25rem;font-weight:700}.psn-stat-value.platinum{color:var(--trophy-platinum)}.psn-stat-value.gold{color:var(--trophy-gold)}.psn-stat-value.silver{color:var(--trophy-silver)}.psn-stat-value.bronze{color:var(--trophy-bronze)}.psn-stat-label{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)}.stat-box.warning .stat-value{color:var(--neon-orange)}.stat-box.danger .stat-value{color:var(--neon-red)}.stat-box.success .stat-value{color:var(--neon-green)}.filter-bar{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-xl);flex-wrap:wrap}.search-box{--search-icon-offset:var(--spacing-md);--search-icon-size:1.15rem;--search-icon-width:2rem;--search-icon-gap:var(--spacing-sm);flex:1;min-width:250px;position:relative}.search-icon{position:absolute;left:var(--search-icon-offset);top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:var(--search-icon-size)}input.search-input{width:100%;padding:var(--spacing-md) var(--spacing-lg);padding-left:calc(var(--search-icon-offset) + var(--search-icon-width) + var(--search-icon-gap));background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;transition:all var(--transition-fast)}input.search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 3px rgba(76,201,240,0.1)}input.search-input::placeholder{color:var(--text-muted)}.filter-select{padding:var(--spacing-md) var(--spacing-lg);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.95rem;cursor:pointer;transition:all var(--transition-fast);min-width:150px}.filter-select:focus{outline:none;border-color:var(--primary-cyan)}.platform-filters{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);flex-wrap:wrap}.platform-filter-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;background:rgba(20,26,35,0.8);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.85rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast)}.platform-filter-btn:hover{border-color:var(--primary-cyan);color:var(--text-primary)}.platform-filter-btn.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.filter-count{font-size:0.7rem;padding:2px 6px;background:rgba(255,255,255,0.1);border-radius:var(--radius-full)}.platform-filter-btn.active .filter-count{background:rgba(76,201,240,0.2)}.bulk-filter-section{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.bulk-action-section{padding:var(--spacing-lg);background:var(--bg-dark);border-radius:var(--radius-md);border:1px solid var(--card-border)}.bulk-preview-count{font-size:1.5rem;font-weight:700;color:var(--primary-cyan);margin-bottom:var(--spacing-sm)}.bulk-game-list{max-height:300px;overflow-y:auto;margin-top:var(--spacing-md)}.bulk-game-item{display:flex;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.bulk-game-item:last-child{border-bottom:none}.psn-search-sort-row{display:flex;gap:var(--spacing-md);margin-bottom:var(--spacing-lg);align-items:center}.psn-search-wrapper{flex:1;position:relative}.psn-search-wrapper .search-input{width:100%;padding-left:calc(var(--search-icon-offset, var(--spacing-md)) + var(--search-icon-width, 2rem) + var(--search-icon-gap, var(--spacing-sm)))}.psn-sort-select{padding:var(--spacing-sm) var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;cursor:pointer}.psn-sort-select:focus{outline:none;border-color:var(--primary-cyan)}.hltb-card{background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-md)}.hltb-card-title{display:flex;align-items:center;gap:var(--spacing-sm);font-family:var(--font-primary);font-size:1rem;font-weight:600;color:var(--text-primary);margin-bottom:var(--spacing-md)}.hltb-search-input{width:100%;padding:10px 14px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;margin-bottom:var(--spacing-sm)}.hltb-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.btn-hltb{width:100%;display:flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:10px 16px;background:rgba(76,201,240,0.1);border:1px solid var(--primary-cyan);border-radius:var(--radius-md);color:var(--primary-cyan);font-family:var(--font-primary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.btn-hltb:hover{background:rgba(76,201,240,0.2)}.btn-hltb:disabled{opacity:0.5;cursor:not-allowed}.hltb-results{margin-top:var(--spacing-md)}.hltb-match-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--spacing-xs);padding-bottom:var(--spacing-sm);margin-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.hltb-match-title{color:var(--primary-cyan);font-weight:600;font-size:0.9rem}.hltb-match-platform{color:var(--text-secondary);font-size:0.8rem}.hltb-match-year{color:var(--neon-orange);font-size:0.8rem;font-weight:500}.hltb-match-developer{color:var(--text-secondary);font-size:0.8rem;font-style:italic}.hltb-match-warning{color:var(--neon-orange);font-size:0.8rem;font-weight:600}.hltb-match-confidence{color:var(--accent-green);font-size:0.8rem;font-weight:500}.hltb-match-alt-notice{display:inline-flex;align-items:center;gap:0.25rem;color:var(--neon-orange);background:rgba(245,158,11,0.12);border:1px solid rgba(245,158,11,0.4);padding:2px 8px;border-radius:var(--radius-full);font-size:0.75rem;font-weight:600;cursor:help;white-space:nowrap}.hltb-time-row{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-sm) 0;border-bottom:1px solid var(--card-border)}.hltb-time-row:last-child{border-bottom:none}.hltb-label{color:var(--text-secondary);font-size:0.85rem}.hltb-value{color:var(--text-primary);font-size:0.85rem;font-weight:500}.hltb-value.highlight{color:var(--primary-cyan)}.hltb-actions{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-md)}.hltb-actions .btn{flex:1}.hltb-empty{color:var(--text-muted);font-size:0.9rem;padding:var(--spacing-sm) 0}.hltb-loading{display:flex;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary);font-size:0.9rem;padding:var(--spacing-sm) 0}.hltb-compact .hltb-time-row{padding:var(--spacing-xs) 0}.hltb-compact .hltb-label,.hltb-compact .hltb-value{font-size:0.8rem}.manual-reader{display:flex;flex-direction:column;width:min(96vw,1400px);height:94vh;background:var(--bg-medium);border:1px solid var(--card-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl)}.manual-reader-header{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);border-bottom:1px solid var(--card-border)}.manual-reader-header h2{flex:1;min-width:0;margin:0;font-family:var(--font-heading);font-size:1.1rem;color:var(--neon-cyan);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.manual-reader-stage{flex:1;min-height:0;display:grid;place-items:center;padding:var(--spacing-sm);overflow:hidden;perspective:2400px;touch-action:pan-y;user-select:none}.manual-reader-stage:focus-visible{outline:2px solid var(--primary-cyan);outline-offset:-2px}.manual-reader-sheet{grid-area:1 / 1;max-width:100%;max-height:100%;box-shadow:var(--shadow-lg);transform-origin:left center;backface-visibility:hidden}.manual-reader-sheet.turn-out{z-index:1;animation:manual-turn-out 0.6s ease-in-out forwards}.manual-reader-sheet.turn-in{z-index:1;animation:manual-turn-in 0.6s ease-in-out forwards}@keyframes manual-turn-out{from{transform:rotateY(0deg)}to{transform:rotateY(-180deg)}}@keyframes manual-turn-in{from{transform:rotateY(-180deg)}to{transform:rotateY(0deg)}}@media (prefers-reduced-motion:reduce){.manual-reader-sheet.turn-out,.manual-reader-sheet.turn-in{animation:none}}.manual-reader-message{grid-area:1 / 1;color:var(--text-secondary);text-align:center}.manual-reader-controls{display:flex;align-items:center;justify-content:center;gap:var(--spacing-lg);padding:var(--spacing-sm);border-top:1px solid var(--card-border)}.manual-reader-controls .btn{white-space:nowrap}.manual-reader-page{min-width:8em;text-align:center;color:var(--text-primary);font-family:var(--font-mono)}@media (max-width:600px){.manual-reader{width:100vw;height:100dvh;border-radius:0}.manual-reader-header .btn{font-size:0.75rem}}.score-badge.score-good{background:rgba(34,197,94,0.15);border-color:var(--neon-green);color:var(--neon-green)}.score-badge.score-mixed{background:rgba(234,179,8,0.15);border-color:var(--neon-orange);color:var(--neon-orange)}.score-badge.score-bad{background:rgba(239,68,68,0.15);border-color:var(--neon-red);color:var(--neon-red)}.bulk-option-label{display:flex;align-items:center;gap:6px;padding:6px 12px;background:rgba(168,85,247,0.1);border:1px solid var(--neon-purple);border-radius:var(--radius-md);color:var(--neon-purple);font-size:0.85rem;cursor:pointer;transition:all var(--transition-fast)}.bulk-option-label:hover{background:rgba(168,85,247,0.2)}.bulk-option-label input[type="checkbox"]{width:16px;height:16px;accent-color:var(--neon-purple);cursor:pointer}.bulk-mode-selector{display:flex;gap:8px;align-items:center}.scrape-mode-toggle{display:flex;align-items:center;gap:12px;padding:6px 16px;background:rgba(100,116,139,0.1);border:1px solid var(--neon-gray);border-radius:var(--radius-md)}.scrape-mode-toggle .mode-label{font-size:0.85rem;font-weight:500;transition:all var(--transition-fast)}.scrape-mode-toggle .mode-label.mode-off{color:var(--text-muted)}.scrape-mode-toggle .mode-label.mode-on{color:var(--text-muted)}.scrape-mode-toggle:has(input:not(:checked)) .mode-off{color:var(--neon-orange);text-shadow:0 0 8px rgba(245,158,11,0.5)}.scrape-mode-toggle:has(input:checked) .mode-on{color:var(--neon-cyan);text-shadow:0 0 8px rgba(76,201,240,0.5)}.keep-media-option{display:flex;align-items:center;gap:6px;font-size:0.85rem;color:var(--text-muted);cursor:pointer}.bulk-mode-selector:has(#scrapeModeToggle:checked) .keep-media-option{display:none}.scrape-mode-toggle .toggle-switch{position:relative;display:inline-block;width:44px;height:24px;flex-shrink:0}.scrape-mode-toggle .toggle-switch input{opacity:0;width:0;height:0}.scrape-mode-toggle .toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:var(--neon-orange);border-radius:24px;transition:all var(--transition-normal);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(245,158,11,0.3)}.scrape-mode-toggle .toggle-slider:before{position:absolute;content:"";height:18px;width:18px;left:3px;bottom:3px;background:white;border-radius:50%;transition:all var(--transition-normal);box-shadow:0 2px 4px rgba(0,0,0,0.3)}.scrape-mode-toggle .toggle-switch input:checked + .toggle-slider{background:var(--neon-cyan);box-shadow:inset 0 2px 4px rgba(0,0,0,0.3),0 0 10px rgba(76,201,240,0.3)}.scrape-mode-toggle .toggle-switch input:checked + .toggle-slider:before{transform:translateX(20px)}.fanart-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;background-size:cover;background-position:center;background-repeat:no-repeat;opacity:0;transition:opacity 0.5s ease,background-image 0.5s ease;pointer-events:none}.fanart-background::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient( to bottom,rgba(10,14,20,0.7) 0%,rgba(10,14,20,0.85) 50%,rgba(10,14,20,0.95) 100% )}.fanart-background.active{opacity:1}.glass-panel{background:rgba(20,28,40,0.7);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(76,201,240,0.2)}.glass-card{background:rgba(20,28,40,0.5);backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px)}.glitch-text{position:relative}.glitch-text::before,.glitch-text::after{content:attr(data-text);position:absolute;top:0;left:0;width:100%;height:100%}.glitch-text::before{left:2px;text-shadow:-2px 0 var(--primary-magenta);clip:rect(24px,550px,90px,0);animation:glitch-anim 3s infinite linear alternate-reverse}.glitch-text::after{left:-2px;text-shadow:-2px 0 var(--primary-cyan);clip:rect(85px,550px,140px,0);animation:glitch-anim2 3s infinite linear alternate-reverse}@keyframes glitch-anim{0%{clip:rect(20px,9999px,40px,0)}25%{clip:rect(60px,9999px,80px,0)}50%{clip:rect(10px,9999px,30px,0)}75%{clip:rect(70px,9999px,90px,0)}100%{clip:rect(30px,9999px,50px,0)}}@keyframes glitch-anim2{0%{clip:rect(80px,9999px,100px,0)}25%{clip:rect(20px,9999px,40px,0)}50%{clip:rect(50px,9999px,70px,0)}75%{clip:rect(10px,9999px,30px,0)}100%{clip:rect(60px,9999px,80px,0)}}.scan-lines{position:absolute;top:0;left:0;width:100%;height:100%;background:repeating-linear-gradient( 0deg,rgba(0,0,0,0.1),rgba(0,0,0,0.1) 1px,transparent 1px,transparent 2px );pointer-events:none;opacity:0;transition:opacity var(--transition-normal)}.game-card-new:hover .scan-lines{opacity:0.3}.card-glow{position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;background:linear-gradient(45deg,var(--primary-cyan),var(--primary-magenta),var(--primary-cyan));border-radius:calc(var(--radius-lg) + 2px);z-index:-1;opacity:0;filter:blur(8px);transition:opacity var(--transition-normal)}.game-card-new:hover .card-glow{opacity:0.5;animation:glow-pulse 2s ease-in-out infinite}@keyframes glow-pulse{0%,100%{opacity:0.3}50%{opacity:0.6}}.filter-bar-new{display:flex;gap:var(--spacing-md);align-items:center;flex-wrap:wrap;padding:var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);position:sticky;top:var(--sticky-top);z-index:100;background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.search-box-integrated{--search-icon-offset:12px;--search-icon-size:1rem;--search-icon-width:1.75rem;--search-icon-gap:var(--spacing-sm);position:relative;min-width:200px;max-width:350px;flex:1}.search-icon-inside{position:absolute;left:var(--search-icon-offset);top:50%;transform:translateY(-50%);font-size:var(--search-icon-size);pointer-events:none;z-index:1}.search-input-integrated{width:100%;padding:var(--spacing-sm) var(--spacing-md);padding-left:calc(var(--search-icon-offset) + var(--search-icon-width) + var(--search-icon-gap));padding-right:32px;background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.95rem;outline:none;transition:border-color var(--transition-fast)}.search-clear-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text-muted);font-size:1.2rem;cursor:pointer;padding:0 4px;line-height:1;opacity:0;transition:opacity 0.2s,color 0.2s;z-index:1}.search-clear-btn:hover{color:var(--primary-cyan)}.search-box-integrated:has(.search-input-integrated:not(:placeholder-shown)) .search-clear-btn{opacity:1}.search-input-integrated:focus{border-color:var(--primary-cyan)}.search-input-integrated::placeholder{color:var(--text-muted)}.filter-buttons{display:flex;gap:var(--spacing-sm);flex-wrap:wrap}.filter-btn{display:flex;align-items:center;gap:var(--spacing-xs);padding:var(--spacing-sm) var(--spacing-md);background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast);font-size:0.85rem}.filter-btn:hover{border-color:var(--primary-cyan);color:var(--primary-cyan);background:rgba(76,201,240,0.1)}.filter-btn:disabled{opacity:0.35;cursor:default;pointer-events:none}.filter-btn.active{border-color:var(--primary-cyan);background:rgba(76,201,240,0.2);color:var(--primary-cyan)}.filter-btn.clear-btn{border-color:var(--status-error);color:var(--status-error)}.filter-btn.clear-btn:hover{background:rgba(255,107,107,0.2)}.filter-btn.ra-filter-btn{border-color:rgba(255,215,0,0.4);color:var(--trophy-display-gold)}.filter-btn.ra-filter-btn:hover{border-color:var(--trophy-display-gold);background:rgba(255,215,0,0.15)}.filter-btn.ra-filter-btn.active{border-color:var(--trophy-display-gold);background:rgba(255,215,0,0.25);color:var(--trophy-display-gold);box-shadow:0 0 10px rgba(255,215,0,0.2)}.filter-btn.bonus-filter-btn{border-color:rgba(245,158,11,0.4);color:var(--neon-orange)}.filter-btn.bonus-filter-btn:hover{border-color:var(--neon-orange);background:rgba(245,158,11,0.15)}.filter-btn.bonus-filter-btn.active{border-color:var(--neon-orange);background:rgba(245,158,11,0.25);color:var(--neon-orange);box-shadow:0 0 10px rgba(245,158,11,0.2)}.filter-btn.refresh-ra-btn{border-color:rgba(76,201,240,0.4);color:var(--primary-cyan)}.filter-btn.refresh-ra-btn:hover{border-color:var(--primary-cyan);background:rgba(76,201,240,0.15)}.filter-btn.refresh-ra-btn:disabled{opacity:0.6;cursor:not-allowed}.filter-tag.ra-tag{background:rgba(255,215,0,0.2);border-color:rgba(255,215,0,0.4);color:var(--trophy-display-gold)}.filter-bar-top{display:flex;align-items:center;gap:var(--spacing-md);width:100%}.bulk-controls{display:flex;gap:var(--spacing-sm);align-items:center;margin-left:auto;flex-wrap:wrap;justify-content:flex-end}.bulk-btn{border-color:rgba(147,51,234,0.4);color:var(--neon-purple)}.bulk-btn:hover{border-color:var(--neon-purple);background:rgba(147,51,234,0.15)}.bulk-btn.active{border-color:var(--neon-purple);background:rgba(147,51,234,0.25);color:var(--neon-purple)}.bulk-action-btn{border-color:rgba(59,130,246,0.4);color:var(--neon-blue)}.bulk-action-btn:hover{border-color:var(--neon-blue);background:rgba(59,130,246,0.15)}.bulk-scrape-btn{border-color:rgba(34,197,94,0.4);color:var(--neon-green)}.bulk-scrape-btn:hover{border-color:var(--neon-green);background:rgba(34,197,94,0.15)}.game-card-wrapper{position:relative}.bulk-select-checkbox{position:absolute;top:8px;right:8px;z-index:20}.bulk-select-checkbox input[type="checkbox"]{width:22px;height:22px;cursor:pointer;accent-color:var(--primary-cyan)}.game-card-wrapper.selected .game-card-new{border-color:var(--primary-cyan);box-shadow:0 0 15px rgba(76,201,240,0.4)}.bulk-scrape-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:3000;align-items:center;justify-content:center}.bulk-scrape-modal.active{display:flex}.bulk-scrape-content{width:90%;max-width:500px;border-radius:var(--radius-lg);overflow:hidden}.bulk-scrape-header{padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);display:flex;justify-content:space-between;align-items:center}.bulk-scrape-header h2{margin:0;color:var(--primary-cyan)}.bulk-scrape-body{padding:var(--spacing-lg)}.bulk-progress-info{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md)}.bulk-progress-text{flex:1;overflow:hidden}#bulkCurrentGame{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text-primary);font-weight:500}.bulk-progress-numbers{color:var(--primary-cyan);font-family:var(--font-mono);font-size:1.1rem;margin-left:var(--spacing-md)}.bulk-progress-bar{width:100%;height:20px;background:var(--bg-medium);border-radius:10px;margin-bottom:var(--spacing-md);border:2px solid rgba(76,201,240,0.5);box-shadow:inset 0 2px 4px rgba(0,0,0,0.5);overflow:hidden;padding:2px}.bulk-progress-fill{display:block;height:100%;width:var(--progress,0%);min-width:0;max-width:100%;background:linear-gradient(90deg,var(--primary-cyan),var(--secondary-magenta));border-radius:8px;box-shadow:0 0 10px rgba(76,201,240,0.5)}.bulk-status{text-align:center;color:var(--text-secondary);font-size:0.9rem;margin-bottom:var(--spacing-md)}.bulk-controls-row{display:flex;justify-content:center;gap:var(--spacing-md);margin-top:var(--spacing-lg);margin-bottom:var(--spacing-md);padding-top:var(--spacing-sm)}.bulk-results{display:flex;justify-content:space-around;padding:var(--spacing-md);background:rgba(0,0,0,0.3);border-radius:var(--radius-md);margin-top:var(--spacing-md)}.bulk-result-item{display:flex;align-items:center;gap:var(--spacing-xs);font-size:0.9rem}.bulk-result-item.success{color:var(--neon-green)}.bulk-result-item.failed{color:var(--neon-red)}.bulk-result-item.skipped{color:var(--neon-orange)}.bulk-scrape-footer{padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border);display:flex;justify-content:center}.active-filter-tags{display:flex;gap:var(--spacing-sm);flex:1;min-width:0;overflow-x:auto;scrollbar-width:thin}.active-filter-tags .filter-tag{flex-shrink:0;white-space:nowrap}.filter-tag{display:flex;align-items:center;gap:var(--spacing-xs);padding:4px 10px;background:var(--primary-cyan);color:var(--bg-dark);border-radius:var(--radius-sm);font-size:0.8rem;font-weight:600}.filter-tag-exclude{background:rgba(239,68,68,0.2);border:1px solid rgba(239,68,68,0.5);color:var(--neon-red)}.filter-tag-remove{cursor:pointer;opacity:0.7;transition:opacity 0.2s}.filter-tag-remove:hover{opacity:1}.alphabet-nav{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md) var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);position:sticky;top:calc(var(--sticky-top) + 80px);z-index:99}.alphabet-label{color:var(--text-muted);font-size:0.85rem;white-space:nowrap}.alphabet-letters{display:flex;flex-wrap:wrap;gap:4px}.alphabet-btn{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.alphabet-btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.active{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.disabled{opacity:0.3;cursor:not-allowed}.alphabet-btn.disabled:hover{background:rgba(76,201,240,0.1);border-color:rgba(76,201,240,0.3);color:var(--text-secondary)}.cards-page{--card-min:380px;--card-gap:var(--spacing-lg);max-width:calc(4 * 480px + 3 * var(--card-gap));margin-inline:auto}.games-grid-new{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--card-min,380px),1fr));grid-auto-rows:1px;column-gap:var(--spacing-lg)}.game-card-wrapper{grid-row-end:span 296;align-self:start;transition:transform var(--transition-normal);contain:layout style paint;content-visibility:auto;contain-intrinsic-size:auto 380px 296px}.game-card-new{position:relative;display:flex;flex-direction:row;border-radius:var(--radius-lg);overflow:hidden;text-decoration:none;transition:all var(--transition-normal);border:1px solid transparent;min-height:240px}.game-card-wrapper:hover{transform:translateY(-4px)}.game-card-new:hover{box-shadow:0 8px 30px rgba(76,201,240,0.2)}.game-card-wrapper.hidden{display:none}.game-card-new.hidden{display:none}.card-boxart-section{flex-shrink:0;width:135px;align-self:stretch;background:var(--bg-medium);display:flex;align-items:center;justify-content:center;position:relative}.card-play-btn{position:absolute;top:var(--spacing-sm);right:var(--spacing-sm);z-index:5;width:28px;height:28px;border-radius:var(--radius-full);border:1px solid var(--primary-cyan);background:var(--bg-darkest);color:var(--primary-cyan);font-size:0.8rem;line-height:1;cursor:pointer;box-shadow:var(--shadow-md);transition:background var(--transition-fast),color var(--transition-fast),box-shadow var(--transition-fast)}.card-play-btn{display:flex;align-items:center;justify-content:center;padding:0}.card-play-btn svg{width:60%;height:60%;fill:currentColor}.card-play-btn:hover,.card-play-btn:focus-visible{background:var(--primary-cyan);color:var(--bg-darkest);box-shadow:var(--shadow-glow-cyan)}.card-play-btn:disabled{cursor:progress}.games-grid-new.bulk-mode .card-play-btn{display:none}.game-card-new.has-play .card-info-section >:first-child{padding-right:calc(28px + var(--spacing-sm))}.card-info-section{flex:1;padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-sm);background:rgba(0,0,0,0.4)}.title-row{display:flex;align-items:flex-start;gap:var(--spacing-sm)}.title-row .game-card-title{flex:1}.game-card-ra-badge{position:absolute;top:8px;left:8px;width:28px;height:28px;background:linear-gradient(135deg,var(--trophy-display-gold) 0%,#ffaa00 100%);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.9rem;z-index:10;box-shadow:0 2px 8px rgba(255,215,0,0.4);border:2px solid rgba(255,255,255,0.3)}.game-card-rating-badge{position:absolute;top:8px;right:8px;z-index:10}.rating-text-badge{font-size:0.65rem;padding:2px 6px;border-radius:var(--radius-sm);font-weight:700;text-transform:uppercase}.rating-text-badge.esrb{background:rgba(0,0,0,0.7);color:var(--text-on-color);border:1px solid var(--text-on-color)}.rating-text-badge.pegi{background:rgba(141,198,63,0.9);color:var(--text-on-color);border:1px solid var(--text-on-color)}.system-badge-row{display:flex;align-items:center;gap:var(--spacing-sm)}.system-badge{padding:3px 8px;background:rgba(76,201,240,0.2);border:1px solid var(--primary-cyan);border-radius:var(--radius-sm);font-size:0.65rem;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:0.5px}.system-badge.nes{background:rgba(198,40,40,0.25);border-color:#e53935;color:#e53935}.system-badge.snes{background:rgba(229,57,53,0.2);border-color:#ef5350;color:#ef5350}.system-badge.n64{background:rgba(244,67,54,0.2);border-color:#f44336;color:#f44336}.system-badge.gc,.system-badge.gamecube{background:rgba(103,58,183,0.2);border-color:#9575cd;color:#9575cd}.system-badge.wii{background:rgba(255,255,255,0.15);border-color:#e0e0e0;color:#e0e0e0}.system-badge.wiiu{background:rgba(33,150,243,0.2);border-color:#42a5f5;color:#42a5f5}.system-badge.switch{background:rgba(229,57,53,0.2);border-color:#ef5350;color:#ef5350}.system-badge.gb{background:rgba(139,195,74,0.25);border-color:#9ccc65;color:#9ccc65}.system-badge.gbc{background:rgba(156,39,176,0.2);border-color:#ba68c8;color:#ba68c8}.system-badge.gba{background:rgba(103,58,183,0.2);border-color:#7e57c2;color:#7e57c2}.system-badge.nds{background:rgba(158,158,158,0.2);border-color:#bdbdbd;color:#bdbdbd}.system-badge.3ds{background:rgba(229,57,53,0.2);border-color:#e57373;color:#e57373}.system-badge.virtualboy,.system-badge.vb{background:rgba(183,28,28,0.3);border-color:#c62828;color:#ef5350}.system-badge.fds{background:rgba(255,193,7,0.2);border-color:#ffc107;color:#ffc107}.system-badge.psx,.system-badge.ps1{background:rgba(144,202,249,0.25);border-color:#90caf9;color:#90caf9}.system-badge.ps2{background:rgba(33,150,243,0.2);border-color:#42a5f5;color:#42a5f5}.system-badge.ps3{background:rgba(21,101,192,0.25);border-color:#1976d2;color:#64b5f6}.system-badge.ps4{background:rgba(13,71,161,0.3);border-color:#1565c0;color:#42a5f5}.system-badge.psp{background:rgba(100,181,246,0.2);border-color:#64b5f6;color:#64b5f6}.system-badge.psvita,.system-badge.vita{background:rgba(30,136,229,0.2);border-color:#1e88e5;color:#42a5f5}.system-badge.sg-1000,.system-badge.sg1000{background:rgba(0,77,64,0.3);border-color:#00897b;color:#4db6ac}.system-badge.mastersystem,.system-badge.sms{background:rgba(0,121,107,0.25);border-color:#00897b;color:#4db6ac}.system-badge.genesis,.system-badge.megadrive{background:rgba(0,150,136,0.2);border-color:#26a69a;color:#26a69a}.system-badge.segacd,.system-badge.megacd{background:rgba(0,137,123,0.25);border-color:#00897b;color:#4db6ac}.system-badge.sega32x,.system-badge.32x{background:rgba(0,105,92,0.3);border-color:#00796b;color:#4db6ac}.system-badge.saturn{background:rgba(128,203,196,0.2);border-color:#80cbc4;color:#80cbc4}.system-badge.dreamcast,.system-badge.dc{background:rgba(255,152,0,0.2);border-color:#ffb74d;color:#ffb74d}.system-badge.gamegear,.system-badge.gg{background:rgba(0,188,212,0.2);border-color:#4dd0e1;color:#4dd0e1}.system-badge.atari2600{background:rgba(230,81,0,0.25);border-color:#ff6d00;color:#ffab40}.system-badge.atari5200{background:rgba(255,111,0,0.2);border-color:#ff9100;color:#ffab40}.system-badge.atari7800{background:rgba(255,143,0,0.2);border-color:#ffa000;color:#ffca28}.system-badge.atarilynx,.system-badge.lynx{background:rgba(255,193,7,0.2);border-color:#ffc107;color:#ffc107}.system-badge.atarijaguar,.system-badge.jaguar{background:rgba(255,87,34,0.2);border-color:#ff7043;color:#ff7043}.system-badge.atarist,.system-badge.st{background:rgba(121,85,72,0.25);border-color:#a1887f;color:#bcaaa4}.system-badge.arcade{background:rgba(233,30,99,0.2);border-color:#ec407a;color:#ec407a}.system-badge.mame{background:rgba(233,30,99,0.2);border-color:#ec407a;color:#ec407a}.system-badge.fbneo{background:rgba(233,30,99,0.2);border-color:#f06292;color:#f06292}.system-badge.cps1,.system-badge.cps2,.system-badge.cps3{background:rgba(216,27,96,0.2);border-color:#e91e63;color:#f48fb1}.system-badge.naomi{background:rgba(194,24,91,0.25);border-color:#d81b60;color:#f48fb1}.system-badge.neogeo{background:rgba(255,193,7,0.25);border-color:#ffc107;color:#ffc107}.system-badge.neogeocd{background:rgba(255,179,0,0.2);border-color:#ffb300;color:#ffc107}.system-badge.ngp{background:rgba(255,213,79,0.2);border-color:#ffd54f;color:#ffd54f}.system-badge.ngpc{background:rgba(255,224,130,0.2);border-color:#ffe082;color:#ffe082}.system-badge.pcengine,.system-badge.tg16{background:rgba(156,39,176,0.2);border-color:#ab47bc;color:#ce93d8}.system-badge.pcenginecd,.system-badge.tg-cd{background:rgba(142,36,170,0.2);border-color:#9c27b0;color:#ce93d8}.system-badge.supergrafx{background:rgba(171,71,188,0.2);border-color:#ba68c8;color:#ce93d8}.system-badge.pcfx{background:rgba(123,31,162,0.25);border-color:#8e24aa;color:#ce93d8}.system-badge.c64{background:rgba(121,85,72,0.25);border-color:#8d6e63;color:#bcaaa4}.system-badge.amiga{background:rgba(141,110,99,0.2);border-color:#a1887f;color:#d7ccc8}.system-badge.vic20{background:rgba(93,64,55,0.3);border-color:#6d4c41;color:#a1887f}.system-badge.xbox{background:rgba(76,175,80,0.2);border-color:#66bb6a;color:#81c784}.system-badge.xbox360{background:rgba(56,142,60,0.25);border-color:#43a047;color:#81c784}.system-badge.3do{background:rgba(255,215,0,0.2);border-color:#ffd700;color:#ffd700}.system-badge.philipscdi,.system-badge.cdi{background:rgba(255,193,7,0.15);border-color:#ffca28;color:#ffca28}.system-badge.dos{background:rgba(96,125,139,0.2);border-color:#78909c;color:#90a4ae}.system-badge.pc{background:rgba(69,90,100,0.25);border-color:#607d8b;color:#90a4ae}.system-badge.pc88,.system-badge.pc98{background:rgba(84,110,122,0.2);border-color:#78909c;color:#b0bec5}.system-badge.msx{background:rgba(0,188,212,0.2);border-color:#00bcd4;color:#4dd0e1}.system-badge.msx2{background:rgba(0,172,193,0.2);border-color:#00acc1;color:#4dd0e1}.system-badge.wonderswan,.system-badge.ws{background:rgba(63,81,181,0.2);border-color:#5c6bc0;color:#7986cb}.system-badge.wonderswancolor,.system-badge.wsc{background:rgba(48,63,159,0.2);border-color:#3f51b5;color:#7986cb}.system-badge.pokemini{background:rgba(255,235,59,0.25);border-color:#ffee58;color:#fff176}.system-badge.nggage,.system-badge.ngage{background:rgba(255,112,67,0.2);border-color:#ff7043;color:#ff8a65}.system-badge.colecovision{background:rgba(183,28,28,0.2);border-color:#c62828;color:#ef5350}.system-badge.intellivision{background:rgba(255,152,0,0.2);border-color:#ff9800;color:#ffb74d}.system-badge.vectrex{background:rgba(255,255,255,0.2);border-color:#eeeeee;color:#eeeeee}.system-badge.scummvm{background:rgba(139,195,74,0.2);border-color:#8bc34a;color:#aed581}.system-badge.daphne{background:rgba(103,58,183,0.2);border-color:#7e57c2;color:#b39ddb}.title-badges{display:flex;gap:4px;flex-shrink:0}.title-badges .ra-badge{width:14px;height:14px;flex-shrink:0;object-fit:contain;filter:drop-shadow(0 0 4px rgba(255,193,7,0.5))}.import-badge.clz-badge{filter:drop-shadow(0 0 4px rgba(168,85,247,0.5))}.import-badge{width:14px;height:14px;flex-shrink:0;object-fit:contain}.import-badge.steam-badge{filter:drop-shadow(0 0 4px rgba(102,192,244,0.5))}.import-badge.xbox-badge{filter:drop-shadow(0 0 4px rgba(16,124,16,0.5))}.import-badge.psn-badge{filter:drop-shadow(0 0 4px rgba(0,55,145,0.5))}.bonus-badge{font-size:0.9rem;filter:drop-shadow(0 0 4px rgba(245,158,11,0.5));flex-shrink:0}.has-bonus-badge{font-size:0.75rem;background:rgba(168,85,247,0.2);border:1px solid var(--neon-purple);color:var(--neon-purple);padding:1px 4px;border-radius:4px;filter:drop-shadow(0 0 4px rgba(168,85,247,0.4));flex-shrink:0}.versions-badge{font-size:0.75rem;background:color-mix(in srgb,var(--neon-cyan) 20%,transparent);border:1px solid var(--neon-cyan);color:var(--neon-cyan);padding:1px 4px;border-radius:4px;flex-shrink:0}.game-card-wrapper.bonus-disc .game-card-new{border-left:3px solid var(--neon-orange);opacity:0.85}.game-card-wrapper.bonus-disc .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(245,158,11,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.clz-import .game-card-new{border-left:3px solid var(--neon-purple)}.game-card-wrapper.clz-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(168,85,247,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.steam-import .game-card-new{border-left:3px solid #66c0f4}.game-card-wrapper.steam-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(102,192,244,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.xbox-import .game-card-new{border-left:3px solid #107c10}.game-card-wrapper.xbox-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(16,124,16,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-wrapper.psn-import .game-card-new{border-left:3px solid #0070d1}.game-card-wrapper.psn-import .game-card-new::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(0,112,209,0.05) 0%,transparent 50%);pointer-events:none;z-index:0}.game-card-title{font-size:0.95rem;font-weight:600;color:var(--text-primary);margin:0;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 12px;flex:1}.meta-item{display:flex;flex-direction:column;gap:1px}.meta-label{font-size:0.6rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.meta-value{font-size:0.75rem;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meta-value.meta-clickable{cursor:pointer;padding:2px 6px;margin:-2px -6px;border-radius:var(--radius-sm);transition:all var(--transition-fast);display:inline-block}.meta-value.meta-clickable:hover{background:rgba(76,201,240,0.15);color:var(--neon-cyan)}.rating-badges{display:flex;gap:var(--spacing-sm);flex-wrap:wrap;margin-top:auto}.rating-badge{padding:3px 8px;border-radius:var(--radius-sm);font-size:0.65rem;font-weight:700}.rating-badge.esrb{background:linear-gradient(135deg,var(--bg-medium),#16213e);color:#fff;border:1px solid #4a4a6a}.rating-badge.pegi{background:linear-gradient(135deg,#0f3460,#16213e);color:var(--primary-cyan);border:1px solid var(--primary-cyan)}.game-badge.achievement-progress{gap:4px;font-size:0.65rem;font-weight:700;font-family:var(--font-mono)}.ach-icon{width:14px;height:14px;object-fit:contain;flex-shrink:0;filter:drop-shadow(0 0 3px rgba(255,255,255,0.3))}.game-badge.achievement-progress.ra{background:linear-gradient(135deg,rgba(74,144,217,0.12) 0%,rgba(212,160,23,0.12) 100%);border-image:linear-gradient(135deg,#4A90D9,#D4A017) 1;color:#D4A017}.game-badge.achievement-progress.ra.ach-complete{background:linear-gradient(135deg,rgba(74,144,217,0.2) 0%,rgba(212,160,23,0.2) 100%);color:#e8b820}.game-badge.achievement-progress.steam{background:linear-gradient(135deg,rgba(27,40,56,0.35) 0%,rgba(102,192,244,0.12) 100%);border-image:linear-gradient(135deg,#2A6DAB,#66C0F4) 1;color:#66C0F4}.game-badge.achievement-progress.steam.ach-complete{background:linear-gradient(135deg,rgba(27,40,56,0.45) 0%,rgba(102,192,244,0.2) 100%);color:#88d4f8}.game-badge.achievement-progress.xbox{background:linear-gradient(135deg,rgba(16,124,16,0.12) 0%,rgba(82,176,67,0.12) 100%);border-image:linear-gradient(135deg,#107C10,#52B043) 1;color:#52B043}.game-badge.achievement-progress.xbox.ach-complete{background:linear-gradient(135deg,rgba(16,124,16,0.2) 0%,rgba(82,176,67,0.2) 100%);color:#6dcc5e}.game-badge.achievement-progress.psn{background:linear-gradient(135deg,rgba(0,48,135,0.15) 0%,rgba(108,168,217,0.12) 100%);border-image:linear-gradient(135deg,#0070D1,#6CA8D9) 1;color:#6CA8D9}.game-badge.achievement-progress.psn.ach-complete{background:linear-gradient(135deg,rgba(0,48,135,0.25) 0%,rgba(108,168,217,0.2) 100%);color:#8dc0e8}.game-badge.achievement-progress.rpcs3{background:linear-gradient(135deg,rgba(74,122,140,0.12) 0%,rgba(168,208,224,0.1) 100%);border-image:linear-gradient(135deg,#4A7A8C,#A8D0E0) 1;color:#A8D0E0}.game-badge.achievement-progress.rpcs3.ach-complete{background:linear-gradient(135deg,rgba(74,122,140,0.2) 0%,rgba(168,208,224,0.18) 100%);color:#c0e0ec}.game-badge.achievement-progress.ach-low{opacity:0.7}.ach-bar{display:inline-block;width:30px;height:4px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden;vertical-align:middle}.ach-fill{display:block;height:100%;border-radius:2px;transition:width 0.3s ease}.ra .ach-fill{background:linear-gradient(90deg,#4A90D9,#D4A017)}.ra.ach-complete .ach-fill{box-shadow:0 0 4px #D4A017}.steam .ach-fill{background:linear-gradient(90deg,#2A6DAB,#66C0F4)}.steam.ach-complete .ach-fill{box-shadow:0 0 4px #66C0F4}.xbox .ach-fill{background:linear-gradient(90deg,#107C10,#52B043)}.xbox.ach-complete .ach-fill{box-shadow:0 0 4px #52B043}.psn .ach-fill{background:linear-gradient(90deg,#0070D1,#6CA8D9)}.psn.ach-complete .ach-fill{box-shadow:0 0 4px #6CA8D9}.rpcs3 .ach-fill{background:linear-gradient(90deg,#4A7A8C,#A8D0E0)}.rpcs3.ach-complete .ach-fill{box-shadow:0 0 4px #A8D0E0}.game-card-image{width:100%;height:100%;overflow:hidden;background:var(--bg-medium);display:flex;align-items:center;justify-content:center;position:absolute;top:0;left:0;right:0;bottom:0}.game-boxart-img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;transition:transform var(--transition-normal)}.game-boxart-img.boxart-3d{object-fit:contain;padding:4px}.game-card-new:hover .game-boxart-img{transform:scale(1.05)}.game-boxart-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--bg-medium),var(--bg-dark))}.placeholder-icon{font-size:3rem;opacity:0.3}.games-count-bar{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-md);min-height:3rem;margin-bottom:var(--spacing-lg);padding:var(--spacing-sm) 0;border-bottom:1px solid var(--card-border)}.showing-text{flex-shrink:0;font-size:0.85rem;color:var(--text-muted);letter-spacing:1px}.showing-text span{color:var(--primary-cyan);font-weight:600}.sort-control{display:flex;align-items:center;gap:var(--spacing-sm)}.sort-label{font-size:0.85rem;color:var(--text-muted);white-space:nowrap}.sort-select{padding:var(--spacing-xs) var(--spacing-sm);background-color:var(--bg-light);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-size:0.85rem;cursor:pointer}.sort-select:focus-visible{outline:2px solid var(--primary-cyan);outline-offset:1px}#gamesEmpty .clear-btn{display:inline-flex;margin-top:var(--spacing-md)}.alphabet-nav.is-disabled{opacity:0.35}.bulk-action-bar{position:sticky;bottom:var(--spacing-md);z-index:101;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:var(--spacing-md);margin-top:var(--spacing-lg);border-radius:var(--radius-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.bulk-action-bar[hidden]{display:none}.filters-toggle-btn{display:none}.filter-modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);z-index:2000;align-items:center;justify-content:center}.filter-modal.active{display:flex}.filter-modal-content{width:90%;max-width:500px;max-height:70vh;border-radius:var(--radius-lg);overflow:hidden;display:flex;flex-direction:column}.filter-modal-header{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-lg);border-bottom:1px solid var(--card-border);gap:var(--spacing-md)}.filter-modal-header h2{color:var(--primary-cyan);font-family:var(--font-heading);margin:0}.filter-mode-toggle{display:flex;border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden}.filter-mode-btn{padding:4px 12px;background:transparent;border:none;color:var(--text-secondary);cursor:pointer;font-size:0.85rem;font-weight:600;transition:all var(--transition-fast)}.filter-mode-btn:hover{color:var(--text-primary)}.filter-mode-btn.active[data-mode="include"]{background:rgba(76,201,240,0.2);color:var(--primary-cyan)}.filter-mode-btn.active[data-mode="exclude"]{background:rgba(239,68,68,0.2);color:var(--neon-red)}.filter-modal-body{padding:var(--spacing-lg);overflow-y:auto;flex:1}.filter-options{display:flex;flex-direction:column;gap:var(--spacing-sm)}.filter-option{display:flex;justify-content:space-between;align-items:center;padding:var(--spacing-md);background:rgba(0,0,0,0.3);border:1px solid var(--card-border);border-radius:var(--radius-md);cursor:pointer;transition:all var(--transition-fast)}.filter-option:hover{border-color:var(--primary-cyan);background:rgba(76,201,240,0.1)}.filter-modal.exclude-mode .filter-option:hover{border-color:var(--neon-red);background:rgba(239,68,68,0.1)}.filter-option-name{color:var(--text-primary)}.filter-option-count{color:var(--text-muted);font-size:0.85rem}button.filter-option{width:100%;font:inherit;color:inherit;text-align:left}.filter-option[aria-checked]::before{content:'';flex:0 0 auto;width:1.1rem;height:1.1rem;margin-right:var(--spacing-sm);border:1px solid var(--card-border);border-radius:var(--radius-sm);line-height:1.1rem;text-align:center}.filter-option[aria-checked] .filter-option-name{flex:1}.filter-option[aria-checked="true"]{border-color:var(--primary-cyan);background:rgba(76,201,240,0.15)}.filter-option[aria-checked="true"]::before{content:'✓';color:var(--primary-cyan);border-color:var(--primary-cyan)}.filter-modal.exclude-mode .filter-option[aria-checked="true"]{border-color:var(--neon-red);background:rgba(239,68,68,0.15)}.filter-modal.exclude-mode .filter-option[aria-checked="true"]::before{content:'✕';color:var(--neon-red);border-color:var(--neon-red)}.filter-modal-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-md);padding:var(--spacing-md) var(--spacing-lg);border-top:1px solid var(--card-border)}.filter-modal-footer[hidden]{display:none}.filter-modal-hint{color:var(--text-muted);font-size:0.85rem}.filter-modal .hint-exclude,.filter-modal.exclude-mode .hint-include{display:none}.filter-modal.exclude-mode .hint-exclude{display:inline}@media (max-width:768px){.games-grid-new{grid-template-columns:1fr;grid-auto-rows:auto;row-gap:var(--spacing-lg)}.game-card-wrapper{grid-row-end:auto}.game-card-new{min-height:180px}.card-boxart-section{width:100px}.meta-grid{grid-template-columns:1fr}.filter-bar-top{flex-wrap:wrap}.filter-buttons{width:100%;justify-content:flex-start}.filters-toggle-btn{display:flex}.filter-bar-new .filter-buttons{display:none;position:absolute;top:100%;left:0;right:0;margin-top:var(--spacing-xs);padding:var(--spacing-md);border:1px solid var(--card-border);border-radius:var(--radius-lg);background:var(--bg-medium);box-shadow:0 8px 24px rgba(0,0,0,0.5)}.filter-bar-new.filters-open .filter-buttons{display:flex}.games-count-bar{flex-wrap:wrap}.games-count-bar .active-filter-tags{order:3;flex-basis:100%;min-height:1.75rem}.filter-btn{flex:1;min-width:80px;justify-content:center}.bulk-controls{width:100%;justify-content:flex-start}.glitch-text::before,.glitch-text::after{display:none}}.game-page-header{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg);position:relative;z-index:1}.game-page-title{font-family:var(--font-display);font-size:2rem;color:var(--primary-cyan);text-shadow:0 0 30px rgba(76,201,240,0.3);margin:0}.header-buttons{display:flex;flex-wrap:wrap;gap:var(--spacing-sm)}.game-page-topbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);position:relative;z-index:1}.game-page-topbar .breadcrumb{margin-bottom:0;min-width:0;flex-wrap:wrap}.game-nav{display:flex;align-items:center;gap:var(--spacing-sm);margin-left:auto;font-size:0.9rem}.game-nav[hidden],.game-nav-link[hidden]{display:none}.game-nav-link{padding:var(--spacing-xs) var(--spacing-sm);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);text-decoration:none;white-space:nowrap;transition:color var(--transition-fast),border-color var(--transition-fast)}.game-nav-link:hover,.game-nav-link:focus-visible{color:var(--primary-cyan);border-color:var(--primary-cyan)}.game-nav-position{color:var(--text-muted);font-variant-numeric:tabular-nums}.play-key-hint{align-self:center;font-size:0.8rem;color:var(--text-secondary)}.last-scan-indicator{display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-md);font-size:0.8rem;color:var(--text-secondary)}.last-scan-indicator .scan-icon{font-size:1rem}.last-scan-indicator .scan-time{color:var(--primary-cyan);font-weight:500}.report-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.report-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1.25rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.report-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.report-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.report-tab .tab-count{font-size:0.75rem;padding:0.15rem 0.5rem;background:rgba(0,0,0,0.3);border-radius:var(--radius-full);color:var(--text-secondary)}.report-tab.active .tab-count{background:var(--primary-cyan);color:var(--bg-darkest)}.report-tab .tab-icon{font-size:1.1rem}.report-panel{display:none}.report-panel.active{display:block;animation:fadeIn 0.3s ease}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.results-table-container{background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-md);overflow:hidden}.settings-tabs{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:color-mix(in srgb,var(--card-bg) 90%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);border-radius:var(--radius-lg);position:sticky;top:var(--sticky-top);z-index:100}.settings-tab{flex:1 1 auto;display:flex;align-items:center;justify-content:center;gap:0.5rem;padding:0.75rem 1rem;background:transparent;border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-secondary);font-family:var(--font-primary);font-size:0.9rem;font-weight:500;cursor:pointer;transition:all var(--transition-fast);text-decoration:none;text-align:center;white-space:nowrap}.settings-tab:hover{background:rgba(76,201,240,0.05);border-color:rgba(76,201,240,0.3);color:var(--text-primary)}.settings-tab.active{background:rgba(76,201,240,0.15);border-color:var(--primary-cyan);color:var(--primary-cyan)}.settings-tab .tab-icon{font-size:1.1rem}.settings-tab-panel{display:none}.settings-tab-panel.active{display:block}.naming-tag-option{padding:0.25rem 0.6rem;border-radius:4px;font-size:0.8rem;font-weight:600;background:rgba(100,116,139,0.15);border:1px solid rgba(100,116,139,0.3);color:var(--text-secondary);cursor:pointer;transition:all 0.15s ease}.naming-tag-option:hover{border-color:var(--neon-green);color:var(--neon-green)}.parent-option{padding:var(--spacing-sm);border-bottom:1px solid var(--card-border);cursor:pointer}.parent-option:hover{background:rgba(76,201,240,0.1)}.tab-subnav{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-lg);padding:var(--spacing-sm);background:rgba(0,0,0,0.75);border-radius:var(--radius-md);position:sticky;top:calc(var(--sticky-top) + 54px);z-index:99;backdrop-filter:blur(10px)}.subnav-link{padding:var(--spacing-xs) var(--spacing-md);color:var(--text-secondary);text-decoration:none;font-size:0.85rem;border-radius:var(--radius-sm);transition:all var(--transition-fast)}.subnav-link:hover{background:rgba(76,201,240,0.1);color:var(--text-primary)}.subnav-link.active{background:rgba(76,201,240,0.15);color:var(--primary-cyan)}.achievement-overview{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--spacing-lg)}.achievement-stat{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-md)}.achievement-stat-icon{font-size:2rem}.achievement-stat-info{display:flex;flex-direction:column}.achievement-stat-label{font-size:0.8rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px}.achievement-stat-value{font-size:1.3rem;font-weight:600;color:var(--neon-cyan)}.dashboard-page{--card-min:280px;--card-gap:var(--spacing-lg)}.systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min,280px),100%),1fr));gap:var(--card-gap,var(--spacing-lg))}.system-card{display:flex;flex-direction:column;padding:var(--spacing-lg);background:rgba(20,26,35,0.6);border:1px solid var(--card-border);border-radius:var(--radius-lg);text-decoration:none;color:inherit;transition:all var(--transition-fast)}.system-card:hover{border-color:var(--neon-cyan);transform:translateY(-3px);box-shadow:0 8px 24px rgba(0,0,0,0.3),0 0 15px rgba(76,201,240,0.15)}.system-card-header{margin-bottom:var(--spacing-md);padding-bottom:var(--spacing-sm);border-bottom:1px solid var(--card-border)}.system-name{font-family:var(--font-heading);font-size:1.1rem;font-weight:600;color:var(--text-primary);margin:0}.system-card-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.system-stat{display:flex;flex-direction:column;align-items:center;text-align:center}.system-stat-value{font-size:1.2rem;font-weight:600;color:var(--neon-cyan)}.system-stat-label{font-size:0.7rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.3px}.system-card-action{margin-top:auto;padding-top:var(--spacing-md);text-align:center;color:var(--neon-cyan);font-size:0.9rem;font-weight:500;opacity:0.7;transition:opacity 0.2s}.system-card:hover .system-card-action{opacity:1}.filter-group{display:flex;align-items:center;gap:0.5rem}.filter-group label{color:var(--text-secondary);font-size:0.85rem;white-space:nowrap}.filter-group select,.filter-group input{padding:0.5rem 0.75rem;background:var(--bg-darker);border:1px solid var(--card-border);border-radius:var(--radius-sm);color:var(--text-primary);font-size:0.85rem}.filter-group select:focus,.filter-group input:focus{outline:none;border-color:var(--primary-cyan)}.alert{padding:var(--spacing-md) var(--spacing-lg);border-radius:var(--radius-md);margin-bottom:var(--spacing-md)}.alert-warning{background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);color:var(--neon-orange)}.alert-danger{background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);color:var(--neon-red)}.alert-success{background:rgba(34,197,94,0.1);border:1px solid rgba(34,197,94,0.3);color:var(--neon-green)}.alert-info{background:rgba(76,201,240,0.1);border:1px solid rgba(76,201,240,0.3);color:var(--primary-cyan)}.alert a{color:inherit;text-decoration:underline}.field-tip{display:block;margin-top:var(--spacing-xs);font-size:0.8rem;color:var(--text-muted)}.changelog-entry{margin-bottom:var(--spacing-xl);padding-bottom:var(--spacing-xl);border-bottom:1px solid var(--card-border)}.changelog-entry:last-child{border-bottom:none}.changelog-version{font-family:var(--font-heading);font-size:1.5rem;color:var(--primary-cyan);margin-bottom:var(--spacing-xs)}.changelog-date{font-size:0.85rem;color:var(--text-muted);margin-bottom:var(--spacing-md)}.changelog-tags{display:flex;flex-wrap:wrap;gap:var(--spacing-xs);margin-bottom:var(--spacing-md)}.changelog-tag{padding:4px 12px;border-radius:var(--radius-full);font-size:0.75rem;font-weight:600;text-transform:uppercase}.tag-feature{background:rgba(76,201,240,0.15);border:1px solid var(--primary-cyan);color:var(--primary-cyan)}.tag-minor{background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);color:var(--neon-purple)}.tag-patch{background:rgba(245,158,11,0.15);border:1px solid var(--neon-orange);color:var(--neon-orange)}.tag-fix{background:rgba(239,68,68,0.15);border:1px solid var(--neon-red);color:var(--neon-red)}.tag-major{background:rgba(247,37,133,0.15);border:1px solid var(--secondary-magenta);color:var(--secondary-magenta)}.tag-ui{background:rgba(168,85,247,0.15);border:1px solid var(--neon-purple);color:var(--neon-purple)}.tag-improvement{background:rgba(76,201,240,0.15);border:1px solid var(--primary-cyan);color:var(--primary-cyan)}.tag-enhancement{background:rgba(34,197,94,0.15);border:1px solid var(--neon-green);color:var(--neon-green)}.tag-initial{background:rgba(247,37,133,0.15);border:1px solid var(--secondary-magenta);color:var(--secondary-magenta)}.changelog-list{list-style:none;padding:0;margin:0}.changelog-list li{padding:var(--spacing-xs) 0;padding-left:var(--spacing-lg);position:relative;color:var(--text-secondary)}.changelog-list li::before{content:'•';position:absolute;left:0;color:var(--primary-cyan)}.login-container{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:var(--spacing-lg)}.login-box{width:100%;max-width:400px;padding:var(--spacing-xl);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-lg);box-shadow:0 0 40px rgba(76,201,240,0.1)}.login-header{text-align:center;margin-bottom:var(--spacing-xl)}.login-logo{font-family:var(--font-heading);font-size:2rem;color:var(--primary-cyan);margin-bottom:var(--spacing-xs)}.login-subtitle{color:var(--text-secondary);font-size:0.9rem}.help-page{--help-text-max:calc(75ch + 2 * var(--spacing-lg));--help-nav-width:220px;--page-max:var(--help-text-max)}.help-page > .help-nav{flex-wrap:nowrap;overflow-x:auto}@media (max-width:600px){.help-page code{overflow-wrap:anywhere}}@media (min-width:1300px){.help-page{--page-max:calc(var(--help-nav-width) + var(--spacing-xl) + var(--help-text-max));display:grid;grid-template-columns:var(--help-nav-width) minmax(0,1fr);column-gap:var(--spacing-xl);align-items:start}.help-page > *{grid-column:2}.help-page > .page-header{grid-column:1 / -1}.help-page > .help-nav{grid-column:1;grid-row:2 / span 100;flex-direction:column;flex-wrap:nowrap;margin:0;max-height:100vh;overflow-x:hidden;overflow-y:auto}.help-page .help-nav-item{white-space:normal}}.help-section{margin-bottom:var(--spacing-xl)}.help-section h2{font-family:var(--font-heading);font-size:1.25rem;color:var(--primary-cyan);margin-bottom:var(--spacing-md)}.help-item{padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.help-item h3{color:var(--text-primary);font-size:1rem;margin-bottom:var(--spacing-xs)}.help-item p{color:var(--text-secondary);font-size:0.9rem;margin:0}.tool-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg)}.tool-description{color:var(--text-secondary);font-size:0.95rem;margin-bottom:var(--spacing-lg);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md);border-left:3px solid var(--primary-cyan)}.tool-results{margin-top:var(--spacing-lg)}.tool-result-item{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-md);background:var(--card-bg);border:1px solid var(--card-border);border-radius:var(--radius-md);margin-bottom:var(--spacing-sm)}.tool-result-item:hover{border-color:var(--primary-cyan)}.tool-result-icon{font-size:1.5rem}.tool-result-info{flex:1}.tool-result-name{color:var(--text-primary);font-weight:500}.tool-result-detail{color:var(--text-muted);font-size:0.85rem}@media (max-width:768px){.settings-tabs,.report-tabs{gap:var(--spacing-xs);padding:var(--spacing-xs)}.settings-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.settings-tab .tab-text{display:none}.settings-tab .tab-icon{font-size:1.25rem}.report-tab{padding:0.5rem 0.75rem;font-size:0.8rem}.report-tab .tab-label{display:none}.report-tab .tab-icon{font-size:1.25rem}}.game-fanart-bg{position:fixed;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;opacity:0;z-index:-1;transition:opacity 0.5s ease}.game-fanart-bg.active{opacity:0.15}.game-fanart-bg::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,transparent 0%,var(--bg-primary) 100%)}.game-compact-layout{max-width:100%}.game-hero{display:flex;gap:var(--spacing-xl);margin-bottom:var(--spacing-xl);background:color-mix(in srgb,var(--bg-medium) 85%,transparent);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-lg)}.hero-boxart{position:relative;flex-shrink:0;width:200px}.hero-boxart-frame{position:relative}.upscale-game-note{margin:var(--spacing-sm) 0 0;font-size:0.75rem;color:var(--text-secondary)}.game-boxart-image{width:100%;height:auto;border-radius:var(--radius-md);box-shadow:0 8px 32px rgba(0,0,0,0.4)}.hero-age-ratings{display:flex;flex-wrap:wrap;gap:5px;margin-top:auto;padding-top:var(--spacing-sm)}.hero-info{flex:1;min-width:0;display:flex;flex-direction:column}.quick-tags{display:flex;flex-wrap:wrap;align-items:center;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.tag{padding:4px 10px;border-radius:4px;font-size:0.8rem;font-weight:500;background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.1)}.quick-tags .tag,.quick-tags .game-badge{height:32px;box-sizing:border-box;line-height:1}.quick-tags .game-badge.age-rating-badge .age-rating-img{height:32px}.tag-system{color:var(--primary-cyan);border-color:var(--primary-cyan)}.hero-description{flex:1;font-size:0.9rem;line-height:1.6;color:var(--text-secondary);margin-bottom:var(--spacing-md);overflow-y:auto;max-height:120px}.key-details{display:flex;flex-wrap:wrap;gap:var(--spacing-lg);padding-top:var(--spacing-md);border-top:1px solid var(--card-border)}.kd-item{display:flex;flex-direction:column;gap:2px}.kd-label{font-size:0.75rem;color:var(--text-muted);text-transform:uppercase}.kd-value{font-size:0.9rem;color:var(--text-primary)}.release-dates-list{display:grid;grid-template-columns:auto auto;column-gap:var(--spacing-sm);margin:0}.release-dates-list dt{color:var(--text-secondary)}.release-dates-list dd{margin:0}.trophy-summary{text-align:center}.trophy-summary-header{display:flex;justify-content:center;align-items:center;gap:var(--spacing-lg);margin-bottom:var(--spacing-md)}.trophy-summary-title{font-size:1.1rem;font-weight:600}.trophy-summary-total{font-size:1.3rem;font-weight:bold;color:var(--primary-cyan)}.trophy-summary-progress{display:flex;align-items:center;gap:var(--spacing-md);max-width:500px;margin:0 auto var(--spacing-lg)}.trophy-progress-bar.large{flex:1;height:12px;background:rgba(255,255,255,0.1);border-radius:6px;overflow:hidden}.trophy-progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-cyan),#00ddff);border-radius:6px}.trophy-progress-percent{font-size:1.2rem;font-weight:bold;color:var(--primary-cyan);min-width:60px}.trophy-summary-counts{display:flex;justify-content:center;gap:var(--spacing-xl);flex-wrap:wrap}.trophy-count{display:flex;align-items:center;gap:var(--spacing-sm);font-size:1.1rem;font-weight:600}.trophy-count img{width:28px;height:28px}.trophy-count.platinum{color:var(--trophy-display-platinum)}.trophy-count.gold{color:var(--trophy-display-gold)}.trophy-count.silver{color:var(--trophy-display-silver)}.trophy-count.bronze{color:var(--trophy-display-bronze)}.bottom-row{display:flex;gap:var(--spacing-lg);margin-bottom:var(--spacing-xl)}.bottom-section{flex:1;background:color-mix(in srgb,var(--bg-medium) 85%,transparent);backdrop-filter:blur(10px);border:1px solid var(--card-border);border-radius:var(--radius-lg);padding:var(--spacing-md)}.section-title-sm{font-size:0.9rem;color:var(--text-secondary);margin-bottom:var(--spacing-md)}.hltb-section{display:flex;flex-direction:column;gap:var(--spacing-sm)}.more-details{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-sm)}.md-item{display:flex;flex-direction:column}.md-item.md-inline{flex-direction:row;align-items:baseline;gap:var(--spacing-sm);grid-column:span 2}.md-label{font-size:0.7rem;color:var(--text-muted);text-transform:uppercase}.md-value{font-size:0.85rem;color:var(--text-primary)}.trophy-columns{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:650px;gap:var(--spacing-xl)}.trophy-columns > .card{height:100%;display:flex;flex-direction:column;overflow:hidden}.trophy-columns > .card + .card{margin-top:0}.trophy-columns > .card .card-header{flex-shrink:0}.trophy-column .card-body{flex:1;overflow-y:auto;min-height:0}.trophy-list{display:flex;flex-direction:column;gap:var(--spacing-md)}.trophy-item{display:flex;gap:var(--spacing-md);padding:var(--spacing-md);background:rgba(0,0,0,0.2);border-radius:var(--radius-md)}.trophy-item.locked{opacity:0.5}.trophy-item.locked .trophy-item-icon img:first-child{filter:grayscale(1) brightness(0.5)}.trophy-item-icon{position:relative;flex-shrink:0}.trophy-item-icon > img:first-child{width:60px;height:60px;border-radius:var(--radius-sm);background:rgba(0,0,0,0.3);object-fit:cover}.trophy-item-icon > img.trophy-type-badge{position:absolute;bottom:-4px;right:-4px;width:24px;height:24px}.trophy-item-icon .badge-placeholder{width:60px;height:60px;border-radius:var(--radius-sm);background:rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;font-size:1.5rem}.trophy-item-info{flex:1;min-width:0}.trophy-item-name{font-weight:600;font-size:0.95rem;margin-bottom:4px}.trophy-item-desc{font-size:0.85rem;color:var(--text-secondary);margin-bottom:8px}.trophy-item-meta{display:flex;gap:var(--spacing-md);align-items:center;flex-wrap:wrap;font-size:0.8rem}.trophy-type-label{padding:2px 8px;border-radius:4px;font-weight:600;text-transform:uppercase;font-size:0.7rem}.trophy-type-label.p{background:var(--trophy-display-platinum);color:var(--trophy-display-text)}.trophy-type-label.g{background:var(--trophy-display-gold);color:var(--trophy-display-text)}.trophy-type-label.s{background:var(--trophy-display-silver);color:var(--trophy-display-text)}.trophy-type-label.b{background:var(--trophy-display-bronze);color:var(--trophy-display-text)}.trophy-timestamp{color:var(--primary-cyan)}.trophy-empty{text-align:center;padding:var(--spacing-xl);color:var(--text-muted);font-style:italic}.hltb-search-input{width:100%;padding:10px 14px;background:var(--bg-dark);border:1px solid var(--card-border);border-radius:var(--radius-md);color:var(--text-primary);font-family:var(--font-primary);font-size:0.9rem;margin-bottom:var(--spacing-sm)}.hltb-search-input:focus{outline:none;border-color:var(--primary-cyan);box-shadow:0 0 0 2px rgba(76,201,240,0.15)}.btn-hltb{width:100%;display:flex;align-items:center;justify-content:center;gap:var(--spacing-sm);padding:10px 16px;background:rgba(76,201,240,0.1);border:1px solid var(--primary-cyan);border-radius:var(--radius-md);color:var(--primary-cyan);font-family:var(--font-primary);font-size:0.85rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.btn-hltb:hover{background:rgba(76,201,240,0.2)}.btn-hltb:disabled{opacity:0.5;cursor:not-allowed}@media (max-width:900px){.trophy-columns{grid-template-columns:1fr;grid-template-rows:auto}.trophy-columns > .card{height:auto;max-height:500px}.game-hero{flex-direction:column;align-items:center}.hero-boxart{width:150px}.bottom-row{flex-direction:column}}.alphabet-nav{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-lg);border-radius:var(--radius-lg);margin-bottom:var(--spacing-lg);background:color-mix(in srgb,var(--bg-medium) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--card-border);position:sticky;top:var(--sticky-top);z-index:99}.alphabet-label{color:var(--text-muted);font-size:0.8rem;white-space:nowrap}.alphabet-letters{display:flex;flex-wrap:wrap;gap:3px}.alphabet-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:rgba(76,201,240,0.08);border:1px solid rgba(76,201,240,0.2);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:0.75rem;font-weight:600;cursor:pointer;transition:all var(--transition-fast);padding:0}.alphabet-btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.active{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.alphabet-btn.disabled{opacity:0.25;cursor:not-allowed}.alphabet-btn.disabled:hover{background:rgba(76,201,240,0.08);border-color:rgba(76,201,240,0.2);color:var(--text-secondary)}@media (max-width:768px){.alphabet-nav{padding:var(--spacing-xs) var(--spacing-md)}.alphabet-btn{width:24px;height:24px;font-size:0.65rem}.alphabet-label{display:none}}.warning-banner{background:linear-gradient(135deg,rgba(255,193,7,0.1),rgba(247,37,133,0.1));border:1px solid rgba(255,193,7,0.3);border-radius:var(--radius-md);padding:1rem 1.25rem;margin-bottom:1.5rem;display:flex;align-items:flex-start;gap:1rem}.warning-banner .warning-icon{font-size:1.5rem}.warning-banner .warning-text h4{color:var(--neon-orange);margin-bottom:0.25rem}.warning-banner .warning-text p{color:var(--text-secondary);font-size:0.9rem;margin:0}.info-banner{background:linear-gradient(135deg,rgba(76,201,240,0.1),rgba(114,9,183,0.1));border:1px solid rgba(76,201,240,0.3);border-radius:var(--radius-md);padding:1rem 1.25rem;margin-bottom:1.5rem;display:flex;align-items:flex-start;gap:1rem}.info-banner .info-icon{font-size:1.5rem}.info-banner .info-text h4{color:var(--text-primary);margin-bottom:0.25rem}.info-banner .info-text p{color:var(--text-secondary);font-size:0.9rem;margin:0}.file-list{background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md);max-height:400px;overflow-y:auto}.file-list-header{display:grid;gap:1rem;padding:0.75rem 1rem;background:var(--bg-medium);border-bottom:1px solid var(--border-color);font-size:0.75rem;text-transform:uppercase;letter-spacing:1px;color:var(--text-secondary);position:sticky;top:0;z-index:10}.file-list-item{display:grid;gap:1rem;padding:0.75rem 1rem;border-bottom:1px solid var(--border-color);font-size:0.9rem;align-items:center}.file-list-item:hover{background:rgba(76,201,240,0.05)}.file-list-item:last-child{border-bottom:none}.file-list-item input[type="checkbox"]{width:18px;height:18px;accent-color:var(--neon-cyan);cursor:pointer}.file-list-item .filename{color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-list-item .filesize{color:var(--text-secondary)}.action-bar{display:flex;gap:1rem;margin-top:1rem;padding:1rem;background:var(--bg-darker);border:1px solid var(--border-color);border-radius:var(--radius-md)}.action-btn{padding:0.4rem 0.75rem;background:rgba(76,201,240,0.15);border:1px solid var(--neon-cyan);border-radius:var(--radius-sm);color:var(--neon-cyan);cursor:pointer;font-size:0.8rem;transition:all var(--transition-fast)}.action-btn:hover{background:rgba(76,201,240,0.25);box-shadow:0 0 10px rgba(76,201,240,0.3)}.theme-selector{display:flex;gap:var(--spacing-md);flex-wrap:wrap}.theme-option{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-md);background:var(--card-bg);border:2px solid var(--card-border);border-radius:var(--radius-md);cursor:pointer;transition:border-color var(--transition-fast),box-shadow var(--transition-fast);min-width:110px}.theme-option:hover{border-color:var(--card-border-hover)}.theme-option.active{border-color:var(--primary-cyan);box-shadow:0 0 12px var(--primary-cyan-glow)}.theme-preview{display:flex;gap:4px;padding:4px;background:rgba(0,0,0,0.3);border-radius:var(--radius-sm)}.theme-swatch{width:24px;height:24px;border-radius:3px}.theme-name{font-family:var(--font-primary);font-size:0.85rem;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px}.theme-option.active .theme-name{color:var(--primary-cyan)}.section-title{font-family:var(--font-display);font-size:1rem;color:var(--text-primary);margin-bottom:1rem;padding-bottom:0.5rem;border-bottom:1px solid var(--border-color);display:flex;align-items:center;gap:0.5rem}.emu-folder-list{list-style:none;margin:0 0 var(--spacing-md);padding:0}.emu-folder-list li{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-sm);padding:var(--spacing-sm) 0;border-bottom:1px solid var(--border-color)}.emu-folder-list code{overflow-wrap:anywhere}.emu-folder-add{display:flex;flex-wrap:wrap;gap:var(--spacing-sm);margin-bottom:var(--spacing-md)}.emu-folder-add .form-input{flex:1 1 16rem}.museum-progress{padding:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.museum-progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-sm)}.museum-progress-title{font-family:var(--font-heading);font-size:0.95rem;color:var(--primary-cyan)}.museum-progress-stats{font-size:0.85rem;color:var(--text-secondary);margin-top:var(--spacing-sm)}.museum-progress-stats .text-success{color:var(--status-success)}.museum-progress-stats .text-warning{color:var(--status-warning)}.museum-progress-stats .text-error{color:var(--status-error)}.museum-timeline{display:flex;flex-direction:column;gap:var(--spacing-xl)}.museum-era{position:relative}.museum-era-header{display:flex;align-items:center;gap:var(--spacing-md);margin-bottom:var(--spacing-lg)}.museum-era-line{width:4px;height:48px;background:linear-gradient(180deg,var(--primary-cyan),transparent);border-radius:2px;flex-shrink:0}.museum-era-info{display:flex;flex-direction:column;gap:2px}.museum-era-title{font-family:var(--font-heading);font-size:1.3rem;color:var(--primary-cyan);margin:0;text-shadow:0 0 20px rgba(76,201,240,0.3)}.museum-era-date{font-family:var(--font-mono);font-size:0.8rem;color:var(--text-muted)}.museum-era-count{font-size:0.8rem;color:var(--text-secondary)}.museum-page{--card-min:260px;--card-gap:var(--spacing-md)}.museum-systems-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.museum-system-card{display:flex;flex-direction:column;position:relative;border-radius:var(--radius-lg);overflow:hidden;text-decoration:none;color:inherit;transition:transform var(--transition-normal),box-shadow var(--transition-normal)}.museum-system-card:hover{transform:translateY(-3px);box-shadow:0 0 20px rgba(76,201,240,0.15);border-color:var(--card-border-hover)}.museum-card-image{height:140px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.2);padding:var(--spacing-md)}.museum-card-image img{max-height:100%;max-width:100%;object-fit:contain}.museum-card-placeholder{font-size:3rem;opacity:0.3}.museum-card-body{padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-xs);flex:1}.museum-card-title{font-family:var(--font-heading);font-size:0.9rem;color:var(--text-primary);margin:0;line-height:1.3}.museum-card-summary{font-size:0.78rem;color:var(--text-secondary);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.4}.museum-card-meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:auto}.museum-meta-chip{display:inline-block;padding:2px 8px;font-size:0.7rem;font-family:var(--font-mono);color:var(--text-secondary);background:rgba(76,201,240,0.08);border:1px solid rgba(76,201,240,0.15);border-radius:var(--radius-full);white-space:nowrap}.museum-content-indicator{position:absolute;top:var(--spacing-sm);right:var(--spacing-sm);width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-full);background:rgba(34,197,94,0.2);color:var(--neon-green);font-size:0.7rem}.museum-system-header{margin-bottom:var(--spacing-xl)}.museum-hero-layout{display:flex;gap:var(--spacing-xl);align-items:flex-start}.museum-hero-image{flex-shrink:0;width:240px;height:200px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.2);border-radius:var(--radius-lg);border:1px solid var(--card-border);padding:var(--spacing-md)}.museum-hero-image img{max-height:100%;max-width:100%;object-fit:contain}.museum-hero-placeholder{font-size:4rem;opacity:0.3}.museum-hero-info{flex:1;display:flex;flex-direction:column;gap:var(--spacing-sm)}.museum-hero-title{font-family:var(--font-heading);font-size:2rem;color:var(--primary-cyan);margin:0;text-shadow:0 0 30px rgba(76,201,240,0.3)}.museum-hero-summary{font-size:1rem;color:var(--text-secondary);line-height:1.5;margin:0}.museum-hero-meta{display:flex;flex-wrap:wrap;gap:var(--spacing-xs)}.museum-hero-actions{display:flex;gap:var(--spacing-sm);margin-top:var(--spacing-sm);flex-wrap:wrap}.museum-nav{display:flex;gap:0;border-bottom:2px solid var(--border-color);margin-bottom:var(--spacing-lg);position:sticky;background:var(--bg-darkest);z-index:10;padding-top:var(--spacing-xs)}.museum-nav-item{padding:var(--spacing-sm) var(--spacing-lg);font-family:var(--font-heading);font-size:0.85rem;color:var(--text-secondary);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-2px;transition:color var(--transition-fast),border-color var(--transition-fast);white-space:nowrap}.museum-nav-item:hover{color:var(--text-primary)}.museum-nav-item.active{color:var(--primary-cyan);border-bottom-color:var(--primary-cyan)}.museum-section{margin-bottom:var(--spacing-xl)}.museum-section-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--spacing-sm)}.museum-section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-md);flex-wrap:wrap;gap:var(--spacing-sm)}.museum-section-title{font-family:var(--font-heading);font-size:1.2rem;color:var(--primary-cyan);margin:0}.museum-history-content{max-width:calc(75ch + 2 * var(--spacing-lg));padding:var(--spacing-lg);line-height:1.75;color:var(--text-primary);font-size:0.95rem}.museum-history-content p{margin:0 0 var(--spacing-md) 0}.museum-history-content p:last-child{margin-bottom:0}.museum-placeholder{padding:var(--spacing-xl);text-align:center}.museum-placeholder .text-muted{color:var(--text-muted)}.museum-generated-badge{font-size:0.75rem;font-family:var(--font-mono);color:var(--text-muted);padding:4px 10px;background:rgba(76,201,240,0.06);border:1px solid rgba(76,201,240,0.12);border-radius:var(--radius-full)}.museum-ai-badge{display:inline-block;padding:2px 8px;font-size:0.7rem;font-family:var(--font-mono);color:var(--neon-purple);background:rgba(168,85,247,0.1);border:1px solid rgba(168,85,247,0.2);border-radius:var(--radius-full)}.museum-system-page{--card-min:280px;--card-gap:var(--spacing-md)}.museum-specs-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--card-gap)}.museum-spec-item{padding:var(--spacing-md);display:flex;flex-direction:column;gap:4px}.museum-spec-label{font-family:var(--font-heading);font-size:0.75rem;color:var(--primary-cyan);text-transform:uppercase;letter-spacing:0.5px}.museum-spec-value{font-size:0.9rem;color:var(--text-primary);font-family:var(--font-mono);word-break:break-word}.museum-top-games-list{display:flex;flex-direction:column;gap:var(--spacing-sm)}.museum-top-game{display:flex;align-items:center;gap:var(--spacing-md);padding:var(--spacing-sm) var(--spacing-md);transition:border-color var(--transition-fast)}.museum-top-game:hover{border-color:var(--card-border-hover)}.museum-rank{font-family:var(--font-heading);font-size:1.3rem;color:var(--primary-cyan);min-width:40px;text-align:center;flex-shrink:0}.museum-top-game-art{width:48px;height:64px;flex-shrink:0;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);overflow:hidden;background:rgba(0,0,0,0.2)}.museum-top-game-art img{max-height:100%;max-width:100%;object-fit:cover}.museum-top-game-placeholder{font-size:1.5rem;opacity:0.3}.museum-top-game-info{flex:1;display:flex;flex-direction:column;gap:2px}.museum-top-game-title{font-family:var(--font-primary);font-weight:600;font-size:0.95rem;color:var(--text-primary);text-decoration:none}a.museum-top-game-title:hover{color:var(--primary-cyan)}.museum-top-game-meta{display:flex;gap:var(--spacing-xs);align-items:center}.museum-controllers-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:var(--spacing-md)}.museum-controller-card{display:flex;flex-direction:column;overflow:hidden;border-radius:var(--radius-lg)}.museum-controller-image{height:180px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.15);padding:var(--spacing-md);position:relative}.museum-controller-image img{max-height:100%;max-width:100%;object-fit:contain;cursor:zoom-in}.museum-controller-image-actions{display:flex;gap:4px}.museum-controller-image-actions .btn{padding:2px 6px;font-size:0.65rem;line-height:1.2;letter-spacing:0.5px;backdrop-filter:blur(8px);background:rgba(0,0,0,0.6);border-color:rgba(76,201,240,0.4);color:var(--primary-cyan);box-shadow:none;text-shadow:none}.museum-controller-image-actions .btn:hover{background:rgba(76,201,240,0.2);border-color:var(--primary-cyan)}.museum-controller-image > .museum-controller-image-actions{position:absolute;bottom:var(--spacing-xs);right:var(--spacing-xs);opacity:0;transition:opacity var(--transition-fast)}.museum-controller-image:hover > .museum-controller-image-actions{opacity:1}.museum-controller-upload-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-sm);background:rgba(10,14,23,0.85);z-index:5;border-radius:inherit}.museum-controller-upload-text{font-family:var(--font-primary);font-size:0.75rem;color:var(--text-secondary);letter-spacing:0.5px}.museum-lightbox{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);z-index:10002;align-items:center;justify-content:center;flex-direction:column}.museum-lightbox.active{display:flex}.museum-lightbox-image{max-width:90%;max-height:80vh;object-fit:contain;border-radius:var(--radius-md);position:relative;z-index:1}.museum-lightbox::before{content:"";position:absolute;top:45%;left:50%;transform:translate(-50%,-50%);width:90vmin;height:90vmin;background:radial-gradient(circle at center,rgba(76,201,240,0.19) 0%,rgba(76,201,240,0.09) 25%,rgba(76,201,240,0.03) 45%,transparent 40%);border-radius:50%;pointer-events:none;z-index:0}.museum-lightbox-close{position:absolute;top:20px;right:35px;font-size:2.5rem;color:var(--text-primary);cursor:pointer;z-index:10003;transition:color var(--transition-fast);background:none;border:none;line-height:1}.museum-lightbox-close:hover{color:var(--primary-cyan)}.museum-lightbox-title{position:absolute;bottom:30px;left:50%;transform:translateX(-50%);color:var(--text-secondary);font-family:var(--font-heading);font-size:0.9rem;white-space:nowrap}.museum-lightbox-hint{position:absolute;bottom:12px;left:50%;transform:translateX(-50%);color:var(--text-muted);font-size:0.75rem;white-space:nowrap}.museum-lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.5);border:2px solid var(--card-border);color:var(--text-primary);font-size:3rem;padding:10px 20px;cursor:pointer;border-radius:var(--radius-md);transition:all var(--transition-fast);z-index:10003}.museum-lightbox-nav:hover{background:rgba(76,201,240,0.3);border-color:var(--primary-cyan);color:var(--primary-cyan)}.museum-lightbox-nav.prev{left:20px}.museum-lightbox-nav.next{right:20px}.museum-controller-placeholder{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-sm);color:var(--text-muted);font-size:2.5rem}.museum-controller-info{padding:var(--spacing-md);display:flex;flex-direction:column;gap:var(--spacing-xs)}.museum-controller-name{font-family:var(--font-heading);font-size:0.85rem;color:var(--text-primary);margin:0}.museum-controller-meta{display:flex;flex-wrap:wrap;gap:4px}.museum-controller-desc{font-size:0.8rem;color:var(--text-secondary);margin:var(--spacing-xs) 0 0 0;line-height:1.4}.museum-controller-buttons{margin-top:var(--spacing-xs);font-size:0.8rem;color:var(--text-secondary)}.museum-controller-buttons summary{cursor:pointer;color:var(--primary-cyan);font-family:var(--font-heading);font-size:0.75rem}.museum-controller-buttons p{margin:var(--spacing-xs) 0 0 0;font-family:var(--font-mono);font-size:0.75rem;line-height:1.5}@media (max-width:768px){.museum-hero-layout{flex-direction:column;align-items:center;text-align:center}.museum-hero-image{width:180px;height:150px}.museum-hero-meta,.museum-hero-actions{justify-content:center}.museum-systems-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.museum-specs-grid{grid-template-columns:1fr}.museum-controllers-grid{grid-template-columns:1fr}.museum-nav{overflow-x:auto;-webkit-overflow-scrolling:touch}}body::before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:linear-gradient( to top right,rgba(76,201,240,0.08) 0%,rgba(114,9,183,0.06) 25%,rgba(247,37,133,0.04) 50%,rgba(20,26,35,0.02) 75%,transparent 100% );pointer-events:none;z-index:-2}body::after{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:repeating-linear-gradient( 0deg,transparent,transparent 2px,rgba(0,0,0,0.03) 2px,rgba(0,0,0,0.03) 4px );pointer-events:none;z-index:9999}@media (prefers-reduced-motion:reduce){body::after{display:none}}@keyframes glow-pulse{0%,100%{text-shadow:0 0 10px var(--primary-cyan-glow),0 0 20px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow)}50%{text-shadow:0 0 15px var(--primary-cyan-glow),0 0 30px var(--primary-cyan-glow),0 0 45px var(--primary-cyan-glow)}}@keyframes pulse-status{0%,100%{opacity:1}50%{opacity:0.5}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes highlight-pulse{0%,100%{box-shadow:0 0 0 0 rgba(76,201,240,0)}50%{box-shadow:0 0 20px 5px rgba(76,201,240,0.4)}}.highlight-jump{animation:highlight-pulse 0.75s ease-in-out 2}@keyframes modalSlideIn{from{opacity:0;transform:scale(0.9) translateY(-20px)}to{opacity:1;transform:scale(1) translateY(0)}}@media (prefers-reduced-motion:reduce){.highlight-jump{animation:none}}.mt-0{margin-top:0}.mt-xs{margin-top:var(--spacing-xs)}.mt-sm{margin-top:var(--spacing-sm)}.mt-md{margin-top:var(--spacing-md)}.mt-lg{margin-top:var(--spacing-lg)}.mt-xl{margin-top:var(--spacing-xl)}.mt-2xl{margin-top:var(--spacing-2xl)}.mb-0{margin-bottom:0}.mb-xs{margin-bottom:var(--spacing-xs)}.mb-sm{margin-bottom:var(--spacing-sm)}.mb-md{margin-bottom:var(--spacing-md)}.mb-lg{margin-bottom:var(--spacing-lg)}.mb-xl{margin-bottom:var(--spacing-xl)}.mb-2xl{margin-bottom:var(--spacing-2xl)}.ml-0{margin-left:0}.ml-xs{margin-left:var(--spacing-xs)}.ml-sm{margin-left:var(--spacing-sm)}.ml-md{margin-left:var(--spacing-md)}.ml-lg{margin-left:var(--spacing-lg)}.ml-xl{margin-left:var(--spacing-xl)}.mr-0{margin-right:0}.mr-xs{margin-right:var(--spacing-xs)}.mr-sm{margin-right:var(--spacing-sm)}.mr-md{margin-right:var(--spacing-md)}.mr-lg{margin-right:var(--spacing-lg)}.mr-xl{margin-right:var(--spacing-xl)}.mx-0{margin-left:0;margin-right:0}.mx-sm{margin-left:var(--spacing-sm);margin-right:var(--spacing-sm)}.mx-md{margin-left:var(--spacing-md);margin-right:var(--spacing-md)}.mx-lg{margin-left:var(--spacing-lg);margin-right:var(--spacing-lg)}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-sm{margin-top:var(--spacing-sm);margin-bottom:var(--spacing-sm)}.my-md{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md)}.my-lg{margin-top:var(--spacing-lg);margin-bottom:var(--spacing-lg)}.pt-0{padding-top:0}.pt-xs{padding-top:var(--spacing-xs)}.pt-sm{padding-top:var(--spacing-sm)}.pt-md{padding-top:var(--spacing-md)}.pt-lg{padding-top:var(--spacing-lg)}.pt-xl{padding-top:var(--spacing-xl)}.pb-0{padding-bottom:0}.pb-xs{padding-bottom:var(--spacing-xs)}.pb-sm{padding-bottom:var(--spacing-sm)}.pb-md{padding-bottom:var(--spacing-md)}.pb-lg{padding-bottom:var(--spacing-lg)}.pb-xl{padding-bottom:var(--spacing-xl)}.pl-0{padding-left:0}.pl-sm{padding-left:var(--spacing-sm)}.pl-md{padding-left:var(--spacing-md)}.pl-lg{padding-left:var(--spacing-lg)}.pr-0{padding-right:0}.pr-sm{padding-right:var(--spacing-sm)}.pr-md{padding-right:var(--spacing-md)}.pr-lg{padding-right:var(--spacing-lg)}.px-0{padding-left:0;padding-right:0}.px-sm{padding-left:var(--spacing-sm);padding-right:var(--spacing-sm)}.px-md{padding-left:var(--spacing-md);padding-right:var(--spacing-md)}.px-lg{padding-left:var(--spacing-lg);padding-right:var(--spacing-lg)}.py-0{padding-top:0;padding-bottom:0}.py-sm{padding-top:var(--spacing-sm);padding-bottom:var(--spacing-sm)}.py-md{padding-top:var(--spacing-md);padding-bottom:var(--spacing-md)}.py-lg{padding-top:var(--spacing-lg);padding-bottom:var(--spacing-lg)}.d-none{display:none}.d-block{display:block}.d-inline{display:inline}.d-inline-block{display:inline-block}.d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.align-start{align-items:flex-start}.align-center{align-items:center}.align-end{align-items:flex-end}.align-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-0{gap:0}.gap-xs{gap:var(--spacing-xs)}.gap-sm{gap:var(--spacing-sm)}.gap-md{gap:var(--spacing-md)}.gap-lg{gap:var(--spacing-lg)}.gap-xl{gap:var(--spacing-xl)}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-primary{color:var(--text-primary)}.text-secondary{color:var(--text-secondary)}.text-muted{color:var(--text-muted)}.text-cyan{color:var(--primary-cyan)}.text-magenta{color:var(--secondary-magenta)}.text-success{color:var(--neon-green)}.text-warning{color:var(--neon-orange)}.text-danger{color:var(--neon-red)}.text-purple{color:var(--neon-purple)}.font-heading{font-family:var(--font-heading)}.font-mono{font-family:var(--font-mono)}.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-sm{font-size:0.85rem}.text-base{font-size:1rem}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-tight{line-height:1.25}.leading-normal{line-height:1.6}.leading-loose{line-height:2}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.break-word{word-wrap:break-word;overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.w-full{width:100%}.w-auto{width:auto}.h-full{height:100%}.h-auto{height:auto}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.border{border:1px solid var(--card-border)}.border-0{border:0}.border-t{border-top:1px solid var(--card-border)}.border-b{border-bottom:1px solid var(--card-border)}.border-l{border-left:1px solid var(--card-border)}.border-r{border-right:1px solid var(--card-border)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-md{border-radius:var(--radius-md)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-full{border-radius:var(--radius-full)}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.opacity-0{opacity:0}.opacity-25{opacity:0.25}.opacity-50{opacity:0.5}.opacity-75{opacity:0.75}.opacity-100{opacity:1}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.select-none{user-select:none}.select-text{user-select:text}.select-all{user-select:all}.mobile-header,.mobile-bottom-bar,.sidebar-scrim{display:none}@media (max-width:992px){:root{--sticky-top:var(--mobile-header-height)}.mobile-header{position:fixed;top:0;left:0;right:0;z-index:1001;height:var(--mobile-header-height);display:flex;align-items:center;gap:var(--spacing-sm);padding:0 var(--spacing-sm);background:var(--bg-medium);border-bottom:1px solid var(--card-border)}.mobile-header-logo{flex:1}.mobile-header-logo .logo-text{font-size:1.25rem}.mobile-menu-toggle,.mobile-search-toggle{display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:none;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer}.mobile-menu-toggle{flex-direction:column;gap:4px}.mobile-menu-toggle span{width:24px;height:3px;background:var(--text-primary);border-radius:2px}.mobile-search-toggle{font-size:1.25rem}.mobile-menu-toggle:focus-visible,.mobile-search-toggle:focus-visible{outline:none;box-shadow:0 0 0 2px var(--primary-cyan)}.sidebar{top:var(--mobile-header-height);height:calc(100dvh - var(--mobile-header-height));width:min(var(--sidebar-width),85vw);background:linear-gradient(180deg,var(--bg-medium) 0%,var(--bg-dark) 100%);backdrop-filter:none;-webkit-backdrop-filter:none;visibility:hidden;transition:transform var(--transition-normal),visibility 0s var(--transition-normal)}.sidebar.open{visibility:visible;transition:transform var(--transition-normal)}.sidebar-header > .logo{display:none}.sidebar-header > .logo + *{margin-top:0}.global-search-list{max-height:50dvh}.sidebar-nav{overflow-y:auto}.sidebar-scrim{display:block;position:fixed;inset:0;z-index:999;background:rgba(0,0,0,0.6);opacity:0;pointer-events:none;transition:opacity var(--transition-normal)}body.mobile-menu-open .sidebar-scrim{opacity:1;pointer-events:auto}body.mobile-menu-open{overflow:hidden}.mobile-bottom-bar{position:fixed;left:0;right:0;bottom:0;z-index:998;display:flex;height:calc(var(--mobile-bar-height) + env(safe-area-inset-bottom, 0px));padding-bottom:env(safe-area-inset-bottom,0px);background:var(--bg-medium);border-top:1px solid var(--card-border)}.mobile-bottom-item{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-width:0;color:var(--text-secondary);text-decoration:none;font-size:0.75rem}.mobile-bottom-icon{font-size:1.25rem}.mobile-bottom-label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mobile-bottom-item.active{color:var(--primary-cyan);box-shadow:inset 0 2px 0 var(--primary-cyan)}.mobile-bottom-item:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--primary-cyan)}.main-content{padding-top:calc(var(--mobile-header-height) + var(--spacing-md));padding-bottom:calc(var(--mobile-bar-height) + env(safe-area-inset-bottom, 0px) + var(--spacing-md))}body:has(.mobile-bottom-bar) .back-to-top-btn{bottom:calc(var(--mobile-bar-height) + 16px)}body:has(.mobile-bottom-bar) .unified-toast-container{bottom:calc(var(--mobile-bar-height) + 12px)}body:has(.mobile-bottom-bar) .launch-indicator{bottom:calc(var(--mobile-bar-height) + var(--spacing-sm))}body:has(.mobile-bottom-bar) .launch-indicator-popover{bottom:calc(var(--mobile-bar-height) + var(--spacing-sm) + 2.5rem)}}@media (max-width:1600px){.content-wrapper{width:80%}}@media (max-width:1200px){.content-wrapper{width:95%;min-width:auto}.game-hero{grid-template-columns:250px 1fr}.game-details-grid{grid-template-columns:1fr}}@media (max-width:992px){.sidebar{transform:translateX(-100%)}.sidebar.open{transform:translateX(0)}.main-content{margin-left:0}.game-hero{grid-template-columns:1fr}.game-boxart-container{max-width:300px;margin:0 auto}.unified-toast-container{left:20px;right:20px;width:auto;max-width:calc(100% - 40px)}.system-controller-grid{grid-template-columns:1fr !important}}@media (max-width:900px){.score-lists-grid{grid-template-columns:1fr}.psn-trophy-lists-row{grid-template-columns:1fr}.psn-info-panels-row{grid-template-columns:1fr}.psn-game-info-card{flex-direction:column}.psn-game-boxart{width:140px}.psn-meta-grid{grid-template-columns:1fr 1fr}.psn-stats-bar{grid-template-columns:repeat(2,1fr)}}@media (max-width:768px){.page-header{flex-direction:column;align-items:flex-start;gap:var(--spacing-md)}.page-title{font-size:1.8rem}.stats-grid{grid-template-columns:1fr}.systems-grid{grid-template-columns:repeat(2,1fr)}.filter-bar{flex-direction:column}.search-box{min-width:100%}.game-meta-badges{flex-direction:column}.game-ratings{flex-direction:column}.game-hero{grid-template-columns:1fr}.hero-boxart{width:200px;margin:0 auto}.tool-tab{padding:0.75rem 0.5rem;font-size:0.8rem}.tool-tab .tab-label{display:none}.tool-tab .tab-icon{font-size:1.25rem}.edit-grid{grid-template-columns:1fr}.psn-games-grid{grid-template-columns:1fr}.psn-search-sort-row{flex-direction:column}.psn-sort-controls{width:100%}.psn-sort-select{flex:1}.psn-profile-bar{flex-direction:column;text-align:center}}@media (max-width:600px){.gdm-header{flex-direction:column;align-items:center;text-align:center}.gdm-boxart-container{width:140px}.gdm-details-grid{grid-template-columns:1fr}.gdm-meta{justify-content:center}.gdm-ratings{justify-content:center}.gdm-actions{flex-direction:column}.gdm-actions .btn{width:100%}.gem-hltb-times{grid-template-columns:1fr}.gem-footer{flex-direction:column}.gem-footer .btn{width:100%}.gem-tabs{flex-wrap:nowrap}.gem-tab{padding:6px 10px;font-size:0.75rem}.gem-grid{grid-template-columns:1fr}.gem-footer-right{flex-direction:column;width:100%}.psn-meta-grid{grid-template-columns:1fr}.psn-trophy-type-breakdown{flex-wrap:wrap;gap:var(--spacing-md)}.platform-filters{justify-content:center}}@media (max-width:480px){.systems-grid{grid-template-columns:1fr}.page-title{font-size:1.5rem;letter-spacing:2px}.stat-value{font-size:2.5rem}} \ No newline at end of file diff --git a/static/css/pages/pages.css b/static/css/pages/pages.css index e50c53a2..0bdec6d1 100644 --- a/static/css/pages/pages.css +++ b/static/css/pages/pages.css @@ -33,6 +33,59 @@ gap: var(--spacing-sm); } +/* The breadcrumb, with Previous / Next through the list the game was opened + from on the right. They wrap below it on a narrow screen. */ +.game-page-topbar { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: var(--spacing-sm); + margin-bottom: var(--spacing-lg); + position: relative; + z-index: 1; +} + +.game-page-topbar .breadcrumb { + margin-bottom: 0; + min-width: 0; + flex-wrap: wrap; +} + +.game-nav { + display: flex; + align-items: center; + gap: var(--spacing-sm); + margin-left: auto; + font-size: 0.9rem; +} + +.game-nav[hidden], +.game-nav-link[hidden] { + display: none; +} + +.game-nav-link { + padding: var(--spacing-xs) var(--spacing-sm); + border: 1px solid var(--card-border); + border-radius: var(--radius-md); + color: var(--text-secondary); + text-decoration: none; + white-space: nowrap; + transition: color var(--transition-fast), border-color var(--transition-fast); +} + +.game-nav-link:hover, +.game-nav-link:focus-visible { + color: var(--primary-cyan); + border-color: var(--primary-cyan); +} + +.game-nav-position { + color: var(--text-muted); + font-variant-numeric: tabular-nums; +} + /* PASS-59-152 — the Scroll Lock hint beside Play on keyboard-computer games. */ .play-key-hint { align-self: center; @@ -999,6 +1052,22 @@ color: var(--text-primary); } +/* Release dates by region: one line per region, earliest first. */ +.release-dates-list { + display: grid; + grid-template-columns: auto auto; + column-gap: var(--spacing-sm); + margin: 0; +} + +.release-dates-list dt { + color: var(--text-secondary); +} + +.release-dates-list dd { + margin: 0; +} + /* Trophy/Achievement Progress Summary */ .trophy-summary { text-align: center; diff --git a/static/js/all-games-controller.js b/static/js/all-games-controller.js index 195bcfec..17d47c24 100644 --- a/static/js/all-games-controller.js +++ b/static/js/all-games-controller.js @@ -372,6 +372,13 @@ const AllGamesController = (function() { totalFiltered = data.total; hasMore = data.has_more; + // The game page's Previous / Next links step through this list. + if (!append) { + const navParams = filterParams(); + if (sortOrder !== 'name') navParams.set('sort', sortOrder); + window.RetroDB?.saveGameNavContext?.(cfg.systemId, navParams); + } + renderGames(data.games, append); if (!append) setEmptyState(data.total === 0); loadedCount = append @@ -626,19 +633,20 @@ const AllGamesController = (function() { } if (game.completion_status && game.completion_status !== 'not_started') { const csClass = game.completion_status === '100_percent' ? 'hundred-percent' : game.completion_status.replace(/_/g, '-'); - const csLabels = { in_progress: '🎮 In Progress', played: '🕹️ Played', completed: '✅ Completed', '100_percent': '🏆 100%' }; - ratingBadges += `${csLabels[game.completion_status] || ''}`; + const csLabels = { in_progress: '🎮 ' + esc(t('In Progress')), played: '🕹️ ' + esc(t('Played')), completed: '✅ ' + esc(t('Completed')), '100_percent': '🏆 100%' }; + ratingBadges += `${csLabels[game.completion_status] || ''}`; } if (game.critic_score) { const cs = Math.round(game.critic_score); const csClass = cs >= 75 ? 'score-good' : cs >= 50 ? 'score-mixed' : 'score-bad'; - ratingBadges += `⭐ ${cs}`; + ratingBadges += `⭐ ${cs}`; } if (game.user_score) { const us = Math.round(game.user_score); const usClass = us >= 75 ? 'score-good' : us >= 50 ? 'score-mixed' : 'score-bad'; - ratingBadges += `👤 ${us}`; + ratingBadges += `👤 ${us}`; } + ratingBadges += window.rpcs3CompatBadgeHtml(game.rpcs3_compat); if (game.achievement_total > 0 && game.achievement_earned != null) { const earned = game.achievement_earned; const pct = Math.round((earned / game.achievement_total) * 100); diff --git a/static/js/game-modals.js b/static/js/game-modals.js index 7d25a517..63e27a74 100644 --- a/static/js/game-modals.js +++ b/static/js/game-modals.js @@ -82,6 +82,42 @@ function _getPreferredRatingFromGame(game) { return null; } +/** + * Badge for a PS3 game's RPCS3 compatibility status ({status, date} from the + * server, read from the local RPCS3's list), or '' when there is none. + * The game page renders the same badge server-side. + */ +function rpcs3CompatBadgeHtml(compat) { + if (!compat || !compat.status) return ''; + const text = { + playable: [t('RPCS3: Playable'), t('Can be completed with playable performance and no game-breaking glitches.')], + ingame: [t('RPCS3: Ingame'), t('Gets in game, but cannot be finished, has serious glitches or runs too slowly.')], + intro: [t('RPCS3: Intro'), t('Shows an image but does not get past the menus.')], + loadable: [t('RPCS3: Loadable'), t('Shows a black screen with a frame rate in the window title.')], + nothing: [t('RPCS3: Nothing'), t('Does not start, or crashes the emulator.')], + }[compat.status]; + if (!text) return ''; + const title = compat.date ? `${text[1]} ${t('Last tested {date}.', {date: compat.date})}` : text[1]; + return `🧩 ${escapeHtml(text[0])}`; +} + +/** Score, sales and RPCS3 badges shown after the age ratings in the quick view. */ +function _buildScoreBadgesHtml(game) { + let html = ''; + if (game.critic_score) { + const scoreClass = game.critic_score >= 75 ? 'score-good' : (game.critic_score >= 50 ? 'score-mixed' : 'score-bad'); + html += `⭐ ${Math.round(game.critic_score)}`; + } + if (game.user_score) { + const userScoreClass = game.user_score >= 75 ? 'score-good' : (game.user_score >= 50 ? 'score-mixed' : 'score-bad'); + html += `👤 ${Math.round(game.user_score)}`; + } + if (game.units_sold) { + html += `📈 ${escapeHtml(t('{count} sold', {count: formatNumber(game.units_sold)}))}`; + } + return html + rpcs3CompatBadgeHtml(game.rpcs3_compat); +} + /** Build age-rating badge HTML (admin sees all, users see preferred only) */ function _buildAgeRatingBadgesHtml(game) { let html = ''; @@ -117,11 +153,67 @@ function gameDetailUrl(gameId) { const path = window.location.pathname; let from = ''; if (path === '/games') from = 'library'; + else if (path.startsWith('/system/')) from = 'system'; else if (path.startsWith('/reports')) from = 'rom_reports'; else if (path.startsWith('/achievements')) from = 'achievements'; return `/game/${gameId}` + (from ? `?from=${from}` : ''); } +// ============================================================================= +// PREVIOUS / NEXT GAME (game page) +// ============================================================================= +// The Library and each console page save their filters and sort for this tab +// whenever they load; the game page reads the copy for the list it was opened +// from and asks /api/games/neighbors for the games either side. + +function gameNavKey(from, systemId) { + return from === 'library' ? 'retrodb.gameNav.library' : `retrodb.gameNav.system.${systemId}`; +} + +/** Called by AllGamesController with the list's filter and sort params. */ +function saveGameNavContext(systemId, params) { + try { + sessionStorage.setItem(gameNavKey(systemId ? 'system' : 'library', systemId), params.toString()); + } catch (e) { /* storage blocked: the game page uses the unfiltered list */ } +} + +async function initGameNav() { + const nav = document.getElementById('gameNav'); + if (!nav) return; + const { gameId, systemId, from } = nav.dataset; + const fallback = from === 'library' ? '' : `system=${encodeURIComponent(systemId)}`; + let saved = null; + try { saved = sessionStorage.getItem(gameNavKey(from, systemId)); } catch (e) { /* blocked */ } + + const neighbors = async (qs) => { + try { + const data = await API.get(`/api/games/neighbors?id=${encodeURIComponent(gameId)}${qs ? '&' + qs : ''}`); + return data && data.found ? data : null; + } catch (e) { + return null; + } + }; + // A game outside the saved filters (opened from search, or edited out of + // them) steps through the whole list instead. + let data = await neighbors(saved ?? fallback); + if (!data && saved !== null && saved !== fallback) data = await neighbors(fallback); + if (!data || data.total < 2) return; + + for (const dir of ['prev', 'next']) { + const link = nav.querySelector(`[data-nav="${dir}"]`); + const target = data[dir]; + if (!link || !target) continue; + link.href = `/game/${target.id}?from=${from}`; + link.title = target.title; + link.hidden = false; + } + nav.querySelector('.game-nav-position').textContent = + `${formatNumber(data.position)} / ${formatNumber(data.total)}`; + nav.hidden = false; +} + +document.addEventListener('DOMContentLoaded', initGameNav); + // ============================================================================= // GAME DETAIL MODAL // ============================================================================= @@ -226,21 +318,19 @@ const GameDetailModal = { // Details document.getElementById('gdmDeveloper').textContent = game.developer || '-'; document.getElementById('gdmPublisher').textContent = game.publisher || '-'; + + // Release date in each region, earliest first (written by the server + // in the user's language). + const releaseDates = game.release_dates || []; + document.getElementById('gdmReleaseDates').textContent = + releaseDates.map(rd => `${rd.region}: ${rd.date}`).join(' · '); + document.getElementById('gdmReleaseDatesRow').style.display = releaseDates.length ? '' : 'none'; document.getElementById('gdmGenre').textContent = tField(game.genre) || '-'; document.getElementById('gdmFranchise').textContent = game.franchise || '-'; // Ratings — admin sees all age ratings, users see preferred only const ratingsEl = document.getElementById('gdmRatings'); - let ratingsHtml = _buildAgeRatingBadgesHtml(game); - if (game.critic_score) { - const scoreClass = game.critic_score >= 75 ? 'score-good' : (game.critic_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `⭐ ${Math.round(game.critic_score)}`; - } - if (game.user_score) { - const userScoreClass = game.user_score >= 75 ? 'score-good' : (game.user_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `👤 ${Math.round(game.user_score)}`; - } - ratingsEl.innerHTML = ratingsHtml; + ratingsEl.innerHTML = _buildAgeRatingBadgesHtml(game) + _buildScoreBadgesHtml(game); // Completion status const completionEl = document.getElementById('gdmCompletion'); @@ -1069,12 +1159,8 @@ const GameEditModal = { // Update ratings in detail modal — admin sees all, users see preferred const ratingsEl = document.getElementById('gdmRatings'); - let ratingsHtml = _buildAgeRatingBadgesHtml(formData); - if (this.currentData.critic_score) { - const scoreClass = this.currentData.critic_score >= 75 ? 'score-good' : (this.currentData.critic_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `⭐ ${Math.round(this.currentData.critic_score)}`; - } - ratingsEl.innerHTML = ratingsHtml; + ratingsEl.innerHTML = _buildAgeRatingBadgesHtml(formData) + + _buildScoreBadgesHtml(this.currentData); // Refresh the game card in the list page with fresh data if (typeof AllGamesController !== 'undefined' && AllGamesController.refreshCards) { @@ -1758,11 +1844,11 @@ const GameEditModal = { const input = document.getElementById('gemFranchise'); if (toggle.checked) { - label.textContent = 'Standalone'; + label.textContent = t('Standalone'); input.style.display = 'none'; input.value = ''; } else { - label.textContent = 'Part of a Series'; + label.textContent = t('Part of a Series'); input.style.display = 'block'; } }, @@ -2036,6 +2122,7 @@ RetroDB.GameDetailModal = GameDetailModal; RetroDB.GameEditModal = GameEditModal; RetroDB.HLTBManager = HLTBManager; RetroDB.gameDetailUrl = gameDetailUrl; +RetroDB.saveGameNavContext = saveGameNavContext; // ============================================================================= // LEGACY FUNCTION EXPORTS (Backward Compatibility) @@ -2049,6 +2136,7 @@ Object.defineProperty(window, 'currentGameDetailId', { set: (v) => { GameDetailModal.currentId = v; } }); +window.rpcs3CompatBadgeHtml = rpcs3CompatBadgeHtml; window.openGameDetailModal = (gameId) => GameDetailModal.open(gameId); window.populateGameDetailModal = (game) => GameDetailModal.populate(game); window.showGameDetailError = (message) => GameDetailModal.showError(message); diff --git a/static/js/games.bundle.js b/static/js/games.bundle.js index b8795ff7..9f5dcab6 100644 --- a/static/js/games.bundle.js +++ b/static/js/games.bundle.js @@ -1116,6 +1116,42 @@ function _getPreferredRatingFromGame(game) { return null; } +/** + * Badge for a PS3 game's RPCS3 compatibility status ({status, date} from the + * server, read from the local RPCS3's list), or '' when there is none. + * The game page renders the same badge server-side. + */ +function rpcs3CompatBadgeHtml(compat) { + if (!compat || !compat.status) return ''; + const text = { + playable: [t('RPCS3: Playable'), t('Can be completed with playable performance and no game-breaking glitches.')], + ingame: [t('RPCS3: Ingame'), t('Gets in game, but cannot be finished, has serious glitches or runs too slowly.')], + intro: [t('RPCS3: Intro'), t('Shows an image but does not get past the menus.')], + loadable: [t('RPCS3: Loadable'), t('Shows a black screen with a frame rate in the window title.')], + nothing: [t('RPCS3: Nothing'), t('Does not start, or crashes the emulator.')], + }[compat.status]; + if (!text) return ''; + const title = compat.date ? `${text[1]} ${t('Last tested {date}.', {date: compat.date})}` : text[1]; + return `🧩 ${escapeHtml(text[0])}`; +} + +/** Score, sales and RPCS3 badges shown after the age ratings in the quick view. */ +function _buildScoreBadgesHtml(game) { + let html = ''; + if (game.critic_score) { + const scoreClass = game.critic_score >= 75 ? 'score-good' : (game.critic_score >= 50 ? 'score-mixed' : 'score-bad'); + html += `⭐ ${Math.round(game.critic_score)}`; + } + if (game.user_score) { + const userScoreClass = game.user_score >= 75 ? 'score-good' : (game.user_score >= 50 ? 'score-mixed' : 'score-bad'); + html += `👤 ${Math.round(game.user_score)}`; + } + if (game.units_sold) { + html += `📈 ${escapeHtml(t('{count} sold', {count: formatNumber(game.units_sold)}))}`; + } + return html + rpcs3CompatBadgeHtml(game.rpcs3_compat); +} + /** Build age-rating badge HTML (admin sees all, users see preferred only) */ function _buildAgeRatingBadgesHtml(game) { let html = ''; @@ -1151,11 +1187,58 @@ function gameDetailUrl(gameId) { const path = window.location.pathname; let from = ''; if (path === '/games') from = 'library'; + else if (path.startsWith('/system/')) from = 'system'; else if (path.startsWith('/reports')) from = 'rom_reports'; else if (path.startsWith('/achievements')) from = 'achievements'; return `/game/${gameId}` + (from ? `?from=${from}` : ''); } +function gameNavKey(from, systemId) { + return from === 'library' ? 'retrodb.gameNav.library' : `retrodb.gameNav.system.${systemId}`; +} + +/** Called by AllGamesController with the list's filter and sort params. */ +function saveGameNavContext(systemId, params) { + try { + sessionStorage.setItem(gameNavKey(systemId ? 'system' : 'library', systemId), params.toString()); + } catch (e) { /* storage blocked: the game page uses the unfiltered list */ } +} + +async function initGameNav() { + const nav = document.getElementById('gameNav'); + if (!nav) return; + const { gameId, systemId, from } = nav.dataset; + const fallback = from === 'library' ? '' : `system=${encodeURIComponent(systemId)}`; + let saved = null; + try { saved = sessionStorage.getItem(gameNavKey(from, systemId)); } catch (e) { /* blocked */ } + + const neighbors = async (qs) => { + try { + const data = await API.get(`/api/games/neighbors?id=${encodeURIComponent(gameId)}${qs ? '&' + qs : ''}`); + return data && data.found ? data : null; + } catch (e) { + return null; + } + }; + let data = await neighbors(saved ?? fallback); + if (!data && saved !== null && saved !== fallback) data = await neighbors(fallback); + if (!data || data.total < 2) return; + + for (const dir of ['prev', 'next']) { + const link = nav.querySelector(`[data-nav="${dir}"]`); + const target = data[dir]; + if (!link || !target) continue; + link.href = `/game/${target.id}?from=${from}`; + link.title = target.title; + link.hidden = false; + } + nav.querySelector('.game-nav-position').textContent = + `${formatNumber(data.position)} / ${formatNumber(data.total)}`; + nav.hidden = false; +} + +document.addEventListener('DOMContentLoaded', initGameNav); + const GameDetailModal = { cache: new Map(), currentId: null, @@ -1245,20 +1328,16 @@ const GameDetailModal = { document.getElementById('gdmDeveloper').textContent = game.developer || '-'; document.getElementById('gdmPublisher').textContent = game.publisher || '-'; + + const releaseDates = game.release_dates || []; + document.getElementById('gdmReleaseDates').textContent = + releaseDates.map(rd => `${rd.region}: ${rd.date}`).join(' · '); + document.getElementById('gdmReleaseDatesRow').style.display = releaseDates.length ? '' : 'none'; document.getElementById('gdmGenre').textContent = tField(game.genre) || '-'; document.getElementById('gdmFranchise').textContent = game.franchise || '-'; const ratingsEl = document.getElementById('gdmRatings'); - let ratingsHtml = _buildAgeRatingBadgesHtml(game); - if (game.critic_score) { - const scoreClass = game.critic_score >= 75 ? 'score-good' : (game.critic_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `⭐ ${Math.round(game.critic_score)}`; - } - if (game.user_score) { - const userScoreClass = game.user_score >= 75 ? 'score-good' : (game.user_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `👤 ${Math.round(game.user_score)}`; - } - ratingsEl.innerHTML = ratingsHtml; + ratingsEl.innerHTML = _buildAgeRatingBadgesHtml(game) + _buildScoreBadgesHtml(game); const completionEl = document.getElementById('gdmCompletion'); if (game.completion_status && game.completion_status !== 'not_started') { @@ -2004,12 +2083,8 @@ const GameEditModal = { } const ratingsEl = document.getElementById('gdmRatings'); - let ratingsHtml = _buildAgeRatingBadgesHtml(formData); - if (this.currentData.critic_score) { - const scoreClass = this.currentData.critic_score >= 75 ? 'score-good' : (this.currentData.critic_score >= 50 ? 'score-mixed' : 'score-bad'); - ratingsHtml += `⭐ ${Math.round(this.currentData.critic_score)}`; - } - ratingsEl.innerHTML = ratingsHtml; + ratingsEl.innerHTML = _buildAgeRatingBadgesHtml(formData) + + _buildScoreBadgesHtml(this.currentData); if (typeof AllGamesController !== 'undefined' && AllGamesController.refreshCards) { AllGamesController.refreshCards(this.currentData.id); @@ -2643,11 +2718,11 @@ const GameEditModal = { const input = document.getElementById('gemFranchise'); if (toggle.checked) { - label.textContent = 'Standalone'; + label.textContent = t('Standalone'); input.style.display = 'none'; input.value = ''; } else { - label.textContent = 'Part of a Series'; + label.textContent = t('Part of a Series'); input.style.display = 'block'; } }, @@ -2896,6 +2971,7 @@ RetroDB.GameDetailModal = GameDetailModal; RetroDB.GameEditModal = GameEditModal; RetroDB.HLTBManager = HLTBManager; RetroDB.gameDetailUrl = gameDetailUrl; +RetroDB.saveGameNavContext = saveGameNavContext; window.gameDetailCache = GameDetailModal.cache; window.currentGameDetailId = null; @@ -2904,6 +2980,7 @@ Object.defineProperty(window, 'currentGameDetailId', { set: (v) => { GameDetailModal.currentId = v; } }); +window.rpcs3CompatBadgeHtml = rpcs3CompatBadgeHtml; window.openGameDetailModal = (gameId) => GameDetailModal.open(gameId); window.populateGameDetailModal = (game) => GameDetailModal.populate(game); window.showGameDetailError = (message) => GameDetailModal.showError(message); diff --git a/templates/_modals/edit_modal.html b/templates/_modals/edit_modal.html index bb328612..47c000ad 100644 --- a/templates/_modals/edit_modal.html +++ b/templates/_modals/edit_modal.html @@ -43,7 +43,7 @@

📝 {{ _('Identity') }}

-