2 Commits

Author SHA1 Message Date
Ponte
4608eefe2f Release: promote preprod to prod (fix edit scroll jump) 2026-03-05 19:27:21 +01:00
Ponte
ccea6b0367 Release: promote preprod to prod (bulk actions + pagination) 2026-02-15 00:31:23 +01:00
3 changed files with 43 additions and 394 deletions

217
app.js
View File

@@ -46,7 +46,6 @@ const toolsCloseBtn = document.getElementById("toolsCloseBtn");
const gamesDrawer = document.getElementById("gamesDrawer"); const gamesDrawer = document.getElementById("gamesDrawer");
const gamesToggleBtn = document.getElementById("gamesToggleBtn"); const gamesToggleBtn = document.getElementById("gamesToggleBtn");
const gamesCloseBtn = document.getElementById("gamesCloseBtn"); const gamesCloseBtn = document.getElementById("gamesCloseBtn");
const drawerBackdrop = document.getElementById("drawerBackdrop");
const totalGamesCount = document.getElementById("totalGamesCount"); const totalGamesCount = document.getElementById("totalGamesCount");
const totalGamesValue = document.getElementById("totalGamesValue"); const totalGamesValue = document.getElementById("totalGamesValue");
const migrateBtn = document.getElementById("migrateBtn"); const migrateBtn = document.getElementById("migrateBtn");
@@ -61,15 +60,6 @@ const googleBackupBtn = document.getElementById("googleBackupBtn");
const googleRestoreBtn = document.getElementById("googleRestoreBtn"); const googleRestoreBtn = document.getElementById("googleRestoreBtn");
const quickSearchInput = document.getElementById("quickSearchInput"); const quickSearchInput = document.getElementById("quickSearchInput");
const quickSearchResults = document.getElementById("quickSearchResults"); const quickSearchResults = document.getElementById("quickSearchResults");
const topNavButtons = document.querySelectorAll(".topnav-btn[data-view]");
const gamesPanel = document.getElementById("gamesPanel");
const statsView = document.getElementById("statsView");
const scannerZone = document.getElementById("scannerZone");
const searchZone = document.getElementById("searchZone");
const statsTotalGames = document.getElementById("statsTotalGames");
const statsTotalValue = document.getElementById("statsTotalValue");
const statsLoanedGames = document.getElementById("statsLoanedGames");
const statsConsoleCount = document.getElementById("statsConsoleCount");
const loanedFilterBtn = document.getElementById("loanedFilterBtn"); const loanedFilterBtn = document.getElementById("loanedFilterBtn");
const bulkActionsBar = document.getElementById("bulkActionsBar"); const bulkActionsBar = document.getElementById("bulkActionsBar");
const bulkSelectPage = document.getElementById("bulkSelectPage"); const bulkSelectPage = document.getElementById("bulkSelectPage");
@@ -115,8 +105,6 @@ let selectedGameIds = new Set();
let currentPage = 1; let currentPage = 1;
let currentPageGameIds = []; let currentPageGameIds = [];
let currentTotalPages = 1; let currentTotalPages = 1;
let currentView = "catalogue";
let pendingFocusGameId = null;
// V2 is now the default UI. Use ?ui=v1 to force legacy mode if needed. // V2 is now the default UI. Use ?ui=v1 to force legacy mode if needed.
const uiParam = new URLSearchParams(window.location.search).get("ui"); const uiParam = new URLSearchParams(window.location.search).get("ui");
const uiV2Enabled = uiParam !== "v1"; const uiV2Enabled = uiParam !== "v1";
@@ -153,23 +141,19 @@ coverFileInput.addEventListener("change", async (event) => {
toolsToggleBtn.addEventListener("click", () => { toolsToggleBtn.addEventListener("click", () => {
gamesDrawer.classList.remove("open"); gamesDrawer.classList.remove("open");
toolsDrawer.classList.toggle("open"); toolsDrawer.classList.toggle("open");
syncDrawersUi();
}); });
toolsCloseBtn.addEventListener("click", () => { toolsCloseBtn.addEventListener("click", () => {
toolsDrawer.classList.remove("open"); toolsDrawer.classList.remove("open");
syncDrawersUi();
}); });
gamesToggleBtn.addEventListener("click", () => { gamesToggleBtn.addEventListener("click", () => {
toolsDrawer.classList.remove("open"); toolsDrawer.classList.remove("open");
gamesDrawer.classList.toggle("open"); gamesDrawer.classList.toggle("open");
syncDrawersUi();
}); });
gamesCloseBtn.addEventListener("click", () => { gamesCloseBtn.addEventListener("click", () => {
gamesDrawer.classList.remove("open"); gamesDrawer.classList.remove("open");
syncDrawersUi();
}); });
document.addEventListener("click", (event) => { document.addEventListener("click", (event) => {
@@ -188,28 +172,18 @@ document.addEventListener("click", (event) => {
} }
toolsDrawer.classList.remove("open"); toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open"); gamesDrawer.classList.remove("open");
syncDrawersUi();
}); });
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
if (event.key === "Escape") { if (event.key === "Escape") {
toolsDrawer.classList.remove("open"); toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open"); gamesDrawer.classList.remove("open");
syncDrawersUi();
if (scannerRunning) { if (scannerRunning) {
stopScanner("Camera arretee."); stopScanner("Camera arretee.");
} }
} }
}); });
if (drawerBackdrop) {
drawerBackdrop.addEventListener("click", () => {
toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open");
syncDrawersUi();
});
}
googleConnectBtn.addEventListener("click", async () => { googleConnectBtn.addEventListener("click", async () => {
try { try {
const payload = await apiRequest("/api/google/connect-url"); const payload = await apiRequest("/api/google/connect-url");
@@ -257,54 +231,13 @@ quickSearchInput.addEventListener("input", (event) => {
renderSearchResults(); renderSearchResults();
}); });
quickSearchResults.addEventListener("click", (event) => {
if (!(event.target instanceof Element)) {
return;
}
const target = event.target.closest(".search-hit[data-game-id]");
if (!(target instanceof HTMLElement)) {
return;
}
const gameId = normalizeText(target.dataset.gameId);
if (!gameId) {
return;
}
openGameFromSearchResult(gameId);
});
quickSearchResults.addEventListener("keydown", (event) => {
if (!(event.target instanceof Element)) {
return;
}
if (event.key !== "Enter" && event.key !== " ") {
return;
}
const target = event.target.closest(".search-hit[data-game-id]");
if (!(target instanceof HTMLElement)) {
return;
}
event.preventDefault();
const gameId = normalizeText(target.dataset.gameId);
if (!gameId) {
return;
}
openGameFromSearchResult(gameId);
});
loanedFilterBtn.addEventListener("click", () => { loanedFilterBtn.addEventListener("click", () => {
setCurrentView(currentView === "loans" ? "catalogue" : "loans"); showLoanedOnly = !showLoanedOnly;
resetPaging();
selectedGameIds.clear();
renderGames();
}); });
for (const navButton of topNavButtons) {
navButton.addEventListener("click", () => {
const view = navButton.dataset.view;
if (!view) {
return;
}
setCurrentView(view);
});
}
if (bulkSelectPage) { if (bulkSelectPage) {
bulkSelectPage.addEventListener("change", (event) => { bulkSelectPage.addEventListener("change", (event) => {
const checked = Boolean(event.target.checked); const checked = Boolean(event.target.checked);
@@ -952,7 +885,6 @@ restoreFileInput.addEventListener("change", async (event) => {
function render() { function render() {
renderV2Chrome(); renderV2Chrome();
renderViewChrome();
renderDataMode(); renderDataMode();
renderGoogleStatus(); renderGoogleStatus();
renderBrandTabs(); renderBrandTabs();
@@ -963,70 +895,6 @@ function render() {
renderCollectionStats(); renderCollectionStats();
} }
function setCurrentView(view) {
const allowed = new Set(["catalogue", "loans", "stats"]);
if (!allowed.has(view)) {
return;
}
currentView = view;
showLoanedOnly = view === "loans";
resetPaging();
selectedGameIds.clear();
toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open");
syncDrawersUi();
render();
}
function syncDrawersUi() {
if (!drawerBackdrop) {
return;
}
const anyDrawerOpen = toolsDrawer.classList.contains("open") || gamesDrawer.classList.contains("open");
drawerBackdrop.classList.toggle("hidden", !anyDrawerOpen);
}
function renderViewChrome() {
const isStats = currentView === "stats";
const isLoans = currentView === "loans";
for (const navButton of topNavButtons) {
navButton.classList.toggle("active", navButton.dataset.view === currentView);
}
if (gamesPanel) {
gamesPanel.classList.toggle("hidden", isStats);
}
if (statsView) {
statsView.classList.toggle("hidden", !isStats);
}
if (gameForm) {
gameForm.classList.toggle("hidden", isLoans || isStats || v2FormCollapsed);
}
if (scannerZone) {
scannerZone.classList.toggle("hidden", isLoans || isStats);
}
if (searchZone) {
searchZone.classList.toggle("hidden", isStats);
}
if (bulkActionsBar) {
bulkActionsBar.classList.toggle("hidden", isStats);
}
if (paginationBar) {
paginationBar.classList.toggle("hidden", isStats);
}
if (v2ToggleFormBtn) {
v2ToggleFormBtn.classList.toggle("hidden", isLoans || isStats);
}
if (v2QuickBackupBtn) {
v2QuickBackupBtn.classList.toggle("hidden", isStats);
}
if (loanedFilterBtn) {
loanedFilterBtn.textContent = isLoans ? "Retour catalogue" : "Aller a la vue prets";
loanedFilterBtn.classList.toggle("hidden", isStats);
}
}
function renderDataMode() { function renderDataMode() {
if (!dataModeInfo) { if (!dataModeInfo) {
return; return;
@@ -1172,24 +1040,9 @@ function renderCollectionStats() {
const value = typeof game.value === "number" ? game.value : Number(game.value); const value = typeof game.value === "number" ? game.value : Number(game.value);
return Number.isFinite(value) ? sum + value : sum; return Number.isFinite(value) ? sum + value : sum;
}, 0); }, 0);
const loanedCount = allGames.filter((game) => normalizeText(game.loanedTo)).length;
const activeConsoles = Object.values(state.gamesByConsole).filter((games) => Array.isArray(games) && games.length > 0)
.length;
totalGamesCount.textContent = String(totalCount); totalGamesCount.textContent = String(totalCount);
totalGamesValue.textContent = `${totalValue.toFixed(2)} EUR`; totalGamesValue.textContent = `${totalValue.toFixed(2)} EUR`;
if (statsTotalGames) {
statsTotalGames.textContent = String(totalCount);
}
if (statsTotalValue) {
statsTotalValue.textContent = `${totalValue.toFixed(2)} EUR`;
}
if (statsLoanedGames) {
statsLoanedGames.textContent = String(loanedCount);
}
if (statsConsoleCount) {
statsConsoleCount.textContent = String(activeConsoles);
}
} }
function showToast(message, type = "info", timeoutMs = 2600) { function showToast(message, type = "info", timeoutMs = 2600) {
@@ -1358,12 +1211,10 @@ function updateBulkAndPaginationUi(pageGames, totalFilteredCount) {
nextPageBtn.disabled = currentPage >= currentTotalPages; nextPageBtn.disabled = currentPage >= currentTotalPages;
} }
if (paginationBar) { if (paginationBar) {
const shouldHidePagination = currentView === "stats" || totalFilteredCount <= pageSizeForViewport(); paginationBar.classList.toggle("hidden", totalFilteredCount <= pageSizeForViewport());
paginationBar.classList.toggle("hidden", shouldHidePagination);
} }
if (bulkActionsBar) { if (bulkActionsBar) {
const shouldHideBulk = currentView === "stats" || totalFilteredCount === 0; bulkActionsBar.classList.toggle("hidden", totalFilteredCount === 0);
bulkActionsBar.classList.toggle("hidden", shouldHideBulk);
} }
} }
@@ -1416,7 +1267,7 @@ function renderSearchResults() {
] ]
.filter(Boolean) .filter(Boolean)
.join(" | "); .join(" | ");
return `<article class="search-hit" data-game-id="${escapeHtml(game.id)}" role="button" tabindex="0" title="Ouvrir la fiche du jeu"><strong>${escapeHtml(game.title || "")}</strong><p>${escapeHtml(meta)}</p></article>`; return `<article class="search-hit"><strong>${escapeHtml(game.title || "")}</strong><p>${escapeHtml(meta)}</p></article>`;
}) })
.join(""); .join("");
@@ -1428,37 +1279,6 @@ function renderSearchResults() {
quickSearchResults.innerHTML = `${header}${items}${more}`; quickSearchResults.innerHTML = `${header}${items}${more}`;
} }
function openGameFromSearchResult(gameId) {
const allGames = collectAllGames();
const game = allGames.find((item) => item.id === gameId);
if (!game) {
return;
}
setCurrentView("catalogue");
if (game.brand && state.brands[game.brand]) {
state.selectedBrand = game.brand;
}
if (game.consoleName) {
state.selectedConsole = game.consoleName;
}
// Narrow the catalogue to quickly open the target card and start inline edit.
v2ConsoleValue = normalizeText(game.consoleName);
v2GenreValue = "";
v2SearchTerm = normalizeText(game.title);
if (v2SearchInput) {
v2SearchInput.value = v2SearchTerm;
}
pendingFocusGameId = gameId;
inlineEditingGameId = gameId;
editingGameId = null;
resetPaging();
persist();
render();
}
function escapeHtml(text) { function escapeHtml(text) {
return String(text) return String(text)
.replaceAll("&", "&amp;") .replaceAll("&", "&amp;")
@@ -1592,15 +1412,15 @@ async function performBulkAction(action) {
function renderGames() { function renderGames() {
const selectedConsole = state.selectedConsole; const selectedConsole = state.selectedConsole;
const inV2 = uiV2Enabled; const inV2 = uiV2Enabled;
gameSectionTitle.textContent = gameSectionTitle.textContent = inV2
currentView === "loans"
? "Jeux pretes"
: inV2
? "Catalogue jeux" ? "Catalogue jeux"
: showLoanedOnly
? "Jeux pretes - Toutes consoles"
: selectedConsole : selectedConsole
? `Jeux - ${selectedConsole}` ? `Jeux - ${selectedConsole}`
: "Jeux"; : "Jeux";
gamesList.innerHTML = ""; gamesList.innerHTML = "";
loanedFilterBtn.textContent = showLoanedOnly ? "Voir tous les jeux" : "Voir jeux pretes";
if (!inV2 && !showLoanedOnly && !selectedConsole) { if (!inV2 && !showLoanedOnly && !selectedConsole) {
gamesList.innerHTML = '<p class="empty">Ajoute une section pour commencer.</p>'; gamesList.innerHTML = '<p class="empty">Ajoute une section pour commencer.</p>';
@@ -1663,12 +1483,6 @@ function renderGames() {
const totalFilteredCount = games.length; const totalFilteredCount = games.length;
const pageSize = pageSizeForViewport(); const pageSize = pageSizeForViewport();
if (pendingFocusGameId) {
const focusIndex = games.findIndex((game) => game.id === pendingFocusGameId);
if (focusIndex >= 0) {
currentPage = Math.floor(focusIndex / pageSize) + 1;
}
}
currentTotalPages = Math.max(1, Math.ceil(totalFilteredCount / pageSize)); currentTotalPages = Math.max(1, Math.ceil(totalFilteredCount / pageSize));
if (currentPage > currentTotalPages) { if (currentPage > currentTotalPages) {
currentPage = currentTotalPages; currentPage = currentTotalPages;
@@ -1687,7 +1501,6 @@ function renderGames() {
for (const game of pageGames) { for (const game of pageGames) {
const card = gameCardTemplate.content.cloneNode(true); const card = gameCardTemplate.content.cloneNode(true);
const article = card.querySelector(".game-card"); const article = card.querySelector(".game-card");
article.dataset.gameId = game.id;
article.classList.add(consoleThemeClass(game.consoleName)); article.classList.add(consoleThemeClass(game.consoleName));
if (inlineEditingGameId === game.id) { if (inlineEditingGameId === game.id) {
article.classList.add("editing"); article.classList.add("editing");
@@ -1795,14 +1608,6 @@ function renderGames() {
gamesList.append(card); gamesList.append(card);
} }
if (pendingFocusGameId) {
const focusedCard = gamesList.querySelector(`.game-card[data-game-id="${pendingFocusGameId}"]`);
if (focusedCard instanceof HTMLElement) {
focusedCard.scrollIntoView({ behavior: "smooth", block: "center" });
}
pendingFocusGameId = null;
}
} }
function startEditMode(game) { function startEditMode(game) {

View File

@@ -13,20 +13,8 @@
<link rel="stylesheet" href="styles.css" /> <link rel="stylesheet" href="styles.css" />
</head> </head>
<body> <body>
<header class="topbar"> <button id="toolsToggleBtn" type="button" class="tools-toggle-btn">Outils</button>
<div class="topbar-brand">
<p class="eyebrow">Collection Manager</p>
<h1>Collection Jeux Video</h1>
</div>
<nav class="topbar-nav" aria-label="Navigation principale">
<button type="button" class="topnav-btn active" data-view="catalogue">Catalogue</button>
<button type="button" class="topnav-btn" data-view="loans">Prets</button>
<button type="button" class="topnav-btn" data-view="stats">Statistiques</button>
<button id="toolsToggleBtn" type="button" class="topnav-btn">Outils</button>
</nav>
</header>
<button id="gamesToggleBtn" type="button" class="games-toggle-btn">Jeux</button> <button id="gamesToggleBtn" type="button" class="games-toggle-btn">Jeux</button>
<div id="drawerBackdrop" class="drawer-backdrop hidden" aria-hidden="true"></div>
<aside id="toolsDrawer" class="tools-drawer" aria-label="Menu outils"> <aside id="toolsDrawer" class="tools-drawer" aria-label="Menu outils">
<div class="tools-header"> <div class="tools-header">
<h2>Outils</h2> <h2>Outils</h2>
@@ -72,29 +60,7 @@
</div> </div>
</aside> </aside>
<main class="app-shell v3-layout"> <main class="app-shell">
<aside class="v3-sidebar panel">
<section class="sidebar-block">
<h2 class="sidebar-title">Plateformes et consoles</h2>
<form id="platformForm" class="grid-form">
<label>
Marque (ex: SONY)
<input id="brandInput" required placeholder="SONY" />
</label>
<label>
Console (ex: PlayStation 5)
<input id="consoleInput" required placeholder="PlayStation 5" />
</label>
<button type="submit">Ajouter section</button>
</form>
</section>
<section class="sidebar-block">
<div id="brandTabs" class="tabs" aria-label="Onglets marques"></div>
<div id="consoleTabs" class="tabs secondary" aria-label="Onglets consoles"></div>
</section>
</aside>
<section class="v3-main">
<div id="v2StickyBar" class="v2-sticky hidden"> <div id="v2StickyBar" class="v2-sticky hidden">
<div class="v2-sticky-title">Vue catalogue</div> <div class="v2-sticky-title">Vue catalogue</div>
<div id="v2StickyCount" class="v2-sticky-count">0 jeu affiche</div> <div id="v2StickyCount" class="v2-sticky-count">0 jeu affiche</div>
@@ -107,34 +73,32 @@
<header class="hero"> <header class="hero">
<div class="hero-copy"> <div class="hero-copy">
<p class="eyebrow">Catalogue perso</p> <p class="eyebrow">Catalogue perso</p>
<p class="subtitle">Gere ta collection, tes prets et la valeur de tes jeux en un seul endroit.</p> <h1>Collection Jeux Video</h1>
</div> </div>
</header> </header>
<section id="statsView" class="panel stats-view hidden">
<section class="panel platform-panel">
<div class="panel-header"> <div class="panel-header">
<h2>Statistiques collection</h2> <h2>Plateformes et consoles</h2>
</div>
<div class="stats-grid">
<article class="stat-card">
<p class="stat-label">Nombre total de jeux</p>
<p id="statsTotalGames" class="stat-value">0</p>
</article>
<article class="stat-card">
<p class="stat-label">Valeur totale estimee</p>
<p id="statsTotalValue" class="stat-value">0.00 EUR</p>
</article>
<article class="stat-card">
<p class="stat-label">Jeux pretes</p>
<p id="statsLoanedGames" class="stat-value">0</p>
</article>
<article class="stat-card">
<p class="stat-label">Consoles actives</p>
<p id="statsConsoleCount" class="stat-value">0</p>
</article>
</div> </div>
<form id="platformForm" class="grid-form">
<label>
Marque (ex: SONY)
<input id="brandInput" required placeholder="SONY" />
</label>
<label>
Console (ex: PlayStation 5)
<input id="consoleInput" required placeholder="PlayStation 5" />
</label>
<button type="submit">Ajouter section</button>
</form>
<div id="brandTabs" class="tabs" aria-label="Onglets marques"></div>
<div id="consoleTabs" class="tabs secondary" aria-label="Onglets consoles"></div>
</section> </section>
<section id="gamesPanel" class="panel games-panel"> <section class="panel games-panel">
<div class="panel-header"> <div class="panel-header">
<h2 id="gameSectionTitle">Jeux</h2> <h2 id="gameSectionTitle">Jeux</h2>
<p id="dataModeInfo" class="data-mode"></p> <p id="dataModeInfo" class="data-mode"></p>
@@ -166,7 +130,7 @@
</label> </label>
</div> </div>
<div class="games-actions-bar"> <div class="games-actions-bar">
<button id="loanedFilterBtn" type="button" class="btn-secondary">Basculer vue prets</button> <button id="loanedFilterBtn" type="button" class="btn-secondary">Voir jeux pretes</button>
</div> </div>
<div id="bulkActionsBar" class="bulk-actions-bar"> <div id="bulkActionsBar" class="bulk-actions-bar">
<label class="checkbox-row"> <label class="checkbox-row">
@@ -178,7 +142,7 @@
<button id="bulkReturnBtn" type="button" class="btn-secondary" disabled>Marquer rendu</button> <button id="bulkReturnBtn" type="button" class="btn-secondary" disabled>Marquer rendu</button>
<button id="bulkDeleteBtn" type="button" class="btn-inline danger" disabled>Supprimer</button> <button id="bulkDeleteBtn" type="button" class="btn-inline danger" disabled>Supprimer</button>
</div> </div>
<div id="scannerZone" class="scanner-zone"> <div class="scanner-zone">
<div class="scanner-header"> <div class="scanner-header">
<strong>Scan camera (mobile)</strong> <strong>Scan camera (mobile)</strong>
<p id="scannerStatus" class="scanner-status">Camera inactive.</p> <p id="scannerStatus" class="scanner-status">Camera inactive.</p>
@@ -191,7 +155,7 @@
<p id="scannerLastCode" class="scanner-last-code hidden"></p> <p id="scannerLastCode" class="scanner-last-code hidden"></p>
</div> </div>
<div id="searchZone" class="search-zone"> <div class="search-zone">
<label> <label>
Recherche rapide anti-doublon Recherche rapide anti-doublon
<input id="quickSearchInput" placeholder="Ex: Destiny, Final Fantasy, Zelda..." /> <input id="quickSearchInput" placeholder="Ex: Destiny, Final Fantasy, Zelda..." />
@@ -262,7 +226,6 @@
<button id="nextPageBtn" type="button" class="btn-secondary">Suivant</button> <button id="nextPageBtn" type="button" class="btn-secondary">Suivant</button>
</div> </div>
</section> </section>
</section>
</main> </main>
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div> <div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>

View File

@@ -27,49 +27,6 @@ body {
min-height: 100vh; min-height: 100vh;
} }
.topbar {
position: sticky;
top: 0;
z-index: 60;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
padding: 0.7rem 1rem;
border-bottom: 1px solid var(--border);
background: rgba(248, 250, 253, 0.92);
backdrop-filter: blur(8px);
}
.topbar-brand h1 {
margin: 0.1rem 0 0;
font-size: clamp(1.05rem, 1.8vw, 1.35rem);
}
.topbar-nav {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
}
.topnav-btn {
background: #dde7f3;
color: #1f324a;
border: 1px solid #cfd9e7;
border-radius: 10px;
padding: 0.48rem 0.72rem;
font-family: inherit;
font-weight: 600;
cursor: pointer;
}
.topnav-btn.active {
background: #0f6ab8;
color: #ffffff;
border-color: #0f6ab8;
}
.tools-toggle-btn { .tools-toggle-btn {
position: fixed; position: fixed;
top: 1rem; top: 1rem;
@@ -108,13 +65,6 @@ body {
transform: translateX(0); transform: translateX(0);
} }
.drawer-backdrop {
position: fixed;
inset: 0;
background: rgba(6, 12, 22, 0.42);
z-index: 35;
}
.games-drawer { .games-drawer {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -211,36 +161,12 @@ body {
} }
.app-shell { .app-shell {
width: min(1320px, 95vw); width: min(1100px, 94vw);
margin: 1rem auto 2rem; margin: 2rem auto;
display: grid; display: grid;
gap: 1rem; gap: 1rem;
} }
.v3-layout {
grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
align-items: start;
}
.v3-main {
display: grid;
gap: 1rem;
}
.v3-sidebar {
position: sticky;
top: 5rem;
}
.sidebar-block + .sidebar-block {
margin-top: 0.9rem;
}
.sidebar-title {
margin: 0 0 0.7rem;
font-size: 1rem;
}
.hero, .hero,
.panel { .panel {
background: var(--surface); background: var(--surface);
@@ -280,12 +206,6 @@ h1 {
color: var(--muted); color: var(--muted);
} }
.stats-grid {
display: grid;
gap: 0.75rem;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.panel { .panel {
padding: 1rem; padding: 1rem;
} }
@@ -398,16 +318,6 @@ h1 {
border-radius: 10px; border-radius: 10px;
padding: 0.5rem 0.6rem; padding: 0.5rem 0.6rem;
background: #ffffff; background: #ffffff;
cursor: pointer;
transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}
.search-hit:hover,
.search-hit:focus-visible {
border-color: #9fc3e6;
box-shadow: 0 0 0 3px rgba(70, 130, 180, 0.16);
transform: translateY(-1px);
outline: none;
} }
.search-hit strong { .search-hit strong {
@@ -909,7 +819,7 @@ body.ui-v2 .game-actions {
body.ui-v2 .hero { body.ui-v2 .hero {
position: sticky; position: sticky;
top: 5.3rem; top: 4.2rem;
z-index: 15; z-index: 15;
} }
@@ -951,22 +861,6 @@ body.ui-v2 .hero {
} }
@media (max-width: 900px) { @media (max-width: 900px) {
.topbar {
padding: 0.6rem 0.7rem;
}
.v3-layout {
grid-template-columns: 1fr;
}
.v3-sidebar {
position: static;
}
.stats-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.v2-toolbar { .v2-toolbar {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
@@ -987,19 +881,6 @@ body.ui-v2 .hero {
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.topbar-nav {
width: 100%;
}
.topnav-btn {
flex: 1 1 calc(50% - 0.3rem);
text-align: center;
}
.stats-grid {
grid-template-columns: 1fr;
}
.grid-form, .grid-form,
.game-form { .game-form {
grid-template-columns: 1fr; grid-template-columns: 1fr;