Commit c13254bc authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: Phase 7.3 — replace 874 hardcoded hex colors with CSS custom properties

Updated tokens.css with aligned color values and added missing tokens
(bg-panel, bg-inset, bg-dark, gold-dark, purple-light, violet, green,
green-light, amber, red-light, red-soft, text-dim, text-light, info).

Updated theme.js applyColors map to support all new tokens via admin
branding panel.

Converted ~85% of all inline hex colors across 50+ JS files to use
var(--token) references. Remaining ~160 are canvas colors (which can't
use CSS vars) and unmapped game-specific colors.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 72ffeaf7
:root { :root {
--bg-deep: #050810; --bg-deep: #0a0a1a;
--bg-base: #0A1020; --bg-base: #0a0a14;
--bg-card: #121A2E; --bg-card: #1a1a2e;
--bg-elevated: #1A2440; --bg-elevated: #1e1e3a;
--bg-hover: #223050; --bg-hover: #2a2a4a;
--bg-panel: #0f0f1e;
--bg-inset: #0a1420;
--bg-dark: #1a1a1a;
--gold: #E4AC38; --gold: #E4AC38;
--gold-soft: #FFCC66; --gold-soft: #FFCC66;
--blue: #2082F0; --gold-dark: #D4940A;
--cyan: #00FFFF; --blue: #3B82F6;
--orange: #E84D1E; --cyan: #06B6D4;
--purple: #6834BE; --orange: #F97316;
--purple: #8B5CF6;
--purple-light: #a78bfa;
--violet: #7C3AED;
--emerald: #10B981; --emerald: #10B981;
--green: #22C55E;
--green-light: #4ade80;
--pink: #EC4899; --pink: #EC4899;
--amber: #F59E0B; --amber: #FBBF24;
--red: #EF4444; --red: #EF4444;
--red-light: #F87171;
--red-soft: #fca5a5;
--chess-primary: #2563EB; --chess-primary: #2563EB;
--chess-secondary: #F5B731; --chess-secondary: #F5B731;
...@@ -40,15 +50,18 @@ ...@@ -40,15 +50,18 @@
--bg-highlight: #10B981; --bg-highlight: #10B981;
--bg-selected: #FFD700; --bg-selected: #FFD700;
--text-primary: #F8FAFC; --text-primary: #f8fafc;
--text-secondary: #94A3B8; --text-secondary: #94a3b8;
--text-muted: #475569; --text-muted: #64748b;
--text-dim: #475569;
--text-light: #e2e8f0;
--success: #34D399; --success: #34D399;
--error: #EF4444; --error: #EF4444;
--warning: #F59E0B; --warning: #F59E0B;
--win: #34D399; --win: #34D399;
--loss: #F87171; --loss: #F87171;
--info: #3B82F6;
--border: rgba(255,255,255,0.06); --border: rgba(255,255,255,0.06);
--border-hover: rgba(255,255,255,0.12); --border-hover: rgba(255,255,255,0.12);
...@@ -87,4 +100,100 @@ ...@@ -87,4 +100,100 @@
--tab-height: 64px; --tab-height: 64px;
--safe-top: env(safe-area-inset-top, 0px); --safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px);
/* Layout: HUD */
--hud-avatar-size: 34px;
--hud-avatar-border: 2px;
--hud-icon-size: 16px;
--hud-bell-size: 18px;
--hud-stat-font: 14px;
--hud-stat-gap: 16px;
--hud-btn-size: 44px;
--hud-badge-size: 16px;
--hud-badge-font: 9px;
--hud-padding-x: 16px;
/* Layout: Tab Bar */
--tab-icon-size: 24px;
--tab-label-font: 11px;
--tab-item-gap: 3px;
--tab-item-padding-x: 12px;
--tab-item-padding-y: 8px;
--tab-item-min-size: 48px;
/* Layout: Home Page */
--home-padding: 16px;
--home-max-width: 340px;
--home-greeting-font: 16px;
--home-greeting-margin: 12px;
/* Layout: Quick Actions */
--quick-btn-icon-size: 42px;
--quick-btn-icon-radius: 12px;
--quick-btn-icon-font: 20px;
--quick-btn-label-font: 10px;
--quick-btn-gap: 8px;
--quick-btn-padding: 6px;
--quick-row-gap: 8px;
--quick-row-margin: 16px;
/* Layout: Game Tiles */
--game-tile-radius: 18px;
--game-tile-gap: 12px;
--game-tile-aspect: 1.1;
--game-tile-icon-size: 48px;
--game-tile-icon-font: 42px;
--game-tile-name-font: 16px;
--game-tile-content-gap: 8px;
/* Layout: Game Menu (Sheet) */
--sheet-radius: 28px;
--sheet-padding-x: 20px;
--sheet-padding-top: 28px;
--sheet-max-height: 75vh;
--sheet-bg: var(--bg-panel);
/* Layout: Menu Buttons */
--gm-btn-padding-y: 18px;
--gm-btn-padding-x: 16px;
--gm-btn-gap: 14px;
--gm-btn-radius: 16px;
--gm-btn-margin-bottom: 10px;
--gm-btn-icon-size: 52px;
--gm-btn-icon-radius: 14px;
--gm-btn-icon-font: 26px;
--gm-btn-title-font: 17px;
--gm-btn-sub-font: 11px;
--gm-btn-arrow-font: 22px;
/* Layout: Menu Chips */
--gm-chip-padding-y: 14px;
--gm-chip-padding-x: 8px;
--gm-chip-gap: 4px;
--gm-chip-radius: 12px;
--gm-chip-font: 11px;
--gm-chip-icon-font: 18px;
--gm-chips-grid-gap: 8px;
--gm-chips-margin-top: 14px;
/* Layout: Menu Close */
--menu-close-size: 36px;
--menu-close-font: 20px;
--menu-header-icon-size: 42px;
--menu-header-icon-radius: 12px;
--menu-header-icon-font: 22px;
--menu-header-title-font: 22px;
--menu-header-gap: 10px;
--menu-header-margin: 20px;
/* Layout: Bot Cards */
--bot-card-avatar-size: 56px;
--bot-card-avatar-border: 2px;
--bot-card-name-font: 14px;
--bot-card-sub-font: 11px;
--bot-card-accent-height: 3px;
--bot-grid-gap: 12px;
--bot-back-btn-size: 44px;
--bot-back-btn-font: 18px;
--bot-title-font: 18px;
} }
...@@ -188,7 +188,7 @@ function updateTournamentBadge(pending) { ...@@ -188,7 +188,7 @@ function updateTournamentBadge(pending) {
if (!badge) { if (!badge) {
badge = document.createElement('div'); badge = document.createElement('div');
badge.className = 'tour-badge'; badge.className = 'tour-badge';
badge.style.cssText = 'position:absolute;top:2px;right:8px;width:8px;height:8px;background:#E4AC38;border-radius:50%;'; badge.style.cssText = 'position:absolute;top:2px;right:8px;width:8px;height:8px;background:var(--gold);border-radius:50%;';
tourTab.style.position = 'relative'; tourTab.style.position = 'relative';
tourTab.appendChild(badge); tourTab.appendChild(badge);
} }
......
...@@ -8,7 +8,7 @@ particleContainer.style.cssText = 'position:fixed;inset:0;pointer-events:none;z- ...@@ -8,7 +8,7 @@ particleContainer.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-
document.body.appendChild(particleContainer); document.body.appendChild(particleContainer);
export function burst(x, y, options = {}) { export function burst(x, y, options = {}) {
const { count = 12, colors = ['#E4AC38', '#FFCC66', '#FFE082'], size = 8, spread = 120, duration = 800, gravity = true, type = 'circle' } = options; const { count = 12, colors = ['var(--gold)', 'var(--gold-soft)', '#FFE082'], size = 8, spread = 120, duration = 800, gravity = true, type = 'circle' } = options;
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const el = document.createElement('div'); const el = document.createElement('div');
...@@ -38,15 +38,15 @@ export function burst(x, y, options = {}) { ...@@ -38,15 +38,15 @@ export function burst(x, y, options = {}) {
} }
export function coinBurst(x, y, count = 8) { export function coinBurst(x, y, count = 8) {
burst(x, y, { count, colors: ['#E4AC38', '#FFCC66', '#FFD700', '#FFC107'], size: 10, spread: 100, type: 'circle' }); burst(x, y, { count, colors: ['var(--gold)', 'var(--gold-soft)', 'var(--gold-soft)', '#FFC107'], size: 10, spread: 100, type: 'circle' });
} }
export function starBurst(x, y, count = 6) { export function starBurst(x, y, count = 6) {
burst(x, y, { count, colors: ['#E4AC38', '#FFCC66', '#FFF'], size: 8, spread: 80, type: 'star' }); burst(x, y, { count, colors: ['var(--gold)', 'var(--gold-soft)', '#FFF'], size: 8, spread: 80, type: 'star' });
} }
export function confetti(x, y, count = 30) { export function confetti(x, y, count = 30) {
burst(x, y, { count, colors: ['#EF4444', '#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899'], size: 8, spread: 200, duration: 1200, type: 'square' }); burst(x, y, { count, colors: ['var(--error)', 'var(--blue)', 'var(--emerald)', 'var(--warning)', 'var(--purple)', 'var(--pink)'], size: 8, spread: 200, duration: 1200, type: 'square' });
} }
// ============ COIN FLY TO TARGET ============ // ============ COIN FLY TO TARGET ============
...@@ -61,7 +61,7 @@ export function coinFlyTo(fromX, fromY, targetSelector, count = 5) { ...@@ -61,7 +61,7 @@ export function coinFlyTo(fromX, fromY, targetSelector, count = 5) {
const el = document.createElement('div'); const el = document.createElement('div');
const startX = fromX + (Math.random() - 0.5) * 40; const startX = fromX + (Math.random() - 0.5) * 40;
const startY = fromY + (Math.random() - 0.5) * 40; const startY = fromY + (Math.random() - 0.5) * 40;
el.style.cssText = `position:fixed;left:${startX}px;top:${startY}px;width:12px;height:12px;background:#E4AC38;border-radius:50%;box-shadow:0 0 6px #E4AC38;z-index:9999;pointer-events:none;`; el.style.cssText = `position:fixed;left:${startX}px;top:${startY}px;width:12px;height:12px;background:var(--gold);border-radius:50%;box-shadow:0 0 6px var(--gold);z-index:9999;pointer-events:none;`;
particleContainer.appendChild(el); particleContainer.appendChild(el);
el.animate([ el.animate([
...@@ -125,7 +125,7 @@ export function hapticSuccess() { haptic([10, 50, 10, 50, 30]); } ...@@ -125,7 +125,7 @@ export function hapticSuccess() { haptic([10, 50, 10, 50, 30]); }
export function hapticError() { haptic([50, 30, 50]); } export function hapticError() { haptic([50, 30, 50]); }
// ============ GLOW / PULSE ============ // ============ GLOW / PULSE ============
export function pulseElement(el, color = '#E4AC38', duration = 400) { export function pulseElement(el, color = 'var(--gold)', duration = 400) {
if (!el) return; if (!el) return;
el.animate([ el.animate([
{ transform: 'scale(1)', boxShadow: `0 0 0px ${color}` }, { transform: 'scale(1)', boxShadow: `0 0 0px ${color}` },
...@@ -134,7 +134,7 @@ export function pulseElement(el, color = '#E4AC38', duration = 400) { ...@@ -134,7 +134,7 @@ export function pulseElement(el, color = '#E4AC38', duration = 400) {
], { duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' }); ], { duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' });
} }
export function glowElement(el, color = '#E4AC38') { export function glowElement(el, color = 'var(--gold)') {
if (!el) return; if (!el) return;
el.style.boxShadow = `0 0 12px ${color}`; el.style.boxShadow = `0 0 12px ${color}`;
el.style.transition = 'box-shadow 0.3s'; el.style.transition = 'box-shadow 0.3s';
...@@ -204,7 +204,7 @@ export function screenFlash(color = 'rgba(255,255,255,0.2)', duration = 400) { ...@@ -204,7 +204,7 @@ export function screenFlash(color = 'rgba(255,255,255,0.2)', duration = 400) {
// ============ FLOATING / BREATHING ============ // ============ FLOATING / BREATHING ============
export function breathe(el, options = {}) { export function breathe(el, options = {}) {
if (!el) return; if (!el) return;
const { scale = 1.02, duration = 2000, glow = false, glowColor = '#E4AC38' } = options; const { scale = 1.02, duration = 2000, glow = false, glowColor = 'var(--gold)' } = options;
const keyframes = glow const keyframes = glow
? [{ transform: 'scale(1)', boxShadow: `0 0 4px ${glowColor}` }, { transform: `scale(${scale})`, boxShadow: `0 0 16px ${glowColor}` }] ? [{ transform: 'scale(1)', boxShadow: `0 0 4px ${glowColor}` }, { transform: `scale(${scale})`, boxShadow: `0 0 16px ${glowColor}` }]
: [{ transform: 'scale(1)' }, { transform: `scale(${scale})` }]; : [{ transform: 'scale(1)' }, { transform: `scale(${scale})` }];
......
...@@ -44,14 +44,14 @@ export function showOpponentDisconnect() { ...@@ -44,14 +44,14 @@ export function showOpponentDisconnect() {
show(` show(`
<div style="text-align:center;padding:32px;max-width:300px;"> <div style="text-align:center;padding:32px;max-width:300px;">
<div style="width:64px;height:64px;margin:0 auto 16px;border-radius:50%;background:#1e1e3a;display:flex;align-items:center;justify-content:center;"> <div style="width:64px;height:64px;margin:0 auto 16px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;">
<div style="width:16px;height:16px;border:3px solid #FBBF24;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></div> <div style="width:16px;height:16px;border:3px solid var(--amber);border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></div>
</div> </div>
<div style="font-size:18px;font-weight:700;color:#f8fafc;margin-bottom:8px;">${t('match.disconnected_title')}</div> <div style="font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:8px;">${t('match.disconnected_title')}</div>
<div style="font-size:13px;color:#94a3b8;margin-bottom:16px;">${t('match.disconnected_waiting')}</div> <div style="font-size:13px;color:var(--text-secondary);margin-bottom:16px;">${t('match.disconnected_waiting')}</div>
<div id="abandon-countdown" style="font-size:32px;font-weight:800;color:#FBBF24;font-family:Inter,monospace;margin-bottom:16px;">60</div> <div id="abandon-countdown" style="font-size:32px;font-weight:800;color:var(--amber);font-family:Inter,monospace;margin-bottom:16px;">60</div>
<div style="font-size:11px;color:#64748b;">${t('match.disconnected_auto_win')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('match.disconnected_auto_win')}</div>
<button id="claim-win-btn" style="margin-top:16px;padding:10px 24px;background:#E4AC38;border:none;border-radius:8px;color:#1a1a1a;font-weight:700;font-size:13px;cursor:pointer;opacity:0.5;pointer-events:none;">${t('match.claim_early_win')}</button> <button id="claim-win-btn" style="margin-top:16px;padding:10px 24px;background:var(--gold);border:none;border-radius:8px;color:var(--bg-dark);font-weight:700;font-size:13px;cursor:pointer;opacity:0.5;pointer-events:none;">${t('match.claim_early_win')}</button>
</div> </div>
<style>@keyframes spin { to { transform: rotate(360deg); } }</style> <style>@keyframes spin { to { transform: rotate(360deg); } }</style>
`); `);
...@@ -89,7 +89,7 @@ export function showOpponentReconnect() { ...@@ -89,7 +89,7 @@ export function showOpponentReconnect() {
hide(); hide();
// Brief toast // Brief toast
showToast(t('match.opponent_returned'), '#34D399'); showToast(t('match.opponent_returned'), 'var(--success)');
audio.play('notification'); audio.play('notification');
} }
...@@ -103,9 +103,9 @@ function showAutoWin() { ...@@ -103,9 +103,9 @@ function showAutoWin() {
show(` show(`
<div style="text-align:center;padding:32px;max-width:300px;"> <div style="text-align:center;padding:32px;max-width:300px;">
<div style="font-size:56px;margin-bottom:12px;animation:float 2s ease-in-out infinite;">${emoji('trophy', '🏆', 56)}</div> <div style="font-size:56px;margin-bottom:12px;animation:float 2s ease-in-out infinite;">${emoji('trophy', '🏆', 56)}</div>
<div style="font-size:22px;font-weight:800;color:#34D399;margin-bottom:8px;">${t('match.you_won')}</div> <div style="font-size:22px;font-weight:800;color:var(--success);margin-bottom:8px;">${t('match.you_won')}</div>
<div style="font-size:14px;color:#94a3b8;margin-bottom:20px;">${t('match.opponent_left')}</div> <div style="font-size:14px;color:var(--text-secondary);margin-bottom:20px;">${t('match.opponent_left')}</div>
<button id="abandon-back-btn" style="padding:12px 32px;background:linear-gradient(135deg,#E4AC38,#FFCC66);border:none;border-radius:10px;color:#1a1a1a;font-weight:700;font-size:15px;cursor:pointer;">${t('match.return')}</button> <button id="abandon-back-btn" style="padding:12px 32px;background:linear-gradient(135deg,var(--gold),var(--gold-soft));border:none;border-radius:10px;color:var(--bg-dark);font-weight:700;font-size:15px;cursor:pointer;">${t('match.return')}</button>
</div> </div>
`); `);
...@@ -124,14 +124,14 @@ function showAutoWin() { ...@@ -124,14 +124,14 @@ function showAutoWin() {
export function showConnectionLost() { export function showConnectionLost() {
show(` show(`
<div style="text-align:center;padding:32px;max-width:280px;"> <div style="text-align:center;padding:32px;max-width:280px;">
<div style="width:48px;height:48px;margin:0 auto 16px;border-radius:50%;background:#EF4444;display:flex;align-items:center;justify-content:center;"> <div style="width:48px;height:48px;margin:0 auto 16px;border-radius:50%;background:var(--error);display:flex;align-items:center;justify-content:center;">
<span style="font-size:24px;">${emoji('warning', '⚠️', 24)}</span> <span style="font-size:24px;">${emoji('warning', '⚠️', 24)}</span>
</div> </div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;margin-bottom:6px;">${t('match.connection_lost')}</div> <div style="font-size:16px;font-weight:700;color:var(--text-primary);margin-bottom:6px;">${t('match.connection_lost')}</div>
<div style="font-size:13px;color:#94a3b8;">${t('match.reconnecting')}</div> <div style="font-size:13px;color:var(--text-secondary);">${t('match.reconnecting')}</div>
<div style="margin-top:16px;"> <div style="margin-top:16px;">
<div style="width:120px;height:4px;background:#1e1e3a;border-radius:2px;margin:0 auto;overflow:hidden;"> <div style="width:120px;height:4px;background:var(--bg-elevated);border-radius:2px;margin:0 auto;overflow:hidden;">
<div style="width:30%;height:100%;background:#FBBF24;border-radius:2px;animation:loading 1.5s ease-in-out infinite;"></div> <div style="width:30%;height:100%;background:var(--amber);border-radius:2px;animation:loading 1.5s ease-in-out infinite;"></div>
</div> </div>
</div> </div>
</div> </div>
...@@ -142,15 +142,15 @@ export function showConnectionLost() { ...@@ -142,15 +142,15 @@ export function showConnectionLost() {
// ========== CONNECTION RESTORED ========== // ========== CONNECTION RESTORED ==========
export function showConnectionRestored() { export function showConnectionRestored() {
hide(); hide();
showToast(t('match.reconnected'), '#34D399'); showToast(t('match.reconnected'), 'var(--success)');
} }
// ========== RECONNECTING (tab refresh recovery) ========== // ========== RECONNECTING (tab refresh recovery) ==========
export function showReconnecting() { export function showReconnecting() {
show(` show(`
<div style="text-align:center;padding:32px;"> <div style="text-align:center;padding:32px;">
<div style="width:48px;height:48px;margin:0 auto 16px;border:3px solid #E4AC38;border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></div> <div style="width:48px;height:48px;margin:0 auto 16px;border:3px solid var(--gold);border-top-color:transparent;border-radius:50%;animation:spin 1s linear infinite;"></div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;">${t('match.restoring')}</div> <div style="font-size:16px;font-weight:700;color:var(--text-primary);">${t('match.restoring')}</div>
</div> </div>
<style>@keyframes spin { to { transform: rotate(360deg); } }</style> <style>@keyframes spin { to { transform: rotate(360deg); } }</style>
`); `);
...@@ -158,7 +158,7 @@ export function showReconnecting() { ...@@ -158,7 +158,7 @@ export function showReconnecting() {
// ========== YOUR TURN REMINDER (if idle too long) ========== // ========== YOUR TURN REMINDER (if idle too long) ==========
export function showTurnReminder() { export function showTurnReminder() {
showToast(t('match.your_turn_warning'), '#FBBF24'); showToast(t('match.your_turn_warning'), 'var(--amber)');
juice.hapticMedium(); juice.hapticMedium();
} }
...@@ -169,8 +169,8 @@ export function showOpponentThinking() { ...@@ -169,8 +169,8 @@ export function showOpponentThinking() {
const el = document.createElement('div'); const el = document.createElement('div');
el.id = 'thinking-indicator'; el.id = 'thinking-indicator';
el.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);background:rgba(30,30,58,0.95);padding:6px 14px;border-radius:20px;font-size:12px;color:#94a3b8;z-index:400;display:flex;align-items:center;gap:6px;border:1px solid rgba(255,255,255,0.06);'; el.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);background:rgba(30,30,58,0.95);padding:6px 14px;border-radius:20px;font-size:12px;color:var(--text-secondary);z-index:400;display:flex;align-items:center;gap:6px;border:1px solid var(--border);';
el.innerHTML = `<div style="width:8px;height:8px;border:2px solid #94a3b8;border-top-color:transparent;border-radius:50%;animation:spin 0.8s linear infinite;"></div> ${t('match.opponent_thinking')}`; el.innerHTML = `<div style="width:8px;height:8px;border:2px solid var(--text-secondary);border-top-color:transparent;border-radius:50%;animation:spin 0.8s linear infinite;"></div> ${t('match.opponent_thinking')}`;
document.body.appendChild(el); document.body.appendChild(el);
} }
...@@ -179,9 +179,9 @@ export function hideOpponentThinking() { ...@@ -179,9 +179,9 @@ export function hideOpponentThinking() {
} }
// ========== TOAST (brief notification) ========== // ========== TOAST (brief notification) ==========
function showToast(message, color = '#f8fafc') { function showToast(message, color = 'var(--text-primary)') {
const toast = document.createElement('div'); const toast = document.createElement('div');
toast.style.cssText = `position:fixed;top:70px;left:50%;transform:translateX(-50%);background:#1a1a2e;border:1px solid ${color}44;padding:10px 20px;border-radius:12px;font-size:13px;font-weight:600;color:${color};z-index:600;box-shadow:0 4px 20px rgba(0,0,0,0.5);animation:toastIn 0.4s cubic-bezier(0.34,1.56,0.64,1);`; toast.style.cssText = `position:fixed;top:70px;left:50%;transform:translateX(-50%);background:var(--bg-card);border:1px solid var(--border);padding:10px 20px;border-radius:12px;font-size:13px;font-weight:600;color:${color};z-index:600;box-shadow:0 4px 20px rgba(0,0,0,0.5);animation:toastIn 0.4s cubic-bezier(0.34,1.56,0.64,1);`;
toast.textContent = message; toast.textContent = message;
document.body.appendChild(toast); document.body.appendChild(toast);
setTimeout(() => { setTimeout(() => {
...@@ -192,17 +192,17 @@ function showToast(message, color = '#f8fafc') { ...@@ -192,17 +192,17 @@ function showToast(message, color = '#f8fafc') {
// ========== BOT REPLACEMENT ========== // ========== BOT REPLACEMENT ==========
export function showBotReplacement() { export function showBotReplacement() {
showToast(t('match.bot_replacement'), '#FBBF24'); showToast(t('match.bot_replacement'), 'var(--amber)');
juice.hapticLight(); juice.hapticLight();
} }
// ========== TURN TIMED OUT ========== // ========== TURN TIMED OUT ==========
export function showTurnTimedOut(isMyTimeout) { export function showTurnTimedOut(isMyTimeout) {
if (isMyTimeout) { if (isMyTimeout) {
showToast(t('match.your_time_expired'), '#EF4444'); showToast(t('match.your_time_expired'), 'var(--error)');
juice.hapticMedium(); juice.hapticMedium();
} else { } else {
showToast(t('match.opponent_time_expired'), '#34D399'); showToast(t('match.opponent_time_expired'), 'var(--success)');
juice.hapticLight(); juice.hapticLight();
} }
} }
......
...@@ -30,7 +30,7 @@ function ensureContainer() { ...@@ -30,7 +30,7 @@ function ensureContainer() {
transform:scale(0.85) translateY(12px); transform:scale(0.85) translateY(12px);
opacity:0; opacity:0;
max-width:320px;width:calc(100% - 48px); max-width:320px;width:calc(100% - 48px);
background:#1a1a2e; background:var(--bg-card);
border:1px solid rgba(255,255,255,0.08); border:1px solid rgba(255,255,255,0.08);
border-radius:20px; border-radius:20px;
padding:28px 24px 20px; padding:28px 24px 20px;
...@@ -89,7 +89,7 @@ export function confirm(message, options = {}) { ...@@ -89,7 +89,7 @@ export function confirm(message, options = {}) {
title = '', title = '',
confirmText = t('common.confirm'), confirmText = t('common.confirm'),
cancelText = t('common.cancel'), cancelText = t('common.cancel'),
confirmColor = '#E4AC38', confirmColor = 'var(--gold)',
danger = false, danger = false,
icon = '' icon = ''
} = options; } = options;
...@@ -97,22 +97,22 @@ export function confirm(message, options = {}) { ...@@ -97,22 +97,22 @@ export function confirm(message, options = {}) {
ensureContainer(); ensureContainer();
audio.play('click'); audio.play('click');
const btnColor = danger ? '#EF4444' : confirmColor; const btnColor = danger ? 'var(--error)' : confirmColor;
modalEl.innerHTML = ` modalEl.innerHTML = `
${icon ? `<div style="font-size:48px;margin-bottom:12px;animation:modalIconPop 400ms cubic-bezier(0.34,1.56,0.64,1) both;">${icon}</div>` : ''} ${icon ? `<div style="font-size:48px;margin-bottom:12px;animation:modalIconPop 400ms cubic-bezier(0.34,1.56,0.64,1) both;">${icon}</div>` : ''}
${title ? `<div style="font-size:18px;font-weight:800;color:#f8fafc;margin-bottom:8px;">${title}</div>` : ''} ${title ? `<div style="font-size:18px;font-weight:800;color:var(--text-primary);margin-bottom:8px;">${title}</div>` : ''}
<div style="font-size:15px;color:#cbd5e1;line-height:1.5;margin-bottom:24px;${!title && !icon ? 'margin-top:4px;' : ''}">${message}</div> <div style="font-size:15px;color:var(--text-light);line-height:1.5;margin-bottom:24px;${!title && !icon ? 'margin-top:4px;' : ''}">${message}</div>
<div style="display:flex;gap:10px;"> <div style="display:flex;gap:10px;">
<button id="modal-cancel" style=" <button id="modal-cancel" style="
flex:1;padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,0.1); flex:1;padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,0.1);
background:rgba(255,255,255,0.04);color:#94a3b8;font-size:14px;font-weight:600; background:rgba(255,255,255,0.04);color:var(--text-secondary);font-size:14px;font-weight:600;
cursor:pointer;font-family:inherit; cursor:pointer;font-family:inherit;
will-change:transform;transition:transform 100ms ease,background 100ms ease; will-change:transform;transition:transform 100ms ease,background 100ms ease;
">${cancelText}</button> ">${cancelText}</button>
<button id="modal-confirm" style=" <button id="modal-confirm" style="
flex:1;padding:14px 16px;border-radius:12px;border:none; flex:1;padding:14px 16px;border-radius:12px;border:none;
background:${btnColor};color:#1a1a1a;font-size:14px;font-weight:700; background:${btnColor};color:var(--bg-dark);font-size:14px;font-weight:700;
cursor:pointer;font-family:inherit; cursor:pointer;font-family:inherit;
will-change:transform;transition:transform 100ms ease; will-change:transform;transition:transform 100ms ease;
">${confirmText}</button> ">${confirmText}</button>
...@@ -171,11 +171,11 @@ export function alert(message, options = {}) { ...@@ -171,11 +171,11 @@ export function alert(message, options = {}) {
modalEl.innerHTML = ` modalEl.innerHTML = `
${icon ? `<div style="font-size:48px;margin-bottom:12px;animation:modalIconPop 400ms cubic-bezier(0.34,1.56,0.64,1) both;">${icon}</div>` : ''} ${icon ? `<div style="font-size:48px;margin-bottom:12px;animation:modalIconPop 400ms cubic-bezier(0.34,1.56,0.64,1) both;">${icon}</div>` : ''}
${title ? `<div style="font-size:18px;font-weight:800;color:#f8fafc;margin-bottom:8px;">${title}</div>` : ''} ${title ? `<div style="font-size:18px;font-weight:800;color:var(--text-primary);margin-bottom:8px;">${title}</div>` : ''}
<div style="font-size:15px;color:#cbd5e1;line-height:1.5;margin-bottom:24px;">${message}</div> <div style="font-size:15px;color:var(--text-light);line-height:1.5;margin-bottom:24px;">${message}</div>
<button id="modal-ok" style=" <button id="modal-ok" style="
width:100%;padding:14px 16px;border-radius:12px;border:none; width:100%;padding:14px 16px;border-radius:12px;border:none;
background:#E4AC38;color:#1a1a1a;font-size:14px;font-weight:700; background:var(--gold);color:var(--bg-dark);font-size:14px;font-weight:700;
cursor:pointer;font-family:inherit; cursor:pointer;font-family:inherit;
will-change:transform;transition:transform 100ms ease; will-change:transform;transition:transform 100ms ease;
">${buttonText}</button> ">${buttonText}</button>
......
...@@ -24,19 +24,19 @@ export function renderOpponentBar(container, opponent, options = {}) { ...@@ -24,19 +24,19 @@ export function renderOpponentBar(container, opponent, options = {}) {
const bar = document.createElement('div'); const bar = document.createElement('div');
bar.id = 'mp-opponent-bar'; bar.id = 'mp-opponent-bar';
bar.style.cssText = 'display:flex;align-items:center;gap:10px;padding:8px 12px;background:#0f0f1e;cursor:pointer;'; bar.style.cssText = 'display:flex;align-items:center;gap:10px;padding:8px 12px;background:var(--bg-panel);cursor:pointer;';
bar.innerHTML = ` bar.innerHTML = `
<div style="position:relative;"> <div style="position:relative;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;"> <div style="width:36px;height:36px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;">
${opponent.avatar_url ? `<img src="${opponent.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 14)} ${opponent.avatar_url ? `<img src="${opponent.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 14)}
</div> </div>
<div id="mp-conn-dot" style="position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;background:#34D399;border:2px solid #0f0f1e;"></div> <div id="mp-conn-dot" style="position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;background:var(--success);border:2px solid var(--bg-panel);"></div>
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${opponent.display_name || opponent.username || t('common.opponent')}</div> <div style="font-size:13px;font-weight:600;color:var(--text-primary);">${opponent.display_name || opponent.username || t('common.opponent')}</div>
${showRating ? `<div style="font-size:11px;color:#64748b;">${emoji('star', '⭐', 11)} ${opponent.rating || opponent.elo_rapid || '1200'}</div>` : ''} ${showRating ? `<div style="font-size:11px;color:var(--text-muted);">${emoji('star', '⭐', 11)} ${opponent.rating || opponent.elo_rapid || '1200'}</div>` : ''}
</div> </div>
<div id="mp-opponent-status" style="font-size:10px;color:#64748b;"></div> <div id="mp-opponent-status" style="font-size:10px;color:var(--text-muted);"></div>
`; `;
// Tap to show profile actions // Tap to show profile actions
...@@ -57,17 +57,17 @@ function showOpponentActions(container, opponent) { ...@@ -57,17 +57,17 @@ function showOpponentActions(container, opponent) {
const menu = document.createElement('div'); const menu = document.createElement('div');
menu.id = 'mp-opponent-menu'; menu.id = 'mp-opponent-menu';
menu.style.cssText = 'position:absolute;top:50px;right:12px;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:8px;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,0.5);display:flex;flex-direction:column;gap:4px;min-width:140px;'; menu.style.cssText = 'position:absolute;top:50px;right:12px;background:var(--bg-card);border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:8px;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,0.5);display:flex;flex-direction:column;gap:4px;min-width:140px;';
menu.innerHTML = ` menu.innerHTML = `
<button class="mp-action" data-action="profile">${emoji('person', '👤', 12)} ${t('mp.profile')}</button> <button class="mp-action" data-action="profile">${emoji('person', '👤', 12)} ${t('mp.profile')}</button>
<button class="mp-action" data-action="friend">${emoji('add_friend', '➕', 12)} ${t('mp.add_friend')}</button> <button class="mp-action" data-action="friend">${emoji('add_friend', '➕', 12)} ${t('mp.add_friend')}</button>
<button class="mp-action" data-action="mute">${emoji('mute', '🔇', 12)} ${t('block.mute')}</button> <button class="mp-action" data-action="mute">${emoji('mute', '🔇', 12)} ${t('block.mute')}</button>
<button class="mp-action" data-action="block" style="color:#EF4444;">${emoji('block', '🚫', 12)} ${t('block.block')}</button> <button class="mp-action" data-action="block" style="color:var(--error);">${emoji('block', '🚫', 12)} ${t('block.block')}</button>
<button class="mp-action" data-action="report" style="color:#EF4444;">${emoji('warning', '⚠️', 12)} ${t('mp.report')}</button> <button class="mp-action" data-action="report" style="color:var(--error);">${emoji('warning', '⚠️', 12)} ${t('mp.report')}</button>
`; `;
const style = document.createElement('style'); const style = document.createElement('style');
style.textContent = '.mp-action{background:none;border:none;color:#e2e8f0;font-size:12px;font-weight:500;padding:8px 12px;text-align:right;cursor:pointer;border-radius:6px;font-family:inherit;width:100%;transition:background 0.1s;}.mp-action:hover{background:rgba(255,255,255,0.05);}.mp-action:active{background:rgba(255,255,255,0.1);}'; style.textContent = '.mp-action{background:none;border:none;color:var(--text-light);font-size:12px;font-weight:500;padding:8px 12px;text-align:right;cursor:pointer;border-radius:6px;font-family:inherit;width:100%;transition:background 0.1s;}.mp-action:hover{background:rgba(255,255,255,0.05);}.mp-action:active{background:rgba(255,255,255,0.1);}';
menu.appendChild(style); menu.appendChild(style);
menu.querySelector('[data-action="friend"]').addEventListener('click', async () => { menu.querySelector('[data-action="friend"]').addEventListener('click', async () => {
...@@ -77,10 +77,10 @@ function showOpponentActions(container, opponent) { ...@@ -77,10 +77,10 @@ function showOpponentActions(container, opponent) {
const result = await addFriendFromGame(opponent.id); const result = await addFriendFromGame(opponent.id);
if (result === true) { if (result === true) {
btn.textContent = t('common.sent'); btn.textContent = t('common.sent');
btn.style.color = '#34D399'; btn.style.color = 'var(--success)';
} else { } else {
btn.textContent = result || t('common.already_sent'); btn.textContent = result || t('common.already_sent');
btn.style.color = '#64748b'; btn.style.color = 'var(--text-muted)';
} }
juice.hapticLight(); juice.hapticLight();
setTimeout(() => menu.remove(), 1500); setTimeout(() => menu.remove(), 1500);
...@@ -93,7 +93,7 @@ function showOpponentActions(container, opponent) { ...@@ -93,7 +93,7 @@ function showOpponentActions(container, opponent) {
try { try {
await net.post('friends.php', { action: 'mute', target_id: opponent.id }); await net.post('friends.php', { action: 'mute', target_id: opponent.id });
btn.textContent = '✓ ' + t('block.muted'); btn.textContent = '✓ ' + t('block.muted');
btn.style.color = '#64748b'; btn.style.color = 'var(--text-muted)';
} catch (e) {} } catch (e) {}
juice.hapticLight(); juice.hapticLight();
setTimeout(() => menu.remove(), 1000); setTimeout(() => menu.remove(), 1000);
...@@ -173,7 +173,7 @@ export function checkForEmote(gameState, myUserId) { ...@@ -173,7 +173,7 @@ export function checkForEmote(gameState, myUserId) {
export function updateConnectionStatus(isConnected) { export function updateConnectionStatus(isConnected) {
const dot = document.getElementById('mp-conn-dot'); const dot = document.getElementById('mp-conn-dot');
if (dot) { if (dot) {
dot.style.background = isConnected ? '#34D399' : '#EF4444'; dot.style.background = isConnected ? 'var(--success)' : 'var(--error)';
} }
lastOpponentPing = Date.now(); lastOpponentPing = Date.now();
} }
...@@ -188,15 +188,15 @@ export function startDisconnectWatch(matchId, matchType, timeoutMs = 60000) { ...@@ -188,15 +188,15 @@ export function startDisconnectWatch(matchId, matchType, timeoutMs = 60000) {
if (elapsed > timeoutMs) { if (elapsed > timeoutMs) {
// Opponent disconnected too long — claim win // Opponent disconnected too long — claim win
if (dot) dot.style.background = '#EF4444'; if (dot) dot.style.background = 'var(--error)';
if (status) status.textContent = t('game.opponent_disconnected'); if (status) status.textContent = t('game.opponent_disconnected');
clearInterval(disconnectTimer); clearInterval(disconnectTimer);
// Could auto-claim win here // Could auto-claim win here
} else if (elapsed > 15000) { } else if (elapsed > 15000) {
if (dot) dot.style.background = '#FBBF24'; if (dot) dot.style.background = 'var(--amber)';
if (status) status.textContent = t('game.weak_connection'); if (status) status.textContent = t('game.weak_connection');
} else { } else {
if (dot) dot.style.background = '#34D399'; if (dot) dot.style.background = 'var(--success)';
if (status) status.textContent = ''; if (status) status.textContent = '';
} }
}, 5000); }, 5000);
......
...@@ -77,16 +77,16 @@ function showPlayerPopup(container, profile) { ...@@ -77,16 +77,16 @@ function showPlayerPopup(container, profile) {
const popup = document.createElement('div'); const popup = document.createElement('div');
popup.id = 'pp-popup'; popup.id = 'pp-popup';
popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:220px;'; popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:var(--bg-card);border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:220px;';
popup.innerHTML = ` popup.innerHTML = `
<div style="width:56px;height:56px;border-radius:50%;background:#2a2a4a;margin:0 auto 10px;display:flex;align-items:center;justify-content:center;font-size:26px;overflow:hidden;"> <div style="width:56px;height:56px;border-radius:50%;background:var(--bg-hover);margin:0 auto 10px;display:flex;align-items:center;justify-content:center;font-size:26px;overflow:hidden;">
${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 26)} ${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 26)}
</div> </div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;">${profile.display_name || profile.username}</div> <div style="font-size:16px;font-weight:700;color:var(--text-primary);">${profile.display_name || profile.username}</div>
<div style="font-size:12px;color:#64748b;margin:4px 0 16px;">Level ${profile.level || 1} · ${profile.elo_rapid || 1200} ELO</div> <div style="font-size:12px;color:var(--text-muted);margin:4px 0 16px;">Level ${profile.level || 1} · ${profile.elo_rapid || 1200} ELO</div>
<div style="display:flex;gap:8px;justify-content:center;"> <div style="display:flex;gap:8px;justify-content:center;">
<button id="pp-add" style="padding:8px 18px;background:#2563EB;border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;">${t('social.add_btn')}</button> <button id="pp-add" style="padding:8px 18px;background:var(--chess-primary);border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;">${t('social.add_btn')}</button>
<button id="pp-close" style="padding:8px 18px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.08);border-radius:8px;color:#94a3b8;font-size:12px;cursor:pointer;">${t('common.close')}</button> <button id="pp-close" style="padding:8px 18px;background:var(--border);border:1px solid rgba(255,255,255,0.08);border-radius:8px;color:var(--text-secondary);font-size:12px;cursor:pointer;">${t('common.close')}</button>
</div> </div>
`; `;
document.body.appendChild(popup); document.body.appendChild(popup);
...@@ -96,7 +96,7 @@ function showPlayerPopup(container, profile) { ...@@ -96,7 +96,7 @@ function showPlayerPopup(container, profile) {
const success = await mp.addFriendFromGame(profile.id); const success = await mp.addFriendFromGame(profile.id);
if (success) { if (success) {
popup.querySelector('#pp-add').textContent = t('common.done'); popup.querySelector('#pp-add').textContent = t('common.done');
popup.querySelector('#pp-add').style.background = '#34D399'; popup.querySelector('#pp-add').style.background = 'var(--success)';
} }
setTimeout(() => popup.remove(), 1000); setTimeout(() => popup.remove(), 1000);
}); });
...@@ -114,21 +114,21 @@ export function injectStyles() { ...@@ -114,21 +114,21 @@ export function injectStyles() {
style.id = 'pp-styles'; style.id = 'pp-styles';
style.textContent = ` style.textContent = `
.player-panel { display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:10px;border:2px solid transparent;transition:all 0.3s; } .player-panel { display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:10px;border:2px solid transparent;transition:all 0.3s; }
.player-panel.active { border-color:var(--gold, #E4AC38);background:rgba(228,172,56,0.05); } .player-panel.active { border-color:var(--gold);background:rgba(228,172,56,0.05); }
.pp-avatar { position:relative;width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:visible; } .pp-avatar { position:relative;width:36px;height:36px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;overflow:visible; }
.pp-avatar img { width:100%;height:100%;object-fit:cover;border-radius:50%; } .pp-avatar img { width:100%;height:100%;object-fit:cover;border-radius:50%; }
.pp-avatar-fallback { font-size:16px; } .pp-avatar-fallback { font-size:16px; }
.pp-status-dot { position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;border:2px solid #0f0f1e;background:#64748b; } .pp-status-dot { position:absolute;bottom:-1px;right:-1px;width:10px;height:10px;border-radius:50%;border:2px solid var(--bg-panel);background:var(--text-muted); }
.pp-status-dot.online { background:#34D399; } .pp-status-dot.online { background:var(--success); }
.pp-status-dot.weak { background:#FBBF24; } .pp-status-dot.weak { background:var(--amber); }
.pp-status-dot.offline { background:#EF4444; } .pp-status-dot.offline { background:var(--error); }
.pp-info { flex:1; } .pp-info { flex:1; }
.pp-name { font-size:13px;font-weight:600;color:#f8fafc; } .pp-name { font-size:13px;font-weight:600;color:var(--text-primary); }
.pp-meta { display:flex;gap:6px;align-items:center;margin-top:2px; } .pp-meta { display:flex;gap:6px;align-items:center;margin-top:2px; }
.pp-level { font-size:10px;color:#64748b; } .pp-level { font-size:10px;color:var(--text-muted); }
.pp-tier { font-size:12px; } .pp-tier { font-size:12px; }
.pp-rating { font-size:11px;color:#94a3b8;font-family:Inter,monospace; } .pp-rating { font-size:11px;color:var(--text-secondary);font-family:Inter,monospace; }
.pp-turn-indicator { width:8px;height:8px;border-radius:50%;background:#E4AC38;animation:pulse 1s ease-in-out infinite; } .pp-turn-indicator { width:8px;height:8px;border-radius:50%;background:var(--gold);animation:pulse 1s ease-in-out infinite; }
`; `;
document.head.appendChild(style); document.head.appendChild(style);
} }
...@@ -32,15 +32,36 @@ function applyColors() { ...@@ -32,15 +32,36 @@ function applyColors() {
bg_base: '--bg-base', bg_base: '--bg-base',
bg_card: '--bg-card', bg_card: '--bg-card',
bg_elevated: '--bg-elevated', bg_elevated: '--bg-elevated',
bg_hover: '--bg-hover',
bg_panel: '--bg-panel',
bg_inset: '--bg-inset',
bg_dark: '--bg-dark',
gold: '--gold', gold: '--gold',
gold_soft: '--gold-soft', gold_soft: '--gold-soft',
gold_dark: '--gold-dark',
blue: '--blue', blue: '--blue',
cyan: '--cyan', cyan: '--cyan',
orange: '--orange',
purple: '--purple', purple: '--purple',
purple_light: '--purple-light',
violet: '--violet',
emerald: '--emerald',
green: '--green',
green_light: '--green-light',
pink: '--pink',
amber: '--amber',
red: '--red',
red_light: '--red-light',
red_soft: '--red-soft',
success: '--success', success: '--success',
error: '--error', error: '--error',
warning: '--warning',
info: '--info',
text_primary: '--text-primary', text_primary: '--text-primary',
text_secondary: '--text-secondary', text_secondary: '--text-secondary',
text_muted: '--text-muted',
text_dim: '--text-dim',
text_light: '--text-light',
chess_primary: '--chess-primary', chess_primary: '--chess-primary',
chess_secondary: '--chess-secondary', chess_secondary: '--chess-secondary',
domino_primary: '--domino-primary', domino_primary: '--domino-primary',
...@@ -108,6 +129,55 @@ function applyAnimations() { ...@@ -108,6 +129,55 @@ function applyAnimations() {
if (themeData.btn_shadow) root.setProperty('--btn-shadow', themeData.btn_shadow); if (themeData.btn_shadow) root.setProperty('--btn-shadow', themeData.btn_shadow);
if (themeData.card_border_width) root.setProperty('--card-border-width', themeData.card_border_width + 'px'); if (themeData.card_border_width) root.setProperty('--card-border-width', themeData.card_border_width + 'px');
if (themeData.input_radius) root.setProperty('--r-input', themeData.input_radius + 'px'); if (themeData.input_radius) root.setProperty('--r-input', themeData.input_radius + 'px');
const layoutPx = {
hud_height: '--hud-height', hud_avatar_size: '--hud-avatar-size',
hud_avatar_border: '--hud-avatar-border', hud_icon_size: '--hud-icon-size',
hud_bell_size: '--hud-bell-size', hud_stat_font: '--hud-stat-font',
hud_stat_gap: '--hud-stat-gap', hud_btn_size: '--hud-btn-size',
hud_badge_size: '--hud-badge-size', hud_badge_font: '--hud-badge-font',
hud_padding_x: '--hud-padding-x',
tab_height: '--tab-height', tab_icon_size: '--tab-icon-size',
tab_label_font: '--tab-label-font', tab_item_gap: '--tab-item-gap',
tab_item_padding_x: '--tab-item-padding-x', tab_item_padding_y: '--tab-item-padding-y',
tab_item_min_size: '--tab-item-min-size',
home_padding: '--home-padding', home_max_width: '--home-max-width',
home_greeting_font: '--home-greeting-font', home_greeting_margin: '--home-greeting-margin',
quick_btn_icon_size: '--quick-btn-icon-size', quick_btn_icon_radius: '--quick-btn-icon-radius',
quick_btn_icon_font: '--quick-btn-icon-font', quick_btn_label_font: '--quick-btn-label-font',
quick_btn_gap: '--quick-btn-gap', quick_btn_padding: '--quick-btn-padding',
quick_row_gap: '--quick-row-gap', quick_row_margin: '--quick-row-margin',
game_tile_radius: '--game-tile-radius', game_tile_gap: '--game-tile-gap',
game_tile_icon_size: '--game-tile-icon-size', game_tile_icon_font: '--game-tile-icon-font',
game_tile_name_font: '--game-tile-name-font', game_tile_content_gap: '--game-tile-content-gap',
sheet_radius: '--sheet-radius', sheet_padding_x: '--sheet-padding-x',
sheet_padding_top: '--sheet-padding-top',
gm_btn_padding_y: '--gm-btn-padding-y', gm_btn_padding_x: '--gm-btn-padding-x',
gm_btn_gap: '--gm-btn-gap', gm_btn_radius: '--gm-btn-radius',
gm_btn_margin_bottom: '--gm-btn-margin-bottom', gm_btn_icon_size: '--gm-btn-icon-size',
gm_btn_icon_radius: '--gm-btn-icon-radius', gm_btn_icon_font: '--gm-btn-icon-font',
gm_btn_title_font: '--gm-btn-title-font', gm_btn_sub_font: '--gm-btn-sub-font',
gm_btn_arrow_font: '--gm-btn-arrow-font',
gm_chip_padding_y: '--gm-chip-padding-y', gm_chip_padding_x: '--gm-chip-padding-x',
gm_chip_gap: '--gm-chip-gap', gm_chip_radius: '--gm-chip-radius',
gm_chip_font: '--gm-chip-font', gm_chip_icon_font: '--gm-chip-icon-font',
gm_chips_grid_gap: '--gm-chips-grid-gap', gm_chips_margin_top: '--gm-chips-margin-top',
menu_close_size: '--menu-close-size', menu_close_font: '--menu-close-font',
menu_header_icon_size: '--menu-header-icon-size', menu_header_icon_radius: '--menu-header-icon-radius',
menu_header_icon_font: '--menu-header-icon-font', menu_header_title_font: '--menu-header-title-font',
menu_header_gap: '--menu-header-gap', menu_header_margin: '--menu-header-margin',
bot_card_avatar_size: '--bot-card-avatar-size', bot_card_avatar_border: '--bot-card-avatar-border',
bot_card_name_font: '--bot-card-name-font', bot_card_sub_font: '--bot-card-sub-font',
bot_card_accent_height: '--bot-card-accent-height', bot_grid_gap: '--bot-grid-gap',
bot_back_btn_size: '--bot-back-btn-size', bot_back_btn_font: '--bot-back-btn-font',
bot_title_font: '--bot-title-font',
};
for (const [key, cssVar] of Object.entries(layoutPx)) {
if (themeData[key]) root.setProperty(cssVar, themeData[key] + 'px');
}
if (themeData.game_tile_aspect) root.setProperty('--game-tile-aspect', themeData.game_tile_aspect);
if (themeData.sheet_max_height) root.setProperty('--sheet-max-height', themeData.sheet_max_height + 'vh');
if (themeData.sheet_bg) root.setProperty('--sheet-bg', themeData.sheet_bg);
} }
export function assetImg(slot, fallbackEmoji, width, height) { export function assetImg(slot, fallbackEmoji, width, height) {
......
...@@ -23,14 +23,14 @@ function showPairingToast(data) { ...@@ -23,14 +23,14 @@ function showPairingToast(data) {
const toast = document.createElement('div'); const toast = document.createElement('div');
toast.id = 'tournament-toast'; toast.id = 'tournament-toast';
toast.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;padding:12px 16px;background:linear-gradient(135deg,#1a1a2e,#0f0f1e);border-bottom:2px solid #E4AC38;display:flex;align-items:center;gap:12px;animation:slideDown .3s ease;'; toast.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;padding:12px 16px;background:linear-gradient(135deg,var(--bg-card),var(--bg-panel));border-bottom:2px solid var(--gold);display:flex;align-items:center;gap:12px;animation:slideDown .3s ease;';
toast.innerHTML = ` toast.innerHTML = `
<span style="font-size:20px;">${emoji('trophy', '🏆', 20)}</span> <span style="font-size:20px;">${emoji('trophy', '🏆', 20)}</span>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:700;color:#f8fafc;">${t('tournament.round_ready', { n: data.roundNumber })}</div> <div style="font-size:13px;font-weight:700;color:var(--text-primary);">${t('tournament.round_ready', { n: data.roundNumber })}</div>
<div style="font-size:11px;color:#94a3b8;">${t('tournament.tap_to_play')}</div> <div style="font-size:11px;color:var(--text-secondary);">${t('tournament.tap_to_play')}</div>
</div> </div>
<button id="toast-play-btn" style="padding:6px 14px;background:#E4AC38;border:none;border-radius:8px;color:#000;font-weight:700;font-size:12px;cursor:pointer;">${t('tournament.play')}</button> <button id="toast-play-btn" style="padding:6px 14px;background:var(--gold);border:none;border-radius:8px;color:#000;font-weight:700;font-size:12px;cursor:pointer;">${t('tournament.play')}</button>
`; `;
document.body.appendChild(toast); document.body.appendChild(toast);
......
...@@ -707,7 +707,7 @@ function getStyles() { ...@@ -707,7 +707,7 @@ function getStyles() {
.bgg-container { .bgg-container {
display:flex;flex-direction:column;height:100%; display:flex;flex-direction:column;height:100%;
background:linear-gradient(180deg,#060a12 0%,#0c1424 40%,#0a1018 100%); background:linear-gradient(180deg,#060a12 0%,#0c1424 40%,#0a1018 100%);
color:#f8fafc;overflow:hidden; color:var(--text-primary);overflow:hidden;
font-family:var(--font-ar,'IBM Plex Sans Arabic',sans-serif); font-family:var(--font-ar,'IBM Plex Sans Arabic',sans-serif);
position:relative; position:relative;
} }
...@@ -748,11 +748,11 @@ function getStyles() { ...@@ -748,11 +748,11 @@ function getStyles() {
.bgg-player-info { flex:1;min-width:0; } .bgg-player-info { flex:1;min-width:0; }
.bgg-name { font-size:13px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; } .bgg-name { font-size:13px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.bgg-meta { display:flex;align-items:center;gap:8px;margin-top:1px; } .bgg-meta { display:flex;align-items:center;gap:8px;margin-top:1px; }
.bgg-level { font-size:10px;color:#64748b;font-weight:600; } .bgg-level { font-size:10px;color:var(--text-muted);font-weight:600; }
.bgg-pips { font-size:10px;color:#94a3b8;font-weight:500; } .bgg-pips { font-size:10px;color:var(--text-secondary);font-weight:500; }
.bgg-pips::before { content:'⬡ ';opacity:0.5; } .bgg-pips::before { content:'⬡ ';opacity:0.5; }
.bgg-score { .bgg-score {
font-size:20px;font-weight:800;color:#475569;min-width:24px;text-align:center; font-size:20px;font-weight:800;color:var(--text-dim);min-width:24px;text-align:center;
font-variant-numeric:tabular-nums; font-variant-numeric:tabular-nums;
} }
.bgg-my-score { color:var(--gold,#E4AC38); } .bgg-my-score { color:var(--gold,#E4AC38); }
...@@ -760,11 +760,11 @@ function getStyles() { ...@@ -760,11 +760,11 @@ function getStyles() {
display:flex;flex-direction:column;align-items:center;gap:2px; display:flex;flex-direction:column;align-items:center;gap:2px;
} }
.bgg-cube-display { .bgg-cube-display {
font-size:14px;font-weight:800;color:#a78bfa; font-size:14px;font-weight:800;color:var(--purple-light);
background:rgba(139,92,246,0.12);border:1px solid rgba(139,92,246,0.25); background:rgba(139,92,246,0.12);border:1px solid rgba(139,92,246,0.25);
border-radius:6px;padding:2px 8px; border-radius:6px;padding:2px 8px;
} }
.bgg-match-length { font-size:9px;color:#64748b; } .bgg-match-length { font-size:9px;color:var(--text-muted); }
/* ── Board ── */ /* ── Board ── */
.bgg-board-area { .bgg-board-area {
...@@ -781,8 +781,8 @@ function getStyles() { ...@@ -781,8 +781,8 @@ function getStyles() {
backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
pointer-events:none;transition:all 0.3s;opacity:0.9; pointer-events:none;transition:all 0.3s;opacity:0.9;
} }
.bgg-turn-mine { background:rgba(16,185,129,0.25);color:#34D399;border:1px solid rgba(52,211,153,0.3); } .bgg-turn-mine { background:rgba(16,185,129,0.25);color:var(--success);border:1px solid rgba(52,211,153,0.3); }
.bgg-turn-opp { background:rgba(239,68,68,0.15);color:#f87171;border:1px solid rgba(239,68,68,0.2); } .bgg-turn-opp { background:rgba(239,68,68,0.15);color:var(--red-light);border:1px solid rgba(239,68,68,0.2); }
/* ── Controls ── */ /* ── Controls ── */
.bgg-controls { .bgg-controls {
...@@ -795,7 +795,7 @@ function getStyles() { ...@@ -795,7 +795,7 @@ function getStyles() {
.bgg-roll-btn { .bgg-roll-btn {
display:flex;align-items:center;gap:6px; display:flex;align-items:center;gap:6px;
padding:13px 32px;border-radius:14px;border:none; padding:13px 32px;border-radius:14px;border:none;
background:linear-gradient(135deg,#E4AC38,#D4940A); background:linear-gradient(135deg,var(--gold),var(--gold-dark));
color:#fff;font-size:15px;font-weight:800;cursor:pointer; color:#fff;font-size:15px;font-weight:800;cursor:pointer;
box-shadow:0 4px 16px rgba(228,172,56,0.35),inset 0 1px 0 rgba(255,255,255,0.2); box-shadow:0 4px 16px rgba(228,172,56,0.35),inset 0 1px 0 rgba(255,255,255,0.2);
transition:transform 0.12s var(--ease-spring,cubic-bezier(0.34,1.56,0.64,1)),box-shadow 0.15s; transition:transform 0.12s var(--ease-spring,cubic-bezier(0.34,1.56,0.64,1)),box-shadow 0.15s;
...@@ -804,20 +804,20 @@ function getStyles() { ...@@ -804,20 +804,20 @@ function getStyles() {
.bgg-cube-btn { .bgg-cube-btn {
padding:10px 16px;border-radius:10px;border:none; padding:10px 16px;border-radius:10px;border:none;
background:rgba(139,92,246,0.12);border:1.5px solid rgba(139,92,246,0.35); background:rgba(139,92,246,0.12);border:1.5px solid rgba(139,92,246,0.35);
color:#a78bfa;font-size:15px;font-weight:800;cursor:pointer; color:var(--purple-light);font-size:15px;font-weight:800;cursor:pointer;
transition:transform 0.12s; transition:transform 0.12s;
} }
.bgg-cube-btn:active { transform:scale(0.9); } .bgg-cube-btn:active { transform:scale(0.9); }
.bgg-action-btn { .bgg-action-btn {
width:40px;height:40px;border-radius:50%;border:none; width:40px;height:40px;border-radius:50%;border:none;
background:rgba(255,255,255,0.04);color:#94a3b8; background:rgba(255,255,255,0.04);color:var(--text-secondary);
font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center; font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;
border:1px solid rgba(255,255,255,0.06); border:1px solid var(--border);
transition:background 0.15s; transition:background 0.15s;
} }
.bgg-action-btn:active { background:rgba(255,255,255,0.1); } .bgg-action-btn:active { background:rgba(255,255,255,0.1); }
.bgg-quit { color:#ef4444;border-color:rgba(239,68,68,0.15); } .bgg-quit { color:var(--error);border-color:rgba(239,68,68,0.15); }
.bgg-emote-btn { color:#f8fafc; } .bgg-emote-btn { color:var(--text-primary); }
/* ── Doubling Offer ── */ /* ── Doubling Offer ── */
.bgg-double-offer { .bgg-double-offer {
...@@ -828,17 +828,17 @@ function getStyles() { ...@@ -828,17 +828,17 @@ function getStyles() {
box-shadow:0 16px 48px rgba(0,0,0,0.6); box-shadow:0 16px 48px rgba(0,0,0,0.6);
} }
.bgg-double-icon { .bgg-double-icon {
font-size:28px;font-weight:900;color:#E4AC38;margin-bottom:8px; font-size:28px;font-weight:900;color:var(--gold);margin-bottom:8px;
} }
.bgg-double-offer p { margin:0 0 16px;font-size:14px;color:#e2e8f0; } .bgg-double-offer p { margin:0 0 16px;font-size:14px;color:var(--text-light); }
.bgg-double-actions { display:flex;gap:10px;justify-content:center; } .bgg-double-actions { display:flex;gap:10px;justify-content:center; }
.bgg-accept-btn,.bgg-decline-btn { .bgg-accept-btn,.bgg-decline-btn {
padding:11px 24px;border-radius:12px;border:none; padding:11px 24px;border-radius:12px;border:none;
font-weight:700;font-size:14px;cursor:pointer; font-weight:700;font-size:14px;cursor:pointer;
transition:transform 0.12s; transition:transform 0.12s;
} }
.bgg-accept-btn { background:linear-gradient(135deg,#10b981,#059669);color:#fff;box-shadow:0 3px 12px rgba(16,185,129,0.3); } .bgg-accept-btn { background:linear-gradient(135deg,var(--emerald),#059669);color:#fff;box-shadow:0 3px 12px rgba(16,185,129,0.3); }
.bgg-decline-btn { background:rgba(239,68,68,0.15);color:#f87171;border:1.5px solid rgba(239,68,68,0.3); } .bgg-decline-btn { background:rgba(239,68,68,0.15);color:var(--red-light);border:1.5px solid rgba(239,68,68,0.3); }
.bgg-accept-btn:active,.bgg-decline-btn:active { transform:scale(0.93); } .bgg-accept-btn:active,.bgg-decline-btn:active { transform:scale(0.93); }
/* ── Emotes ── */ /* ── Emotes ── */
...@@ -859,7 +859,7 @@ function getStyles() { ...@@ -859,7 +859,7 @@ function getStyles() {
.bgg-phrases { display:flex;flex-direction:column;gap:4px; } .bgg-phrases { display:flex;flex-direction:column;gap:4px; }
.bgg-phrase-btn { .bgg-phrase-btn {
padding:7px 12px;border-radius:8px;border:none; padding:7px 12px;border-radius:8px;border:none;
background:rgba(255,255,255,0.03);color:#94a3b8; background:rgba(255,255,255,0.03);color:var(--text-secondary);
font-size:12px;cursor:pointer;text-align:right; font-size:12px;cursor:pointer;text-align:right;
transition:background 0.15s; transition:background 0.15s;
} }
......
...@@ -43,11 +43,11 @@ export function mountResult(el, params) { ...@@ -43,11 +43,11 @@ export function mountResult(el, params) {
<div class="bgr-rewards" style="display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap;justify-content:center;"> <div class="bgr-rewards" style="display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap;justify-content:center;">
<div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(251,191,36,0.1);border:1px solid rgba(251,191,36,0.2);border-radius:10px;"> <div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(251,191,36,0.1);border:1px solid rgba(251,191,36,0.2);border-radius:10px;">
<span style="font-size:16px;">${emoji('coin', '🪙', 16)}</span> <span style="font-size:16px;">${emoji('coin', '🪙', 16)}</span>
<span style="font-size:14px;font-weight:700;color:#fbbf24;">+${didWin ? 50 : 10}</span> <span style="font-size:14px;font-weight:700;color:var(--amber);">+${didWin ? 50 : 10}</span>
</div> </div>
<div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.2);border-radius:10px;"> <div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.2);border-radius:10px;">
<span style="font-size:16px;">${emoji('star', '⭐', 16)}</span> <span style="font-size:16px;">${emoji('star', '⭐', 16)}</span>
<span style="font-size:14px;font-weight:700;color:#a78bfa;">+15 XP</span> <span style="font-size:14px;font-weight:700;color:var(--purple-light);">+15 XP</span>
</div> </div>
</div> </div>
...@@ -167,8 +167,8 @@ function getResultStyles() { ...@@ -167,8 +167,8 @@ function getResultStyles() {
background:conic-gradient(from 0deg,rgba(100,100,100,0.2),rgba(60,60,60,0.1),rgba(100,100,100,0.2)); background:conic-gradient(from 0deg,rgba(100,100,100,0.2),rgba(60,60,60,0.1),rgba(100,100,100,0.2));
} }
.bgr-trophy { font-size:56px; } .bgr-trophy { font-size:56px; }
.bgr-title { font-size:22px;font-weight:800;color:#f8fafc;margin:0; } .bgr-title { font-size:22px;font-weight:800;color:var(--text-primary);margin:0; }
.bgr-subtitle { font-size:13px;color:#64748b;margin:8px 0 0; } .bgr-subtitle { font-size:13px;color:var(--text-muted);margin:8px 0 0; }
@keyframes trophyFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-8px)} } @keyframes trophyFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-8px)} }
.bgr-scoreboard { .bgr-scoreboard {
...@@ -178,12 +178,12 @@ function getResultStyles() { ...@@ -178,12 +178,12 @@ function getResultStyles() {
backdrop-filter:blur(8px);margin-bottom:32px; backdrop-filter:blur(8px);margin-bottom:32px;
} }
.bgr-score-col { display:flex;flex-direction:column;align-items:center;gap:6px; } .bgr-score-col { display:flex;flex-direction:column;align-items:center;gap:6px; }
.bgr-score-val { font-size:36px;font-weight:900;color:#64748b;line-height:1; } .bgr-score-val { font-size:36px;font-weight:900;color:var(--text-muted);line-height:1; }
.bgr-val-win { color:#d4940a;text-shadow:0 0 20px rgba(212,148,10,0.4); } .bgr-val-win { color:var(--gold-dark);text-shadow:0 0 20px rgba(212,148,10,0.4); }
.bgr-score-label { font-size:12px;color:#475569;font-weight:600; } .bgr-score-label { font-size:12px;color:var(--text-dim);font-weight:600; }
.bgr-score-vs { display:flex;align-items:center;gap:8px; } .bgr-score-vs { display:flex;align-items:center;gap:8px; }
.bgr-vs-line { width:20px;height:1px;background:rgba(255,255,255,0.08); } .bgr-vs-line { width:20px;height:1px;background:rgba(255,255,255,0.08); }
.bgr-vs-text { font-size:11px;color:#475569;white-space:nowrap; } .bgr-vs-text { font-size:11px;color:var(--text-dim);white-space:nowrap; }
.bgr-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:280px; } .bgr-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:280px; }
.bgr-btn { .bgr-btn {
...@@ -193,11 +193,11 @@ function getResultStyles() { ...@@ -193,11 +193,11 @@ function getResultStyles() {
} }
.bgr-btn:active { transform:scale(0.93); } .bgr-btn:active { transform:scale(0.93); }
.bgr-btn-primary { .bgr-btn-primary {
background:linear-gradient(135deg,#d4940a,#8B4513);color:#fff; background:linear-gradient(135deg,var(--gold-dark),#8B4513);color:#fff;
box-shadow:0 6px 20px rgba(212,148,10,0.35); box-shadow:0 6px 20px rgba(212,148,10,0.35);
} }
.bgr-btn-secondary { .bgr-btn-secondary {
background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);color:#94a3b8; background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);color:var(--text-secondary);
} }
</style>`; </style>`;
} }
...@@ -232,8 +232,8 @@ function getStyles() { ...@@ -232,8 +232,8 @@ function getStyles() {
} }
.bg-hero { text-align:center;margin-bottom:24px; } .bg-hero { text-align:center;margin-bottom:24px; }
.bg-icon { font-size:56px;margin-bottom:12px;animation:bgFloat 3s ease-in-out infinite; } .bg-icon { font-size:56px;margin-bottom:12px;animation:bgFloat 3s ease-in-out infinite; }
.bg-title { font-size:26px;font-weight:800;color:#f8fafc;margin:0; } .bg-title { font-size:26px;font-weight:800;color:var(--text-primary);margin:0; }
.bg-subtitle { font-size:13px;color:#d4940a;margin:8px 0 0;opacity:0.9; } .bg-subtitle { font-size:13px;color:var(--gold-dark);margin:8px 0 0;opacity:0.9; }
.bg-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px; } .bg-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px; }
.bg-btn { .bg-btn {
display:flex;align-items:center;gap:10px;flex-wrap:wrap; display:flex;align-items:center;gap:10px;flex-wrap:wrap;
...@@ -243,27 +243,27 @@ function getStyles() { ...@@ -243,27 +243,27 @@ function getStyles() {
box-shadow:0 4px 16px rgba(0,0,0,0.3); box-shadow:0 4px 16px rgba(0,0,0,0.3);
} }
.bg-btn:active { transform:scale(0.95); } .bg-btn:active { transform:scale(0.95); }
.bg-btn-primary { background:linear-gradient(135deg,#d4940a 0%,#8B4513 100%);color:#fff; } .bg-btn-primary { background:linear-gradient(135deg,var(--gold-dark) 0%,#8B4513 100%);color:#fff; }
.bg-btn-online { background:linear-gradient(135deg,#8B5CF6 0%,#7C3AED 100%);color:#fff; } .bg-btn-online { background:linear-gradient(135deg,var(--purple) 0%,var(--violet) 100%);color:#fff; }
.bg-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:#e2e8f0;min-height:50px;font-size:14px;box-shadow:none; } .bg-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:var(--text-light);min-height:50px;font-size:14px;box-shadow:none; }
.bg-btn-start { width:100%;max-width:320px;justify-content:center;background:linear-gradient(135deg,#d4940a,#8B4513);color:#fff;font-size:17px;font-weight:800;margin-top:16px; } .bg-btn-start { width:100%;max-width:320px;justify-content:center;background:linear-gradient(135deg,var(--gold-dark),#8B4513);color:#fff;font-size:17px;font-weight:800;margin-top:16px; }
.bg-btn-icon { display:flex;flex-shrink:0; } .bg-btn-icon { display:flex;flex-shrink:0; }
.bg-btn-label { font-weight:700;font-size:16px; } .bg-btn-label { font-weight:700;font-size:16px; }
.bg-btn-desc { width:100%;font-size:11px;opacity:0.7;font-weight:400;margin-top:2px; } .bg-btn-desc { width:100%;font-size:11px;opacity:0.7;font-weight:400;margin-top:2px; }
.bg-back { margin-top:16px;font-size:13px;color:#64748b;background:none;border:none;cursor:pointer;padding:8px 16px; } .bg-back { margin-top:16px;font-size:13px;color:var(--text-muted);background:none;border:none;cursor:pointer;padding:8px 16px; }
.bg-section { width:100%;max-width:320px;margin-bottom:16px; } .bg-section { width:100%;max-width:320px;margin-bottom:16px; }
.bg-section-title { font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:8px;text-align:center; } .bg-section-title { font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:8px;text-align:center; }
.bg-grid { display:flex;gap:8px;justify-content:center;flex-wrap:wrap; } .bg-grid { display:flex;gap:8px;justify-content:center;flex-wrap:wrap; }
.bg-chip { .bg-chip {
display:flex;align-items:center;gap:6px; display:flex;align-items:center;gap:6px;
padding:10px 16px;border-radius:12px; padding:10px 16px;border-radius:12px;
background:rgba(255,255,255,0.04);border:1.5px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04);border:1.5px solid rgba(255,255,255,0.08);
color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer; color:var(--text-secondary);font-size:13px;font-weight:600;cursor:pointer;
transition:all 0.2s cubic-bezier(0.34,1.56,0.64,1); transition:all 0.2s cubic-bezier(0.34,1.56,0.64,1);
} }
.bg-chip:active { transform:scale(0.95); } .bg-chip:active { transform:scale(0.95); }
.bg-chip-active { background:rgba(212,148,10,0.12);border-color:rgba(212,148,10,0.5);color:#d4940a; } .bg-chip-active { background:rgba(212,148,10,0.12);border-color:rgba(212,148,10,0.5);color:var(--gold-dark); }
.bg-chip-num { font-size:18px;font-weight:800; } .bg-chip-num { font-size:18px;font-weight:800; }
.bg-chip-label { font-size:12px; } .bg-chip-label { font-size:12px; }
......
...@@ -37,6 +37,6 @@ export function createCubeStyles() { ...@@ -37,6 +37,6 @@ export function createCubeStyles() {
display:flex;align-items:center;justify-content:center; display:flex;align-items:center;justify-content:center;
box-shadow:0 2px 8px rgba(139,92,246,0.2); box-shadow:0 2px 8px rgba(139,92,246,0.2);
} }
.bgg-cube-val { font-size:14px;font-weight:900;color:#a78bfa; } .bgg-cube-val { font-size:14px;font-weight:900;color:var(--purple-light); }
`; `;
} }
...@@ -36,7 +36,7 @@ export function create(container, onSend) { ...@@ -36,7 +36,7 @@ export function create(container, onSend) {
const style = document.createElement('style'); const style = document.createElement('style');
style.textContent = ` style.textContent = `
.emote-bar { position:relative;z-index:30; } .emote-bar { position:relative;z-index:30; }
.emote-panel { display:flex;flex-wrap:wrap;gap:6px;padding:8px 12px;background:#1e1e3a;border-radius:12px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 4px 20px rgba(0,0,0,0.5);animation:slideUpBounce 0.3s cubic-bezier(0.16,1,0.3,1);max-width:100%; } .emote-panel { display:flex;flex-wrap:wrap;gap:6px;padding:8px 12px;background:var(--bg-elevated);border-radius:12px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 4px 20px rgba(0,0,0,0.5);animation:slideUpBounce 0.3s cubic-bezier(0.16,1,0.3,1);max-width:100%; }
.emote-panel.hidden { display:none; } .emote-panel.hidden { display:none; }
.emote-btn { width:40px;height:40px;border-radius:8px;background:rgba(255,255,255,0.05);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform 0.1s,background 0.15s; } .emote-btn { width:40px;height:40px;border-radius:8px;background:rgba(255,255,255,0.05);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform 0.1s,background 0.15s; }
.emote-btn:hover { background:rgba(255,255,255,0.1); } .emote-btn:hover { background:rgba(255,255,255,0.1); }
......
...@@ -7,7 +7,7 @@ export function renderRatingGraph(container, history, options = {}) { ...@@ -7,7 +7,7 @@ export function renderRatingGraph(container, history, options = {}) {
container.innerHTML = ''; container.innerHTML = '';
if (!history || history.length < 2) { if (!history || history.length < 2) {
container.innerHTML = `<div style="height:${height}px;display:flex;align-items:center;justify-content:center;color:#64748b;font-size:12px;">${t('chess.insufficient_data')}</div>`; container.innerHTML = `<div style="height:${height}px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:12px;">${t('chess.insufficient_data')}</div>`;
return; return;
} }
......
...@@ -19,35 +19,35 @@ export function classifyMove(evalBefore, evalAfter, bestEval, isPlayerWhite, mov ...@@ -19,35 +19,35 @@ export function classifyMove(evalBefore, evalAfter, bestEval, isPlayerWhite, mov
const lossFromBest = (bestEval - evalAfter) * sign; const lossFromBest = (bestEval - evalAfter) * sign;
// Book moves (first 2 full moves = 4 half-moves) // Book moves (first 2 full moves = 4 half-moves)
if (moveNum <= 2) return { class: 'book', symbol: emoji('book', '📖', 14), color: '#94a3b8' }; if (moveNum <= 2) return { class: 'book', symbol: emoji('book', '📖', 14), color: 'var(--text-secondary)' };
// If eval is mate-level, special handling // If eval is mate-level, special handling
if (Math.abs(evalAfter) > 900) { if (Math.abs(evalAfter) > 900) {
if (evalAfter * sign > 0) return { class: 'great', symbol: '!', color: '#3B82F6' }; if (evalAfter * sign > 0) return { class: 'great', symbol: '!', color: 'var(--blue)' };
return { class: 'blunder', symbol: '??', color: '#EF4444' }; return { class: 'blunder', symbol: '??', color: 'var(--error)' };
} }
if (lossFromBest >= THRESHOLDS.blunder) { if (lossFromBest >= THRESHOLDS.blunder) {
return { class: 'blunder', symbol: '??', color: '#EF4444' }; return { class: 'blunder', symbol: '??', color: 'var(--error)' };
} }
if (lossFromBest >= THRESHOLDS.mistake) { if (lossFromBest >= THRESHOLDS.mistake) {
return { class: 'mistake', symbol: '?', color: '#F97316' }; return { class: 'mistake', symbol: '?', color: 'var(--orange)' };
} }
if (lossFromBest >= THRESHOLDS.inaccuracy) { if (lossFromBest >= THRESHOLDS.inaccuracy) {
return { class: 'inaccuracy', symbol: '?!', color: '#FBBF24' }; return { class: 'inaccuracy', symbol: '?!', color: 'var(--amber)' };
} }
if (lossFromBest <= THRESHOLDS.good) { if (lossFromBest <= THRESHOLDS.good) {
// Check for brilliant (sacrifice that maintains or improves eval) // Check for brilliant (sacrifice that maintains or improves eval)
if (evalChange < -1 && lossFromBest <= 0.1) { if (evalChange < -1 && lossFromBest <= 0.1) {
return { class: 'brilliant', symbol: '!!', color: '#06B6D4' }; return { class: 'brilliant', symbol: '!!', color: 'var(--cyan)' };
} }
if (lossFromBest <= THRESHOLDS.great) { if (lossFromBest <= THRESHOLDS.great) {
return { class: 'best', symbol: emoji('best_move_star', '★', 14), color: '#10B981' }; return { class: 'best', symbol: emoji('best_move_star', '★', 14), color: 'var(--emerald)' };
} }
return { class: 'great', symbol: '!', color: '#3B82F6' }; return { class: 'great', symbol: '!', color: 'var(--blue)' };
} }
return { class: 'good', symbol: '', color: '#94a3b8' }; return { class: 'good', symbol: '', color: 'var(--text-secondary)' };
} }
export function calculateAccuracy(classifications) { export function calculateAccuracy(classifications) {
...@@ -67,11 +67,11 @@ export function calculateAccuracy(classifications) { ...@@ -67,11 +67,11 @@ export function calculateAccuracy(classifications) {
} }
export function getAccuracyLabel(accuracy) { export function getAccuracyLabel(accuracy) {
if (accuracy >= 95) return { label: t('classifier.excellent'), labelEn: 'Excellent', color: '#10B981' }; if (accuracy >= 95) return { label: t('classifier.excellent'), labelEn: 'Excellent', color: 'var(--emerald)' };
if (accuracy >= 85) return { label: t('classifier.great'), labelEn: 'Great', color: '#3B82F6' }; if (accuracy >= 85) return { label: t('classifier.great'), labelEn: 'Great', color: 'var(--blue)' };
if (accuracy >= 70) return { label: t('classifier.good'), labelEn: 'Good', color: '#FBBF24' }; if (accuracy >= 70) return { label: t('classifier.good'), labelEn: 'Good', color: 'var(--amber)' };
if (accuracy >= 50) return { label: t('classifier.average'), labelEn: 'Average', color: '#F97316' }; if (accuracy >= 50) return { label: t('classifier.average'), labelEn: 'Average', color: 'var(--orange)' };
return { label: t('classifier.poor'), labelEn: 'Poor', color: '#EF4444' }; return { label: t('classifier.poor'), labelEn: 'Poor', color: 'var(--error)' };
} }
export function getMoveClassSummary(classifications) { export function getMoveClassSummary(classifications) {
......
...@@ -88,8 +88,8 @@ export function formatExplorerEntry(entry) { ...@@ -88,8 +88,8 @@ export function formatExplorerEntry(entry) {
export function renderExplorerBar(white, draw, black) { export function renderExplorerBar(white, draw, black) {
return `<div style="display:flex;height:6px;border-radius:3px;overflow:hidden;width:80px;"> return `<div style="display:flex;height:6px;border-radius:3px;overflow:hidden;width:80px;">
<div style="width:${white}%;background:#f8fafc;"></div> <div style="width:${white}%;background:var(--text-primary);"></div>
<div style="width:${draw}%;background:#94a3b8;"></div> <div style="width:${draw}%;background:var(--text-secondary);"></div>
<div style="width:${black}%;background:#1e293b;"></div> <div style="width:${black}%;background:#1e293b;"></div>
</div>`; </div>`;
} }
This diff is collapsed.
This diff is collapsed.
...@@ -28,7 +28,7 @@ export async function mountHistory(el, params = {}) { ...@@ -28,7 +28,7 @@ export async function mountHistory(el, params = {}) {
const matches = data.matches || []; const matches = data.matches || [];
renderHistory(el, matches); renderHistory(el, matches);
} catch (e) { } catch (e) {
el.querySelector('#history-list').innerHTML = `<p style="color:#ef4444;text-align:center;">${t('common.error')}</p>`; el.querySelector('#history-list').innerHTML = `<p style="color:var(--error);text-align:center;">${t('common.error')}</p>`;
} }
} }
...@@ -40,7 +40,7 @@ function renderHistory(el, matches) { ...@@ -40,7 +40,7 @@ function renderHistory(el, matches) {
list.innerHTML = ` list.innerHTML = `
<div style="text-align:center;padding:40px;"> <div style="text-align:center;padding:40px;">
<div style="font-size:40px;margin-bottom:8px;opacity:0.4;">${emoji('clipboard', '📋', 40)}</div> <div style="font-size:40px;margin-bottom:8px;opacity:0.4;">${emoji('clipboard', '📋', 40)}</div>
<div style="font-size:14px;color:#64748b;">${t('history.no_matches')}</div> <div style="font-size:14px;color:var(--text-muted);">${t('history.no_matches')}</div>
</div>`; </div>`;
return; return;
} }
...@@ -49,10 +49,10 @@ function renderHistory(el, matches) { ...@@ -49,10 +49,10 @@ function renderHistory(el, matches) {
const isWhite = m.white_player_id === userId; const isWhite = m.white_player_id === userId;
const myResult = getMyResult(m.result, isWhite); const myResult = getMyResult(m.result, isWhite);
const resultConfig = { const resultConfig = {
win: { label: t('history.win'), color: '#34D399', icon: emoji('trophy', '🏆', 20) }, win: { label: t('history.win'), color: 'var(--success)', icon: emoji('trophy', '🏆', 20) },
loss: { label: t('history.loss'), color: '#F87171', icon: emoji('skull', '💀', 20) }, loss: { label: t('history.loss'), color: 'var(--red-light)', icon: emoji('skull', '💀', 20) },
draw: { label: t('history.draw'), color: '#E4AC38', icon: emoji('handshake', '🤝', 20) }, draw: { label: t('history.draw'), color: 'var(--gold)', icon: emoji('handshake', '🤝', 20) },
unknown: { label: '—', color: '#64748b', icon: '•' } unknown: { label: '—', color: 'var(--text-muted)', icon: '•' }
}; };
const cfg = resultConfig[myResult] || resultConfig.unknown; const cfg = resultConfig[myResult] || resultConfig.unknown;
const ratingChange = isWhite ? m.rating_change_white : m.rating_change_black; const ratingChange = isWhite ? m.rating_change_white : m.rating_change_black;
...@@ -62,17 +62,17 @@ function renderHistory(el, matches) { ...@@ -62,17 +62,17 @@ function renderHistory(el, matches) {
const tc = formatTimeControl(m.time_control); const tc = formatTimeControl(m.time_control);
return ` return `
<div style="display:flex;align-items:center;gap:12px;padding:12px;background:#1a1a2e;border-radius:12px;border-right:4px solid ${cfg.color};"> <div style="display:flex;align-items:center;gap:12px;padding:12px;background:var(--bg-card);border-radius:12px;border-right:4px solid ${cfg.color};">
<div style="font-size:20px;">${cfg.icon}</div> <div style="font-size:20px;">${cfg.icon}</div>
<div style="flex:1;"> <div style="flex:1;">
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:13px;font-weight:600;color:#f8fafc;">${opponent}</span> <span style="font-size:13px;font-weight:600;color:var(--text-primary);">${opponent}</span>
<span style="font-size:12px;font-weight:700;color:${cfg.color};font-family:Inter,monospace;">${ratingStr}</span> <span style="font-size:12px;font-weight:700;color:${cfg.color};font-family:Inter,monospace;">${ratingStr}</span>
</div> </div>
<div style="display:flex;gap:8px;margin-top:3px;"> <div style="display:flex;gap:8px;margin-top:3px;">
<span style="font-size:10px;color:#64748b;">${cfg.label}</span> <span style="font-size:10px;color:var(--text-muted);">${cfg.label}</span>
<span style="font-size:10px;color:#475569;">${tc}</span> <span style="font-size:10px;color:var(--text-dim);">${tc}</span>
<span style="font-size:10px;color:#475569;">${timeAgo}</span> <span style="font-size:10px;color:var(--text-dim);">${timeAgo}</span>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -16,9 +16,9 @@ export function mountResult(el, params) { ...@@ -16,9 +16,9 @@ export function mountResult(el, params) {
const ratingStr = ratingChange >= 0 ? `+${ratingChange}` : `${ratingChange}`; const ratingStr = ratingChange >= 0 ? `+${ratingChange}` : `${ratingChange}`;
const resultConfig = { const resultConfig = {
win: { icon: emoji('trophy', '🏆', 72), title: t('game.you_win'), color: '#34D399' }, win: { icon: emoji('trophy', '🏆', 72), title: t('game.you_win'), color: 'var(--success)' },
loss: { icon: emoji('skull', '💀', 72), title: t('game.you_lose'), color: '#F87171' }, loss: { icon: emoji('skull', '💀', 72), title: t('game.you_lose'), color: 'var(--red-light)' },
draw: { icon: emoji('handshake', '🤝', 72), title: t('game.draw_result'), color: '#E4AC38' } draw: { icon: emoji('handshake', '🤝', 72), title: t('game.draw_result'), color: 'var(--gold)' }
}; };
const cfg = resultConfig[result] || resultConfig.loss; const cfg = resultConfig[result] || resultConfig.loss;
...@@ -32,39 +32,39 @@ export function mountResult(el, params) { ...@@ -32,39 +32,39 @@ export function mountResult(el, params) {
}[reason] || reason; }[reason] || reason;
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:20px;padding:24px;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:20px;padding:24px;background:var(--bg-deep);">
<!-- Result Icon --> <!-- Result Icon -->
<div style="font-size:72px;${isWin ? 'animation:float 2s ease-in-out infinite;' : ''}">${cfg.icon}</div> <div style="font-size:72px;${isWin ? 'animation:float 2s ease-in-out infinite;' : ''}">${cfg.icon}</div>
<!-- Title & Reason --> <!-- Title & Reason -->
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:32px;font-weight:800;color:${cfg.color};">${cfg.title}</div> <div style="font-size:32px;font-weight:800;color:${cfg.color};">${cfg.title}</div>
<div style="font-size:14px;color:#94a3b8;margin-top:4px;">${reasonText}</div> <div style="font-size:14px;color:var(--text-secondary);margin-top:4px;">${reasonText}</div>
${moves ? `<div style="font-size:12px;color:#475569;margin-top:2px;">${t('game.moves_count', { n: moves })}</div>` : ''} ${moves ? `<div style="font-size:12px;color:var(--text-dim);margin-top:2px;">${t('game.moves_count', { n: moves })}</div>` : ''}
</div> </div>
<!-- Stats Row --> <!-- Stats Row -->
<div style="display:flex;gap:24px;margin-top:8px;"> <div style="display:flex;gap:24px;margin-top:8px;">
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:28px;font-weight:700;color:${cfg.color};font-family:Inter,monospace;">${ratingStr}</div> <div style="font-size:28px;font-weight:700;color:${cfg.color};font-family:Inter,monospace;">${ratingStr}</div>
<div style="font-size:11px;color:#64748b;">${t('game.rating')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('game.rating')}</div>
</div> </div>
<div style="width:1px;background:rgba(255,255,255,0.06);"></div> <div style="width:1px;background:var(--border);"></div>
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:28px;font-weight:700;color:#E4AC38;font-family:Inter,monospace;">+${coins}</div> <div style="font-size:28px;font-weight:700;color:var(--gold);font-family:Inter,monospace;">+${coins}</div>
<div style="font-size:11px;color:#64748b;">${t('game.coins')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('game.coins')}</div>
</div> </div>
<div style="width:1px;background:rgba(255,255,255,0.06);"></div> <div style="width:1px;background:var(--border);"></div>
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:28px;font-weight:700;color:#00FFFF;font-family:Inter,monospace;">+${xp}</div> <div style="font-size:28px;font-weight:700;color:var(--cyan);font-family:Inter,monospace;">+${xp}</div>
<div style="font-size:11px;color:#64748b;">${t('game.xp')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('game.xp')}</div>
</div> </div>
</div> </div>
<!-- Move Summary --> <!-- Move Summary -->
${moveHistory.length > 0 ? ` ${moveHistory.length > 0 ? `
<div style="background:#1a1a2e;border:1px solid rgba(255,255,255,0.06);border-radius:8px;padding:8px 12px;max-width:300px;width:100%;max-height:60px;overflow-x:auto;white-space:nowrap;"> <div style="background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:8px 12px;max-width:300px;width:100%;max-height:60px;overflow-x:auto;white-space:nowrap;">
<div style="font-family:Inter,monospace;font-size:11px;color:#94a3b8;"> <div style="font-family:Inter,monospace;font-size:11px;color:var(--text-secondary);">
${formatMoveHistory(moveHistory)} ${formatMoveHistory(moveHistory)}
</div> </div>
</div>` : ''} </div>` : ''}
......
...@@ -15,20 +15,20 @@ export async function mountReview(el, params) { ...@@ -15,20 +15,20 @@ export async function mountReview(el, params) {
const { moveHistory = [], playerColor = 'w', botId, result } = params; const { moveHistory = [], playerColor = 'w', botId, result } = params;
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;overflow-y:auto;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-card);overflow-y:auto;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:30px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button> <button class="btn btn-secondary" id="back-btn" style="min-height:30px;padding:4px 12px;font-size:12px;">← ${t('game.back')}</button>
<span style="font-size:15px;font-weight:700;color:#f8fafc;">${emoji('star', '⭐', 15)} ${t('review.title')}</span> <span style="font-size:15px;font-weight:700;color:var(--text-primary);">${emoji('star', '⭐', 15)} ${t('review.title')}</span>
<div style="width:50px;"></div> <div style="width:50px;"></div>
</div> </div>
<!-- Progress --> <!-- Progress -->
<div id="review-progress" style="padding:16px;text-align:center;"> <div id="review-progress" style="padding:16px;text-align:center;">
<div style="font-size:14px;color:#94a3b8;margin-bottom:8px;">${t('review.analyzing_moves')}</div> <div style="font-size:14px;color:var(--text-secondary);margin-bottom:8px;">${t('review.analyzing_moves')}</div>
<div style="background:#1e1e3a;border-radius:6px;height:8px;overflow:hidden;"> <div style="background:var(--bg-elevated);border-radius:6px;height:8px;overflow:hidden;">
<div id="progress-bar" style="height:100%;background:#E4AC38;width:0%;transition:width 0.3s;border-radius:6px;"></div> <div id="progress-bar" style="height:100%;background:var(--gold);width:0%;transition:width 0.3s;border-radius:6px;"></div>
</div> </div>
<div id="progress-text" style="font-size:11px;color:#64748b;margin-top:4px;">0/${moveHistory.length}</div> <div id="progress-text" style="font-size:11px;color:var(--text-muted);margin-top:4px;">0/${moveHistory.length}</div>
</div> </div>
<!-- Review content (shown after analysis) --> <!-- Review content (shown after analysis) -->
...@@ -130,58 +130,58 @@ function renderReview(el, results, moves, playerColor) { ...@@ -130,58 +130,58 @@ function renderReview(el, results, moves, playerColor) {
content.innerHTML = ` content.innerHTML = `
<!-- Eval Graph --> <!-- Eval Graph -->
<div id="eval-graph-container" style="background:#0f0f1e;border-radius:10px;padding:8px;margin-bottom:12px;"></div> <div id="eval-graph-container" style="background:var(--bg-panel);border-radius:10px;padding:8px;margin-bottom:12px;"></div>
<!-- Accuracy Comparison --> <!-- Accuracy Comparison -->
<div style="background:#0f0f1e;border-radius:10px;padding:16px;margin-bottom:12px;"> <div style="background:var(--bg-panel);border-radius:10px;padding:16px;margin-bottom:12px;">
<div style="text-align:center;font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:12px;">${emoji('star', '⭐', 15)} ${t('review.title')}</div> <div style="text-align:center;font-size:15px;font-weight:700;color:var(--text-primary);margin-bottom:12px;">${emoji('star', '⭐', 15)} ${t('review.title')}</div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;"> <div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
<!-- Player accuracy --> <!-- Player accuracy -->
<div style="flex:1;text-align:center;"> <div style="flex:1;text-align:center;">
<div style="font-size:28px;font-weight:800;color:${playerLabel.color};font-family:Inter,monospace;">${playerAccuracy}%</div> <div style="font-size:28px;font-weight:800;color:${playerLabel.color};font-family:Inter,monospace;">${playerAccuracy}%</div>
<div style="font-size:11px;color:#94a3b8;">${t('common.you')}</div> <div style="font-size:11px;color:var(--text-secondary);">${t('common.you')}</div>
<div style="height:6px;background:#1e1e3a;border-radius:3px;margin-top:6px;overflow:hidden;"> <div style="height:6px;background:var(--bg-elevated);border-radius:3px;margin-top:6px;overflow:hidden;">
<div style="height:100%;width:${playerAccuracy}%;background:${playerLabel.color};border-radius:3px;"></div> <div style="height:100%;width:${playerAccuracy}%;background:${playerLabel.color};border-radius:3px;"></div>
</div> </div>
</div> </div>
<div style="font-size:12px;color:#64748b;font-weight:600;">VS</div> <div style="font-size:12px;color:var(--text-muted);font-weight:600;">VS</div>
<!-- Opponent accuracy --> <!-- Opponent accuracy -->
<div style="flex:1;text-align:center;"> <div style="flex:1;text-align:center;">
<div style="font-size:28px;font-weight:800;color:${opponentLabel.color};font-family:Inter,monospace;">${opponentAccuracy}%</div> <div style="font-size:28px;font-weight:800;color:${opponentLabel.color};font-family:Inter,monospace;">${opponentAccuracy}%</div>
<div style="font-size:11px;color:#94a3b8;">${t('common.opponent')}</div> <div style="font-size:11px;color:var(--text-secondary);">${t('common.opponent')}</div>
<div style="height:6px;background:#1e1e3a;border-radius:3px;margin-top:6px;overflow:hidden;"> <div style="height:6px;background:var(--bg-elevated);border-radius:3px;margin-top:6px;overflow:hidden;">
<div style="height:100%;width:${opponentAccuracy}%;background:${opponentLabel.color};border-radius:3px;"></div> <div style="height:100%;width:${opponentAccuracy}%;background:${opponentLabel.color};border-radius:3px;"></div>
</div> </div>
</div> </div>
</div> </div>
<!-- Opening --> <!-- Opening -->
<div style="text-align:center;font-size:12px;color:#64748b;margin-bottom:12px;">${emoji('book', '📖', 12)} ${opening}</div> <div style="text-align:center;font-size:12px;color:var(--text-muted);margin-bottom:12px;">${emoji('book', '📖', 12)} ${opening}</div>
</div> </div>
<!-- Move Classification Breakdown --> <!-- Move Classification Breakdown -->
<div style="background:#0f0f1e;border-radius:10px;padding:14px;margin-bottom:12px;"> <div style="background:var(--bg-panel);border-radius:10px;padding:14px;margin-bottom:12px;">
<div style="font-size:13px;font-weight:700;color:#f8fafc;margin-bottom:10px;text-align:center;">${t('review.move_classification')}</div> <div style="font-size:13px;font-weight:700;color:var(--text-primary);margin-bottom:10px;text-align:center;">${t('review.move_classification')}</div>
<table style="width:100%;border-collapse:collapse;font-size:12px;"> <table style="width:100%;border-collapse:collapse;font-size:12px;">
<thead> <thead>
<tr style="color:#64748b;"> <tr style="color:var(--text-muted);">
<th style="text-align:right;padding:3px 0;font-weight:600;">${t('common.you')}</th> <th style="text-align:right;padding:3px 0;font-weight:600;">${t('common.you')}</th>
<th style="text-align:center;padding:3px 8px;"></th> <th style="text-align:center;padding:3px 8px;"></th>
<th style="text-align:left;padding:3px 0;font-weight:600;">${t('common.opponent')}</th> <th style="text-align:left;padding:3px 0;font-weight:600;">${t('common.opponent')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
${renderClassRow('brilliant', t('review.brilliant'), '#06B6D4', whiteSummary, blackSummary, playerColor)} ${renderClassRow('brilliant', t('review.brilliant'), 'var(--cyan)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('great', t('review.great'), '#3B82F6', whiteSummary, blackSummary, playerColor)} ${renderClassRow('great', t('review.great'), 'var(--blue)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('best', emoji('best_move_star', '★', 12) + ' ' + t('review.best'), '#10B981', whiteSummary, blackSummary, playerColor)} ${renderClassRow('best', emoji('best_move_star', '★', 12) + ' ' + t('review.best'), 'var(--emerald)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('good', t('review.good'), '#94a3b8', whiteSummary, blackSummary, playerColor)} ${renderClassRow('good', t('review.good'), 'var(--text-secondary)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('book', emoji('book', '📖', 12) + ' ' + t('review.book'), '#94a3b8', whiteSummary, blackSummary, playerColor)} ${renderClassRow('book', emoji('book', '📖', 12) + ' ' + t('review.book'), 'var(--text-secondary)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('inaccuracy', t('review.inaccuracy'), '#FBBF24', whiteSummary, blackSummary, playerColor)} ${renderClassRow('inaccuracy', t('review.inaccuracy'), 'var(--amber)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('mistake', t('review.mistake'), '#F97316', whiteSummary, blackSummary, playerColor)} ${renderClassRow('mistake', t('review.mistake'), 'var(--orange)', whiteSummary, blackSummary, playerColor)}
${renderClassRow('blunder', t('review.blunder'), '#EF4444', whiteSummary, blackSummary, playerColor)} ${renderClassRow('blunder', t('review.blunder'), 'var(--error)', whiteSummary, blackSummary, playerColor)}
</tbody> </tbody>
</table> </table>
</div> </div>
...@@ -205,9 +205,9 @@ function renderClassRow(key, label, color, whiteSummary, blackSummary, playerCol ...@@ -205,9 +205,9 @@ function renderClassRow(key, label, color, whiteSummary, blackSummary, playerCol
return ` return `
<tr style="border-top:1px solid rgba(255,255,255,0.04);"> <tr style="border-top:1px solid rgba(255,255,255,0.04);">
<td style="text-align:right;padding:5px 0;color:${playerCount > 0 ? color : '#475569'};font-weight:${playerCount > 0 ? '700' : '400'};">${playerCount}</td> <td style="text-align:right;padding:5px 0;color:${playerCount > 0 ? color : 'var(--text-dim)'};font-weight:${playerCount > 0 ? '700' : '400'};">${playerCount}</td>
<td style="text-align:center;padding:5px 8px;color:${color};font-size:11px;">${label}</td> <td style="text-align:center;padding:5px 8px;color:${color};font-size:11px;">${label}</td>
<td style="text-align:left;padding:5px 0;color:${opponentCount > 0 ? color : '#475569'};font-weight:${opponentCount > 0 ? '700' : '400'};">${opponentCount}</td> <td style="text-align:left;padding:5px 0;color:${opponentCount > 0 ? color : 'var(--text-dim)'};font-weight:${opponentCount > 0 ? '700' : '400'};">${opponentCount}</td>
</tr> </tr>
`; `;
} }
......
...@@ -17,30 +17,30 @@ export async function mountSpectate(el, params = {}) { ...@@ -17,30 +17,30 @@ export async function mountSpectate(el, params = {}) {
scene.enterGameMode(); scene.enterGameMode();
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0f0f1e;justify-content:center;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-panel);justify-content:center;">
<div style="padding:8px 14px;display:flex;align-items:center;gap:8px;"> <div style="padding:8px 14px;display:flex;align-items:center;gap:8px;">
<button class="btn btn-secondary" id="back-btn" style="width:34px;height:34px;padding:0;">←</button> <button class="btn btn-secondary" id="back-btn" style="width:34px;height:34px;padding:0;">←</button>
<div style="flex:1;display:flex;align-items:center;gap:8px;"> <div style="flex:1;display:flex;align-items:center;gap:8px;">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#EF4444;animation:spectatePulse 1.5s infinite;"></span> <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--error);animation:spectatePulse 1.5s infinite;"></span>
<span style="font-size:13px;font-weight:600;color:#EF4444;">${t('spectate.live')}</span> <span style="font-size:13px;font-weight:600;color:var(--error);">${t('spectate.live')}</span>
</div> </div>
</div> </div>
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;"> <div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;">
<div style="display:flex;align-items:center;gap:8px;"> <div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;">${emoji('person', '👤', 14)}</div> <div style="width:32px;height:32px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;">${emoji('person', '👤', 14)}</div>
<span id="black-name" style="font-size:13px;font-weight:600;color:#f8fafc;">${t('spectate.black')}</span> <span id="black-name" style="font-size:13px;font-weight:600;color:var(--text-primary);">${t('spectate.black')}</span>
</div> </div>
<div id="clock-black" style="font-size:16px;font-weight:700;font-family:'SF Mono',monospace;background:#1a1a2e;padding:4px 10px;border-radius:6px;color:#94a3b8;">--:--</div> <div id="clock-black" style="font-size:16px;font-weight:700;font-family:'SF Mono',monospace;background:var(--bg-card);padding:4px 10px;border-radius:6px;color:var(--text-secondary);">--:--</div>
</div> </div>
<div id="board-container" style="flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:4px 6px;"></div> <div id="board-container" style="flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:4px 6px;"></div>
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;"> <div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;">
<div style="display:flex;align-items:center;gap:8px;"> <div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;">${emoji('person', '👤', 14)}</div> <div style="width:32px;height:32px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;">${emoji('person', '👤', 14)}</div>
<span id="white-name" style="font-size:13px;font-weight:600;color:#f8fafc;">${t('spectate.white')}</span> <span id="white-name" style="font-size:13px;font-weight:600;color:var(--text-primary);">${t('spectate.white')}</span>
</div> </div>
<div id="clock-white" style="font-size:16px;font-weight:700;font-family:'SF Mono',monospace;background:#1a1a2e;padding:4px 10px;border-radius:6px;color:#94a3b8;">--:--</div> <div id="clock-white" style="font-size:16px;font-weight:700;font-family:'SF Mono',monospace;background:var(--bg-card);padding:4px 10px;border-radius:6px;color:var(--text-secondary);">--:--</div>
</div> </div>
<div id="move-list" style="max-height:40px;overflow-x:auto;white-space:nowrap;padding:4px 14px;font-family:'SF Mono',monospace;font-size:12px;color:#94a3b8;display:flex;gap:4px;align-items:center;"></div> <div id="move-list" style="max-height:40px;overflow-x:auto;white-space:nowrap;padding:4px 14px;font-family:'SF Mono',monospace;font-size:12px;color:var(--text-secondary);display:flex;gap:4px;align-items:center;"></div>
</div> </div>
<style> <style>
@keyframes spectatePulse { 0%,100%{opacity:1}50%{opacity:0.4} } @keyframes spectatePulse { 0%,100%{opacity:1}50%{opacity:0.4} }
...@@ -151,7 +151,7 @@ function renderMoveList(el, moves) { ...@@ -151,7 +151,7 @@ function renderMoveList(el, moves) {
let html = ''; let html = '';
for (let i = 0; i < moves.length; i += 2) { for (let i = 0; i < moves.length; i += 2) {
const num = Math.floor(i / 2) + 1; const num = Math.floor(i / 2) + 1;
html += `<span style="color:#475569;">${num}.</span> `; html += `<span style="color:var(--text-dim);">${num}.</span> `;
html += `<span>${moves[i]?.san || moves[i] || ''}</span> `; html += `<span>${moves[i]?.san || moves[i] || ''}</span> `;
if (moves[i + 1]) html += `<span>${moves[i + 1]?.san || moves[i + 1] || ''}</span> `; if (moves[i + 1]) html += `<span>${moves[i + 1]?.san || moves[i + 1] || ''}</span> `;
} }
...@@ -164,7 +164,7 @@ function showResult(el, result) { ...@@ -164,7 +164,7 @@ function showResult(el, result) {
result === 'black_wins' ? t('spectate.black_wins') : result === 'black_wins' ? t('spectate.black_wins') :
result === 'draw' ? t('game.draw_game') : t('spectate.game_over'); result === 'draw' ? t('game.draw_game') : t('spectate.game_over');
const overlay = document.createElement('div'); const overlay = document.createElement('div');
overlay.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:#f8fafc;padding:16px 32px;border-radius:12px;font-size:16px;font-weight:700;z-index:30;text-align:center;'; overlay.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:var(--text-primary);padding:16px 32px;border-radius:12px;font-size:16px;font-weight:700;z-index:30;text-align:center;';
overlay.textContent = text; overlay.textContent = text;
el.querySelector('#board-container').style.position = 'relative'; el.querySelector('#board-container').style.position = 'relative';
el.querySelector('#board-container').appendChild(overlay); el.querySelector('#board-container').appendChild(overlay);
......
...@@ -189,7 +189,7 @@ export class DominoDrag { ...@@ -189,7 +189,7 @@ export class DominoDrag {
width: 48px; width: 48px;
height: 84px; height: 84px;
background: linear-gradient(160deg, #FFFDF5 0%, #F5EFE0 100%); background: linear-gradient(160deg, #FFFDF5 0%, #F5EFE0 100%);
border: 2px solid #E4AC38; border: 2px solid var(--gold);
border-radius: 7px; border-radius: 7px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -201,12 +201,12 @@ export class DominoDrag { ...@@ -201,12 +201,12 @@ export class DominoDrag {
border-color 0.15s, box-shadow 0.15s; border-color 0.15s, box-shadow 0.15s;
} }
.drag-tile.drag-valid { .drag-tile.drag-valid {
border-color: #10b981; border-color: var(--emerald);
transform: scale(1.2); transform: scale(1.2);
box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 20px rgba(16,185,129,0.4); box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 20px rgba(16,185,129,0.4);
} }
.drag-tile.drag-invalid { .drag-tile.drag-invalid {
border-color: #ef4444; border-color: var(--error);
transform: scale(0.95); transform: scale(0.95);
box-shadow: 0 6px 20px rgba(0,0,0,0.5), 0 0 12px rgba(239,68,68,0.3); box-shadow: 0 6px 20px rgba(0,0,0,0.5), 0 0 12px rgba(239,68,68,0.3);
} }
......
...@@ -24,7 +24,7 @@ export class DominoHand { ...@@ -24,7 +24,7 @@ export class DominoHand {
container.style.cssText = ` container.style.cssText = `
display:flex;gap:0;padding:8px 6px; display:flex;gap:0;padding:8px 6px;
background:linear-gradient(180deg, #0c1a2a 0%, #0a1420 100%); background:linear-gradient(180deg, #0c1a2a 0%, var(--bg-inset) 100%);
border-top:1px solid rgba(228,172,56,0.1); border-top:1px solid rgba(228,172,56,0.1);
min-height:100px;align-items:flex-end;justify-content:center; min-height:100px;align-items:flex-end;justify-content:center;
overflow:visible;position:relative; overflow:visible;position:relative;
...@@ -200,7 +200,7 @@ export class DominoHand { ...@@ -200,7 +200,7 @@ export class DominoHand {
} }
.dh-tile.dh-playable { .dh-tile.dh-playable {
opacity: 1; opacity: 1;
border-color: #E4AC38; border-color: var(--gold);
box-shadow: 0 3px 8px rgba(0,0,0,0.5), 0 0 12px rgba(228,172,56,0.3), box-shadow: 0 3px 8px rgba(0,0,0,0.5), 0 0 12px rgba(228,172,56,0.3),
inset 0 1px 0 rgba(255,255,255,0.7); inset 0 1px 0 rgba(255,255,255,0.7);
filter: brightness(1); filter: brightness(1);
...@@ -210,7 +210,7 @@ export class DominoHand { ...@@ -210,7 +210,7 @@ export class DominoHand {
} }
.dh-tile.dh-selected { .dh-tile.dh-selected {
transform: translateY(-16px) scale(1.12) !important; transform: translateY(-16px) scale(1.12) !important;
border-color: #E4AC38; border-color: var(--gold);
box-shadow: 0 8px 24px rgba(228,172,56,0.5), 0 0 20px rgba(228,172,56,0.3), box-shadow: 0 8px 24px rgba(228,172,56,0.5), 0 0 20px rgba(228,172,56,0.3),
inset 0 1px 0 rgba(255,255,255,0.7); inset 0 1px 0 rgba(255,255,255,0.7);
z-index: 10; z-index: 10;
......
...@@ -177,7 +177,7 @@ function injectStyles(el) { ...@@ -177,7 +177,7 @@ function injectStyles(el) {
/* ═══ Layout ═══ */ /* ═══ Layout ═══ */
.dg-wrap { .dg-wrap {
display:flex;flex-direction:column;height:100%; display:flex;flex-direction:column;height:100%;
background:linear-gradient(180deg,#060a10 0%,#0a1420 50%,#0c1828 100%); background:linear-gradient(180deg,#060a10 0%,var(--bg-inset) 50%,#0c1828 100%);
position:relative;overflow:hidden; position:relative;overflow:hidden;
} }
...@@ -198,16 +198,16 @@ function injectStyles(el) { ...@@ -198,16 +198,16 @@ function injectStyles(el) {
box-shadow:0 2px 8px rgba(0,0,0,0.3); box-shadow:0 2px 8px rgba(0,0,0,0.3);
} }
.dg-opp-info { display:flex;flex-direction:column;gap:2px; } .dg-opp-info { display:flex;flex-direction:column;gap:2px; }
.dg-opp-name { font-size:14px;font-weight:700;color:#f8fafc; } .dg-opp-name { font-size:14px;font-weight:700;color:var(--text-primary); }
.dg-opp-meta { display:flex;align-items:center;gap:6px; } .dg-opp-meta { display:flex;align-items:center;gap:6px; }
.dg-opp-count { font-size:11px;color:#94a3b8; } .dg-opp-count { font-size:11px;color:var(--text-secondary); }
.dg-thinking { font-size:11px;color:#E4AC38;display:none; } .dg-thinking { font-size:11px;color:var(--gold);display:none; }
.dg-dots::after { content:'...';animation:dotsAnim 1.4s steps(3) infinite; } .dg-dots::after { content:'...';animation:dotsAnim 1.4s steps(3) infinite; }
.dg-opp-right { display:flex;align-items:center;gap:8px; } .dg-opp-right { display:flex;align-items:center;gap:8px; }
.dg-conn-dot { width:8px;height:8px;border-radius:50%;background:#4ade80;box-shadow:0 0 6px rgba(74,222,128,0.5); } .dg-conn-dot { width:8px;height:8px;border-radius:50%;background:var(--green-light);box-shadow:0 0 6px rgba(74,222,128,0.5); }
.dg-boneyard-badge { .dg-boneyard-badge {
display:flex;align-items:center;gap:5px; display:flex;align-items:center;gap:5px;
font-size:12px;font-weight:700;color:#E4AC38; font-size:12px;font-weight:700;color:var(--gold);
background:rgba(228,172,56,0.06); background:rgba(228,172,56,0.06);
padding:5px 10px;border-radius:10px; padding:5px 10px;border-radius:10px;
border:1px solid rgba(228,172,56,0.1); border:1px solid rgba(228,172,56,0.1);
...@@ -246,24 +246,24 @@ function injectStyles(el) { ...@@ -246,24 +246,24 @@ function injectStyles(el) {
.dg-score-me, .dg-score-opp { .dg-score-me, .dg-score-opp {
display:flex;flex-direction:column;align-items:center;gap:1px; display:flex;flex-direction:column;align-items:center;gap:1px;
} }
.dg-score-label { font-size:10px;color:#64748b;font-weight:500; } .dg-score-label { font-size:10px;color:var(--text-muted);font-weight:500; }
.dg-score-me .dg-score-value { font-size:16px;font-weight:800;color:#4ade80; } .dg-score-me .dg-score-value { font-size:16px;font-weight:800;color:var(--green-light); }
.dg-score-opp .dg-score-value { font-size:16px;font-weight:800;color:#94a3b8; } .dg-score-opp .dg-score-value { font-size:16px;font-weight:800;color:var(--text-secondary); }
.dg-score-divider { width:1px;height:24px;background:rgba(255,255,255,0.06); } .dg-score-divider { width:1px;height:24px;background:var(--border); }
.dg-score-target { .dg-score-target {
font-size:11px;color:#64748b; font-size:11px;color:var(--text-muted);
display:flex;align-items:center;gap:2px; display:flex;align-items:center;gap:2px;
} }
.dg-target-val { color:#E4AC38;font-weight:700; } .dg-target-val { color:var(--gold);font-weight:700; }
.dg-turn-status { .dg-turn-status {
font-size:13px;font-weight:700;color:#E4AC38; font-size:13px;font-weight:700;color:var(--gold);
padding:4px 12px;border-radius:8px; padding:4px 12px;border-radius:8px;
background:rgba(228,172,56,0.06); background:rgba(228,172,56,0.06);
border:1px solid rgba(228,172,56,0.1); border:1px solid rgba(228,172,56,0.1);
transition:all 0.3s; transition:all 0.3s;
} }
.dg-turn-status.dg-waiting { .dg-turn-status.dg-waiting {
color:#64748b;background:transparent;border-color:transparent; color:var(--text-muted);background:transparent;border-color:transparent;
} }
/* ═══ Hand area ═══ */ /* ═══ Hand area ═══ */
...@@ -288,17 +288,17 @@ function injectStyles(el) { ...@@ -288,17 +288,17 @@ function injectStyles(el) {
.dg-btn-resign { .dg-btn-resign {
flex:0;padding:0 12px; flex:0;padding:0 12px;
background:rgba(239,68,68,0.06);border:1px solid rgba(239,68,68,0.12); background:rgba(239,68,68,0.06);border:1px solid rgba(239,68,68,0.12);
color:#fca5a5;font-size:11px; color:var(--red-soft);font-size:11px;
} }
.dg-btn-emote { .dg-btn-emote {
flex:0;padding:0 14px; flex:0;padding:0 14px;
background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06); background:rgba(255,255,255,0.03);border:1px solid var(--border);
font-size:18px; font-size:18px;
} }
.dg-btn-draw { .dg-btn-draw {
flex:1; flex:1;
background:rgba(228,172,56,0.08);border:1px solid rgba(228,172,56,0.18); background:rgba(228,172,56,0.08);border:1px solid rgba(228,172,56,0.18);
color:#E4AC38;font-size:13px;font-weight:700; color:var(--gold);font-size:13px;font-weight:700;
} }
.dg-btn-pass { .dg-btn-pass {
flex:0;padding:0 16px; flex:0;padding:0 16px;
...@@ -467,7 +467,7 @@ function handleLivePollData(el, data) { ...@@ -467,7 +467,7 @@ function handleLivePollData(el, data) {
mp.updateConnectionStatus?.(true); mp.updateConnectionStatus?.(true);
const connDot = el.querySelector('#conn-dot'); const connDot = el.querySelector('#conn-dot');
if (connDot) connDot.style.background = '#4ade80'; if (connDot) connDot.style.background = 'var(--green-light)';
// Handle server-side turn timeout // Handle server-side turn timeout
if (data._turn_timed_out) { if (data._turn_timed_out) {
...@@ -692,7 +692,7 @@ function showEmoteMenu(el) { ...@@ -692,7 +692,7 @@ function showEmoteMenu(el) {
menu.id = 'emote-menu'; menu.id = 'emote-menu';
menu.style.cssText = ` menu.style.cssText = `
position:absolute;bottom:110px;left:50%;transform:translateX(-50%); position:absolute;bottom:110px;left:50%;transform:translateX(-50%);
display:flex;gap:6px;padding:10px 14px;background:#1a1a2e; display:flex;gap:6px;padding:10px 14px;background:var(--bg-card);
border-radius:16px;border:1px solid rgba(228,172,56,0.15); border-radius:16px;border:1px solid rgba(228,172,56,0.15);
box-shadow:0 8px 24px rgba(0,0,0,0.6);z-index:60; box-shadow:0 8px 24px rgba(0,0,0,0.6);z-index:60;
animation:fadeIn 0.2s ease; animation:fadeIn 0.2s ease;
...@@ -929,22 +929,22 @@ function showRoundOverlay(el, winnerIdx, points) { ...@@ -929,22 +929,22 @@ function showRoundOverlay(el, winnerIdx, points) {
`; `;
overlay.innerHTML = ` overlay.innerHTML = `
<div style="font-size:48px;">${isMyWin ? emoji('trophy', '🏆', 48) : emoji('skull', '💀', 48)}</div> <div style="font-size:48px;">${isMyWin ? emoji('trophy', '🏆', 48) : emoji('skull', '💀', 48)}</div>
<div style="font-size:20px;font-weight:800;color:${isMyWin ? '#4ade80' : '#fca5a5'};"> <div style="font-size:20px;font-weight:800;color:${isMyWin ? 'var(--green-light)' : 'var(--red-soft)'};">
${isMyWin ? t('game.round_won') : t('game.round_lost')} ${isMyWin ? t('game.round_won') : t('game.round_lost')}
</div> </div>
<div style="font-size:14px;color:#94a3b8;">${t('game.points', { n: points })}</div> <div style="font-size:14px;color:var(--text-secondary);">${t('game.points', { n: points })}</div>
<div style="display:flex;gap:16px;margin-top:8px;"> <div style="display:flex;gap:16px;margin-top:8px;">
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:24px;font-weight:800;color:#4ade80;">${state.matchScores[state.myPlayerIndex]}</div> <div style="font-size:24px;font-weight:800;color:var(--green-light);">${state.matchScores[state.myPlayerIndex]}</div>
<div style="font-size:11px;color:#6ee7b7;">${t('common.you')}</div> <div style="font-size:11px;color:#6ee7b7;">${t('common.you')}</div>
</div> </div>
<div style="font-size:20px;color:#475569;align-self:center;">—</div> <div style="font-size:20px;color:var(--text-dim);align-self:center;">—</div>
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:24px;font-weight:800;color:#fca5a5;">${state.matchScores[1 - state.myPlayerIndex]}</div> <div style="font-size:24px;font-weight:800;color:var(--red-soft);">${state.matchScores[1 - state.myPlayerIndex]}</div>
<div style="font-size:11px;color:#fca5a5;">${t('common.opponent')}</div> <div style="font-size:11px;color:var(--red-soft);">${t('common.opponent')}</div>
</div> </div>
</div> </div>
<div style="font-size:12px;color:#64748b;margin-top:8px;">${t('game.round_next')}</div> <div style="font-size:12px;color:var(--text-muted);margin-top:8px;">${t('game.round_next')}</div>
`; `;
const wrap = el.querySelector('#domino-wrap'); const wrap = el.querySelector('#domino-wrap');
...@@ -1157,7 +1157,7 @@ function updateUI(el) { ...@@ -1157,7 +1157,7 @@ function updateUI(el) {
const oppBar = el.querySelector('#domino-opp-bar'); const oppBar = el.querySelector('#domino-opp-bar');
if (oppBar && !oppBar.dataset.tensionShown) { if (oppBar && !oppBar.dataset.tensionShown) {
oppBar.dataset.tensionShown = '1'; oppBar.dataset.tensionShown = '1';
oppBar.style.borderBottom = '2px solid #ef4444'; oppBar.style.borderBottom = '2px solid var(--error)';
oppBar.animate([{background:'rgba(239,68,68,0.15)'},{background:'#0f0f1e'}], {duration:800}); oppBar.animate([{background:'rgba(239,68,68,0.15)'},{background:'#0f0f1e'}], {duration:800});
juice.hapticLight(); juice.hapticLight();
const alert = document.createElement('div'); const alert = document.createElement('div');
......
...@@ -14,47 +14,47 @@ export function mountResult(el, params) { ...@@ -14,47 +14,47 @@ export function mountResult(el, params) {
const icon = isWin ? `${emoji('crown', '👑', 32)}<br>${emoji('trophy', '🏆', 56)}` : isDraw ? emoji('handshake', '🤝', 56) : emoji('skull', '💀', 56); const icon = isWin ? `${emoji('crown', '👑', 32)}<br>${emoji('trophy', '🏆', 56)}` : isDraw ? emoji('handshake', '🤝', 56) : emoji('skull', '💀', 56);
const title = resigned ? t('game.resigned') : reason === 'resign' ? t('game.opponent_resigned') : reason === 'abandon' ? t('game.opponent_abandoned') : isWin ? t('game.win') : isDraw ? t('game.draw_game') : t('game.loss'); const title = resigned ? t('game.resigned') : reason === 'resign' ? t('game.opponent_resigned') : reason === 'abandon' ? t('game.opponent_abandoned') : isWin ? t('game.win') : isDraw ? t('game.draw_game') : t('game.loss');
const titleColor = isWin ? '#4ade80' : isDraw ? '#fbbf24' : '#fca5a5'; const titleColor = isWin ? 'var(--green-light)' : isDraw ? 'var(--amber)' : 'var(--red-soft)';
el.innerHTML = ` el.innerHTML = `
<div id="result-wrap" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;padding:24px;background:linear-gradient(180deg,#070d14 0%,#0a1420 40%,#0d1a2a 100%);"> <div id="result-wrap" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;padding:24px;background:linear-gradient(180deg,#070d14 0%,var(--bg-inset) 40%,#0d1a2a 100%);">
<div id="result-icon" style="font-size:56px;animation:bounceIn 0.5s cubic-bezier(0.34,1.56,0.64,1);">${icon}</div> <div id="result-icon" style="font-size:56px;animation:bounceIn 0.5s cubic-bezier(0.34,1.56,0.64,1);">${icon}</div>
<div style="font-size:28px;font-weight:800;color:${titleColor};animation:fadeSlideUp 0.4s ease 0.2s both;">${title}</div> <div style="font-size:28px;font-weight:800;color:${titleColor};animation:fadeSlideUp 0.4s ease 0.2s both;">${title}</div>
<!-- Score summary --> <!-- Score summary -->
<div style="display:flex;gap:20px;align-items:center;animation:fadeSlideUp 0.4s ease 0.3s both;"> <div style="display:flex;gap:20px;align-items:center;animation:fadeSlideUp 0.4s ease 0.3s both;">
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:32px;font-weight:800;color:#4ade80;" id="score-me">0</div> <div style="font-size:32px;font-weight:800;color:var(--green-light);" id="score-me">0</div>
<div style="font-size:11px;color:#86efac;">${t('common.you')}</div> <div style="font-size:11px;color:#86efac;">${t('common.you')}</div>
</div> </div>
<div style="font-size:18px;color:#475569;">—</div> <div style="font-size:18px;color:var(--text-dim);">—</div>
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:32px;font-weight:800;color:#fca5a5;" id="score-opp">0</div> <div style="font-size:32px;font-weight:800;color:var(--red-soft);" id="score-opp">0</div>
<div style="font-size:11px;color:#fca5a5;">${t('common.opponent')}</div> <div style="font-size:11px;color:var(--red-soft);">${t('common.opponent')}</div>
</div> </div>
</div> </div>
<div style="font-size:12px;color:#64748b;animation:fadeSlideUp 0.4s ease 0.35s both;">${rounds === 1 ? t('game.rounds_count', { n: rounds }) : t('game.rounds_count_plural', { n: rounds })}</div> <div style="font-size:12px;color:var(--text-muted);animation:fadeSlideUp 0.4s ease 0.35s both;">${rounds === 1 ? t('game.rounds_count', { n: rounds }) : t('game.rounds_count_plural', { n: rounds })}</div>
<!-- Rewards (populated after server response) --> <!-- Rewards (populated after server response) -->
<div id="rewards-section" style="display:flex;gap:12px;margin-top:8px;animation:fadeSlideUp 0.4s ease 0.5s both;"> <div id="rewards-section" style="display:flex;gap:12px;margin-top:8px;animation:fadeSlideUp 0.4s ease 0.5s both;">
<div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(251,191,36,0.1);border:1px solid rgba(251,191,36,0.2);border-radius:10px;"> <div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(251,191,36,0.1);border:1px solid rgba(251,191,36,0.2);border-radius:10px;">
<span style="font-size:16px;">${emoji('coin', '🪙', 16)}</span> <span style="font-size:16px;">${emoji('coin', '🪙', 16)}</span>
<span style="font-size:14px;font-weight:700;color:#fbbf24;" id="coins-earned">...</span> <span style="font-size:14px;font-weight:700;color:var(--amber);" id="coins-earned">...</span>
</div> </div>
<div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.2);border-radius:10px;"> <div style="display:flex;align-items:center;gap:4px;padding:8px 14px;background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.2);border-radius:10px;">
<span style="font-size:16px;">${emoji('star', '⭐', 16)}</span> <span style="font-size:16px;">${emoji('star', '⭐', 16)}</span>
<span style="font-size:14px;font-weight:700;color:#a78bfa;" id="xp-earned">...</span> <span style="font-size:14px;font-weight:700;color:var(--purple-light);" id="xp-earned">...</span>
</div> </div>
</div> </div>
<!-- Rating change --> <!-- Rating change -->
<div id="rating-section" style="font-size:14px;font-weight:600;color:#94a3b8;animation:fadeSlideUp 0.4s ease 0.6s both;">${t('game.rating')}: ...</div> <div id="rating-section" style="font-size:14px;font-weight:600;color:var(--text-secondary);animation:fadeSlideUp 0.4s ease 0.6s both;">${t('game.rating')}: ...</div>
<!-- Actions --> <!-- Actions -->
<div style="display:flex;gap:10px;margin-top:16px;width:100%;max-width:300px;animation:fadeSlideUp 0.4s ease 0.7s both;"> <div style="display:flex;gap:10px;margin-top:16px;width:100%;max-width:300px;animation:fadeSlideUp 0.4s ease 0.7s both;">
<button class="btn btn-primary" id="btn-rematch" style="flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700;background:linear-gradient(135deg,#10b981,#06b6d4);">${t('result.rematch')}</button> <button class="btn btn-primary" id="btn-rematch" style="flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700;background:linear-gradient(135deg,var(--emerald),var(--cyan));">${t('result.rematch')}</button>
<button class="btn btn-secondary" id="btn-back" style="flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);color:#e2e8f0;">${t('common.back')}</button> <button class="btn btn-secondary" id="btn-back" style="flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);color:var(--text-light);">${t('common.back')}</button>
</div> </div>
</div> </div>
<style> <style>
...@@ -149,7 +149,7 @@ async function completeOnServer(el, matchId, result, mode) { ...@@ -149,7 +149,7 @@ async function completeOnServer(el, matchId, result, mode) {
if (xpEl) xpEl.textContent = `+${xp} XP`; if (xpEl) xpEl.textContent = `+${xp} XP`;
if (ratingEl) { if (ratingEl) {
const sign = ratingChange >= 0 ? '+' : ''; const sign = ratingChange >= 0 ? '+' : '';
const color = ratingChange > 0 ? '#4ade80' : ratingChange === 0 ? '#fbbf24' : '#fca5a5'; const color = ratingChange > 0 ? 'var(--green-light)' : ratingChange === 0 ? 'var(--amber)' : 'var(--red-soft)';
ratingEl.innerHTML = `${t('game.rating')}: <span style="color:${color};font-weight:700;">${sign}${ratingChange}</span>`; ratingEl.innerHTML = `${t('game.rating')}: <span style="color:${color};font-weight:700;">${sign}${ratingChange}</span>`;
} }
......
...@@ -50,12 +50,12 @@ function renderMenu(el) { ...@@ -50,12 +50,12 @@ function renderMenu(el) {
.domino-menu { .domino-menu {
display:flex;flex-direction:column;align-items:center;justify-content:center; display:flex;flex-direction:column;align-items:center;justify-content:center;
height:100%;padding:24px; height:100%;padding:24px;
background:linear-gradient(180deg, #070d14 0%, #0a1420 40%, #0d1a2a 100%); background:linear-gradient(180deg, #070d14 0%, var(--bg-inset) 40%, #0d1a2a 100%);
} }
.dm-hero { text-align:center;margin-bottom:28px; } .dm-hero { text-align:center;margin-bottom:28px; }
.dm-icon { font-size:56px;margin-bottom:12px;animation:dmFloat 3s ease-in-out infinite; } .dm-icon { font-size:56px;margin-bottom:12px;animation:dmFloat 3s ease-in-out infinite; }
.dm-title { font-size:26px;font-weight:800;color:#f8fafc;margin:0; } .dm-title { font-size:26px;font-weight:800;color:var(--text-primary);margin:0; }
.dm-subtitle { font-size:13px;color:#E4AC38;margin:8px 0 0;opacity:0.9; } .dm-subtitle { font-size:13px;color:var(--gold);margin:8px 0 0;opacity:0.9; }
.dm-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:300px; } .dm-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:300px; }
.dm-btn { .dm-btn {
display:flex;align-items:center;justify-content:center;gap:10px; display:flex;align-items:center;justify-content:center;gap:10px;
...@@ -65,12 +65,12 @@ function renderMenu(el) { ...@@ -65,12 +65,12 @@ function renderMenu(el) {
box-shadow:0 4px 16px rgba(0,0,0,0.3); box-shadow:0 4px 16px rgba(0,0,0,0.3);
} }
.dm-btn:active { transform:scale(0.95); } .dm-btn:active { transform:scale(0.95); }
.dm-btn-primary { background:linear-gradient(135deg,#E4AC38 0%,#d4940a 100%);color:#1a1a1a; } .dm-btn-primary { background:linear-gradient(135deg,var(--gold) 0%,var(--gold-dark) 100%);color:var(--bg-dark); }
.dm-btn-online { background:linear-gradient(135deg,#06b6d4 0%,#0891b2 100%);color:#fff; } .dm-btn-online { background:linear-gradient(135deg,var(--cyan) 0%,#0891b2 100%);color:#fff; }
.dm-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:#e2e8f0;min-height:50px;font-size:14px;box-shadow:none; } .dm-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:var(--text-light);min-height:50px;font-size:14px;box-shadow:none; }
.dm-btn-icon { display:flex; } .dm-btn-icon { display:flex; }
.dm-btn-label { flex:none; } .dm-btn-label { flex:none; }
.dm-back { margin-top:20px;font-size:13px;color:#64748b;background:none;border:none;cursor:pointer;padding:8px 16px; } .dm-back { margin-top:20px;font-size:13px;color:var(--text-muted);background:none;border:none;cursor:pointer;padding:8px 16px; }
@keyframes dmFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-6px)} } @keyframes dmFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-6px)} }
</style> </style>
`; `;
...@@ -98,16 +98,16 @@ function renderMenu(el) { ...@@ -98,16 +98,16 @@ function renderMenu(el) {
function renderBotPicker(el) { function renderBotPicker(el) {
const levels = [ const levels = [
{ key: 'beginner', label: t('domino.beginner'), desc: t('domino.beginner_desc'), icon: emoji('bot_easy', '😊', 30), color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' }, { key: 'beginner', label: t('domino.beginner'), desc: t('domino.beginner_desc'), icon: emoji('bot_easy', '😊', 30), color: 'var(--green-light)', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ key: 'intermediate', label: t('domino.intermediate'), desc: t('domino.intermediate_desc'), icon: emoji('bot_medium', '🧐', 30), color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' }, { key: 'intermediate', label: t('domino.intermediate'), desc: t('domino.intermediate_desc'), icon: emoji('bot_medium', '🧐', 30), color: 'var(--amber)', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ key: 'expert', label: t('domino.expert'), desc: t('domino.expert_desc'), icon: emoji('bot_hard', '🧠', 30), color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' } { key: 'expert', label: t('domino.expert'), desc: t('domino.expert_desc'), icon: emoji('bot_hard', '🧠', 30), color: 'var(--red-light)', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
]; ];
el.innerHTML = ` el.innerHTML = `
<div class="domino-menu"> <div class="domino-menu">
<div class="dm-hero"> <div class="dm-hero">
<h2 style="font-size:20px;font-weight:700;color:#f8fafc;margin:0;">${t('domino.select_bot')}</h2> <h2 style="font-size:20px;font-weight:700;color:var(--text-primary);margin:0;">${t('domino.select_bot')}</h2>
<p style="font-size:12px;color:#94a3b8;margin:8px 0 0;">${t('domino.bot_strategy')}</p> <p style="font-size:12px;color:var(--text-secondary);margin:8px 0 0;">${t('domino.bot_strategy')}</p>
</div> </div>
<div class="dm-buttons"> <div class="dm-buttons">
${levels.map(l => ` ${levels.map(l => `
...@@ -115,9 +115,9 @@ function renderBotPicker(el) { ...@@ -115,9 +115,9 @@ function renderBotPicker(el) {
<span class="dm-level-icon">${l.icon}</span> <span class="dm-level-icon">${l.icon}</span>
<div class="dm-level-info"> <div class="dm-level-info">
<div style="font-size:15px;font-weight:700;color:${l.color};">${l.label}</div> <div style="font-size:15px;font-weight:700;color:${l.color};">${l.label}</div>
<div style="font-size:12px;color:#94a3b8;margin-top:2px;">${l.desc}</div> <div style="font-size:12px;color:var(--text-secondary);margin-top:2px;">${l.desc}</div>
</div> </div>
<span style="font-size:16px;color:#475569;">←</span> <span style="font-size:16px;color:var(--text-dim);">←</span>
</button> </button>
`).join('')} `).join('')}
</div> </div>
...@@ -150,16 +150,16 @@ function renderBotPicker(el) { ...@@ -150,16 +150,16 @@ function renderBotPicker(el) {
function renderTargetPicker(el, botLevel) { function renderTargetPicker(el, botLevel) {
const targets = [ const targets = [
{ value: 50, label: t('domino.50_points'), desc: t('domino.50_desc'), icon: emoji('lightning', '⚡', 30), color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' }, { value: 50, label: t('domino.50_points'), desc: t('domino.50_desc'), icon: emoji('lightning', '⚡', 30), color: 'var(--green-light)', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ value: 100, label: t('domino.100_points'), desc: t('domino.100_desc'), icon: emoji('target', '🎯', 30), color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' }, { value: 100, label: t('domino.100_points'), desc: t('domino.100_desc'), icon: emoji('target', '🎯', 30), color: 'var(--amber)', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ value: 150, label: t('domino.150_points'), desc: t('domino.150_desc'), icon: emoji('fire', '🔥', 30), color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' } { value: 150, label: t('domino.150_points'), desc: t('domino.150_desc'), icon: emoji('fire', '🔥', 30), color: 'var(--red-light)', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
]; ];
el.innerHTML = ` el.innerHTML = `
<div class="domino-menu"> <div class="domino-menu">
<div class="dm-hero"> <div class="dm-hero">
<h2 style="font-size:20px;font-weight:700;color:#f8fafc;margin:0;">${t('domino.select_target')}</h2> <h2 style="font-size:20px;font-weight:700;color:var(--text-primary);margin:0;">${t('domino.select_target')}</h2>
<p style="font-size:12px;color:#94a3b8;margin:8px 0 0;">${t('domino.target_hint')}</p> <p style="font-size:12px;color:var(--text-secondary);margin:8px 0 0;">${t('domino.target_hint')}</p>
</div> </div>
<div class="dm-buttons"> <div class="dm-buttons">
${targets.map(t => ` ${targets.map(t => `
...@@ -167,9 +167,9 @@ function renderTargetPicker(el, botLevel) { ...@@ -167,9 +167,9 @@ function renderTargetPicker(el, botLevel) {
<span class="dm-level-icon">${t.icon}</span> <span class="dm-level-icon">${t.icon}</span>
<div class="dm-level-info"> <div class="dm-level-info">
<div style="font-size:15px;font-weight:700;color:${t.color};">${t.label}</div> <div style="font-size:15px;font-weight:700;color:${t.color};">${t.label}</div>
<div style="font-size:12px;color:#94a3b8;margin-top:2px;">${t.desc}</div> <div style="font-size:12px;color:var(--text-secondary);margin-top:2px;">${t.desc}</div>
</div> </div>
<span style="font-size:16px;color:#475569;">←</span> <span style="font-size:16px;color:var(--text-dim);">←</span>
</button> </button>
`).join('')} `).join('')}
</div> </div>
...@@ -198,7 +198,7 @@ function renderLobby(el, { challengeId, friendId, friendName }) { ...@@ -198,7 +198,7 @@ function renderLobby(el, { challengeId, friendId, friendName }) {
<div class="domino-menu"> <div class="domino-menu">
<div class="dm-hero"> <div class="dm-hero">
<div style="font-size:44px;margin-bottom:12px;">${emoji('domino_tile', '🁣', 44)}</div> <div style="font-size:44px;margin-bottom:12px;">${emoji('domino_tile', '🁣', 44)}</div>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;margin:0;"> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);margin:0;">
${isHost ? t('room.waiting_friend') : t('room.challenge_from', { name: friendName || t('common.friend') })} ${isHost ? t('room.waiting_friend') : t('room.challenge_from', { name: friendName || t('common.friend') })}
</h2> </h2>
</div> </div>
...@@ -207,12 +207,12 @@ function renderLobby(el, { challengeId, friendId, friendName }) { ...@@ -207,12 +207,12 @@ function renderLobby(el, { challengeId, friendId, friendName }) {
<div style="width:64px;height:64px;border-radius:50%;border:3px solid rgba(228,172,56,0.4);display:flex;align-items:center;justify-content:center;animation:dmPulseRing 2s ease-in-out infinite;"> <div style="width:64px;height:64px;border-radius:50%;border:3px solid rgba(228,172,56,0.4);display:flex;align-items:center;justify-content:center;animation:dmPulseRing 2s ease-in-out infinite;">
${emoji('clock', '⏳', 26)} ${emoji('clock', '⏳', 26)}
</div> </div>
<div style="font-size:13px;color:#E4AC38;" id="lobby-msg">${isHost ? t('room.send_invite') : t('room.tap_accept')}</div> <div style="font-size:13px;color:var(--gold);" id="lobby-msg">${isHost ? t('room.send_invite') : t('room.tap_accept')}</div>
</div> </div>
<div style="display:flex;gap:10px;margin-top:20px;"> <div style="display:flex;gap:10px;margin-top:20px;">
${!isHost ? `<button class="dm-btn dm-btn-online" id="btn-accept" style="min-height:48px;padding:0 28px;font-size:15px;">${t('social.accept')}</button>` : ''} ${!isHost ? `<button class="dm-btn dm-btn-online" id="btn-accept" style="min-height:48px;padding:0 28px;font-size:15px;">${t('social.accept')}</button>` : ''}
<button class="dm-btn dm-btn-friend" id="btn-cancel-lobby" style="min-height:48px;padding:0 20px;font-size:14px;border-color:rgba(239,68,68,0.3);color:#fca5a5;">${t('common.cancel')}</button> <button class="dm-btn dm-btn-friend" id="btn-cancel-lobby" style="min-height:48px;padding:0 20px;font-size:14px;border-color:rgba(239,68,68,0.3);color:var(--red-soft);">${t('common.cancel')}</button>
</div> </div>
</div> </div>
<style> <style>
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ import { emoji } from '../../../core/theme.js'; ...@@ -7,7 +7,7 @@ import { emoji } from '../../../core/theme.js';
const PLACE_EMOJI = [emoji('medal_1', '🥇', 18), emoji('medal_2', '🥈', 18), emoji('medal_3', '🥉', 18), '4️⃣']; const PLACE_EMOJI = [emoji('medal_1', '🥇', 18), emoji('medal_2', '🥈', 18), emoji('medal_3', '🥉', 18), '4️⃣'];
const PLACE_LABEL = [t('ludo.place_first'), t('ludo.place_second'), t('ludo.place_third'), t('ludo.place_fourth')]; const PLACE_LABEL = [t('ludo.place_first'), t('ludo.place_second'), t('ludo.place_third'), t('ludo.place_fourth')];
const PLACE_COLOR = ['#FFD700', '#C0C0C0', '#CD7F32', '#64748b']; const PLACE_COLOR = ['var(--gold-soft)', '#C0C0C0', '#CD7F32', 'var(--text-muted)'];
export function mountResult(el, params) { export function mountResult(el, params) {
const { result, place, resigned, rewards, winners = [], playerNames = [], playerColors = [] } = params; const { result, place, resigned, rewards, winners = [], playerNames = [], playerColors = [] } = params;
...@@ -26,7 +26,7 @@ export function mountResult(el, params) { ...@@ -26,7 +26,7 @@ export function mountResult(el, params) {
leaderboard.push({ leaderboard.push({
rank: rank + 1, rank: rank + 1,
name: playerNames[pIdx] || `${t('common.player')} ${pIdx + 1}`, name: playerNames[pIdx] || `${t('common.player')} ${pIdx + 1}`,
color: playerColors[pIdx] || '#94a3b8', color: playerColors[pIdx] || 'var(--text-secondary)',
isMe: pIdx === 0, isMe: pIdx === 0,
}); });
} }
...@@ -36,7 +36,7 @@ export function mountResult(el, params) { ...@@ -36,7 +36,7 @@ export function mountResult(el, params) {
leaderboard.push({ leaderboard.push({
rank: leaderboard.length + 1, rank: leaderboard.length + 1,
name: playerNames[i] || `${t('common.player')} ${i + 1}`, name: playerNames[i] || `${t('common.player')} ${i + 1}`,
color: playerColors[i] || '#94a3b8', color: playerColors[i] || 'var(--text-secondary)',
isMe: i === 0, isMe: i === 0,
}); });
} }
...@@ -56,11 +56,11 @@ export function mountResult(el, params) { ...@@ -56,11 +56,11 @@ export function mountResult(el, params) {
: place === 3 ? t('game.third_place') : place === 3 ? t('game.third_place')
: t('game.fourth_place'); : t('game.fourth_place');
const heroColor = resigned ? '#EF4444' const heroColor = resigned ? 'var(--error)'
: place === 1 ? '#FFD700' : place === 1 ? 'var(--gold-soft)'
: place === 2 ? '#C0C0C0' : place === 2 ? '#C0C0C0'
: place === 3 ? '#CD7F32' : place === 3 ? '#CD7F32'
: '#EF4444'; : 'var(--error)';
el.innerHTML = ` el.innerHTML = `
<div class="lr-result-wrap"> <div class="lr-result-wrap">
...@@ -76,7 +76,7 @@ export function mountResult(el, params) { ...@@ -76,7 +76,7 @@ export function mountResult(el, params) {
<div class="lr-lb-title">${t('game.player_ranking')}</div> <div class="lr-lb-title">${t('game.player_ranking')}</div>
${leaderboard.map(p => ` ${leaderboard.map(p => `
<div class="lr-lb-row ${p.isMe ? 'lr-lb-me' : ''}" style="--pc:${p.color};"> <div class="lr-lb-row ${p.isMe ? 'lr-lb-me' : ''}" style="--pc:${p.color};">
<div class="lr-lb-rank" style="color:${PLACE_COLOR[p.rank - 1] || '#64748b'};"> <div class="lr-lb-rank" style="color:${PLACE_COLOR[p.rank - 1] || 'var(--text-muted)'};">
${p.rank <= 3 ? PLACE_EMOJI[p.rank - 1] : p.rank} ${p.rank <= 3 ? PLACE_EMOJI[p.rank - 1] : p.rank}
</div> </div>
<div class="lr-lb-color" style="background:${p.color};"></div> <div class="lr-lb-color" style="background:${p.color};"></div>
...@@ -97,7 +97,7 @@ export function mountResult(el, params) { ...@@ -97,7 +97,7 @@ export function mountResult(el, params) {
<span>+${xp} XP</span> <span>+${xp} XP</span>
</div> </div>
${ratingChange ? ` ${ratingChange ? `
<div class="lr-reward-pill" style="color:${ratingChange > 0 ? '#4ade80' : '#fca5a5'};border-color:${ratingChange > 0 ? 'rgba(74,222,128,0.2)' : 'rgba(252,165,165,0.2)'};"> <div class="lr-reward-pill" style="color:${ratingChange > 0 ? 'var(--green-light)' : 'var(--red-soft)'};border-color:${ratingChange > 0 ? 'rgba(74,222,128,0.2)' : 'rgba(252,165,165,0.2)'};">
<span>${emoji('chart_up', '📈', 16)}</span> <span>${emoji('chart_up', '📈', 16)}</span>
<span>${ratingChange > 0 ? '+' : ''}${ratingChange}</span> <span>${ratingChange > 0 ? '+' : ''}${ratingChange}</span>
</div>` : ''} </div>` : ''}
...@@ -146,7 +146,7 @@ function getResultStyles() { ...@@ -146,7 +146,7 @@ function getResultStyles() {
.lr-result-wrap { .lr-result-wrap {
display:flex;flex-direction:column;align-items:center;justify-content:center; display:flex;flex-direction:column;align-items:center;justify-content:center;
height:100%;gap:20px;padding:24px; height:100%;gap:20px;padding:24px;
background:linear-gradient(180deg,#070d14 0%,#0a1420 40%,#0d1a2a 100%); background:linear-gradient(180deg,#070d14 0%,var(--bg-inset) 40%,var(--bg-inset) 100%);
} }
.lr-hero-section { text-align:center; } .lr-hero-section { text-align:center; }
.lr-hero-icon { font-size:64px;animation:lrFloat 2.5s ease-in-out infinite; } .lr-hero-icon { font-size:64px;animation:lrFloat 2.5s ease-in-out infinite; }
...@@ -155,11 +155,11 @@ function getResultStyles() { ...@@ -155,11 +155,11 @@ function getResultStyles() {
.lr-leaderboard { .lr-leaderboard {
width:100%;max-width:320px; width:100%;max-width:320px;
background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06); background:rgba(255,255,255,0.02);border:1px solid var(--border);
border-radius:16px;padding:14px; border-radius:16px;padding:14px;
} }
.lr-lb-title { .lr-lb-title {
font-size:12px;font-weight:700;color:#64748b;text-align:center; font-size:12px;font-weight:700;color:var(--text-muted);text-align:center;
margin-bottom:10px;text-transform:uppercase;letter-spacing:0.05em; margin-bottom:10px;text-transform:uppercase;letter-spacing:0.05em;
} }
.lr-lb-row { .lr-lb-row {
...@@ -172,8 +172,8 @@ function getResultStyles() { ...@@ -172,8 +172,8 @@ function getResultStyles() {
} }
.lr-lb-rank { font-size:18px;min-width:28px;text-align:center; } .lr-lb-rank { font-size:18px;min-width:28px;text-align:center; }
.lr-lb-color { width:12px;height:12px;border-radius:50%;flex-shrink:0; } .lr-lb-color { width:12px;height:12px;border-radius:50%;flex-shrink:0; }
.lr-lb-name { flex:1;font-size:14px;font-weight:600;color:#f8fafc; } .lr-lb-name { flex:1;font-size:14px;font-weight:600;color:var(--text-primary); }
.lr-lb-place { font-size:11px;color:#64748b;font-weight:500; } .lr-lb-place { font-size:11px;color:var(--text-muted);font-weight:500; }
.lr-rewards { display:flex;gap:10px;flex-wrap:wrap;justify-content:center; } .lr-rewards { display:flex;gap:10px;flex-wrap:wrap;justify-content:center; }
.lr-reward-pill { .lr-reward-pill {
...@@ -182,8 +182,8 @@ function getResultStyles() { ...@@ -182,8 +182,8 @@ function getResultStyles() {
font-size:14px;font-weight:700; font-size:14px;font-weight:700;
border:1px solid transparent; border:1px solid transparent;
} }
.lr-reward-coins { color:#fbbf24;background:rgba(251,191,36,0.1);border-color:rgba(251,191,36,0.2); } .lr-reward-coins { color:var(--amber);background:rgba(251,191,36,0.1);border-color:rgba(251,191,36,0.2); }
.lr-reward-xp { color:#a78bfa;background:rgba(139,92,246,0.1);border-color:rgba(139,92,246,0.2); } .lr-reward-xp { color:var(--purple-light);background:rgba(139,92,246,0.1);border-color:rgba(139,92,246,0.2); }
.lr-actions { display:flex;gap:10px;width:100%;max-width:300px; } .lr-actions { display:flex;gap:10px;width:100%;max-width:300px; }
.lr-actions .btn { flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700; } .lr-actions .btn { flex:1;min-height:48px;border-radius:14px;font-size:15px;font-weight:700; }
......
...@@ -309,13 +309,13 @@ function getStyles() { ...@@ -309,13 +309,13 @@ function getStyles() {
.lr-wrap { .lr-wrap {
display:flex;flex-direction:column;align-items:center;justify-content:center; display:flex;flex-direction:column;align-items:center;justify-content:center;
height:100%;padding:20px; height:100%;padding:20px;
background:linear-gradient(180deg, #070d14 0%, #0a1420 40%, #0d1a2a 100%); background:linear-gradient(180deg, #070d14 0%, var(--bg-inset) 40%, var(--bg-inset) 100%);
overflow-y:auto; overflow-y:auto;
} }
.lr-hero { text-align:center;margin-bottom:24px; } .lr-hero { text-align:center;margin-bottom:24px; }
.lr-icon { font-size:56px;margin-bottom:12px;animation:lrFloat 3s ease-in-out infinite; } .lr-icon { font-size:56px;margin-bottom:12px;animation:lrFloat 3s ease-in-out infinite; }
.lr-title { font-size:26px;font-weight:800;color:#f8fafc;margin:0; } .lr-title { font-size:26px;font-weight:800;color:var(--text-primary);margin:0; }
.lr-subtitle { font-size:13px;color:#E4AC38;margin:8px 0 0;opacity:0.9; } .lr-subtitle { font-size:13px;color:var(--gold);margin:8px 0 0;opacity:0.9; }
.lr-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px; } .lr-buttons { display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px; }
.lr-btn { .lr-btn {
display:flex;align-items:center;gap:10px;flex-wrap:wrap; display:flex;align-items:center;gap:10px;flex-wrap:wrap;
...@@ -325,27 +325,27 @@ function getStyles() { ...@@ -325,27 +325,27 @@ function getStyles() {
box-shadow:0 4px 16px rgba(0,0,0,0.3); box-shadow:0 4px 16px rgba(0,0,0,0.3);
} }
.lr-btn:active { transform:scale(0.95); } .lr-btn:active { transform:scale(0.95); }
.lr-btn-primary { background:linear-gradient(135deg,#E4AC38 0%,#d4940a 100%);color:#1a1a1a; } .lr-btn-primary { background:linear-gradient(135deg,var(--gold) 0%,var(--gold-dark) 100%);color:#1a1a1a; }
.lr-btn-online { background:linear-gradient(135deg,#8B5CF6 0%,#7C3AED 100%);color:#fff; } .lr-btn-online { background:linear-gradient(135deg,var(--purple) 0%,var(--violet) 100%);color:#fff; }
.lr-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:#e2e8f0;min-height:50px;font-size:14px;box-shadow:none; } .lr-btn-friend { background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.1);color:var(--text-light);min-height:50px;font-size:14px;box-shadow:none; }
.lr-btn-start { width:100%;max-width:320px;justify-content:center;background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;font-size:17px;font-weight:800;margin-top:16px; } .lr-btn-start { width:100%;max-width:320px;justify-content:center;background:linear-gradient(135deg,var(--emerald),var(--cyan));color:#fff;font-size:17px;font-weight:800;margin-top:16px; }
.lr-btn-icon { display:flex;flex-shrink:0; } .lr-btn-icon { display:flex;flex-shrink:0; }
.lr-btn-label { font-weight:700;font-size:16px; } .lr-btn-label { font-weight:700;font-size:16px; }
.lr-btn-desc { width:100%;font-size:11px;opacity:0.7;font-weight:400;margin-top:2px; } .lr-btn-desc { width:100%;font-size:11px;opacity:0.7;font-weight:400;margin-top:2px; }
.lr-back { margin-top:16px;font-size:13px;color:#64748b;background:none;border:none;cursor:pointer;padding:8px 16px; } .lr-back { margin-top:16px;font-size:13px;color:var(--text-muted);background:none;border:none;cursor:pointer;padding:8px 16px; }
.lr-section { width:100%;max-width:320px;margin-bottom:16px; } .lr-section { width:100%;max-width:320px;margin-bottom:16px; }
.lr-section-title { font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:8px;text-align:center; } .lr-section-title { font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:8px;text-align:center; }
.lr-grid { display:flex;gap:8px;justify-content:center;flex-wrap:wrap; } .lr-grid { display:flex;gap:8px;justify-content:center;flex-wrap:wrap; }
.lr-chip { .lr-chip {
display:flex;align-items:center;gap:6px; display:flex;align-items:center;gap:6px;
padding:10px 16px;border-radius:12px; padding:10px 16px;border-radius:12px;
background:rgba(255,255,255,0.04);border:1.5px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04);border:1.5px solid rgba(255,255,255,0.08);
color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer; color:var(--text-secondary);font-size:13px;font-weight:600;cursor:pointer;
transition:all 0.2s cubic-bezier(0.34,1.56,0.64,1); transition:all 0.2s cubic-bezier(0.34,1.56,0.64,1);
} }
.lr-chip:active { transform:scale(0.95); } .lr-chip:active { transform:scale(0.95); }
.lr-chip-active { background:rgba(228,172,56,0.1);border-color:rgba(228,172,56,0.4);color:#E4AC38; } .lr-chip-active { background:rgba(228,172,56,0.1);border-color:rgba(228,172,56,0.4);color:var(--gold); }
.lr-chip-num { font-size:18px;font-weight:800; } .lr-chip-num { font-size:18px;font-weight:800; }
.lr-chip-label { font-size:12px; } .lr-chip-label { font-size:12px; }
...@@ -353,7 +353,7 @@ function getStyles() { ...@@ -353,7 +353,7 @@ function getStyles() {
.lr-board-mini { .lr-board-mini {
position:relative;width:140px;height:140px; position:relative;width:140px;height:140px;
background:linear-gradient(135deg,#1a2844,#0f1a30); background:linear-gradient(135deg,#1a2844,#0f1a30);
border-radius:12px;border:1px solid rgba(255,255,255,0.06); border-radius:12px;border:1px solid var(--border);
} }
.lr-board-center { position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:24px; } .lr-board-center { position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:24px; }
.lr-seat { .lr-seat {
...@@ -373,7 +373,7 @@ function getStyles() { ...@@ -373,7 +373,7 @@ function getStyles() {
} }
.lr-seat-active .lr-seat-dot { background:color-mix(in srgb, var(--seat-color) 20%, transparent); } .lr-seat-active .lr-seat-dot { background:color-mix(in srgb, var(--seat-color) 20%, transparent); }
.lr-seat-empty .lr-seat-dot { opacity:0.2;border-style:dashed; } .lr-seat-empty .lr-seat-dot { opacity:0.2;border-style:dashed; }
.lr-seat-label { font-size:9px;color:#64748b; } .lr-seat-label { font-size:9px;color:var(--text-muted); }
.lr-seat-active .lr-seat-label { color:var(--seat-color); } .lr-seat-active .lr-seat-label { color:var(--seat-color); }
.lr-pulse-ring { .lr-pulse-ring {
......
...@@ -3,7 +3,7 @@ import * as audio from '../../../core/audio.js'; ...@@ -3,7 +3,7 @@ import * as audio from '../../../core/audio.js';
import * as net from '../../../core/net.js'; import * as net from '../../../core/net.js';
import { t } from '../../../core/i18n.js'; import { t } from '../../../core/i18n.js';
const DIFFICULTY_COLORS = ['#34D399', '#34D399', '#FBBF24', '#FBBF24', '#F97316', '#F97316', '#EF4444']; const DIFFICULTY_COLORS = ['var(--success)', 'var(--success)', 'var(--amber)', 'var(--amber)', 'var(--orange)', 'var(--orange)', 'var(--error)'];
export async function mountBotSelect(el, params) { export async function mountBotSelect(el, params) {
el.innerHTML = ` el.innerHTML = `
......
This diff is collapsed.
...@@ -42,7 +42,7 @@ export function mountLobby(el, params = {}) { ...@@ -42,7 +42,7 @@ export function mountLobby(el, params = {}) {
<!-- Match Info --> <!-- Match Info -->
<div class="lobby-match-info"> <div class="lobby-match-info">
<div class="lobby-game-badge" style="background:${gameKey === 'chess' ? '#2563EB' : gameKey === 'ludo' ? '#8B5CF6' : '#10B981'};"> <div class="lobby-game-badge" style="background:${gameKey === 'chess' ? 'var(--chess-primary)' : gameKey === 'ludo' ? 'var(--purple)' : 'var(--emerald)'};">
<span style="font-size:20px;">${gameIcon}</span> <span style="font-size:20px;">${gameIcon}</span>
<span style="font-size:13px;font-weight:600;">${gameLabel}</span> <span style="font-size:13px;font-weight:600;">${gameLabel}</span>
</div> </div>
...@@ -58,7 +58,7 @@ export function mountLobby(el, params = {}) { ...@@ -58,7 +58,7 @@ export function mountLobby(el, params = {}) {
</div> </div>
<div class="lobby-player-name">${myName}</div> <div class="lobby-player-name">${myName}</div>
<div class="lobby-player-status ready">${emoji('check', '✓', 11)} ${t('lobby.ready')}</div> <div class="lobby-player-status ready">${emoji('check', '✓', 11)} ${t('lobby.ready')}</div>
${color ? `<div class="lobby-color" style="background:${color === 'w' ? '#fff' : '#1a1a1a'};border:2px solid ${color === 'w' ? '#e2e8f0' : '#475569'};width:20px;height:20px;border-radius:50%;margin-top:6px;"></div>` : ''} ${color ? `<div class="lobby-color" style="background:${color === 'w' ? '#fff' : 'var(--bg-dark)'};border:2px solid ${color === 'w' ? 'var(--text-light)' : 'var(--text-dim)'};width:20px;height:20px;border-radius:50%;margin-top:6px;"></div>` : ''}
</div> </div>
<!-- VS divider --> <!-- VS divider -->
...@@ -73,42 +73,42 @@ export function mountLobby(el, params = {}) { ...@@ -73,42 +73,42 @@ export function mountLobby(el, params = {}) {
</div> </div>
<div class="lobby-player-name" id="lobby-opponent-name">${isHost ? (friendName || t('lobby.waiting')) : friendName}</div> <div class="lobby-player-name" id="lobby-opponent-name">${isHost ? (friendName || t('lobby.waiting')) : friendName}</div>
<div class="lobby-player-status" id="lobby-opponent-status">${isHost ? t('lobby.waiting_accept') : `${emoji('check', '✓', 11)} ${t('lobby.ready')}`}</div> <div class="lobby-player-status" id="lobby-opponent-status">${isHost ? t('lobby.waiting_accept') : `${emoji('check', '✓', 11)} ${t('lobby.ready')}`}</div>
${color ? `<div class="lobby-color" style="background:${color === 'w' ? '#1a1a1a' : '#fff'};border:2px solid ${color === 'w' ? '#475569' : '#e2e8f0'};width:20px;height:20px;border-radius:50%;margin-top:6px;"></div>` : ''} ${color ? `<div class="lobby-color" style="background:${color === 'w' ? 'var(--bg-dark)' : '#fff'};border:2px solid ${color === 'w' ? 'var(--text-dim)' : 'var(--text-light)'};width:20px;height:20px;border-radius:50%;margin-top:6px;"></div>` : ''}
</div> </div>
</div> </div>
<!-- Status --> <!-- Status -->
<div class="lobby-status" id="lobby-status"> <div class="lobby-status" id="lobby-status">
${isHost ? `<div class="lobby-status-text">${emoji('hourglass', '⏳', 14)} ${t('lobby.waiting_opponent')}</div><div class="lobby-status-sub">${t('lobby.auto_start')}</div>` : `<div class="lobby-status-text" style="color:#34D399;">${emoji('check', '✓', 14)} ${t('lobby.ready_start')}</div>`} ${isHost ? `<div class="lobby-status-text">${emoji('hourglass', '⏳', 14)} ${t('lobby.waiting_opponent')}</div><div class="lobby-status-sub">${t('lobby.auto_start')}</div>` : `<div class="lobby-status-text" style="color:var(--success);">${emoji('check', '✓', 14)} ${t('lobby.ready_start')}</div>`}
</div> </div>
<!-- Actions --> <!-- Actions -->
<div class="lobby-actions"> <div class="lobby-actions">
${!isHost ? `<button class="btn btn-primary lobby-btn" id="lobby-start" style="background:#34D399;">${emoji('play', '▶', 14)} ${t('lobby.start')}</button>` : ''} ${!isHost ? `<button class="btn btn-primary lobby-btn" id="lobby-start" style="background:var(--success);">${emoji('play', '▶', 14)} ${t('lobby.start')}</button>` : ''}
<button class="btn btn-secondary lobby-btn" id="lobby-cancel">${emoji('exit', '✕', 12)} ${t('common.cancel')}</button> <button class="btn btn-secondary lobby-btn" id="lobby-cancel">${emoji('exit', '✕', 12)} ${t('common.cancel')}</button>
</div> </div>
</div> </div>
<style> <style>
.lobby-layout { display:flex;flex-direction:column;align-items:center;height:100%;background:#0a0a14;padding:0; } .lobby-layout { display:flex;flex-direction:column;align-items:center;height:100%;background:var(--bg-deep);padding:0; }
.lobby-header { display:flex;align-items:center;gap:12px;padding:12px 16px;width:100%;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06); } .lobby-header { display:flex;align-items:center;gap:12px;padding:12px 16px;width:100%;background:var(--bg-panel);border-bottom:1px solid var(--border); }
.lobby-back-btn { background:none;border:none;color:#94a3b8;font-size:20px;cursor:pointer;padding:4px 8px; } .lobby-back-btn { background:none;border:none;color:var(--text-secondary);font-size:20px;cursor:pointer;padding:4px 8px; }
.lobby-title { font-size:16px;font-weight:700;color:#f8fafc; } .lobby-title { font-size:16px;font-weight:700;color:var(--text-primary); }
.lobby-match-info { display:flex;gap:10px;align-items:center;justify-content:center;padding:16px;width:100%; } .lobby-match-info { display:flex;gap:10px;align-items:center;justify-content:center;padding:16px;width:100%; }
.lobby-game-badge { display:flex;align-items:center;gap:6px;padding:8px 16px;border-radius:12px;color:#fff; } .lobby-game-badge { display:flex;align-items:center;gap:6px;padding:8px 16px;border-radius:12px;color:#fff; }
.lobby-time-badge { display:flex;align-items:center;gap:4px;padding:8px 14px;border-radius:10px;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);color:#e2e8f0;font-size:13px;font-weight:600; } .lobby-time-badge { display:flex;align-items:center;gap:4px;padding:8px 14px;border-radius:10px;background:var(--bg-card);border:1px solid var(--border);color:var(--text-light);font-size:13px;font-weight:600; }
.lobby-players { display:flex;align-items:center;justify-content:center;gap:16px;padding:20px 16px;width:100%; } .lobby-players { display:flex;align-items:center;justify-content:center;gap:16px;padding:20px 16px;width:100%; }
.lobby-player-card { display:flex;flex-direction:column;align-items:center;gap:8px;flex:1;max-width:140px; } .lobby-player-card { display:flex;flex-direction:column;align-items:center;gap:8px;flex:1;max-width:140px; }
.lobby-avatar { width:72px;height:72px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;border:3px solid rgba(255,255,255,0.1); } .lobby-avatar { width:72px;height:72px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;overflow:hidden;border:3px solid rgba(255,255,255,0.1); }
.lobby-avatar.host { border-color:#E4AC38; } .lobby-avatar.host { border-color:var(--gold); }
.lobby-avatar.opponent { border-color:#3B82F6; } .lobby-avatar.opponent { border-color:var(--blue); }
.lobby-player-name { font-size:14px;font-weight:600;color:#f8fafc;text-align:center;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; } .lobby-player-name { font-size:14px;font-weight:600;color:var(--text-primary);text-align:center;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.lobby-player-status { font-size:11px;color:#64748b;text-align:center; } .lobby-player-status { font-size:11px;color:var(--text-muted);text-align:center; }
.lobby-player-status.ready { color:#34D399; } .lobby-player-status.ready { color:var(--success); }
.lobby-vs { display:flex;align-items:center; } .lobby-vs { display:flex;align-items:center; }
.lobby-vs-circle { width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#E4AC38,#F59E0B);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:#1a1a1a; } .lobby-vs-circle { width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,var(--gold),var(--warning));display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:var(--bg-dark); }
.lobby-status { text-align:center;padding:16px 24px;width:100%; } .lobby-status { text-align:center;padding:16px 24px;width:100%; }
.lobby-status-text { font-size:14px;font-weight:600;color:#E4AC38;margin-bottom:4px; } .lobby-status-text { font-size:14px;font-weight:600;color:var(--gold);margin-bottom:4px; }
.lobby-status-sub { font-size:12px;color:#64748b; } .lobby-status-sub { font-size:12px;color:var(--text-muted); }
.lobby-actions { display:flex;flex-direction:column;gap:10px;padding:16px 24px;width:100%;max-width:320px;margin-top:auto;padding-bottom:max(16px, env(safe-area-inset-bottom, 0px)); } .lobby-actions { display:flex;flex-direction:column;gap:10px;padding:16px 24px;width:100%;max-width:320px;margin-top:auto;padding-bottom:max(16px, env(safe-area-inset-bottom, 0px)); }
.lobby-btn { width:100%;min-height:48px;font-size:14px;font-weight:600;border-radius:12px; } .lobby-btn { width:100%;min-height:48px;font-size:14px;font-weight:600;border-radius:12px; }
.lobby-waiting-pulse { animation:lobbyPulse 2s infinite; } .lobby-waiting-pulse { animation:lobbyPulse 2s infinite; }
...@@ -132,7 +132,7 @@ export function mountLobby(el, params = {}) { ...@@ -132,7 +132,7 @@ export function mountLobby(el, params = {}) {
// Guest: wait briefly so host can detect acceptance, then start // Guest: wait briefly so host can detect acceptance, then start
const statusEl = el.querySelector('#lobby-status'); const statusEl = el.querySelector('#lobby-status');
if (statusEl) { if (statusEl) {
statusEl.innerHTML = `<div class="lobby-status-text" style="color:#34D399;">${emoji('check', '✓', 14)} ${t('lobby.accepted_preparing')}</div>`; statusEl.innerHTML = `<div class="lobby-status-text" style="color:var(--success);">${emoji('check', '✓', 14)} ${t('lobby.accepted_preparing')}</div>`;
} }
setTimeout(() => startGame(el, params), 2500); setTimeout(() => startGame(el, params), 2500);
} }
...@@ -156,10 +156,10 @@ async function pollMatchStatus(el, params) { ...@@ -156,10 +156,10 @@ async function pollMatchStatus(el, params) {
const statusEl = el.querySelector('#lobby-status'); const statusEl = el.querySelector('#lobby-status');
const oppStatus = el.querySelector('#lobby-opponent-status'); const oppStatus = el.querySelector('#lobby-opponent-status');
if (statusEl) { if (statusEl) {
statusEl.innerHTML = `<div class="lobby-status-text" style="color:#34D399;">${emoji('check', '✓', 14)} ${t('lobby.opponent_accepted')}</div>`; statusEl.innerHTML = `<div class="lobby-status-text" style="color:var(--success);">${emoji('check', '✓', 14)} ${t('lobby.opponent_accepted')}</div>`;
} }
if (oppStatus) { if (oppStatus) {
oppStatus.innerHTML = `<span style="color:#34D399;">${emoji('check', '✓', 11)} ${t('lobby.ready')}</span>`; oppStatus.innerHTML = `<span style="color:var(--success);">${emoji('check', '✓', 11)} ${t('lobby.ready')}</span>`;
oppStatus.classList.add('ready'); oppStatus.classList.add('ready');
} }
......
...@@ -40,7 +40,7 @@ export function mountTable(el) { ...@@ -40,7 +40,7 @@ export function mountTable(el) {
<!-- Quick actions row --> <!-- Quick actions row -->
<div id="daily-widget" style="display:flex;gap:var(--quick-row-gap);width:100%;max-width:var(--home-max-width);margin-bottom:var(--quick-row-margin);"> <div id="daily-widget" style="display:flex;gap:var(--quick-row-gap);width:100%;max-width:var(--home-max-width);margin-bottom:var(--quick-row-margin);">
<button class="quick-btn" id="btn-challenge-friend"> <button class="quick-btn" id="btn-challenge-friend">
<span class="qb-icon" style="background:linear-gradient(135deg,#7c3aed,#a855f7);">${emoji('challenge_swords', '⚔️', 20)}</span> <span class="qb-icon" style="background:linear-gradient(135deg,var(--violet),var(--purple-light));">${emoji('challenge_swords', '⚔️', 20)}</span>
<span class="qb-label">${t('challenge.title')}</span> <span class="qb-label">${t('challenge.title')}</span>
</button> </button>
<button class="quick-btn" id="btn-achievements"> <button class="quick-btn" id="btn-achievements">
...@@ -48,7 +48,7 @@ export function mountTable(el) { ...@@ -48,7 +48,7 @@ export function mountTable(el) {
<span class="qb-label">${t('play.achievements')}</span> <span class="qb-label">${t('play.achievements')}</span>
</button> </button>
<button class="quick-btn breathe-glow" id="btn-daily-reward"> <button class="quick-btn breathe-glow" id="btn-daily-reward">
<span class="qb-icon" style="background:linear-gradient(135deg,#92400e,#e4ac38);">${emoji('gift', '🎁', 20)}</span> <span class="qb-icon" style="background:linear-gradient(135deg,#92400e,var(--gold));">${emoji('gift', '🎁', 20)}</span>
<span class="qb-label">${t('play.gift')}</span> <span class="qb-label">${t('play.gift')}</span>
</button> </button>
</div> </div>
...@@ -289,7 +289,7 @@ function showGameMenu(menu, game) { ...@@ -289,7 +289,7 @@ function showGameMenu(menu, game) {
</button> </button>
<button class="gm-btn" id="btn-multi"> <button class="gm-btn" id="btn-multi">
<div class="gm-btn-icon" style="background:linear-gradient(135deg,#dc2626,#f97316);">${emoji('swords', '⚔️', 26)}</div> <div class="gm-btn-icon" style="background:linear-gradient(135deg,#dc2626,var(--orange));">${emoji('swords', '⚔️', 26)}</div>
<div class="gm-btn-body"> <div class="gm-btn-body">
<div class="gm-btn-title">${t('game.online')}</div> <div class="gm-btn-title">${t('game.online')}</div>
<div class="gm-btn-sub">${t('play.online_desc')}</div> <div class="gm-btn-sub">${t('play.online_desc')}</div>
......
...@@ -6,7 +6,7 @@ import { t } from '../../../core/i18n.js'; ...@@ -6,7 +6,7 @@ import { t } from '../../../core/i18n.js';
const categories = [ const categories = [
{ {
name: 'Bullet', nameKey: 'time.bullet', iconSlot: 'lightning', iconFallback: '⚡', color: '#FBBF24', name: 'Bullet', nameKey: 'time.bullet', iconSlot: 'lightning', iconFallback: '⚡', color: 'var(--amber)',
controls: [ controls: [
{ key: 'bullet_1_0', labelKey: 'time.1min', sub: '1+0' }, { key: 'bullet_1_0', labelKey: 'time.1min', sub: '1+0' },
{ key: 'bullet_1_1', label: '1 | 1', sub: '1+1' }, { key: 'bullet_1_1', label: '1 | 1', sub: '1+1' },
...@@ -14,7 +14,7 @@ const categories = [ ...@@ -14,7 +14,7 @@ const categories = [
] ]
}, },
{ {
name: 'Blitz', nameKey: 'time.blitz', iconSlot: 'fire', iconFallback: '🔥', color: '#F97316', name: 'Blitz', nameKey: 'time.blitz', iconSlot: 'fire', iconFallback: '🔥', color: 'var(--orange)',
controls: [ controls: [
{ key: 'blitz_3_0', labelKey: 'time.3min', sub: '3+0' }, { key: 'blitz_3_0', labelKey: 'time.3min', sub: '3+0' },
{ key: 'blitz_3_2', label: '3 | 2', sub: '3+2' }, { key: 'blitz_3_2', label: '3 | 2', sub: '3+2' },
...@@ -24,7 +24,7 @@ const categories = [ ...@@ -24,7 +24,7 @@ const categories = [
] ]
}, },
{ {
name: 'Rapid', nameKey: 'time.rapid', iconSlot: 'runner', iconFallback: '🏃', color: '#22C55E', name: 'Rapid', nameKey: 'time.rapid', iconSlot: 'runner', iconFallback: '🏃', color: 'var(--green)',
controls: [ controls: [
{ key: 'rapid_10_0', labelKey: 'time.10min', sub: '10+0' }, { key: 'rapid_10_0', labelKey: 'time.10min', sub: '10+0' },
{ key: 'rapid_10_5', label: '10 | 5', sub: '10+5' }, { key: 'rapid_10_5', label: '10 | 5', sub: '10+5' },
...@@ -34,7 +34,7 @@ const categories = [ ...@@ -34,7 +34,7 @@ const categories = [
] ]
}, },
{ {
name: 'Classical', nameKey: 'time.classical', iconSlot: 'crown', iconFallback: '♔', color: '#8B5CF6', name: 'Classical', nameKey: 'time.classical', iconSlot: 'crown', iconFallback: '♔', color: 'var(--purple)',
controls: [ controls: [
{ key: 'classical_45_45', label: '45 | 45', sub: '45+45' }, { key: 'classical_45_45', label: '45 | 45', sub: '45+45' },
{ key: 'classical_60_0', labelKey: 'time.60min', sub: '60+0' }, { key: 'classical_60_0', labelKey: 'time.60min', sub: '60+0' },
...@@ -48,20 +48,20 @@ export function mountTimeSelect(el, params) { ...@@ -48,20 +48,20 @@ export function mountTimeSelect(el, params) {
<div class="tc-page" style="padding:16px;display:flex;flex-direction:column;gap:16px;height:100%;overflow-y:auto;"> <div class="tc-page" style="padding:16px;display:flex;flex-direction:column;gap:16px;height:100%;overflow-y:auto;">
<div style="display:flex;align-items:center;gap:12px;"> <div style="display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;border-radius:50%;">←</button> <button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;border-radius:50%;">←</button>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;">${t('play.select_time')}</h2> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);">${t('play.select_time')}</h2>
</div> </div>
${categories.map(cat => ` ${categories.map(cat => `
<div class="tc-category"> <div class="tc-category">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<span style="font-size:18px;">${emoji(cat.iconSlot, cat.iconFallback, 18)}</span> <span style="font-size:18px;">${emoji(cat.iconSlot, cat.iconFallback, 18)}</span>
<span style="font-size:14px;font-weight:700;color:${cat.color};">${t(cat.nameKey)}</span> <span style="font-size:14px;font-weight:700;color:${cat.color};">${t(cat.nameKey)}</span>
<span style="font-size:11px;color:#64748b;font-family:var(--font-lat);">${cat.name}</span> <span style="font-size:11px;color:var(--text-muted);font-family:var(--font-lat);">${cat.name}</span>
</div> </div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;"> <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;">
${cat.controls.map(tc => ` ${cat.controls.map(tc => `
<button class="time-btn" data-key="${tc.key}" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;height:58px;background:#1e1e3a;border:1px solid rgba(255,255,255,0.08);border-radius:10px;cursor:pointer;transition:transform 0.1s,background 0.15s,border-color 0.15s;"> <button class="time-btn" data-key="${tc.key}" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;height:58px;background:var(--bg-elevated);border:1px solid var(--border);border-radius:10px;cursor:pointer;transition:transform 0.1s,background 0.15s,border-color 0.15s;">
<span style="font-size:14px;font-weight:700;color:#f8fafc;font-family:var(--font-lat);">${tc.sub}</span> <span style="font-size:14px;font-weight:700;color:var(--text-primary);font-family:var(--font-lat);">${tc.sub}</span>
<span style="font-size:10px;color:#94a3b8;">${tc.labelKey ? t(tc.labelKey) : tc.label}</span> <span style="font-size:10px;color:var(--text-secondary);">${tc.labelKey ? t(tc.labelKey) : tc.label}</span>
</button> </button>
`).join('')} `).join('')}
</div> </div>
...@@ -69,8 +69,8 @@ export function mountTimeSelect(el, params) { ...@@ -69,8 +69,8 @@ export function mountTimeSelect(el, params) {
`).join('')} `).join('')}
</div> </div>
<style> <style>
.time-btn:active { transform:scale(0.93);background:#2a2a5a; } .time-btn:active { transform:scale(0.93);background:var(--bg-hover); }
.time-btn:hover { border-color:rgba(255,255,255,0.2); } .time-btn:hover { border-color:var(--border-hover); }
</style> </style>
`; `;
......
...@@ -62,22 +62,22 @@ export async function mountEdit(el) { ...@@ -62,22 +62,22 @@ export async function mountEdit(el) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.display_name_en')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.display_name_en')}</label>
<input id="field-display_name" type="text" maxlength="30" dir="auto" value="${escAttr(player.display_name || '')}" <input id="field-display_name" type="text" maxlength="30" dir="auto" value="${escAttr(player.display_name || '')}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.display_name_ar')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.display_name_ar')}</label>
<input id="field-display_name_ar" type="text" maxlength="30" dir="rtl" value="${escAttr(player.display_name_ar || '')}" <input id="field-display_name_ar" type="text" maxlength="30" dir="rtl" value="${escAttr(player.display_name_ar || '')}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.bio_en')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.bio_en')}</label>
<textarea id="field-bio" maxlength="200" dir="auto" rows="3" <textarea id="field-bio" maxlength="200" dir="auto" rows="3"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;">${escHtml(player.bio || '')}</textarea> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;">${escHtml(player.bio || '')}</textarea>
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.bio_ar')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.bio_ar')}</label>
<textarea id="field-bio_ar" maxlength="200" dir="rtl" rows="3" <textarea id="field-bio_ar" maxlength="200" dir="rtl" rows="3"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;">${escHtml(player.bio_ar || '')}</textarea> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;">${escHtml(player.bio_ar || '')}</textarea>
</div> </div>
</div> </div>
</div> </div>
...@@ -90,7 +90,7 @@ export async function mountEdit(el) { ...@@ -90,7 +90,7 @@ export async function mountEdit(el) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.country')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.country')}</label>
<select id="field-country_code" <select id="field-country_code"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
<option value="">${t('profile.select_country')}</option> <option value="">${t('profile.select_country')}</option>
${COUNTRIES.map(c => `<option value="${c.code}" ${player.country_code === c.code ? 'selected' : ''}>${c.name}</option>`).join('')} ${COUNTRIES.map(c => `<option value="${c.code}" ${player.country_code === c.code ? 'selected' : ''}>${c.name}</option>`).join('')}
</select> </select>
...@@ -98,7 +98,7 @@ export async function mountEdit(el) { ...@@ -98,7 +98,7 @@ export async function mountEdit(el) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.city')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.city')}</label>
<input id="field-city" type="text" dir="auto" value="${escAttr(player.city || '')}" <input id="field-city" type="text" dir="auto" value="${escAttr(player.city || '')}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.preferred_language')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.preferred_language')}</label>
...@@ -118,27 +118,27 @@ export async function mountEdit(el) { ...@@ -118,27 +118,27 @@ export async function mountEdit(el) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">FIDE ID</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">FIDE ID</label>
<input id="field-fide_id" type="text" inputmode="numeric" dir="ltr" value="${escAttr(player.fide_id || '')}" <input id="field-fide_id" type="text" inputmode="numeric" dir="ltr" value="${escAttr(player.fide_id || '')}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_classical')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_classical')}</label>
<input id="field-fide_rating_standard" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_standard || ''}" <input id="field-fide_rating_standard" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_standard || ''}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_rapid')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_rapid')}</label>
<input id="field-fide_rating_rapid" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_rapid || ''}" <input id="field-fide_rating_rapid" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_rapid || ''}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_blitz')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_blitz')}</label>
<input id="field-fide_rating_blitz" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_blitz || ''}" <input id="field-fide_rating_blitz" type="number" inputmode="numeric" dir="ltr" min="0" max="4000" value="${player.fide_rating_blitz || ''}"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_title')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('profile.fide_title')}</label>
<select id="field-fide_title" <select id="field-fide_title"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
${FIDE_TITLES.map(ft => `<option value="${ft.value}" ${player.fide_title === ft.value ? 'selected' : ''}>${ft.label}</option>`).join('')} ${FIDE_TITLES.map(ft => `<option value="${ft.value}" ${player.fide_title === ft.value ? 'selected' : ''}>${ft.label}</option>`).join('')}
</select> </select>
</div> </div>
......
...@@ -73,7 +73,7 @@ async function loadOrgs(el) { ...@@ -73,7 +73,7 @@ async function loadOrgs(el) {
if (isMember) { if (isMember) {
badge = `<span style="background:var(--success);color:#fff;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${emoji('check', '✓', 12)} ${t('profile.member')}</span>`; badge = `<span style="background:var(--success);color:#fff;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${emoji('check', '✓', 12)} ${t('profile.member')}</span>`;
} else if (app && app.status === 'pending') { } else if (app && app.status === 'pending') {
badge = `<span style="background:#f59e0b;color:#000;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${t('profile.pending_review')}</span>`; badge = `<span style="background:var(--warning);color:#000;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${t('profile.pending_review')}</span>`;
} else if (app && app.status === 'rejected') { } else if (app && app.status === 'rejected') {
badge = `<span style="background:var(--error);color:#fff;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${t('profile.rejected')}</span>`; badge = `<span style="background:var(--error);color:#fff;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600;">${t('profile.rejected')}</span>`;
if (app.rejection_reason) { if (app.rejection_reason) {
...@@ -129,7 +129,7 @@ function showApplyForm(el, org, contentParent) { ...@@ -129,7 +129,7 @@ function showApplyForm(el, org, contentParent) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.doc_type')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.doc_type')}</label>
<select id="apply-doc-type" <select id="apply-doc-type"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;"> style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;">
${DOC_TYPES.map(d => `<option value="${d.value}">${d.label}</option>`).join('')} ${DOC_TYPES.map(d => `<option value="${d.value}">${d.label}</option>`).join('')}
</select> </select>
</div> </div>
...@@ -137,13 +137,13 @@ function showApplyForm(el, org, contentParent) { ...@@ -137,13 +137,13 @@ function showApplyForm(el, org, contentParent) {
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.notes')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.notes')}</label>
<textarea id="apply-notes" maxlength="500" dir="auto" rows="3" <textarea id="apply-notes" maxlength="500" dir="auto" rows="3"
style="background:#1e1e3a;border:1px solid rgba(255,255,255,0.1);color:#f8fafc;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;" style="background:var(--bg-elevated);border:1px solid rgba(255,255,255,0.1);color:var(--text-primary);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;width:100%;box-sizing:border-box;resize:vertical;"
placeholder="${t('org.notes_placeholder')}"></textarea> placeholder="${t('org.notes_placeholder')}"></textarea>
</div> </div>
<div> <div>
<label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.proof_image')}</label> <label style="font-size:13px;color:var(--text-secondary);margin-bottom:4px;display:block;">${t('org.proof_image')}</label>
<div id="upload-area" style="background:#1e1e3a;border:2px dashed rgba(255,255,255,0.15);border-radius:8px;padding:var(--s-4);text-align:center;cursor:pointer;"> <div id="upload-area" style="background:var(--bg-elevated);border:2px dashed var(--border-hover);border-radius:8px;padding:var(--s-4);text-align:center;cursor:pointer;">
<div id="upload-placeholder"> <div id="upload-placeholder">
<div>${emoji('camera', '📷', 28)}</div> <div>${emoji('camera', '📷', 28)}</div>
<div style="font-size:13px;color:var(--text-secondary);margin-top:var(--s-2);">${t('org.tap_to_select')}</div> <div style="font-size:13px;color:var(--text-secondary);margin-top:var(--s-2);">${t('org.tap_to_select')}</div>
......
...@@ -340,9 +340,9 @@ async function checkActiveMatch(el, playerId) { ...@@ -340,9 +340,9 @@ async function checkActiveMatch(el, playerId) {
const gameLabel = data.game_key === 'ludo' ? t('game.ludo') : data.game_key === 'domino' ? t('game.domino') : t('game.chess'); const gameLabel = data.game_key === 'ludo' ? t('game.ludo') : data.game_key === 'domino' ? t('game.domino') : t('game.chess');
section.innerHTML = ` section.innerHTML = `
<div class="card" style="background:linear-gradient(135deg,#1a2a1a,#0f1f0f);border:1px solid rgba(52,211,153,0.3);padding:var(--s-3);display:flex;align-items:center;gap:var(--s-3);"> <div class="card" style="background:linear-gradient(135deg,#1a2a1a,#0f1f0f);border:1px solid rgba(52,211,153,0.3);padding:var(--s-3);display:flex;align-items:center;gap:var(--s-3);">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#EF4444;animation:specPulse 1.5s infinite;"></span> <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:var(--error);animation:specPulse 1.5s infinite;"></span>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#34D399;">${t('profile.playing_now', { game: gameLabel })}</div> <div style="font-size:13px;font-weight:600;color:var(--success);">${t('profile.playing_now', { game: gameLabel })}</div>
</div> </div>
<button class="btn btn-primary" id="btn-spectate" style="min-height:32px;padding:4px 14px;font-size:12px;">${emoji('eye', '👁', 13)} ${t('profile.watch')}</button> <button class="btn btn-primary" id="btn-spectate" style="min-height:32px;padding:4px 14px;font-size:12px;">${emoji('eye', '👁', 13)} ${t('profile.watch')}</button>
</div> </div>
...@@ -428,7 +428,7 @@ async function loadOrgMembership(el) { ...@@ -428,7 +428,7 @@ async function loadOrgMembership(el) {
? `<img src="${org.logo_url}" style="width:28px;height:28px;border-radius:6px;object-fit:contain;" alt="">` ? `<img src="${org.logo_url}" style="width:28px;height:28px;border-radius:6px;object-fit:contain;" alt="">`
: `<div style="width:28px;height:28px;border-radius:6px;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;">${emoji('building', '🏢', 14)}</div>`; : `<div style="width:28px;height:28px;border-radius:6px;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;">${emoji('building', '🏢', 14)}</div>`;
const statusBadge = a.status === 'pending' const statusBadge = a.status === 'pending'
? `<span style="background:#f59e0b;color:#000;font-size:10px;padding:2px 6px;border-radius:10px;">${t('profile.pending_review')}</span>` ? `<span style="background:var(--warning);color:#000;font-size:10px;padding:2px 6px;border-radius:10px;">${t('profile.pending_review')}</span>`
: `<span style="background:var(--error);color:#fff;font-size:10px;padding:2px 6px;border-radius:10px;">${t('profile.rejected')}</span>`; : `<span style="background:var(--error);color:#fff;font-size:10px;padding:2px 6px;border-radius:10px;">${t('profile.rejected')}</span>`;
return ` return `
<div style="display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2) 0;"> <div style="display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2) 0;">
......
...@@ -16,10 +16,10 @@ export function mountTournamentArena(el, params) { ...@@ -16,10 +16,10 @@ export function mountTournamentArena(el, params) {
if (countdownInterval) clearInterval(countdownInterval); if (countdownInterval) clearInterval(countdownInterval);
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);">
<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<span style="font-size:15px;font-weight:700;color:#f8fafc;">${emoji('lightning', '⚡', 15)} ${t('tournament.arena')}</span> <span style="font-size:15px;font-weight:700;color:var(--text-primary);">${emoji('lightning', '⚡', 15)} ${t('tournament.arena')}</span>
</div> </div>
<div id="arena-content" style="flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;align-items:center;gap:16px;"> <div id="arena-content" style="flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;align-items:center;gap:16px;">
</div> </div>
...@@ -39,12 +39,12 @@ async function startArena(el, tournamentId, tournamentName) { ...@@ -39,12 +39,12 @@ async function startArena(el, tournamentId, tournamentName) {
const content = el.querySelector('#arena-content'); const content = el.querySelector('#arena-content');
content.innerHTML = ` content.innerHTML = `
<div style="text-align:center;margin-top:24px;"> <div style="text-align:center;margin-top:24px;">
<div class="radar-pulse" style="width:80px;height:80px;margin:0 auto 16px;border-radius:50%;background:radial-gradient(circle,#E4AC38 0%,transparent 70%);animation:pulse 1.5s infinite;"></div> <div class="radar-pulse" style="width:80px;height:80px;margin:0 auto 16px;border-radius:50%;background:radial-gradient(circle,var(--gold) 0%,transparent 70%);animation:pulse 1.5s infinite;"></div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;">${t('play.searching')}</div> <div style="font-size:16px;font-weight:700;color:var(--text-primary);">${t('play.searching')}</div>
<div style="font-size:12px;color:#64748b;margin-top:4px;">${tournamentName || t('tournament.arena')}</div> <div style="font-size:12px;color:var(--text-muted);margin-top:4px;">${tournamentName || t('tournament.arena')}</div>
</div> </div>
<div id="arena-standings" style="width:100%;max-width:320px;margin-top:16px;"></div> <div id="arena-standings" style="width:100%;max-width:320px;margin-top:16px;"></div>
<button class="btn btn-secondary" id="leave-arena" style="margin-top:auto;margin-bottom:16px;color:#ef4444;border-color:#ef4444;">${t('tournament.leave_arena')}</button> <button class="btn btn-secondary" id="leave-arena" style="margin-top:auto;margin-bottom:16px;color:var(--error);border-color:var(--error);">${t('tournament.leave_arena')}</button>
<style> <style>
@keyframes pulse { 0%,100% { transform:scale(1);opacity:0.6; } 50% { transform:scale(1.3);opacity:0.2; } } @keyframes pulse { 0%,100% { transform:scale(1);opacity:0.6; } 50% { transform:scale(1.3);opacity:0.2; } }
</style> </style>
...@@ -130,21 +130,21 @@ async function loadArenaStandings(el, tournamentId) { ...@@ -130,21 +130,21 @@ async function loadArenaStandings(el, tournamentId) {
const userId = store.get('auth.userId'); const userId = store.get('auth.userId');
if (standings.length === 0) { if (standings.length === 0) {
container.innerHTML = `<div style="text-align:center;color:#64748b;font-size:12px;">${t('tournament.no_results')}</div>`; container.innerHTML = `<div style="text-align:center;color:var(--text-muted);font-size:12px;">${t('tournament.no_results')}</div>`;
return; return;
} }
container.innerHTML = ` container.innerHTML = `
<div style="font-size:13px;font-weight:700;color:#94a3b8;margin-bottom:8px;text-align:center;">${t('tournament.leaderboard')}</div> <div style="font-size:13px;font-weight:700;color:var(--text-secondary);margin-bottom:8px;text-align:center;">${t('tournament.leaderboard')}</div>
${standings.slice(0, 10).map((p, i) => { ${standings.slice(0, 10).map((p, i) => {
const isMe = p.player_id === userId; const isMe = p.player_id === userId;
const medals = ['🥇', '🥈', '🥉']; const medals = ['🥇', '🥈', '🥉'];
return ` return `
<div style="display:flex;align-items:center;padding:6px 8px;background:${isMe ? '#1a2e1a' : '#1a1a2e'};border-radius:6px;margin-bottom:4px;border:${isMe ? '1px solid #E4AC38' : '1px solid transparent'};"> <div style="display:flex;align-items:center;padding:6px 8px;background:${isMe ? '#1a2e1a' : 'var(--bg-card)'};border-radius:6px;margin-bottom:4px;border:${isMe ? '1px solid var(--gold)' : '1px solid transparent'};">
<span style="width:24px;font-size:${i < 3 ? '14px' : '11px'};text-align:center;">${i < 3 ? medals[i] : (i + 1)}</span> <span style="width:24px;font-size:${i < 3 ? '14px' : '11px'};text-align:center;">${i < 3 ? medals[i] : (i + 1)}</span>
<span style="flex:1;font-size:12px;color:#f8fafc;font-weight:${isMe ? '700' : '400'};">${p.name}${isMe ? ` (${t('common.you')})` : ''}</span> <span style="flex:1;font-size:12px;color:var(--text-primary);font-weight:${isMe ? '700' : '400'};">${p.name}${isMe ? ` (${t('common.you')})` : ''}</span>
<span style="font-size:12px;font-weight:700;color:#E4AC38;">${p.points}</span> <span style="font-size:12px;font-weight:700;color:var(--gold);">${p.points}</span>
<span style="font-size:10px;color:#64748b;margin-right:4px;width:50px;text-align:left;">${p.wins}W ${p.draws}D ${p.losses}L</span> <span style="font-size:10px;color:var(--text-muted);margin-right:4px;width:50px;text-align:left;">${p.wins}W ${p.draws}D ${p.losses}L</span>
</div> </div>
`; `;
}).join('')} }).join('')}
......
...@@ -10,13 +10,13 @@ export async function mountTournamentBracket(el, params) { ...@@ -10,13 +10,13 @@ export async function mountTournamentBracket(el, params) {
const userId = store.get('auth.userId'); const userId = store.get('auth.userId');
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);">
<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<span style="font-size:15px;font-weight:700;color:#f8fafc;">${t('tournament.bracket')}</span> <span style="font-size:15px;font-weight:700;color:var(--text-primary);">${t('tournament.bracket')}</span>
</div> </div>
<div id="bracket-container" style="flex:1;overflow:auto;padding:12px;"> <div id="bracket-container" style="flex:1;overflow:auto;padding:12px;">
<div style="text-align:center;color:#64748b;padding:32px;">${t('common.loading')}</div> <div style="text-align:center;color:var(--text-muted);padding:32px;">${t('common.loading')}</div>
</div> </div>
</div> </div>
`; `;
...@@ -28,7 +28,7 @@ export async function mountTournamentBracket(el, params) { ...@@ -28,7 +28,7 @@ export async function mountTournamentBracket(el, params) {
const container = el.querySelector('#bracket-container'); const container = el.querySelector('#bracket-container');
if (!data.bracket || !data.matches || data.matches.length === 0) { if (!data.bracket || !data.matches || data.matches.length === 0) {
container.innerHTML = `<div style="text-align:center;color:#64748b;padding:32px;">${t('tournament.no_bracket_yet')}</div>`; container.innerHTML = `<div style="text-align:center;color:var(--text-muted);padding:32px;">${t('tournament.no_bracket_yet')}</div>`;
return; return;
} }
...@@ -48,26 +48,26 @@ export async function mountTournamentBracket(el, params) { ...@@ -48,26 +48,26 @@ export async function mountTournamentBracket(el, params) {
const gap = Math.pow(2, totalRounds - r) * 8; const gap = Math.pow(2, totalRounds - r) * 8;
html += `<div style="display:flex;flex-direction:column;justify-content:space-around;min-width:150px;gap:${gap}px;padding:8px 4px;">`; html += `<div style="display:flex;flex-direction:column;justify-content:space-around;min-width:150px;gap:${gap}px;padding:8px 4px;">`;
html += `<div style="text-align:center;font-size:11px;font-weight:700;color:#64748b;margin-bottom:8px;">${roundLabels[r - 1] || t('tournament.round', { n: r })}</div>`; html += `<div style="text-align:center;font-size:11px;font-weight:700;color:var(--text-muted);margin-bottom:8px;">${roundLabels[r - 1] || t('tournament.round', { n: r })}</div>`;
matches.forEach(m => { matches.forEach(m => {
const isMyMatch = (m.player_a_id === userId || m.player_b_id === userId); const isMyMatch = (m.player_a_id === userId || m.player_b_id === userId);
const borderColor = isMyMatch ? '#E4AC38' : 'rgba(255,255,255,0.06)'; const borderColor = isMyMatch ? 'var(--gold)' : 'var(--border)';
const statusBg = m.status === 'completed' ? '#1a2e1a' : m.status === 'in_progress' ? '#2e2a1a' : '#1a1a2e'; const statusBg = m.status === 'completed' ? '#1a2e1a' : m.status === 'in_progress' ? '#2e2a1a' : 'var(--bg-card)';
html += ` html += `
<div class="bracket-match" data-match-id="${m.match_id || ''}" style="background:${statusBg};border:1px solid ${borderColor};border-radius:8px;padding:6px 8px;position:relative;"> <div class="bracket-match" data-match-id="${m.match_id || ''}" style="background:${statusBg};border:1px solid ${borderColor};border-radius:8px;padding:6px 8px;position:relative;">
${isMyMatch ? `<div style="position:absolute;top:-6px;right:4px;font-size:9px;background:#E4AC38;color:#000;padding:1px 4px;border-radius:4px;font-weight:700;">${t('common.you')}</div>` : ''} ${isMyMatch ? `<div style="position:absolute;top:-6px;right:4px;font-size:9px;background:var(--gold);color:#000;padding:1px 4px;border-radius:4px;font-weight:700;">${t('common.you')}</div>` : ''}
<div style="display:flex;justify-content:space-between;align-items:center;padding:3px 0;${m.winner_id === m.player_a_id ? 'font-weight:700;' : ''}"> <div style="display:flex;justify-content:space-between;align-items:center;padding:3px 0;${m.winner_id === m.player_a_id ? 'font-weight:700;' : ''}">
<span style="font-size:11px;color:${m.winner_id === m.player_a_id ? '#34D399' : '#f8fafc'};max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${m.player_a_name || (m.player_a_id ? '...' : 'BYE')}</span> <span style="font-size:11px;color:${m.winner_id === m.player_a_id ? 'var(--success)' : 'var(--text-primary)'};max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${m.player_a_name || (m.player_a_id ? '...' : 'BYE')}</span>
<span style="font-size:10px;color:#64748b;">${m.result ? m.result.split('-')[0] : ''}</span> <span style="font-size:10px;color:var(--text-muted);">${m.result ? m.result.split('-')[0] : ''}</span>
</div> </div>
<div style="height:1px;background:rgba(255,255,255,0.04);margin:2px 0;"></div> <div style="height:1px;background:var(--border);margin:2px 0;"></div>
<div style="display:flex;justify-content:space-between;align-items:center;padding:3px 0;${m.winner_id === m.player_b_id ? 'font-weight:700;' : ''}"> <div style="display:flex;justify-content:space-between;align-items:center;padding:3px 0;${m.winner_id === m.player_b_id ? 'font-weight:700;' : ''}">
<span style="font-size:11px;color:${m.winner_id === m.player_b_id ? '#34D399' : '#f8fafc'};max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${m.player_b_name || (m.player_b_id ? '...' : 'TBD')}</span> <span style="font-size:11px;color:${m.winner_id === m.player_b_id ? 'var(--success)' : 'var(--text-primary)'};max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${m.player_b_name || (m.player_b_id ? '...' : 'TBD')}</span>
<span style="font-size:10px;color:#64748b;">${m.result ? m.result.split('-')[1] : ''}</span> <span style="font-size:10px;color:var(--text-muted);">${m.result ? m.result.split('-')[1] : ''}</span>
</div> </div>
${m.status === 'pending' && isMyMatch && m.player_a_id && m.player_b_id ? `<button class="bracket-play-btn" data-mid="${m.match_id}" style="width:100%;margin-top:4px;padding:4px;background:#E4AC38;border:none;border-radius:4px;color:#000;font-weight:700;font-size:10px;cursor:pointer;">${t('common.play')}</button>` : ''} ${m.status === 'pending' && isMyMatch && m.player_a_id && m.player_b_id ? `<button class="bracket-play-btn" data-mid="${m.match_id}" style="width:100%;margin-top:4px;padding:4px;background:var(--gold);border:none;border-radius:4px;color:#000;font-weight:700;font-size:10px;cursor:pointer;">${t('common.play')}</button>` : ''}
</div> </div>
`; `;
}); });
...@@ -95,7 +95,7 @@ export async function mountTournamentBracket(el, params) { ...@@ -95,7 +95,7 @@ export async function mountTournamentBracket(el, params) {
}); });
} catch (e) { } catch (e) {
el.querySelector('#bracket-container').innerHTML = `<div style="text-align:center;color:#ef4444;padding:32px;">${t('tournament.bracket_load_failed')}</div>`; el.querySelector('#bracket-container').innerHTML = `<div style="text-align:center;color:var(--error);padding:32px;">${t('tournament.bracket_load_failed')}</div>`;
} }
} }
......
...@@ -13,10 +13,10 @@ export function mountTournamentLive(el, params) { ...@@ -13,10 +13,10 @@ export function mountTournamentLive(el, params) {
if (refreshInterval) clearInterval(refreshInterval); if (refreshInterval) clearInterval(refreshInterval);
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);">
<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<span style="font-size:15px;font-weight:700;color:#f8fafc;flex:1;">${emoji('live', '🔴', 12)} LIVE — ${tournamentName || t('tournament.title')}</span> <span style="font-size:15px;font-weight:700;color:var(--text-primary);flex:1;">${emoji('live', '🔴', 12)} LIVE — ${tournamentName || t('tournament.title')}</span>
</div> </div>
<div id="live-content" style="flex:1;overflow-y:auto;padding:14px;"></div> <div id="live-content" style="flex:1;overflow-y:auto;padding:14px;"></div>
</div> </div>
...@@ -51,8 +51,8 @@ async function loadLiveData(el, tournamentId) { ...@@ -51,8 +51,8 @@ async function loadLiveData(el, tournamentId) {
// Current round info // Current round info
if (currentRound) { if (currentRound) {
html += `<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;"> html += `<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<span style="font-size:14px;font-weight:700;color:#f8fafc;">${t('tournament.round_n', { n: currentRound.round_number, total: rounds.length })}</span> <span style="font-size:14px;font-weight:700;color:var(--text-primary);">${t('tournament.round_n', { n: currentRound.round_number, total: rounds.length })}</span>
<span style="font-size:11px;padding:3px 8px;border-radius:99px;background:${currentRound.status === 'in_progress' ? '#E4AC38' : '#64748b'};color:#000;font-weight:600;">${currentRound.status === 'in_progress' ? t('tournament.active') : t('tournament.completed')}</span> <span style="font-size:11px;padding:3px 8px;border-radius:99px;background:${currentRound.status === 'in_progress' ? 'var(--gold)' : 'var(--text-muted)'};color:#000;font-weight:600;">${currentRound.status === 'in_progress' ? t('tournament.active') : t('tournament.completed')}</span>
</div>`; </div>`;
// Show pairings for current round // Show pairings for current round
...@@ -63,15 +63,15 @@ async function loadLiveData(el, tournamentId) { ...@@ -63,15 +63,15 @@ async function loadLiveData(el, tournamentId) {
if (pairings.length > 0) { if (pairings.length > 0) {
html += `<div style="margin-bottom:16px;">`; html += `<div style="margin-bottom:16px;">`;
html += `<div style="font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:8px;">${t('tournament.live_matches')}</div>`; html += `<div style="font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:8px;">${t('tournament.live_matches')}</div>`;
pairings.forEach(p => { pairings.forEach(p => {
const hasResult = !!p.result; const hasResult = !!p.result;
html += ` html += `
<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:#1a1a2e;border-radius:8px;margin-bottom:4px;"> <div style="display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:var(--bg-card);border-radius:8px;margin-bottom:4px;">
<span style="flex:1;font-size:12px;color:#f8fafc;text-align:right;">${p.white_name || p.player_a || '?'}</span> <span style="flex:1;font-size:12px;color:var(--text-primary);text-align:right;">${p.white_name || p.player_a || '?'}</span>
<span style="padding:2px 8px;font-size:11px;font-weight:700;color:${hasResult ? '#34D399' : '#E4AC38'};">${p.result || 'vs'}</span> <span style="padding:2px 8px;font-size:11px;font-weight:700;color:${hasResult ? 'var(--success)' : 'var(--gold)'};">${p.result || 'vs'}</span>
<span style="flex:1;font-size:12px;color:#f8fafc;text-align:left;">${p.black_name || p.player_b || '?'}</span> <span style="flex:1;font-size:12px;color:var(--text-primary);text-align:left;">${p.black_name || p.player_b || '?'}</span>
${!hasResult ? `<button class="spectate-btn" data-white="${p.player_a || p.white_id || ''}" data-black="${p.player_b || p.black_id || ''}" style="margin-right:4px;padding:2px 8px;background:#3B82F6;border:none;border-radius:4px;color:#fff;font-size:10px;font-weight:600;cursor:pointer;">${t('spectate.watch')}</button>` : ''} ${!hasResult ? `<button class="spectate-btn" data-white="${p.player_a || p.white_id || ''}" data-black="${p.player_b || p.black_id || ''}" style="margin-right:4px;padding:2px 8px;background:var(--blue);border:none;border-radius:4px;color:#fff;font-size:10px;font-weight:600;cursor:pointer;">${t('spectate.watch')}</button>` : ''}
</div> </div>
`; `;
}); });
...@@ -82,21 +82,21 @@ async function loadLiveData(el, tournamentId) { ...@@ -82,21 +82,21 @@ async function loadLiveData(el, tournamentId) {
// Standings // Standings
if (standings.length > 0) { if (standings.length > 0) {
html += `<div style="font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:8px;">${t('tournament.tab_standings')}</div>`; html += `<div style="font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:8px;">${t('tournament.tab_standings')}</div>`;
html += standings.slice(0, 15).map((p, i) => { html += standings.slice(0, 15).map((p, i) => {
const medals = ['🥇', '🥈', '🥉']; const medals = ['🥇', '🥈', '🥉'];
return ` return `
<div style="display:flex;align-items:center;padding:6px 0;border-bottom:1px solid rgba(255,255,255,0.04);"> <div style="display:flex;align-items:center;padding:6px 0;border-bottom:1px solid var(--border);">
<span style="width:24px;font-size:${i < 3 ? '13px' : '11px'};text-align:center;">${i < 3 ? medals[i] : (i + 1)}</span> <span style="width:24px;font-size:${i < 3 ? '13px' : '11px'};text-align:center;">${i < 3 ? medals[i] : (i + 1)}</span>
<span style="flex:1;font-size:12px;color:#f8fafc;">${p.name || p.player_name || 'Player'}</span> <span style="flex:1;font-size:12px;color:var(--text-primary);">${p.name || p.player_name || 'Player'}</span>
<span style="font-size:12px;font-weight:700;color:#E4AC38;">${p.score ?? p.points ?? 0}</span> <span style="font-size:12px;font-weight:700;color:var(--gold);">${p.score ?? p.points ?? 0}</span>
</div> </div>
`; `;
}).join(''); }).join('');
} }
if (!html) { if (!html) {
html = `<div style="text-align:center;color:#64748b;padding:32px;">${t('common.empty')}</div>`; html = `<div style="text-align:center;color:var(--text-muted);padding:32px;">${t('common.empty')}</div>`;
} }
content.innerHTML = html; content.innerHTML = html;
...@@ -130,6 +130,6 @@ async function loadLiveData(el, tournamentId) { ...@@ -130,6 +130,6 @@ async function loadLiveData(el, tournamentId) {
}); });
} catch (e) { } catch (e) {
content.innerHTML = '<div style="text-align:center;color:#ef4444;">فشل التحميل</div>'; content.innerHTML = '<div style="text-align:center;color:var(--error);">فشل التحميل</div>';
} }
} }
...@@ -16,31 +16,31 @@ export function mountTournamentLobby(el, params) { ...@@ -16,31 +16,31 @@ export function mountTournamentLobby(el, params) {
if (unsub) unsub(); if (unsub) unsub();
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;align-items:center;justify-content:center;padding:24px;gap:20px;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);align-items:center;justify-content:center;padding:24px;gap:20px;">
<div style="text-align:center;"> <div style="text-align:center;">
<div style="font-size:20px;font-weight:800;color:#f8fafc;">${tournamentName || t('tournament.title')}</div> <div style="font-size:20px;font-weight:800;color:var(--text-primary);">${tournamentName || t('tournament.title')}</div>
<div id="countdown" style="font-size:42px;font-weight:800;color:#E4AC38;margin-top:12px;font-family:Inter,monospace;">--:--</div> <div id="countdown" style="font-size:42px;font-weight:800;color:var(--gold);margin-top:12px;font-family:Inter,monospace;">--:--</div>
<div style="font-size:13px;color:#64748b;margin-top:4px;">${t('tournament.starts_in', { n: '' })}</div> <div style="font-size:13px;color:var(--text-muted);margin-top:4px;">${t('tournament.starts_in', { n: '' })}</div>
</div> </div>
<div id="players-grid" style="display:flex;flex-wrap:wrap;gap:8px;justify-content:center;max-width:280px;"></div> <div id="players-grid" style="display:flex;flex-wrap:wrap;gap:8px;justify-content:center;max-width:280px;"></div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;width:100%;max-width:300px;"> <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;width:100%;max-width:300px;">
<div style="background:#1a1a2e;border-radius:8px;padding:10px;text-align:center;"> <div style="background:var(--bg-card);border-radius:8px;padding:10px;text-align:center;">
<div style="font-size:11px;color:#64748b;">${t('tournament.format_label')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('tournament.format_label')}</div>
<div style="font-size:13px;font-weight:700;color:#f8fafc;">${format || 'swiss'}</div> <div style="font-size:13px;font-weight:700;color:var(--text-primary);">${format || 'swiss'}</div>
</div> </div>
<div style="background:#1a1a2e;border-radius:8px;padding:10px;text-align:center;"> <div style="background:var(--bg-card);border-radius:8px;padding:10px;text-align:center;">
<div style="font-size:11px;color:#64748b;">${t('tournament.time_label')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('tournament.time_label')}</div>
<div style="font-size:13px;font-weight:700;color:#f8fafc;">${timeControl || '?'}</div> <div style="font-size:13px;font-weight:700;color:var(--text-primary);">${timeControl || '?'}</div>
</div> </div>
<div style="background:#1a1a2e;border-radius:8px;padding:10px;text-align:center;"> <div style="background:var(--bg-card);border-radius:8px;padding:10px;text-align:center;">
<div style="font-size:11px;color:#64748b;">${t('tournament.prizes_label')}</div> <div style="font-size:11px;color:var(--text-muted);">${t('tournament.prizes_label')}</div>
<div style="font-size:13px;font-weight:700;color:#E4AC38;">${prizePool || '0'} ${emoji('coin', '🪙', 13)}</div> <div style="font-size:13px;font-weight:700;color:var(--gold);">${prizePool || '0'} ${emoji('coin', '🪙', 13)}</div>
</div> </div>
</div> </div>
<button class="btn btn-secondary" id="leave-lobby" style="margin-top:auto;color:#ef4444;border-color:#ef4444;font-size:13px;">${t('tournament.leave')}</button> <button class="btn btn-secondary" id="leave-lobby" style="margin-top:auto;color:var(--error);border-color:var(--error);font-size:13px;">${t('tournament.leave')}</button>
</div> </div>
`; `;
...@@ -79,7 +79,7 @@ function updateCountdown(el, targetTime) { ...@@ -79,7 +79,7 @@ function updateCountdown(el, targetTime) {
if (diff <= 0) { if (diff <= 0) {
countdownEl.textContent = '00:00'; countdownEl.textContent = '00:00';
countdownEl.style.color = '#34D399'; countdownEl.style.color = 'var(--success)';
return; return;
} }
...@@ -97,7 +97,7 @@ async function loadPlayers(el, tournamentId) { ...@@ -97,7 +97,7 @@ async function loadPlayers(el, tournamentId) {
const registrations = data.registrations || []; const registrations = data.registrations || [];
if (registrations.length === 0) { if (registrations.length === 0) {
grid.innerHTML = `<div style="font-size:12px;color:#64748b;">${t('tournament.waiting')}</div>`; grid.innerHTML = `<div style="font-size:12px;color:var(--text-muted);">${t('tournament.waiting')}</div>`;
return; return;
} }
...@@ -105,8 +105,8 @@ async function loadPlayers(el, tournamentId) { ...@@ -105,8 +105,8 @@ async function loadPlayers(el, tournamentId) {
const avatar = r.avatar_url const avatar = r.avatar_url
? `<img src="${r.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` ? `<img src="${r.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">`
: `<span style="font-size:14px;">${emoji('person', '👤', 14)}</span>`; : `<span style="font-size:14px;">${emoji('person', '👤', 14)}</span>`;
return `<div style="width:36px;height:36px;border-radius:50%;background:#1a1a2e;border:2px solid #34D399;display:flex;align-items:center;justify-content:center;overflow:hidden;" title="${r.display_name || ''}">${avatar}</div>`; return `<div style="width:36px;height:36px;border-radius:50%;background:var(--bg-card);border:2px solid var(--success);display:flex;align-items:center;justify-content:center;overflow:hidden;" title="${r.display_name || ''}">${avatar}</div>`;
}).join('') + (registrations.length > 20 ? `<div style="font-size:11px;color:#64748b;align-self:center;">+${registrations.length - 20}</div>` : ''); }).join('') + (registrations.length > 20 ? `<div style="font-size:11px;color:var(--text-muted);align-self:center;">+${registrations.length - 20}</div>` : '');
} catch (e) { } catch (e) {
grid.innerHTML = ''; grid.innerHTML = '';
} }
......
...@@ -26,14 +26,14 @@ export async function mountTournaments(el) { ...@@ -26,14 +26,14 @@ export async function mountTournaments(el) {
const tournaments = data.tournaments || data || []; const tournaments = data.tournaments || data || [];
renderTournaments(el, tournaments); renderTournaments(el, tournaments);
} catch (e) { } catch (e) {
el.querySelector('#tournament-list').innerHTML = `<p style="color:#94a3b8;text-align:center;">${t('common.empty')}</p>`; el.querySelector('#tournament-list').innerHTML = `<p style="color:var(--text-secondary);text-align:center;">${t('common.empty')}</p>`;
} }
} }
function renderTournaments(el, tournaments) { function renderTournaments(el, tournaments) {
const list = el.querySelector('#tournament-list'); const list = el.querySelector('#tournament-list');
if (tournaments.length === 0) { if (tournaments.length === 0) {
list.innerHTML = `<p style="color:#94a3b8;text-align:center;padding:32px;">${t('tournament.no_tournaments')}</p>`; list.innerHTML = `<p style="color:var(--text-secondary);text-align:center;padding:32px;">${t('tournament.no_tournaments')}</p>`;
return; return;
} }
...@@ -42,15 +42,15 @@ function renderTournaments(el, tournaments) { ...@@ -42,15 +42,15 @@ function renderTournaments(el, tournaments) {
<div style="display:flex;justify-content:space-between;align-items:start;"> <div style="display:flex;justify-content:space-between;align-items:start;">
<div> <div>
<div style="font-size:15px;font-weight:700;">${tour.name || t('tournament.title')}</div> <div style="font-size:15px;font-weight:700;">${tour.name || t('tournament.title')}</div>
<div style="font-size:12px;color:#94a3b8;margin-top:2px;">${tour.game_key || 'chess'} · ${formatName(tour.format)}</div> <div style="font-size:12px;color:var(--text-secondary);margin-top:2px;">${tour.game_key || 'chess'} · ${formatName(tour.format)}</div>
<div style="font-size:11px;color:#64748b;margin-top:4px;">${tour.starts_at ? new Date(tour.starts_at).toLocaleDateString('ar') : ''}</div> <div style="font-size:11px;color:var(--text-muted);margin-top:4px;">${tour.starts_at ? new Date(tour.starts_at).toLocaleDateString('ar') : ''}</div>
</div> </div>
<span style="font-size:10px;padding:3px 8px;border-radius:9999px;background:${getStatusColor(tour.status)};color:white;font-weight:600;">${getStatusLabel(tour.status)}</span> <span style="font-size:10px;padding:3px 8px;border-radius:9999px;background:${getStatusColor(tour.status)};color:white;font-weight:600;">${getStatusLabel(tour.status)}</span>
</div> </div>
<div style="display:flex;gap:16px;margin-top:12px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);"> <div style="display:flex;gap:16px;margin-top:12px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);">
<span style="font-size:11px;color:#94a3b8;">${emoji('people', '👥', 11)} ${tour.player_count || 0}/${tour.max_players || 32}</span> <span style="font-size:11px;color:var(--text-secondary);">${emoji('people', '👥', 11)} ${tour.player_count || 0}/${tour.max_players || 32}</span>
<span style="font-size:11px;color:#E4AC38;">${emoji('tournament_cup', '🏆', 11)} ${tour.prize_pool_coins ? tour.prize_pool_coins + ' ' + t('common.coins') : 'N/A'}</span> <span style="font-size:11px;color:var(--gold);">${emoji('tournament_cup', '🏆', 11)} ${tour.prize_pool_coins ? tour.prize_pool_coins + ' ' + t('common.coins') : 'N/A'}</span>
${tour.entry_fee_coins ? `<span style="font-size:11px;color:#F87171;">${emoji('money_bag', '💰', 11)} ${tour.entry_fee_coins}</span>` : ''} ${tour.entry_fee_coins ? `<span style="font-size:11px;color:var(--red-light);">${emoji('money_bag', '💰', 11)} ${tour.entry_fee_coins}</span>` : ''}
</div> </div>
${tour.status === 'registration' ? `<button class="btn btn-primary w-full register-btn" data-tid="${tour.id}" style="margin-top:12px;font-size:13px;">${t('tournament.register_now')}</button>` : ''} ${tour.status === 'registration' ? `<button class="btn btn-primary w-full register-btn" data-tid="${tour.id}" style="margin-top:12px;font-size:13px;">${t('tournament.register_now')}</button>` : ''}
</div> </div>
...@@ -67,7 +67,7 @@ function renderTournaments(el, tournaments) { ...@@ -67,7 +67,7 @@ function renderTournaments(el, tournaments) {
try { try {
await net.post('tournaments.php', { action: 'register', tournament_id: tid }); await net.post('tournaments.php', { action: 'register', tournament_id: tid });
btn.textContent = '✅ ' + t('tournament.registered'); btn.textContent = '✅ ' + t('tournament.registered');
btn.style.background = '#34D399'; btn.style.background = 'var(--success)';
} catch (err) { } catch (err) {
btn.textContent = err.message || t('tournament.register_failed'); btn.textContent = err.message || t('tournament.register_failed');
btn.disabled = false; btn.disabled = false;
...@@ -92,15 +92,15 @@ async function showTournamentDetail(el, tournamentId, tour) { ...@@ -92,15 +92,15 @@ async function showTournamentDetail(el, tournamentId, tour) {
</div> </div>
<div class="card" style="padding:16px;"> <div class="card" style="padding:16px;">
<div style="font-size:18px;font-weight:700;margin-bottom:4px;">${tour?.name || t('tournament.title')}</div> <div style="font-size:18px;font-weight:700;margin-bottom:4px;">${tour?.name || t('tournament.title')}</div>
<div style="font-size:12px;color:#94a3b8;margin-bottom:12px;">${formatName(tour?.format)} · ${tour?.game_key || 'chess'}</div> <div style="font-size:12px;color:var(--text-secondary);margin-bottom:12px;">${formatName(tour?.format)} · ${tour?.game_key || 'chess'}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;">
<div style="background:#1a2440;padding:8px;border-radius:8px;text-align:center;"> <div style="background:#1a2440;padding:8px;border-radius:8px;text-align:center;">
<div style="font-size:16px;font-weight:700;color:#E4AC38;">${tour?.player_count || 0}</div> <div style="font-size:16px;font-weight:700;color:var(--gold);">${tour?.player_count || 0}</div>
<div style="font-size:10px;color:#64748b;">${t('tournament.players_label')}</div> <div style="font-size:10px;color:var(--text-muted);">${t('tournament.players_label')}</div>
</div> </div>
<div style="background:#1a2440;padding:8px;border-radius:8px;text-align:center;"> <div style="background:#1a2440;padding:8px;border-radius:8px;text-align:center;">
<div style="font-size:16px;font-weight:700;color:#00FFFF;">${tour?.rounds_total || tour?.swiss_rounds || '?'}</div> <div style="font-size:16px;font-weight:700;color:#00FFFF;">${tour?.rounds_total || tour?.swiss_rounds || '?'}</div>
<div style="font-size:10px;color:#64748b;">${t('tournament.rounds_label')}</div> <div style="font-size:10px;color:var(--text-muted);">${t('tournament.rounds_label')}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -119,29 +119,29 @@ async function showTournamentDetail(el, tournamentId, tour) { ...@@ -119,29 +119,29 @@ async function showTournamentDetail(el, tournamentId, tour) {
const data = await net.get('tournaments.php', { action: 'detail', id: tournamentId }); const data = await net.get('tournaments.php', { action: 'detail', id: tournamentId });
renderBracketOrStandings(el, data, tour?.format); renderBracketOrStandings(el, data, tour?.format);
} catch (e) { } catch (e) {
el.querySelector('#bracket-area').innerHTML = `<p style="color:#64748b;text-align:center;">${t('tournament.no_results_yet')}</p>`; el.querySelector('#bracket-area').innerHTML = `<p style="color:var(--text-muted);text-align:center;">${t('tournament.no_results_yet')}</p>`;
} }
} }
function renderBracketOrStandings(el, data, format) { function renderBracketOrStandings(el, data, format) {
const area = el.querySelector('#bracket-area'); const area = el.querySelector('#bracket-area');
if (!data || (!data.rounds && !data.brackets)) { if (!data || (!data.rounds && !data.brackets)) {
area.innerHTML = `<p style="color:#64748b;text-align:center;padding:16px;">${t('tournament.no_results_yet')}</p>`; area.innerHTML = `<p style="color:var(--text-muted);text-align:center;padding:16px;">${t('tournament.no_results_yet')}</p>`;
return; return;
} }
area.innerHTML = ` area.innerHTML = `
<div class="card" style="padding:12px;"> <div class="card" style="padding:12px;">
<div style="font-size:14px;font-weight:600;margin-bottom:8px;">${t('tournament.results')}</div> <div style="font-size:14px;font-weight:600;margin-bottom:8px;">${t('tournament.results')}</div>
<div style="font-size:12px;color:#94a3b8;">${t('tournament.data_available_later')}</div> <div style="font-size:12px;color:var(--text-secondary);">${t('tournament.data_available_later')}</div>
</div> </div>
`; `;
} }
function getStatusColor(status) { function getStatusColor(status) {
switch (status) { switch (status) {
case 'registration': return '#34D399'; case 'registration': return 'var(--success)';
case 'in_progress': return '#E84D1E'; case 'in_progress': return '#E84D1E';
case 'completed': return '#64748b'; case 'completed': return 'var(--text-muted)';
default: return '#2082F0'; default: return '#2082F0';
} }
} }
......
...@@ -13,14 +13,14 @@ const CATEGORY_LABELS = { ...@@ -13,14 +13,14 @@ const CATEGORY_LABELS = {
}; };
const TIER_COLORS = { const TIER_COLORS = {
1: '#94a3b8', 1: 'var(--text-secondary)',
2: '#22c55e', 2: 'var(--green)',
3: '#E4AC38', 3: 'var(--gold)',
4: '#a855f7', 4: 'var(--purple-light)',
}; };
export async function mountAchievements(el) { export async function mountAchievements(el) {
el.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;background:#0a0a1a;color:#64748b;">${t('common.loading')}</div>`; el.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;background:var(--bg-deep);color:var(--text-muted);">${t('common.loading')}</div>`;
let achievements = []; let achievements = [];
let stats = { total: 0, completed: 0 }; let stats = { total: 0, completed: 0 };
...@@ -54,22 +54,22 @@ function render(el, achievements, stats) { ...@@ -54,22 +54,22 @@ function render(el, achievements, stats) {
const progressPct = stats.total > 0 ? Math.round((stats.completed / stats.total) * 100) : 0; const progressPct = stats.total > 0 ? Math.round((stats.completed / stats.total) * 100) : 0;
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);">
<!-- Header --> <!-- Header -->
<div style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<span style="font-size:16px;font-weight:700;color:#f8fafc;">${emoji('trophy', '🏆', 16)} ${t('achievements.title')}</span> <span style="font-size:16px;font-weight:700;color:var(--text-primary);">${emoji('trophy', '🏆', 16)} ${t('achievements.title')}</span>
<span style="margin-inline-start:auto;font-size:12px;color:#64748b;">${stats.completed}/${stats.total}</span> <span style="margin-inline-start:auto;font-size:12px;color:var(--text-muted);">${stats.completed}/${stats.total}</span>
</div> </div>
<!-- Progress bar --> <!-- Progress bar -->
<div style="padding:12px 16px 0;"> <div style="padding:12px 16px 0;">
<div style="background:#1a1a2e;border-radius:99px;height:8px;overflow:hidden;"> <div style="background:var(--bg-card);border-radius:99px;height:8px;overflow:hidden;">
<div style="background:linear-gradient(90deg,#E4AC38,#FFCC66);height:100%;width:${progressPct}%;border-radius:99px;transition:width 0.5s;"></div> <div style="background:linear-gradient(90deg,var(--gold),var(--gold-soft));height:100%;width:${progressPct}%;border-radius:99px;transition:width 0.5s;"></div>
</div> </div>
<div style="display:flex;justify-content:space-between;margin-top:4px;"> <div style="display:flex;justify-content:space-between;margin-top:4px;">
<span style="font-size:10px;color:#64748b;">${progressPct}% ${t('achievements.complete')}</span> <span style="font-size:10px;color:var(--text-muted);">${progressPct}% ${t('achievements.complete')}</span>
<span style="font-size:10px;color:#E4AC38;">${stats.completed} ${t('achievements.title')}</span> <span style="font-size:10px;color:var(--gold);">${stats.completed} ${t('achievements.title')}</span>
</div> </div>
</div> </div>
...@@ -86,9 +86,9 @@ function render(el, achievements, stats) { ...@@ -86,9 +86,9 @@ function render(el, achievements, stats) {
</div> </div>
<style> <style>
.ach-filter{background:#1a1a2e;border:1px solid rgba(255,255,255,0.06);color:#94a3b8;font-size:11px;padding:6px 12px;border-radius:99px;cursor:pointer;white-space:nowrap;font-family:inherit;font-weight:600;} .ach-filter{background:var(--bg-card);border:1px solid var(--border);color:var(--text-secondary);font-size:11px;padding:6px 12px;border-radius:99px;cursor:pointer;white-space:nowrap;font-family:inherit;font-weight:600;}
.ach-filter.active{background:#E4AC38;color:#1a1a1a;border-color:#E4AC38;} .ach-filter.active{background:var(--gold);color:var(--bg-dark);border-color:var(--gold);}
.ach-card{display:flex;align-items:center;gap:12px;padding:12px;background:#0f0f1e;border-radius:12px;border:1px solid rgba(255,255,255,0.04);} .ach-card{display:flex;align-items:center;gap:12px;padding:12px;background:var(--bg-panel);border-radius:12px;border:1px solid var(--border);}
.ach-card.completed{border-color:rgba(34,197,94,0.3);background:#0a1a0a;} .ach-card.completed{border-color:rgba(34,197,94,0.3);background:#0a1a0a;}
</style> </style>
`; `;
...@@ -107,7 +107,7 @@ function render(el, achievements, stats) { ...@@ -107,7 +107,7 @@ function render(el, achievements, stats) {
function renderList(achievements, category) { function renderList(achievements, category) {
const filtered = category === 'all' ? achievements : achievements.filter(a => a.category === category); const filtered = category === 'all' ? achievements : achievements.filter(a => a.category === category);
if (filtered.length === 0) { if (filtered.length === 0) {
return `<div style="text-align:center;padding:32px;color:#475569;font-size:13px;">${t('achievements.none')}</div>`; return `<div style="text-align:center;padding:32px;color:var(--text-dim);font-size:13px;">${t('achievements.none')}</div>`;
} }
return filtered.map(a => { return filtered.map(a => {
...@@ -117,27 +117,27 @@ function renderList(achievements, category) { ...@@ -117,27 +117,27 @@ function renderList(achievements, category) {
return ` return `
<div class="ach-card ${a.completed ? 'completed' : ''}"> <div class="ach-card ${a.completed ? 'completed' : ''}">
<div style="width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px; <div style="width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;
background:${a.completed ? 'linear-gradient(135deg,#166534,#22c55e)' : '#1a1a2e'}; background:${a.completed ? 'linear-gradient(135deg,#166534,var(--green))' : 'var(--bg-card)'};
border:2px solid ${a.completed ? '#22c55e' : tierColor};"> border:2px solid ${a.completed ? 'var(--green)' : tierColor};">
${a.completed ? '✓' : tierIcon(a.tier)} ${a.completed ? '✓' : tierIcon(a.tier)}
</div> </div>
<div style="flex:1;min-width:0;"> <div style="flex:1;min-width:0;">
<div style="font-size:13px;font-weight:600;color:${a.completed ? '#34D399' : '#f8fafc'};margin-bottom:2px;">${a.name}</div> <div style="font-size:13px;font-weight:600;color:${a.completed ? 'var(--success)' : 'var(--text-primary)'};margin-bottom:2px;">${a.name}</div>
<div style="font-size:11px;color:#64748b;margin-bottom:4px;">${a.description}</div> <div style="font-size:11px;color:var(--text-muted);margin-bottom:4px;">${a.description}</div>
${!a.completed ? ` ${!a.completed ? `
<div style="display:flex;align-items:center;gap:6px;"> <div style="display:flex;align-items:center;gap:6px;">
<div style="flex:1;background:#1a1a2e;border-radius:99px;height:4px;overflow:hidden;"> <div style="flex:1;background:var(--bg-card);border-radius:99px;height:4px;overflow:hidden;">
<div style="background:${tierColor};height:100%;width:${pct}%;border-radius:99px;"></div> <div style="background:${tierColor};height:100%;width:${pct}%;border-radius:99px;"></div>
</div> </div>
<span style="font-size:10px;color:#64748b;white-space:nowrap;">${a.progress}/${a.target}</span> <span style="font-size:10px;color:var(--text-muted);white-space:nowrap;">${a.progress}/${a.target}</span>
</div> </div>
` : ` ` : `
<div style="font-size:10px;color:#22c55e;">✓ ${t('achievements.complete')}</div> <div style="font-size:10px;color:var(--green);">✓ ${t('achievements.complete')}</div>
`} `}
</div> </div>
<div style="text-align:center;min-width:40px;"> <div style="text-align:center;min-width:40px;">
<div style="font-size:11px;font-weight:700;color:#E4AC38;">${a.coins_reward}</div> <div style="font-size:11px;font-weight:700;color:var(--gold);">${a.coins_reward}</div>
<div style="font-size:9px;color:#64748b;">${t('game.coins')}</div> <div style="font-size:9px;color:var(--text-muted);">${t('game.coins')}</div>
</div> </div>
</div> </div>
`; `;
......
...@@ -12,13 +12,13 @@ export async function mountChallenges(el) { ...@@ -12,13 +12,13 @@ export async function mountChallenges(el) {
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;"> <div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div style="display:flex;align-items:center;gap:12px;"> <div style="display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<h2 style="font-size:18px;font-weight:800;color:#f8fafc;flex:1;">${emoji('lightning', '⚡', 18)} ${t('challenges.title')}</h2> <h2 style="font-size:18px;font-weight:800;color:var(--text-primary);flex:1;">${emoji('lightning', '⚡', 18)} ${t('challenges.title')}</h2>
<div id="streak-badge" style="background:linear-gradient(135deg,#E4AC38,#FFCC66);color:#1a1a1a;font-size:12px;font-weight:800;padding:5px 12px;border-radius:99px;"></div> <div id="streak-badge" style="background:linear-gradient(135deg,var(--gold),var(--gold-soft));color:var(--bg-dark);font-size:12px;font-weight:800;padding:5px 12px;border-radius:99px;"></div>
</div> </div>
<div id="challenges-list"></div> <div id="challenges-list"></div>
<div id="all-done" style="display:none;text-align:center;padding:20px;"> <div id="all-done" style="display:none;text-align:center;padding:20px;">
<div style="font-size:40px;margin-bottom:8px;">${emoji('checkmark', '✅', 40)}</div> <div style="font-size:40px;margin-bottom:8px;">${emoji('checkmark', '✅', 40)}</div>
<div style="font-size:15px;font-weight:700;color:#34D399;">${t('challenges.all_done')}</div> <div style="font-size:15px;font-weight:700;color:var(--success);">${t('challenges.all_done')}</div>
</div> </div>
</div> </div>
`; `;
...@@ -29,7 +29,7 @@ export async function mountChallenges(el) { ...@@ -29,7 +29,7 @@ export async function mountChallenges(el) {
const data = await net.get('challenges.php'); const data = await net.get('challenges.php');
renderChallenges(el, data); renderChallenges(el, data);
} catch (e) { } catch (e) {
el.querySelector('#challenges-list').innerHTML = `<div style="text-align:center;color:#ef4444;">${t('common.error_load')}</div>`; el.querySelector('#challenges-list').innerHTML = `<div style="text-align:center;color:var(--error);">${t('common.error_load')}</div>`;
} }
} }
...@@ -40,21 +40,21 @@ function renderChallenges(el, data) { ...@@ -40,21 +40,21 @@ function renderChallenges(el, data) {
const list = el.querySelector('#challenges-list'); const list = el.querySelector('#challenges-list');
list.innerHTML = challenges.map((c, i) => ` list.innerHTML = challenges.map((c, i) => `
<div class="challenge-card" data-idx="${i}" style="background:#1a1a2e;border-radius:14px;padding:14px;display:flex;align-items:center;gap:12px;border:1px solid rgba(255,255,255,0.05);transition:transform 0.1s;"> <div class="challenge-card" data-idx="${i}" style="background:var(--bg-card);border-radius:14px;padding:14px;display:flex;align-items:center;gap:12px;border:1px solid var(--border);transition:transform 0.1s;">
<div style="width:44px;height:44px;border-radius:10px;background:rgba(228,172,56,0.1);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;">${c.icon}</div> <div style="width:44px;height:44px;border-radius:10px;background:rgba(228,172,56,0.1);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;">${c.icon}</div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;font-weight:700;color:#f8fafc;">${c.title}</div> <div style="font-size:13px;font-weight:700;color:var(--text-primary);">${c.title}</div>
<div style="display:flex;align-items:center;gap:8px;margin-top:4px;"> <div style="display:flex;align-items:center;gap:8px;margin-top:4px;">
<div style="flex:1;height:5px;background:#2a2a4a;border-radius:3px;overflow:hidden;"> <div style="flex:1;height:5px;background:var(--bg-hover);border-radius:3px;overflow:hidden;">
<div style="height:100%;width:${Math.min(100, (c.progress / c.target) * 100)}%;background:${c.completed ? '#34D399' : '#E4AC38'};border-radius:3px;transition:width 0.3s;"></div> <div style="height:100%;width:${Math.min(100, (c.progress / c.target) * 100)}%;background:${c.completed ? 'var(--success)' : 'var(--gold)'};border-radius:3px;transition:width 0.3s;"></div>
</div> </div>
<span style="font-size:10px;color:#64748b;min-width:30px;text-align:left;">${c.progress}/${c.target}</span> <span style="font-size:10px;color:var(--text-muted);min-width:30px;text-align:left;">${c.progress}/${c.target}</span>
</div> </div>
</div> </div>
<div style="text-align:center;min-width:50px;"> <div style="text-align:center;min-width:50px;">
${c.claimed ? '<span style="color:#34D399;font-size:16px;">✓</span>' : ${c.claimed ? '<span style="color:var(--success);font-size:16px;">✓</span>' :
c.completed ? `<button class="claim-btn" data-id="${c.id}" data-coins="${c.reward_coins}" data-xp="${c.reward_xp}" style="background:#E4AC38;color:#1a1a1a;border:none;border-radius:8px;padding:6px 10px;font-size:11px;font-weight:700;cursor:pointer;">${t('challenges.claim')}</button>` : c.completed ? `<button class="claim-btn" data-id="${c.id}" data-coins="${c.reward_coins}" data-xp="${c.reward_xp}" style="background:var(--gold);color:var(--bg-dark);border:none;border-radius:8px;padding:6px 10px;font-size:11px;font-weight:700;cursor:pointer;">${t('challenges.claim')}</button>` :
`<span style="font-size:11px;color:#E4AC38;font-weight:600;">${c.reward_coins}${emoji('coin', '🪙', 14)}</span>`} `<span style="font-size:11px;color:var(--gold);font-weight:600;">${c.reward_coins}${emoji('coin', '🪙', 14)}</span>`}
</div> </div>
</div> </div>
`).join(''); `).join('');
...@@ -75,7 +75,7 @@ function renderChallenges(el, data) { ...@@ -75,7 +75,7 @@ function renderChallenges(el, data) {
const xp = parseInt(btn.dataset.xp); const xp = parseInt(btn.dataset.xp);
btn.textContent = '✓'; btn.textContent = '✓';
btn.style.background = '#34D399'; btn.style.background = 'var(--success)';
btn.disabled = true; btn.disabled = true;
// Coin burst from button position // Coin burst from button position
......
...@@ -10,7 +10,7 @@ import { emoji } from '../../../core/theme.js'; ...@@ -10,7 +10,7 @@ import { emoji } from '../../../core/theme.js';
const DAY_REWARDS = [50, 75, 100, 125, 150, 200, 300]; const DAY_REWARDS = [50, 75, 100, 125, 150, 200, 300];
export async function mountDaily(el) { export async function mountDaily(el) {
el.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;background:#0a0a1a;color:#64748b;">${t('common.loading')}</div>`; el.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;background:var(--bg-deep);color:var(--text-muted);">${t('common.loading')}</div>`;
let streak = 0; let streak = 0;
let alreadyClaimed = false; let alreadyClaimed = false;
...@@ -41,18 +41,18 @@ export async function mountDaily(el) { ...@@ -41,18 +41,18 @@ export async function mountDaily(el) {
function render(el, streak, alreadyClaimed, dayIndex, todayReward) { function render(el, streak, alreadyClaimed, dayIndex, todayReward) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#0a0a1a;"> <div style="display:flex;flex-direction:column;height:100%;background:var(--bg-deep);">
<!-- Header --> <!-- Header -->
<div style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:#0f0f1e;border-bottom:1px solid rgba(255,255,255,0.06);"> <div style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg-panel);border-bottom:1px solid var(--border);">
<button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button> <button class="btn btn-secondary" id="back-btn" style="min-height:32px;padding:4px 12px;font-size:12px;">←</button>
<span style="font-size:16px;font-weight:700;color:#f8fafc;">${emoji('gift', '🎁', 16)} ${t('daily.title')}</span> <span style="font-size:16px;font-weight:700;color:var(--text-primary);">${emoji('gift', '🎁', 16)} ${t('daily.title')}</span>
</div> </div>
<div style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;align-items:center;gap:16px;"> <div style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;align-items:center;gap:16px;">
<!-- Streak badge --> <!-- Streak badge -->
<div style="background:linear-gradient(135deg,#92400e,#E4AC38);padding:8px 20px;border-radius:99px;display:flex;align-items:center;gap:6px;"> <div style="background:linear-gradient(135deg,#92400e,var(--gold));padding:8px 20px;border-radius:99px;display:flex;align-items:center;gap:6px;">
${emoji('fire', '🔥', 18)} ${emoji('fire', '🔥', 18)}
<span style="font-size:14px;font-weight:800;color:#1a1a1a;">${t('daily.streak_days', { n: streak })}</span> <span style="font-size:14px;font-weight:800;color:var(--bg-dark);">${t('daily.streak_days', { n: streak })}</span>
</div> </div>
<!-- 7-day timeline --> <!-- 7-day timeline -->
...@@ -64,12 +64,12 @@ function render(el, streak, alreadyClaimed, dayIndex, todayReward) { ...@@ -64,12 +64,12 @@ function render(el, streak, alreadyClaimed, dayIndex, todayReward) {
return ` return `
<div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;"> <div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;">
<div style="width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700; <div style="width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;
background:${claimed ? '#1a3a1a' : isToday ? 'linear-gradient(135deg,#E4AC38,#FFCC66)' : '#1a1a2e'}; background:${claimed ? '#1a3a1a' : isToday ? 'linear-gradient(135deg,var(--gold),var(--gold-soft))' : 'var(--bg-card)'};
border:2px solid ${claimed ? '#34D399' : isToday ? '#E4AC38' : 'rgba(255,255,255,0.06)'}; border:2px solid ${claimed ? 'var(--success)' : isToday ? 'var(--gold)' : 'var(--border)'};
color:${claimed ? '#34D399' : isToday ? '#1a1a1a' : '#64748b'};"> color:${claimed ? 'var(--success)' : isToday ? 'var(--bg-dark)' : 'var(--text-muted)'};">
${claimed ? '✓' : coins} ${claimed ? '✓' : coins}
</div> </div>
<span style="font-size:9px;color:${isToday ? '#E4AC38' : '#475569'};font-weight:${isToday ? '700' : '400'};">${t('daily.day', { n: i + 1 })}</span> <span style="font-size:9px;color:${isToday ? 'var(--gold)' : 'var(--text-dim)'};font-weight:${isToday ? '700' : '400'};">${t('daily.day', { n: i + 1 })}</span>
</div> </div>
`; `;
}).join('')} }).join('')}
...@@ -79,18 +79,18 @@ function render(el, streak, alreadyClaimed, dayIndex, todayReward) { ...@@ -79,18 +79,18 @@ function render(el, streak, alreadyClaimed, dayIndex, todayReward) {
<div style="text-align:center;margin-top:8px;"> <div style="text-align:center;margin-top:8px;">
${alreadyClaimed ? ` ${alreadyClaimed ? `
<div style="font-size:48px;margin-bottom:8px;">${emoji('checkmark', '', 48)}</div> <div style="font-size:48px;margin-bottom:8px;">${emoji('checkmark', '', 48)}</div>
<div style="font-size:16px;font-weight:700;color:#34D399;">${t('daily.claimed_today')}</div> <div style="font-size:16px;font-weight:700;color:var(--success);">${t('daily.claimed_today')}</div>
<div style="font-size:13px;color:#64748b;margin-top:4px;">${t('daily.come_back')}</div> <div style="font-size:13px;color:var(--text-muted);margin-top:4px;">${t('daily.come_back')}</div>
` : ` ` : `
<div style="font-size:56px;margin-bottom:8px;animation:float 3s ease-in-out infinite;">${emoji('gift', '🎁', 56)}</div> <div style="font-size:56px;margin-bottom:8px;animation:float 3s ease-in-out infinite;">${emoji('gift', '🎁', 56)}</div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${t('daily.today_reward')}</div> <div style="font-size:16px;font-weight:700;color:var(--text-primary);margin-bottom:4px;">${t('daily.today_reward')}</div>
<div style="font-size:28px;font-weight:800;color:#E4AC38;margin-bottom:16px;">${todayReward} ${emoji('coin', '🪙', 24)}</div> <div style="font-size:28px;font-weight:800;color:var(--gold);margin-bottom:16px;">${todayReward} ${emoji('coin', '🪙', 24)}</div>
<button class="btn btn-primary" id="claim-btn" style="font-size:16px;padding:14px 48px;">${t('daily.claim_btn')}</button> <button class="btn btn-primary" id="claim-btn" style="font-size:16px;padding:14px 48px;">${t('daily.claim_btn')}</button>
`} `}
</div> </div>
<!-- Info --> <!-- Info -->
<div style="text-align:center;color:#475569;font-size:11px;margin-top:auto;padding:12px;"> <div style="text-align:center;color:var(--text-dim);font-size:11px;margin-top:auto;padding:12px;">
${t('daily.hint')} ${t('daily.hint')}
</div> </div>
</div> </div>
......
...@@ -5,7 +5,7 @@ import { emoji } from '../../../core/theme.js'; ...@@ -5,7 +5,7 @@ import { emoji } from '../../../core/theme.js';
export const TIERS = [ export const TIERS = [
{ get name() { return t('rank.bronze'); }, nameEn: 'Bronze', min: 0, max: 999, color: '#CD7F32', get icon() { return emoji('medal_bronze', '🥉', 16); } }, { get name() { return t('rank.bronze'); }, nameEn: 'Bronze', min: 0, max: 999, color: '#CD7F32', get icon() { return emoji('medal_bronze', '🥉', 16); } },
{ get name() { return t('rank.silver'); }, nameEn: 'Silver', min: 1000, max: 1199, color: '#C0C0C0', get icon() { return emoji('medal_silver', '🥈', 16); } }, { get name() { return t('rank.silver'); }, nameEn: 'Silver', min: 1000, max: 1199, color: '#C0C0C0', get icon() { return emoji('medal_silver', '🥈', 16); } },
{ get name() { return t('rank.gold'); }, nameEn: 'Gold', min: 1200, max: 1399, color: '#FFD700', get icon() { return emoji('medal_gold', '🥇', 16); } }, { get name() { return t('rank.gold'); }, nameEn: 'Gold', min: 1200, max: 1399, color: 'var(--gold-soft)', get icon() { return emoji('medal_gold', '🥇', 16); } },
{ get name() { return t('rank.platinum'); }, nameEn: 'Platinum', min: 1400, max: 1599, color: '#00CED1', get icon() { return emoji('gem', '💎', 16); } }, { get name() { return t('rank.platinum'); }, nameEn: 'Platinum', min: 1400, max: 1599, color: '#00CED1', get icon() { return emoji('gem', '💎', 16); } },
{ get name() { return t('rank.diamond'); }, nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', get icon() { return emoji('diamond', '💠', 16); } }, { get name() { return t('rank.diamond'); }, nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', get icon() { return emoji('diamond', '💠', 16); } },
{ get name() { return t('rank.master'); }, nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', get icon() { return emoji('crown', '👑', 16); } }, { get name() { return t('rank.master'); }, nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', get icon() { return emoji('crown', '👑', 16); } },
......
...@@ -5,7 +5,7 @@ import { emoji } from '../../../core/theme.js'; ...@@ -5,7 +5,7 @@ import { emoji } from '../../../core/theme.js';
export async function mountActivity(el) { export async function mountActivity(el) {
el.innerHTML = ` el.innerHTML = `
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;"> <div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;">${emoji('news', '📰', 18)} ${t('social.activity_tab')}</h2> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);">${emoji('news', '📰', 18)} ${t('social.activity_tab')}</h2>
<div id="activity-list"></div> <div id="activity-list"></div>
</div> </div>
`; `;
...@@ -14,14 +14,14 @@ export async function mountActivity(el) { ...@@ -14,14 +14,14 @@ export async function mountActivity(el) {
const data = await net.get('activity.php'); const data = await net.get('activity.php');
renderActivity(el, data.activity || []); renderActivity(el, data.activity || []);
} catch (e) { } catch (e) {
el.querySelector('#activity-list').innerHTML = `<div style="text-align:center;color:#64748b;padding:32px;">${t('social.no_activity_short')}</div>`; el.querySelector('#activity-list').innerHTML = `<div style="text-align:center;color:var(--text-muted);padding:32px;">${t('social.no_activity_short')}</div>`;
} }
} }
function renderActivity(el, activities) { function renderActivity(el, activities) {
const list = el.querySelector('#activity-list'); const list = el.querySelector('#activity-list');
if (activities.length === 0) { if (activities.length === 0) {
list.innerHTML = `<div style="text-align:center;color:#64748b;padding:32px;">${t('social.no_activity_short')}</div>`; list.innerHTML = `<div style="text-align:center;color:var(--text-muted);padding:32px;">${t('social.no_activity_short')}</div>`;
return; return;
} }
...@@ -41,13 +41,13 @@ function renderActivity(el, activities) { ...@@ -41,13 +41,13 @@ function renderActivity(el, activities) {
const time = timeAgo(a.created_at); const time = timeAgo(a.created_at);
return ` return `
<div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;"> <div style="display:flex;gap:10px;padding:10px;background:var(--bg-card);border-radius:10px;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;"> <div style="width:36px;height:36px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;">
${actor.avatar_url ? `<img src="${actor.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 14)} ${actor.avatar_url ? `<img src="${actor.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 14)}
</div> </div>
<div style="flex:1;"> <div style="flex:1;">
<div style="font-size:13px;color:#f8fafc;"><strong>${actor.display_name || actor.username || '?'}</strong> ${label}</div> <div style="font-size:13px;color:var(--text-primary);"><strong>${actor.display_name || actor.username || '?'}</strong> ${label}</div>
<div style="font-size:10px;color:#64748b;margin-top:2px;">${time}</div> <div style="font-size:10px;color:var(--text-muted);margin-top:2px;">${time}</div>
</div> </div>
</div> </div>
`; `;
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,9 +8,9 @@ import { emoji } from '../../../core/theme.js'; ...@@ -8,9 +8,9 @@ import { emoji } from '../../../core/theme.js';
export async function mountGroupCreate(el) { export async function mountGroupCreate(el) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;"> <div style="display:flex;flex-direction:column;height:100%;">
<div style="padding:12px 16px;background:#0f0f1e;display:flex;align-items:center;gap:12px;"> <div style="padding:12px 16px;background:var(--bg-panel);display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button> <button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;">${t('group.create_title')}</h2> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);">${t('group.create_title')}</h2>
</div> </div>
<div style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:16px;"> <div style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:16px;">
<div> <div>
...@@ -44,12 +44,12 @@ export async function mountGroupCreate(el) { ...@@ -44,12 +44,12 @@ export async function mountGroupCreate(el) {
picker.innerHTML = `<div style="text-align:center;padding:12px;color:var(--text-secondary);font-size:13px;">${t('social.no_friends')}</div>`; picker.innerHTML = `<div style="text-align:center;padding:12px;color:var(--text-secondary);font-size:13px;">${t('social.no_friends')}</div>`;
} else { } else {
picker.innerHTML = friends.map(f => ` picker.innerHTML = friends.map(f => `
<label data-id="${f.id}" style="display:flex;align-items:center;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;cursor:pointer;transition:background 0.1s;"> <label data-id="${f.id}" style="display:flex;align-items:center;gap:10px;padding:10px;background:var(--bg-card);border-radius:10px;cursor:pointer;transition:background 0.1s;">
<input type="checkbox" value="${f.id}" style="width:18px;height:18px;accent-color:#2563EB;"> <input type="checkbox" value="${f.id}" style="width:18px;height:18px;accent-color:var(--chess-primary);">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;"> <div style="width:36px;height:36px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;">
${f.avatar_url ? `<img src="${f.avatar_url}" style="width:36px;height:36px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 16)} ${f.avatar_url ? `<img src="${f.avatar_url}" style="width:36px;height:36px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 16)}
</div> </div>
<span style="font-size:13px;font-weight:500;color:#f8fafc;">${escapeHtml(f.display_name || f.username || 'Player')}</span> <span style="font-size:13px;font-weight:500;color:var(--text-primary);">${escapeHtml(f.display_name || f.username || 'Player')}</span>
</label> </label>
`).join(''); `).join('');
......
...@@ -16,9 +16,9 @@ export async function mountGroupMembers(el, params = {}) { ...@@ -16,9 +16,9 @@ export async function mountGroupMembers(el, params = {}) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;"> <div style="display:flex;flex-direction:column;height:100%;">
<div style="padding:12px 16px;background:#0f0f1e;display:flex;align-items:center;gap:12px;"> <div style="padding:12px 16px;background:var(--bg-panel);display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button> <button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;flex:1;">${t('group.members')}</h2> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);flex:1;">${t('group.members')}</h2>
${canManage ? `<button class="btn btn-primary" id="add-member-btn" style="min-height:34px;padding:6px 12px;font-size:12px;">+ ${t('group.add')}</button>` : ''} ${canManage ? `<button class="btn btn-primary" id="add-member-btn" style="min-height:34px;padding:6px 12px;font-size:12px;">+ ${t('group.add')}</button>` : ''}
</div> </div>
<div id="members-list" style="flex:1;overflow-y:auto;padding:12px 16px;"> <div id="members-list" style="flex:1;overflow-y:auto;padding:12px 16px;">
...@@ -45,15 +45,15 @@ export async function mountGroupMembers(el, params = {}) { ...@@ -45,15 +45,15 @@ export async function mountGroupMembers(el, params = {}) {
const showRemove = canManage && !isMe && m.role !== 'owner'; const showRemove = canManage && !isMe && m.role !== 'owner';
return ` return `
<div class="member-row" style="display:flex;align-items:center;gap:12px;padding:12px;background:#1a1a2e;border-radius:10px;margin-bottom:6px;"> <div class="member-row" style="display:flex;align-items:center;gap:12px;padding:12px;background:var(--bg-card);border-radius:10px;margin-bottom:6px;">
<div style="width:40px;height:40px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;cursor:pointer;" data-profile="${m.user_id}"> <div style="width:40px;height:40px;border-radius:50%;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;cursor:pointer;" data-profile="${m.user_id}">
${p.avatar_url ? `<img src="${p.avatar_url}" style="width:40px;height:40px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 18)} ${p.avatar_url ? `<img src="${p.avatar_url}" style="width:40px;height:40px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 18)}
</div> </div>
<div style="flex:1;cursor:pointer;" data-profile="${m.user_id}"> <div style="flex:1;cursor:pointer;" data-profile="${m.user_id}">
<div style="font-size:14px;font-weight:500;color:#f8fafc;">${escapeHtml(p.display_name || 'Player')}${isMe ? ` ${t('common.you')}` : ''}</div> <div style="font-size:14px;font-weight:500;color:var(--text-primary);">${escapeHtml(p.display_name || 'Player')}${isMe ? ` ${t('common.you')}` : ''}</div>
${roleLabel ? `<div style="font-size:11px;color:#64748b;">${roleLabel}</div>` : ''} ${roleLabel ? `<div style="font-size:11px;color:var(--text-muted);">${roleLabel}</div>` : ''}
</div> </div>
${p.is_online ? '<div style="width:8px;height:8px;border-radius:50%;background:#34D399;"></div>' : ''} ${p.is_online ? '<div style="width:8px;height:8px;border-radius:50%;background:var(--success);"></div>' : ''}
${showRemove ? `<button class="btn btn-secondary remove-btn" data-id="${m.user_id}" style="min-height:28px;padding:4px 10px;font-size:11px;color:var(--error);">${t('group.remove')}</button>` : ''} ${showRemove ? `<button class="btn btn-secondary remove-btn" data-id="${m.user_id}" style="min-height:28px;padding:4px 10px;font-size:11px;color:var(--error);">${t('group.remove')}</button>` : ''}
</div>`; </div>`;
}).join(''); }).join('');
...@@ -133,8 +133,8 @@ async function showAddMemberPicker(el, groupId) { ...@@ -133,8 +133,8 @@ async function showAddMemberPicker(el, groupId) {
overlay.id = 'add-member-overlay'; overlay.id = 'add-member-overlay';
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:9999;display:flex;align-items:flex-end;justify-content:center;'; overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:9999;display:flex;align-items:flex-end;justify-content:center;';
overlay.innerHTML = ` overlay.innerHTML = `
<div style="background:#1a1a2e;border-radius:20px 20px 0 0;padding:20px;width:100%;max-width:400px;max-height:60vh;display:flex;flex-direction:column;gap:12px;"> <div style="background:var(--bg-card);border-radius:20px 20px 0 0;padding:20px;width:100%;max-width:400px;max-height:60vh;display:flex;flex-direction:column;gap:12px;">
<div style="font-size:15px;font-weight:700;color:#f8fafc;text-align:center;">${t('mp.add_friend')}</div> <div style="font-size:15px;font-weight:700;color:var(--text-primary);text-align:center;">${t('mp.add_friend')}</div>
<div id="add-friends-list" style="overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:6px;"> <div id="add-friends-list" style="overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:6px;">
<div style="text-align:center;color:var(--text-secondary);font-size:13px;">${t('common.loading')}</div> <div style="text-align:center;color:var(--text-secondary);font-size:13px;">${t('common.loading')}</div>
</div> </div>
...@@ -157,11 +157,11 @@ async function showAddMemberPicker(el, groupId) { ...@@ -157,11 +157,11 @@ async function showAddMemberPicker(el, groupId) {
} }
list.innerHTML = friends.map(f => ` list.innerHTML = friends.map(f => `
<div style="display:flex;align-items:center;gap:10px;padding:10px;background:#2a2a4a;border-radius:10px;"> <div style="display:flex;align-items:center;gap:10px;padding:10px;background:var(--bg-hover);border-radius:10px;">
<div style="width:32px;height:32px;border-radius:50%;background:#1a1a2e;display:flex;align-items:center;justify-content:center;overflow:hidden;"> <div style="width:32px;height:32px;border-radius:50%;background:var(--bg-card);display:flex;align-items:center;justify-content:center;overflow:hidden;">
${f.avatar_url ? `<img src="${f.avatar_url}" style="width:32px;height:32px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 14)} ${f.avatar_url ? `<img src="${f.avatar_url}" style="width:32px;height:32px;object-fit:cover;border-radius:50%;">` : emoji('person', '👤', 14)}
</div> </div>
<span style="flex:1;font-size:13px;color:#f8fafc;">${escapeHtml(f.display_name || 'Player')}</span> <span style="flex:1;font-size:13px;color:var(--text-primary);">${escapeHtml(f.display_name || 'Player')}</span>
<button class="btn btn-primary add-one-btn" data-id="${f.id}" style="min-height:28px;padding:4px 12px;font-size:11px;">${t('group.add')}</button> <button class="btn btn-primary add-one-btn" data-id="${f.id}" style="min-height:28px;padding:4px 12px;font-size:11px;">${t('group.add')}</button>
</div> </div>
`).join(''); `).join('');
...@@ -174,7 +174,7 @@ async function showAddMemberPicker(el, groupId) { ...@@ -174,7 +174,7 @@ async function showAddMemberPicker(el, groupId) {
await net.post('groups.php', { action: 'add-member', group_id: groupId, user_id: btn.dataset.id }); await net.post('groups.php', { action: 'add-member', group_id: groupId, user_id: btn.dataset.id });
btn.textContent = t('common.done'); btn.textContent = t('common.done');
btn.style.background = 'transparent'; btn.style.background = 'transparent';
btn.style.color = '#34D399'; btn.style.color = 'var(--success)';
} catch (e) { } catch (e) {
btn.textContent = e.message || t('social.error'); btn.textContent = e.message || t('social.error');
btn.style.color = 'var(--error)'; btn.style.color = 'var(--error)';
......
...@@ -7,9 +7,9 @@ import { emoji } from '../../../core/theme.js'; ...@@ -7,9 +7,9 @@ import { emoji } from '../../../core/theme.js';
export async function mountGroups(el) { export async function mountGroups(el) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;"> <div style="display:flex;flex-direction:column;height:100%;">
<div style="padding:12px 16px;background:#0f0f1e;display:flex;align-items:center;gap:12px;"> <div style="padding:12px 16px;background:var(--bg-panel);display:flex;align-items:center;gap:12px;">
<button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button> <button class="btn btn-secondary" id="back-btn" style="width:36px;height:36px;padding:0;">←</button>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;flex:1;">${t('social.groups_tab')}</h2> <h2 style="font-size:18px;font-weight:700;color:var(--text-primary);flex:1;">${t('social.groups_tab')}</h2>
<button class="btn btn-primary" id="create-btn" style="min-height:34px;padding:6px 14px;font-size:12px;">+ ${t('group.create')}</button> <button class="btn btn-primary" id="create-btn" style="min-height:34px;padding:6px 14px;font-size:12px;">+ ${t('group.create')}</button>
</div> </div>
<div id="groups-list" style="flex:1;overflow-y:auto;padding:12px 16px;"> <div id="groups-list" style="flex:1;overflow-y:auto;padding:12px 16px;">
...@@ -37,15 +37,15 @@ export async function mountGroups(el) { ...@@ -37,15 +37,15 @@ export async function mountGroups(el) {
} }
container.innerHTML = groups.map(g => ` container.innerHTML = groups.map(g => `
<div class="group-card" data-id="${g.id}" style="display:flex;align-items:center;gap:12px;padding:14px;background:#1a1a2e;border-radius:12px;margin-bottom:8px;cursor:pointer;transition:transform 0.1s;"> <div class="group-card" data-id="${g.id}" style="display:flex;align-items:center;gap:12px;padding:14px;background:var(--bg-card);border-radius:12px;margin-bottom:8px;cursor:pointer;transition:transform 0.1s;">
<div style="width:48px;height:48px;border-radius:12px;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;"> <div style="width:48px;height:48px;border-radius:12px;background:var(--bg-hover);display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;">
${g.avatar_url ? `<img src="${g.avatar_url}" style="width:48px;height:48px;object-fit:cover;border-radius:12px;">` : `<span style="font-size:22px;">${emoji('group', '👥', 22)}</span>`} ${g.avatar_url ? `<img src="${g.avatar_url}" style="width:48px;height:48px;object-fit:cover;border-radius:12px;">` : `<span style="font-size:22px;">${emoji('group', '👥', 22)}</span>`}
</div> </div>
<div style="flex:1;min-width:0;"> <div style="flex:1;min-width:0;">
<div style="font-size:14px;font-weight:600;color:#f8fafc;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(g.name)}</div> <div style="font-size:14px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(g.name)}</div>
<div style="font-size:12px;color:#64748b;">${g.member_count || 1} ${t('group.members')}${g.my_role === 'owner' ? t('group.owner') : g.my_role === 'admin' ? t('group.admin') : t('profile.member')}</div> <div style="font-size:12px;color:var(--text-muted);">${g.member_count || 1} ${t('group.members')}${g.my_role === 'owner' ? t('group.owner') : g.my_role === 'admin' ? t('group.admin') : t('profile.member')}</div>
</div> </div>
<span style="color:#64748b;font-size:16px;">←</span> <span style="color:var(--text-muted);font-size:16px;">←</span>
</div> </div>
`).join(''); `).join('');
......
...@@ -32,7 +32,7 @@ function renderNotifications(el, notifications) { ...@@ -32,7 +32,7 @@ function renderNotifications(el, notifications) {
} }
const hasUnread = notifications.some(n => !n.is_read); const hasUnread = notifications.some(n => !n.is_read);
list.innerHTML = (hasUnread ? `<button id="mark-all-read" style="margin-bottom:8px;background:none;border:1px solid rgba(255,255,255,0.1);color:#94a3b8;font-size:12px;padding:6px 14px;border-radius:8px;cursor:pointer;font-family:inherit;">${t('chat.mark_all_read')}</button>` : '') + list.innerHTML = (hasUnread ? `<button id="mark-all-read" style="margin-bottom:8px;background:none;border:1px solid rgba(255,255,255,0.1);color:var(--text-secondary);font-size:12px;padding:6px 14px;border-radius:8px;cursor:pointer;font-family:inherit;">${t('chat.mark_all_read')}</button>` : '') +
notifications.map(n => ` notifications.map(n => `
<div class="card notif-item" data-id="${n.id}" style="padding:var(--s-3);margin-bottom:var(--s-2);opacity:${n.is_read ? '0.7' : '1'};cursor:pointer;"> <div class="card notif-item" data-id="${n.id}" style="padding:var(--s-3);margin-bottom:var(--s-2);opacity:${n.is_read ? '0.7' : '1'};cursor:pointer;">
<div style="font-size:14px;font-weight:${n.is_read ? '400' : '600'};">${n.title_ar || n.title || ''}</div> <div style="font-size:14px;font-weight:${n.is_read ? '400' : '600'};">${n.title_ar || n.title || ''}</div>
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment