diff --git a/.gitignore b/.gitignore index cde8d4c..eac2525 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ cache/ __pycache__/ *.pyc lara-chess/ +node_modules/ +package-lock.json diff --git a/README.md b/README.md index 6501f9a..ec4c347 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Live-Überwachung von Lara Kiesewetters Partien bei der **ODJM (Offene Deutsche Jugendmeisterschaft)** 2026. Die App lädt automatisch PGN-Daten von der Deutschen Schachjugend, zeigt das aktuelle Spielbrett an und listet alle Partien von Lara. +![Screenshot](screenshot.png) + ## Features - **Live-Brett** – Visuelle Darstellung der aktuellen Partie mit chessboard.js diff --git a/build-less.js b/build-less.js new file mode 100644 index 0000000..467e1b1 --- /dev/null +++ b/build-less.js @@ -0,0 +1,29 @@ +const less = require('less'); +const fs = require('fs'); +const path = require('path'); + +const src = path.join(__dirname, 'style.less'); +const dest = path.join(__dirname, 'style.css'); + +fs.readFile(src, 'utf8', (err, data) => { + if (err) { + console.error('[LESS] Fehler beim Lesen:', err.message); + process.exit(1); + } + less.render(data, { + filename: src, + compress: false, + sourceMap: false, + }).then(output => { + fs.writeFile(dest, output.css, 'utf8', err => { + if (err) { + console.error('[LESS] Fehler beim Schreiben:', err.message); + process.exit(1); + } + console.log('[LESS] style.less → style.css erfolgreich kompiliert'); + }); + }).catch(err => { + console.error('[LESS] Kompilierungsfehler:', err.message); + process.exit(1); + }); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3cce36a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,13 @@ +import js from "@eslint/js"; +import globals from "globals"; +import json from "@eslint/json"; +import markdown from "@eslint/markdown"; +import css from "@eslint/css"; +import { defineConfig } from "eslint/config"; + +export default defineConfig([ + { files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } }, + { files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] }, + { files: ["**/*.md"], plugins: { markdown }, language: "markdown/commonmark", extends: ["markdown/recommended"] }, + { files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] }, +]); diff --git a/package.json b/package.json new file mode 100644 index 0000000..67886c2 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "build:css": "node build-less.js" + }, + "devDependencies": { + "@eslint/css": "^1.2.0", + "@eslint/js": "^10.0.1", + "@eslint/json": "^1.2.0", + "@eslint/markdown": "^8.0.2", + "eslint": "^10.4.0", + "globals": "^17.6.0", + "less": "^4.6.4" + } +} diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..f1b694d Binary files /dev/null and b/screenshot.png differ diff --git a/server.py b/server.py index 2613e89..e75c049 100644 --- a/server.py +++ b/server.py @@ -7,6 +7,7 @@ Serviert statische Dateien direkt. import http.server import socketserver import os +import subprocess PORT = int(os.environ.get("PORT", 8111)) @@ -21,6 +22,7 @@ class StaticHandler(http.server.BaseHTTPRequestHandler): content_types = { ".html": "text/html", ".css": "text/css", + ".less": "text/css", ".js": "application/javascript", ".json": "application/json", ".png": "image/png", @@ -46,6 +48,17 @@ class StaticHandler(http.server.BaseHTTPRequestHandler): print(f"[{self.log_date_time_string()}] {args[0]}") def main(): + # LESS → CSS beim Serverstart kompilieren + print("[BUILD] Kompiliere style.less -> style.css ...") + try: + result = subprocess.run(["node", "build-less.js"], capture_output=True, text=True, cwd=os.path.dirname(os.path.abspath(__file__))) + if result.returncode == 0: + print(f"[BUILD] {result.stdout.strip()}") + else: + print(f"[BUILD] FEHLER: {result.stderr.strip()}") + except FileNotFoundError: + print("[BUILD] node nicht gefunden – überspringe LESS-Kompilierung") + print("=" * 50) print(" [TROPHY] Lara Kiesewetter – Live Schachturnier") print("=" * 50) diff --git a/style.css b/style.css index 823c98a..11cea79 100644 --- a/style.css +++ b/style.css @@ -1,442 +1,390 @@ * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } - body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); - color: #e0e0e0; - min-height: 100vh; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); + color: #e0e0e0; + min-height: 100vh; } - header { - background: rgba(0, 0, 0, 0.4); - padding: 16px 24px; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 12px; - border-bottom: 2px solid #e94560; + background: rgba(0, 0, 0, 0.4); + padding: 16px 24px; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 12px; + border-bottom: 2px solid #e94560; } - header h1 { - font-size: 1.5rem; - color: #fff; - text-shadow: 0 0 10px rgba(233, 69, 96, 0.5); + font-size: 1.5rem; + color: #fff; + text-shadow: 0 0 10px rgba(233, 69, 96, 0.5); } - #status-bar { - display: flex; - align-items: center; - gap: 16px; - font-size: 0.85rem; - color: #aaa; + display: flex; + align-items: center; + gap: 16px; + font-size: 0.85rem; + color: #aaa; } - -#last-update, #refresh-timer { - font-family: 'Courier New', monospace; - font-size: 0.9rem; - color: #4ade80; - background: rgba(0, 0, 0, 0.4); - padding: 4px 10px; - border-radius: 6px; - border: 1px solid rgba(74, 222, 128, 0.2); +#last-update, +#refresh-timer { + font-family: 'Courier New', monospace; + font-size: 0.9rem; + color: #4ade80; + background: rgba(0, 0, 0, 0.4); + padding: 4px 10px; + border-radius: 6px; + border: 1px solid rgba(74, 222, 128, 0.2); } - #refresh-btn { - background: #e94560; - border: none; - color: white; - width: 36px; - height: 36px; - border-radius: 50%; - cursor: pointer; - font-size: 1.2rem; - transition: transform 0.3s, background 0.3s; + background: #e94560; + border: none; + color: white; + width: 36px; + height: 36px; + border-radius: 50%; + cursor: pointer; + font-size: 1.2rem; + transition: transform 0.3s, background 0.3s; } - #refresh-btn:hover { - background: #ff6b6b; - transform: rotate(180deg); + background: #ff6b6b; + transform: rotate(180deg); } - #main-content { - display: flex; - gap: 24px; - padding: 24px; - max-width: 1400px; - margin: 0 auto; - align-items: flex-start; + display: flex; + gap: 24px; + padding: 24px; + max-width: 1400px; + margin: 0 auto; + align-items: flex-start; } - /* Board Section */ #board-section { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; } - .player-info { - display: flex; - align-items: center; - gap: 12px; - width: 100%; - max-width: 500px; - padding: 12px 16px; - background: rgba(0, 0, 0, 0.3); - border-radius: 12px; - border: 2px solid transparent; - transition: border-color 0.3s, box-shadow 0.3s; + display: flex; + align-items: center; + gap: 12px; + width: 100%; + max-width: 500px; + padding: 12px 16px; + background: rgba(0, 0, 0, 0.3); + border-radius: 12px; + border: 2px solid transparent; + transition: border-color 0.3s, box-shadow 0.3s; } - .player-info.active { - border-color: #e94560; - box-shadow: 0 0 15px rgba(233, 69, 96, 0.3); + border-color: #e94560; + box-shadow: 0 0 15px rgba(233, 69, 96, 0.3); } - .player-avatar { - font-size: 2rem; - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - background: rgba(255, 255, 255, 0.1); - border-radius: 50%; - flex-shrink: 0; + font-size: 2rem; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + border-radius: 50%; + flex-shrink: 0; } - .player-details { - flex: 1; - display: flex; - flex-direction: column; - gap: 2px; + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; } - .player-name { - font-weight: 600; - font-size: 1.1rem; - color: #fff; + font-weight: 600; + font-size: 1.1rem; + color: #fff; } - .player-elo { - font-size: 0.85rem; - color: #aaa; + font-size: 0.85rem; + color: #aaa; } - .player-clock { - font-family: 'Courier New', monospace; - font-size: 1.3rem; - font-weight: bold; - background: rgba(0, 0, 0, 0.5); - padding: 6px 12px; - border-radius: 8px; - min-width: 100px; - text-align: center; - color: #4ade80; + font-family: 'Courier New', monospace; + font-size: 1.3rem; + font-weight: bold; + background: rgba(0, 0, 0, 0.5); + padding: 6px 12px; + border-radius: 8px; + min-width: 100px; + text-align: center; + color: #4ade80; } - #board { - width: 100%; - max-width: 500px; + width: 100%; + max-width: 500px; } - #pgn-panel { - width: 100%; - max-width: 500px; - background: rgba(0, 0, 0, 0.3); - border-radius: 12px; - padding: 12px 16px; + width: 100%; + max-width: 500px; + background: rgba(0, 0, 0, 0.3); + border-radius: 12px; + padding: 12px 16px; } - .pgn-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; - color: #e94560; - font-weight: bold; - font-size: 0.9rem; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + color: #e94560; + font-weight: bold; + font-size: 0.9rem; } - #copy-pgn-btn { - background: rgba(255, 255, 255, 0.1); - border: none; - color: #ccc; - width: 32px; - height: 32px; - border-radius: 6px; - cursor: pointer; - font-size: 1rem; - transition: background 0.2s; + background: rgba(255, 255, 255, 0.1); + border: none; + color: #ccc; + width: 32px; + height: 32px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + transition: background 0.2s; } - #copy-pgn-btn:hover { - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.2); } - #pgn-text { - font-family: 'Courier New', monospace; - font-size: 0.78rem; - line-height: 1.5; - color: #aaa; - white-space: pre-wrap; - word-break: break-all; - max-height: 160px; - overflow-y: auto; - padding: 8px; - background: rgba(0, 0, 0, 0.4); - border-radius: 6px; - user-select: text; + font-family: 'Courier New', monospace; + font-size: 0.78rem; + line-height: 1.5; + color: #aaa; + white-space: pre-wrap; + word-break: break-all; + max-height: 160px; + overflow-y: auto; + padding: 8px; + background: rgba(0, 0, 0, 0.4); + border-radius: 6px; + user-select: text; } - /* Info Section */ #info-section { - flex: 0 0 380px; - display: flex; - flex-direction: column; - gap: 20px; + flex: 0 0 380px; + display: flex; + flex-direction: column; + gap: 20px; } - #game-info h2 { - font-size: 1.3rem; - color: #e94560; - margin-bottom: 8px; + font-size: 1.3rem; + color: #e94560; + margin-bottom: 8px; } - #result-info { - font-size: 1.1rem; - padding: 8px 12px; - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; + font-size: 1.1rem; + padding: 8px 12px; + background: rgba(0, 0, 0, 0.3); + border-radius: 8px; } - -#moves-panel, #all-games-panel, #standings-panel { - background: rgba(0, 0, 0, 0.3); - border-radius: 12px; - padding: 16px; +#moves-panel, +#all-games-panel, +#standings-panel { + background: rgba(0, 0, 0, 0.3); + border-radius: 12px; + padding: 16px; } - -#moves-panel h3, #all-games-panel h3, #standings-panel h3 { - margin-bottom: 12px; - color: #e94560; - font-size: 1rem; +#moves-panel h3, +#all-games-panel h3, +#standings-panel h3 { + margin-bottom: 12px; + color: #e94560; + font-size: 1rem; } - #standings-content { - font-size: 0.9rem; - line-height: 1.6; + font-size: 0.9rem; + line-height: 1.6; } - #standings-content .standings-loading { - color: #888; - font-style: italic; + color: #888; + font-style: italic; } - .standings-row { - display: flex; - justify-content: space-between; - padding: 6px 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + display: flex; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); } - .standings-row:last-child { - border-bottom: none; + border-bottom: none; } - .standings-label { - color: #aaa; + color: #aaa; } - .standings-value { - color: #fff; - font-weight: 600; + color: #fff; + font-weight: 600; } - .standings-rank { - font-size: 2rem; - font-weight: bold; - color: #e94560; - text-align: center; - padding: 8px 0; + font-size: 2rem; + font-weight: bold; + color: #e94560; + text-align: center; + padding: 8px 0; } - .standings-rank-label { - font-size: 0.8rem; - color: #888; - text-align: center; + font-size: 0.8rem; + color: #888; + text-align: center; } - .standings-header { - text-align: center; - margin-bottom: 8px; - color: #ffd700; - font-size: 0.85rem; + text-align: center; + margin-bottom: 8px; + color: #ffd700; + font-size: 0.85rem; } - #moves-list { - max-height: 300px; - overflow-y: auto; - font-family: 'Courier New', monospace; - font-size: 0.95rem; - line-height: 1.8; - display: flex; - flex-wrap: wrap; - gap: 4px; + max-height: 300px; + overflow-y: auto; + font-family: 'Courier New', monospace; + font-size: 0.95rem; + line-height: 1.8; + display: flex; + flex-wrap: wrap; + gap: 4px; } - #moves-list .move-number { - color: #888; - font-weight: bold; + color: #888; + font-weight: bold; } - #moves-list .move { - color: #e0e0e0; - cursor: pointer; - padding: 2px 6px; - border-radius: 4px; - transition: background 0.2s; + color: #e0e0e0; + cursor: pointer; + padding: 2px 6px; + border-radius: 4px; + transition: background 0.2s; } - #moves-list .move:hover { - background: rgba(233, 69, 96, 0.3); + background: rgba(233, 69, 96, 0.3); } - #moves-list .move.current { - background: #e94560; - color: #fff; + background: #e94560; + color: #fff; } - #moves-list .lara-move { - color: #ffd700; - font-weight: bold; + color: #ffd700; + font-weight: bold; } - #moves-list .opp-move { - color: #aaa; + color: #aaa; } - #all-games-list { - display: flex; - flex-direction: column; - gap: 8px; + display: flex; + flex-direction: column; + gap: 8px; } - .game-entry { - padding: 10px 12px; - background: rgba(255, 255, 255, 0.05); - border-radius: 8px; - cursor: pointer; - transition: background 0.2s; - border-left: 3px solid transparent; + padding: 10px 12px; + background: rgba(255, 255, 255, 0.05); + border-radius: 8px; + cursor: pointer; + transition: background 0.2s; + border-left: 3px solid transparent; } - .game-entry:hover { - background: rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.1); } - .game-entry.active { - border-left-color: #e94560; - background: rgba(233, 69, 96, 0.15); + border-left-color: #e94560; + background: rgba(233, 69, 96, 0.15); } - .game-entry .game-round { - font-weight: bold; - color: #e94560; - font-size: 0.85rem; + font-weight: bold; + color: #e94560; + font-size: 0.85rem; } - .game-entry .game-players { - font-size: 0.9rem; - color: #ccc; - margin-top: 2px; + font-size: 0.9rem; + color: #ccc; + margin-top: 2px; } - .game-entry .game-result { - font-size: 0.8rem; - color: #888; - margin-top: 2px; + font-size: 0.8rem; + color: #888; + margin-top: 2px; } - /* Overlays */ -#loading-overlay, #error-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(26, 26, 46, 0.95); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - z-index: 1000; - gap: 16px; +#loading-overlay, +#error-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(26, 26, 46, 0.95); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 1000; + gap: 16px; } - -#loading-overlay p, #error-overlay p { - font-size: 1.2rem; +#loading-overlay p, +#error-overlay p { + font-size: 1.2rem; } - #error-overlay button { - padding: 10px 24px; - background: #e94560; - color: white; - border: none; - border-radius: 8px; - cursor: pointer; - font-size: 1rem; - margin-top: 8px; + padding: 10px 24px; + background: #e94560; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 1rem; + margin-top: 8px; } - .spinner { - width: 50px; - height: 50px; - border: 4px solid rgba(233, 69, 96, 0.3); - border-top-color: #e94560; - border-radius: 50%; - animation: spin 0.8s linear infinite; + width: 50px; + height: 50px; + border: 4px solid rgba(233, 69, 96, 0.3); + border-top-color: #e94560; + border-radius: 50%; + animation: spin 0.8s linear infinite; } - @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } - /* Scrollbar */ ::-webkit-scrollbar { - width: 6px; + width: 6px; } - ::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0.2); - border-radius: 3px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; } - ::-webkit-scrollbar-thumb { - background: #e94560; - border-radius: 3px; + background: #e94560; + border-radius: 3px; } - /* Responsive */ @media (max-width: 900px) { - #main-content { - flex-direction: column; - align-items: center; - } - - #info-section { - flex: none; - width: 100%; - max-width: 500px; - } - - header h1 { - font-size: 1.2rem; - } + #main-content { + flex-direction: column; + align-items: center; + } + #info-section { + flex: none; + width: 100%; + max-width: 500px; + } + header h1 { + font-size: 1.2rem; + } } diff --git a/style.less b/style.less new file mode 100644 index 0000000..0923b8f --- /dev/null +++ b/style.less @@ -0,0 +1,454 @@ +// Farbvariablen +@primary: #e94560; +@primary-glow: rgba(233, 69, 96, 0.3); +@bg-dark: rgba(0, 0, 0, 0.3); +@bg-darker: rgba(0, 0, 0, 0.4); +@text-muted: #aaa; +@text-light: #e0e0e0; +@text-white: #fff; +@accent-green: #4ade80; +@gold: #ffd700; +@font-mono: 'Courier New', monospace; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); + color: @text-light; + min-height: 100vh; +} + +header { + background: rgba(0, 0, 0, 0.4); + padding: 16px 24px; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 12px; + border-bottom: 2px solid @primary; + + h1 { + font-size: 1.5rem; + color: @text-white; + text-shadow: 0 0 10px rgba(233, 69, 96, 0.5); + } +} + +#status-bar { + display: flex; + align-items: center; + gap: 16px; + font-size: 0.85rem; + color: @text-muted; +} + +#last-update, #refresh-timer { + font-family: @font-mono; + font-size: 0.9rem; + color: @accent-green; + background: @bg-darker; + padding: 4px 10px; + border-radius: 6px; + border: 1px solid rgba(74, 222, 128, 0.2); +} + +#refresh-btn { + background: @primary; + border: none; + color: white; + width: 36px; + height: 36px; + border-radius: 50%; + cursor: pointer; + font-size: 1.2rem; + transition: transform 0.3s, background 0.3s; + + &:hover { + background: #ff6b6b; + transform: rotate(180deg); + } +} + +#main-content { + display: flex; + gap: 24px; + padding: 24px; + max-width: 1400px; + margin: 0 auto; + align-items: flex-start; +} + +/* Board Section */ +#board-section { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.player-info { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + max-width: 500px; + padding: 12px 16px; + background: @bg-dark; + border-radius: 12px; + border: 2px solid transparent; + transition: border-color 0.3s, box-shadow 0.3s; + + &.active { + border-color: @primary; + box-shadow: 0 0 15px @primary-glow; + } +} + +.player-avatar { + font-size: 2rem; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + border-radius: 50%; + flex-shrink: 0; +} + +.player-details { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; +} + +.player-name { + font-weight: 600; + font-size: 1.1rem; + color: @text-white; +} + +.player-elo { + font-size: 0.85rem; + color: @text-muted; +} + +.player-clock { + font-family: @font-mono; + font-size: 1.3rem; + font-weight: bold; + background: rgba(0, 0, 0, 0.5); + padding: 6px 12px; + border-radius: 8px; + min-width: 100px; + text-align: center; + color: @accent-green; +} + +#board { + width: 100%; + max-width: 500px; +} + +#pgn-panel { + width: 100%; + max-width: 500px; + background: @bg-dark; + border-radius: 12px; + padding: 12px 16px; +} + +.pgn-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + color: @primary; + font-weight: bold; + font-size: 0.9rem; +} + +#copy-pgn-btn { + background: rgba(255, 255, 255, 0.1); + border: none; + color: #ccc; + width: 32px; + height: 32px; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + transition: background 0.2s; + + &:hover { + background: rgba(255, 255, 255, 0.2); + } +} + +#pgn-text { + font-family: @font-mono; + font-size: 0.78rem; + line-height: 1.5; + color: @text-muted; + white-space: pre-wrap; + word-break: break-all; + max-height: 160px; + overflow-y: auto; + padding: 8px; + background: @bg-darker; + border-radius: 6px; + user-select: text; +} + +/* Info Section */ +#info-section { + flex: 0 0 380px; + display: flex; + flex-direction: column; + gap: 20px; +} + +#game-info h2 { + font-size: 1.3rem; + color: @primary; + margin-bottom: 8px; +} + +#result-info { + font-size: 1.1rem; + padding: 8px 12px; + background: @bg-dark; + border-radius: 8px; +} + +#moves-panel, #all-games-panel, #standings-panel { + background: @bg-dark; + border-radius: 12px; + padding: 16px; + + h3 { + margin-bottom: 12px; + color: @primary; + font-size: 1rem; + } +} + +#standings-content { + font-size: 0.9rem; + line-height: 1.6; + + .standings-loading { + color: #888; + font-style: italic; + } +} + +.standings-row { + display: flex; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + + &:last-child { + border-bottom: none; + } +} + +.standings-label { + color: @text-muted; +} + +.standings-value { + color: @text-white; + font-weight: 600; +} + +.standings-rank { + font-size: 2rem; + font-weight: bold; + color: @primary; + text-align: center; + padding: 8px 0; +} + +.standings-rank-label { + font-size: 0.8rem; + color: #888; + text-align: center; +} + +.standings-header { + text-align: center; + margin-bottom: 8px; + color: @gold; + font-size: 0.85rem; +} + +#moves-list { + max-height: 300px; + overflow-y: auto; + font-family: @font-mono; + font-size: 0.95rem; + line-height: 1.8; + display: flex; + flex-wrap: wrap; + gap: 4px; + + .move-number { + color: #888; + font-weight: bold; + } + + .move { + color: @text-light; + cursor: pointer; + padding: 2px 6px; + border-radius: 4px; + transition: background 0.2s; + + &:hover { + background: @primary-glow; + } + + &.current { + background: @primary; + color: @text-white; + } + } + + .lara-move { + color: @gold; + font-weight: bold; + } + + .opp-move { + color: @text-muted; + } +} + +#all-games-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.game-entry { + padding: 10px 12px; + background: rgba(255, 255, 255, 0.05); + border-radius: 8px; + cursor: pointer; + transition: background 0.2s; + border-left: 3px solid transparent; + + &:hover { + background: rgba(255, 255, 255, 0.1); + } + + &.active { + border-left-color: @primary; + background: rgba(233, 69, 96, 0.15); + } + + .game-round { + font-weight: bold; + color: @primary; + font-size: 0.85rem; + } + + .game-players { + font-size: 0.9rem; + color: #ccc; + margin-top: 2px; + } + + .game-result { + font-size: 0.8rem; + color: #888; + margin-top: 2px; + } +} + +/* Overlays */ +#loading-overlay, #error-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(26, 26, 46, 0.95); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 1000; + gap: 16px; + + p { + font-size: 1.2rem; + } +} + +#error-overlay button { + padding: 10px 24px; + background: @primary; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 1rem; + margin-top: 8px; +} + +.spinner { + width: 50px; + height: 50px; + border: 4px solid @primary-glow; + border-top-color: @primary; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; +} + +::-webkit-scrollbar-thumb { + background: @primary; + border-radius: 3px; +} + +/* Responsive */ +@media (max-width: 900px) { + #main-content { + flex-direction: column; + align-items: center; + } + + #info-section { + flex: none; + width: 100%; + max-width: 500px; + } + + header h1 { + font-size: 1.2rem; + } +}