Spaces:
Running
Running
Commit
·
dfac64b
1
Parent(s):
0f896f8
Fix auto-training and add dynamic MVP/Championship predictions
Browse files- server.py +6 -0
- src/auto_trainer.py +28 -9
- src/continuous_learner.py +69 -23
- src/prediction_pipeline.py +62 -7
- static/{index-UMhzWs-z.css → assets/index-DJ5mbnNj.css} +1 -1
- static/assets/index-pWNzHy8O.js +0 -0
- static/index-Cqd4wFSK.js +0 -0
- static/index.html +2 -2
server.py
CHANGED
|
@@ -375,6 +375,12 @@ def smart_retrain_model():
|
|
| 375 |
trainer = AutoTrainer()
|
| 376 |
result = trainer.run_training_cycle()
|
| 377 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
if not result.get("success"):
|
| 379 |
logger.warning(f"Training failed: {result.get('error', 'Unknown error')}")
|
| 380 |
# Restore backup
|
|
|
|
| 375 |
trainer = AutoTrainer()
|
| 376 |
result = trainer.run_training_cycle()
|
| 377 |
|
| 378 |
+
# Handle skipped training (no new games) - not an error
|
| 379 |
+
if result.get("skipped"):
|
| 380 |
+
logger.info(f"Training skipped: {result.get('reason', 'No new games')}")
|
| 381 |
+
# Don't restore backup, just return - model is still valid
|
| 382 |
+
return
|
| 383 |
+
|
| 384 |
if not result.get("success"):
|
| 385 |
logger.warning(f"Training failed: {result.get('error', 'Unknown error')}")
|
| 386 |
# Restore backup
|
src/auto_trainer.py
CHANGED
|
@@ -229,7 +229,7 @@ class AutoTrainer:
|
|
| 229 |
Uses ContinuousLearner for the actual training.
|
| 230 |
|
| 231 |
Returns:
|
| 232 |
-
Dict with 'success', 'accuracy', and optionally 'error' keys
|
| 233 |
"""
|
| 234 |
try:
|
| 235 |
from src.continuous_learner import ContinuousLearner
|
|
@@ -240,20 +240,39 @@ class AutoTrainer:
|
|
| 240 |
|
| 241 |
accuracy = result.get("metrics", {}).get("test_accuracy", 0)
|
| 242 |
success = result.get("model_retrained", False)
|
|
|
|
| 243 |
|
| 244 |
if success:
|
| 245 |
logger.info(f"AutoTrainer: Training cycle complete. Accuracy: {accuracy:.2%}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
else:
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
|
|
|
|
|
|
| 254 |
except Exception as e:
|
| 255 |
logger.error(f"AutoTrainer: Training cycle failed: {e}")
|
| 256 |
-
return {"success": False, "error": str(e), "accuracy": 0}
|
| 257 |
|
| 258 |
def force_retrain(self):
|
| 259 |
"""Force an immediate model retrain."""
|
|
|
|
| 229 |
Uses ContinuousLearner for the actual training.
|
| 230 |
|
| 231 |
Returns:
|
| 232 |
+
Dict with 'success', 'accuracy', 'skipped', and optionally 'error' keys
|
| 233 |
"""
|
| 234 |
try:
|
| 235 |
from src.continuous_learner import ContinuousLearner
|
|
|
|
| 240 |
|
| 241 |
accuracy = result.get("metrics", {}).get("test_accuracy", 0)
|
| 242 |
success = result.get("model_retrained", False)
|
| 243 |
+
games_ingested = result.get("games_ingested", 0)
|
| 244 |
|
| 245 |
if success:
|
| 246 |
logger.info(f"AutoTrainer: Training cycle complete. Accuracy: {accuracy:.2%}")
|
| 247 |
+
return {
|
| 248 |
+
"success": True,
|
| 249 |
+
"skipped": False,
|
| 250 |
+
"accuracy": accuracy,
|
| 251 |
+
"games_ingested": games_ingested
|
| 252 |
+
}
|
| 253 |
+
elif games_ingested == 0:
|
| 254 |
+
# No new data - this is NOT an error, just nothing to train on
|
| 255 |
+
logger.info(f"AutoTrainer: No new games available for training")
|
| 256 |
+
return {
|
| 257 |
+
"success": True, # Not a failure
|
| 258 |
+
"skipped": True, # Just skipped
|
| 259 |
+
"reason": "No new games to train on",
|
| 260 |
+
"accuracy": accuracy,
|
| 261 |
+
"games_ingested": 0
|
| 262 |
+
}
|
| 263 |
else:
|
| 264 |
+
# Games ingested but training failed
|
| 265 |
+
logger.warning(f"AutoTrainer: Training failed after ingesting {games_ingested} games")
|
| 266 |
+
return {
|
| 267 |
+
"success": False,
|
| 268 |
+
"skipped": False,
|
| 269 |
+
"error": "Training failed - will retry next cycle",
|
| 270 |
+
"accuracy": accuracy,
|
| 271 |
+
"games_ingested": games_ingested
|
| 272 |
+
}
|
| 273 |
except Exception as e:
|
| 274 |
logger.error(f"AutoTrainer: Training cycle failed: {e}")
|
| 275 |
+
return {"success": False, "skipped": False, "error": str(e), "accuracy": 0}
|
| 276 |
|
| 277 |
def force_retrain(self):
|
| 278 |
"""Force an immediate model retrain."""
|
src/continuous_learner.py
CHANGED
|
@@ -56,8 +56,14 @@ class ContinuousLearner:
|
|
| 56 |
import json
|
| 57 |
if self.checkpoint_file.exists():
|
| 58 |
with open(self.checkpoint_file, 'r') as f:
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
def _save_checkpoint(self, checkpoint: Dict):
|
| 63 |
"""Save checkpoint after processing."""
|
|
@@ -83,19 +89,13 @@ class ContinuousLearner:
|
|
| 83 |
|
| 84 |
# Load checkpoint
|
| 85 |
checkpoint = self._load_checkpoint()
|
| 86 |
-
|
| 87 |
|
| 88 |
-
# Filter to
|
| 89 |
-
|
| 90 |
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
return 0
|
| 94 |
-
|
| 95 |
-
logger.info(f"Found {len(new_games)} new completed games")
|
| 96 |
-
|
| 97 |
-
# Update prediction tracker with results
|
| 98 |
-
for game in new_games:
|
| 99 |
winner = game["home_team"] if game["home_score"] > game["away_score"] else game["away_team"]
|
| 100 |
self.prediction_tracker.update_result(
|
| 101 |
game_id=game["game_id"],
|
|
@@ -103,20 +103,59 @@ class ContinuousLearner:
|
|
| 103 |
home_score=game["home_score"],
|
| 104 |
away_score=game["away_score"]
|
| 105 |
)
|
| 106 |
-
|
| 107 |
|
| 108 |
-
#
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
self._save_checkpoint(checkpoint)
|
| 117 |
|
| 118 |
-
logger.info(f"
|
| 119 |
-
|
| 120 |
|
| 121 |
def _append_games_to_raw_data(self, games: List[Dict]):
|
| 122 |
"""Append new game data to raw parquet files."""
|
|
@@ -262,6 +301,13 @@ class ContinuousLearner:
|
|
| 262 |
metrics = self.retrain_model(incremental=True)
|
| 263 |
results["model_retrained"] = "error" not in metrics
|
| 264 |
results["metrics"] = metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
logger.info("Update cycle complete")
|
| 267 |
return results
|
|
|
|
| 56 |
import json
|
| 57 |
if self.checkpoint_file.exists():
|
| 58 |
with open(self.checkpoint_file, 'r') as f:
|
| 59 |
+
checkpoint = json.load(f)
|
| 60 |
+
# Ensure both fields exist for backward compatibility
|
| 61 |
+
if "trained_game_ids" not in checkpoint:
|
| 62 |
+
checkpoint["trained_game_ids"] = []
|
| 63 |
+
if "prediction_game_ids" not in checkpoint:
|
| 64 |
+
checkpoint["prediction_game_ids"] = checkpoint.get("last_game_ids", [])
|
| 65 |
+
return checkpoint
|
| 66 |
+
return {"last_game_date": None, "last_game_ids": [], "trained_game_ids": [], "prediction_game_ids": []}
|
| 67 |
|
| 68 |
def _save_checkpoint(self, checkpoint: Dict):
|
| 69 |
"""Save checkpoint after processing."""
|
|
|
|
| 89 |
|
| 90 |
# Load checkpoint
|
| 91 |
checkpoint = self._load_checkpoint()
|
| 92 |
+
prediction_ids = set(checkpoint.get("prediction_game_ids", checkpoint.get("last_game_ids", [])))
|
| 93 |
|
| 94 |
+
# Filter to games not yet tracked for predictions
|
| 95 |
+
new_for_predictions = [g for g in final_games if g["game_id"] not in prediction_ids]
|
| 96 |
|
| 97 |
+
# Update prediction tracker with results (for ALL new games)
|
| 98 |
+
for game in new_for_predictions:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
winner = game["home_team"] if game["home_score"] > game["away_score"] else game["away_team"]
|
| 100 |
self.prediction_tracker.update_result(
|
| 101 |
game_id=game["game_id"],
|
|
|
|
| 103 |
home_score=game["home_score"],
|
| 104 |
away_score=game["away_score"]
|
| 105 |
)
|
| 106 |
+
prediction_ids.add(game["game_id"])
|
| 107 |
|
| 108 |
+
# Update prediction checkpoint immediately (tracks which games have been processed for accuracy)
|
| 109 |
+
checkpoint["prediction_game_ids"] = list(prediction_ids)[-100:]
|
| 110 |
+
checkpoint["last_game_ids"] = checkpoint["prediction_game_ids"] # Backward compat
|
| 111 |
+
checkpoint["last_game_date"] = datetime.now().isoformat()
|
| 112 |
+
self._save_checkpoint(checkpoint)
|
| 113 |
|
| 114 |
+
if new_for_predictions:
|
| 115 |
+
logger.info(f"Updated predictions for {len(new_for_predictions)} games")
|
| 116 |
+
|
| 117 |
+
# For TRAINING, check against trained_game_ids (different from prediction tracking)
|
| 118 |
+
trained_ids = set(checkpoint.get("trained_game_ids", []))
|
| 119 |
+
new_for_training = [g for g in final_games if g["game_id"] not in trained_ids]
|
| 120 |
+
|
| 121 |
+
if not new_for_training:
|
| 122 |
+
logger.info("All completed games already trained on")
|
| 123 |
+
return 0
|
| 124 |
+
|
| 125 |
+
logger.info(f"Found {len(new_for_training)} new games for training")
|
| 126 |
+
|
| 127 |
+
# Append new games to raw data for training
|
| 128 |
+
self._append_games_to_raw_data(new_for_training)
|
| 129 |
+
|
| 130 |
+
# Store the game IDs to be marked as trained AFTER successful training
|
| 131 |
+
# This is stored temporarily; mark_games_as_trained() will persist them
|
| 132 |
+
self._pending_training_games = [g["game_id"] for g in new_for_training]
|
| 133 |
+
|
| 134 |
+
logger.info(f"Ingested {len(new_for_training)} new games for training")
|
| 135 |
+
return len(new_for_training)
|
| 136 |
+
|
| 137 |
+
def mark_games_as_trained(self, game_ids: List[str] = None):
|
| 138 |
+
"""
|
| 139 |
+
Mark games as successfully trained on. Only call after training succeeds.
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
game_ids: List of game IDs to mark as trained. If None, uses pending games.
|
| 143 |
+
"""
|
| 144 |
+
if game_ids is None:
|
| 145 |
+
game_ids = getattr(self, '_pending_training_games', [])
|
| 146 |
+
|
| 147 |
+
if not game_ids:
|
| 148 |
+
return
|
| 149 |
+
|
| 150 |
+
checkpoint = self._load_checkpoint()
|
| 151 |
+
trained_ids = set(checkpoint.get("trained_game_ids", []))
|
| 152 |
+
trained_ids.update(game_ids)
|
| 153 |
+
|
| 154 |
+
checkpoint["trained_game_ids"] = list(trained_ids)[-100:] # Keep last 100
|
| 155 |
self._save_checkpoint(checkpoint)
|
| 156 |
|
| 157 |
+
logger.info(f"Marked {len(game_ids)} games as trained")
|
| 158 |
+
self._pending_training_games = []
|
| 159 |
|
| 160 |
def _append_games_to_raw_data(self, games: List[Dict]):
|
| 161 |
"""Append new game data to raw parquet files."""
|
|
|
|
| 301 |
metrics = self.retrain_model(incremental=True)
|
| 302 |
results["model_retrained"] = "error" not in metrics
|
| 303 |
results["metrics"] = metrics
|
| 304 |
+
|
| 305 |
+
# Only mark games as trained AFTER successful training
|
| 306 |
+
if results["model_retrained"]:
|
| 307 |
+
self.mark_games_as_trained()
|
| 308 |
+
logger.info("Training successful - games marked as trained")
|
| 309 |
+
else:
|
| 310 |
+
logger.warning("Training failed - games will be retried next cycle")
|
| 311 |
|
| 312 |
logger.info("Update cycle complete")
|
| 313 |
return results
|
src/prediction_pipeline.py
CHANGED
|
@@ -712,14 +712,69 @@ class PredictionPipeline:
|
|
| 712 |
})
|
| 713 |
|
| 714 |
def get_championship_odds(self, team_df: pd.DataFrame = None) -> pd.DataFrame:
|
| 715 |
-
"""Get current championship odds."""
|
| 716 |
if team_df is None:
|
| 717 |
-
#
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 723 |
|
| 724 |
return self.champ_model.get_top_contenders(team_df)
|
| 725 |
|
|
|
|
| 712 |
})
|
| 713 |
|
| 714 |
def get_championship_odds(self, team_df: pd.DataFrame = None) -> pd.DataFrame:
|
| 715 |
+
"""Get current championship odds using LIVE standings data from NBA API."""
|
| 716 |
if team_df is None:
|
| 717 |
+
# Fetch real current season standings from NBA API
|
| 718 |
+
max_retries = 1 # Fail fast and use fallback
|
| 719 |
+
|
| 720 |
+
for attempt in range(max_retries):
|
| 721 |
+
try:
|
| 722 |
+
from nba_api.stats.endpoints import leaguestandings
|
| 723 |
+
import time
|
| 724 |
+
|
| 725 |
+
time.sleep(0.5)
|
| 726 |
+
|
| 727 |
+
standings = leaguestandings.LeagueStandings(
|
| 728 |
+
season='2025-26',
|
| 729 |
+
timeout=30
|
| 730 |
+
)
|
| 731 |
+
df = standings.get_data_frames()[0]
|
| 732 |
+
|
| 733 |
+
if df.empty:
|
| 734 |
+
logger.warning("NBA API returned empty standings data")
|
| 735 |
+
continue
|
| 736 |
+
|
| 737 |
+
logger.info(f"Got standings for {len(df)} teams from NBA API")
|
| 738 |
+
|
| 739 |
+
# Build team DataFrame with required columns
|
| 740 |
+
team_df = pd.DataFrame({
|
| 741 |
+
'TEAM_ABBREVIATION': df['TeamCity'].apply(lambda x: NBA_TEAMS.get(
|
| 742 |
+
next((tid for tid, abbr in NBA_TEAMS.items()
|
| 743 |
+
if x.lower() in abbr.lower() or abbr.lower() in x.lower()), 0),
|
| 744 |
+
'UNK'
|
| 745 |
+
)),
|
| 746 |
+
'W_PCT': df['WinPCT'].fillna(0.5),
|
| 747 |
+
'NET_RATING': df['NetRating'].fillna(0) if 'NetRating' in df.columns else 0,
|
| 748 |
+
})
|
| 749 |
+
|
| 750 |
+
# If team abbreviations didn't map well, try using TeamAbbreviation directly if available
|
| 751 |
+
if 'TeamAbbreviation' in df.columns:
|
| 752 |
+
team_df['TEAM_ABBREVIATION'] = df['TeamAbbreviation']
|
| 753 |
+
|
| 754 |
+
# Add ELO ratings from our feature generator
|
| 755 |
+
elo_ratings = {}
|
| 756 |
+
for team_id, abbrev in NBA_TEAMS.items():
|
| 757 |
+
elo_ratings[abbrev] = self.feature_gen.elo.get_rating(team_id)
|
| 758 |
+
|
| 759 |
+
team_df['ELO'] = team_df['TEAM_ABBREVIATION'].map(elo_ratings).fillna(1500)
|
| 760 |
+
|
| 761 |
+
logger.info(f"Successfully built championship data for {len(team_df)} teams")
|
| 762 |
+
break
|
| 763 |
+
|
| 764 |
+
except Exception as e:
|
| 765 |
+
logger.warning(f"Championship standings fetch attempt {attempt + 1} failed: {e}")
|
| 766 |
+
continue
|
| 767 |
+
else:
|
| 768 |
+
# All retries failed - use fallback mock data
|
| 769 |
+
logger.warning("Using fallback championship odds data")
|
| 770 |
+
team_df = pd.DataFrame({
|
| 771 |
+
"TEAM_ABBREVIATION": ["OKC", "CLE", "BOS", "DEN", "MEM", "HOU", "NYK", "GSW",
|
| 772 |
+
"MIN", "LAL", "MIL", "PHX", "DAL", "MIA", "SAC", "IND"],
|
| 773 |
+
"W_PCT": [0.74, 0.70, 0.66, 0.62, 0.60, 0.58, 0.56, 0.54,
|
| 774 |
+
0.52, 0.50, 0.48, 0.46, 0.44, 0.42, 0.40, 0.38],
|
| 775 |
+
"NET_RATING": [10.5, 8.2, 7.5, 6.0, 5.5, 4.5, 4.0, 3.5,
|
| 776 |
+
3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0, -0.5]
|
| 777 |
+
})
|
| 778 |
|
| 779 |
return self.champ_model.get_top_contenders(team_df)
|
| 780 |
|
static/{index-UMhzWs-z.css → assets/index-DJ5mbnNj.css}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #000000;--bg-secondary: #0d0d0d;--bg-tertiary: #141414;--bg-card: #0a0a0a;--bg-card-hover: #111111;--bg-elevated: #1a1a1a;--text-primary: #ffffff;--text-secondary: #b3b3b3;--text-muted: #666666;--text-dim: #444444;--accent-primary: #00c8ff;--accent-secondary: #ff6b35;--accent-success: #00d26a;--accent-warning: #ffb800;--accent-danger: #ff3b3b;--gradient-primary: linear-gradient(135deg, #00c8ff 0%, #0099cc 100%);--gradient-secondary: linear-gradient(135deg, #ff6b35 0%, #cc5529 100%);--gradient-subtle: linear-gradient(180deg, rgba(255, 255, 255, .03) 0%, rgba(255, 255, 255, 0) 100%);--border-subtle: 1px solid rgba(255, 255, 255, .06);--border-medium: 1px solid rgba(255, 255, 255, .1);--border-focus: 1px solid var(--accent-primary);--shadow-sm: 0 2px 8px rgba(0, 0, 0, .3);--shadow-md: 0 4px 16px rgba(0, 0, 0, .4);--shadow-lg: 0 8px 32px rgba(0, 0, 0, .5);--shadow-glow: 0 0 20px rgba(0, 200, 255, .15);--radius-xs: 4px;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-10: 40px;--space-12: 48px;--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono: "SF Mono", "Monaco", "Inconsolata", monospace;--transition-fast: .1s ease;--transition-base: .2s ease;--transition-slow: .3s ease}html{font-size:14px}body{font-family:var(--font-sans);background:var(--bg-primary);color:var(--text-primary);line-height:1.5;min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#root{min-height:100vh}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;letter-spacing:-.025em;color:var(--text-primary)}h1{font-size:2rem;font-weight:700}h2{font-size:1.5rem}h3{font-size:1.125rem}h4{font-size:1rem}.text-gradient{background:var(--gradient-primary);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.text-muted{color:var(--text-muted)}.text-secondary{color:var(--text-secondary)}.app-layout{display:flex;min-height:100vh;transition:all var(--transition-slow)}.app-layout.sidebar-collapsed .main-content{margin-left:72px}.sidebar{width:240px;background:var(--bg-secondary);border-right:var(--border-subtle);position:fixed;top:0;left:0;height:100vh;z-index:100;display:flex;flex-direction:column;padding:var(--space-5);transition:all .3s cubic-bezier(.4,0,.2,1);overflow:hidden}.sidebar.collapsed{width:72px;padding:var(--space-4) var(--space-3)}.sidebar.collapsed.hovered{width:240px;padding:var(--space-5);background:#0d0d0df2;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);box-shadow:0 8px 32px #00000080,0 0 0 1px #ffffff14;border-right:1px solid rgba(255,255,255,.1)}.sidebar-header{padding-bottom:var(--space-6);border-bottom:var(--border-subtle);margin-bottom:var(--space-4);display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)}.sidebar-logo{display:flex;align-items:center;gap:var(--space-3);min-width:0;flex:1}.sidebar-logo-icon{width:32px;height:32px;min-width:32px;background:var(--gradient-primary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;transition:transform .3s ease,box-shadow .3s ease}.sidebar:hover .sidebar-logo-icon{transform:scale(1.05);box-shadow:0 0 20px #00c8ff4d}.sidebar-logo-text{font-size:1.125rem;font-weight:700;letter-spacing:-.02em;white-space:nowrap;opacity:1;transition:opacity .2s ease .1s,transform .2s ease}.sidebar.collapsed .sidebar-logo-text{opacity:0;transform:translate(-10px);transition:opacity .1s ease,transform .1s ease}.sidebar.collapsed.hovered .sidebar-logo-text{opacity:1;transform:translate(0);transition:opacity .2s ease .1s,transform .2s ease .1s}.sidebar-toggle{width:28px;height:28px;min-width:28px;border-radius:var(--radius-sm);background:var(--bg-tertiary);border:var(--border-subtle);color:var(--text-secondary);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;opacity:0;transform:scale(.8)}.sidebar:hover .sidebar-toggle{opacity:1;transform:scale(1)}.sidebar-toggle:hover{background:var(--bg-elevated);color:var(--accent-primary);border-color:var(--accent-primary);box-shadow:0 0 12px #00c8ff33}.sidebar-toggle svg{width:16px;height:16px;transition:transform .3s ease}.sidebar-toggle:hover svg{transform:scale(1.1)}.sidebar-nav{flex:1;display:flex;flex-direction:column;gap:var(--space-1);overflow-y:auto;overflow-x:hidden}.nav-section{margin-bottom:var(--space-4)}.nav-section-title{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-dim);padding:var(--space-2) var(--space-3);margin-bottom:var(--space-1);white-space:nowrap;opacity:1;transition:opacity .2s ease}.sidebar.collapsed .nav-section-title{opacity:0;height:0;padding:0;margin:0;overflow:hidden}.sidebar.collapsed.hovered .nav-section-title{opacity:1;height:auto;padding:var(--space-2) var(--space-3);margin-bottom:var(--space-1)}.nav-item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);font-weight:500;font-size:.875rem;position:relative;overflow:hidden}.nav-item:before{content:"";position:absolute;inset:0;background:radial-gradient(circle at left center,rgba(0,200,255,.1) 0%,transparent 70%);opacity:0;transition:opacity .3s ease}.nav-item:hover:before{opacity:1}.nav-item:hover{background:var(--bg-tertiary);color:var(--text-primary);transform:translate(4px)}.nav-item.active{background:#00c8ff1f;color:var(--accent-primary)}.nav-item.active:after{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:3px;height:20px;background:var(--accent-primary);border-radius:0 2px 2px 0;box-shadow:0 0 10px #00c8ff80}.nav-icon{width:20px;height:20px;min-width:20px;opacity:.7;transition:all .2s ease}.nav-item:hover .nav-icon,.nav-item.active .nav-icon{opacity:1;transform:scale(1.1)}.nav-item.active .nav-icon{filter:drop-shadow(0 0 4px rgba(0,200,255,.5))}.nav-text{white-space:nowrap;opacity:1;transition:opacity .2s ease .1s}.sidebar.collapsed .nav-text{opacity:0;width:0;overflow:hidden;transition:opacity .1s ease}.sidebar.collapsed.hovered .nav-text{opacity:1;width:auto;transition:opacity .2s ease .15s}.sidebar.collapsed .nav-item{justify-content:center;padding:var(--space-3)}.sidebar.collapsed.hovered .nav-item{justify-content:flex-start}.sidebar-footer{margin-top:auto;padding-top:var(--space-4);border-top:var(--border-subtle)}.sidebar-footer .btn{justify-content:flex-start}.sidebar.collapsed .sidebar-footer .btn{justify-content:center;padding:var(--space-3)}.sidebar.collapsed.hovered .sidebar-footer .btn{justify-content:flex-start;padding:var(--space-3) var(--space-5)}.sidebar-version{margin-top:var(--space-3);font-size:.6875rem;color:var(--text-dim);text-align:center;opacity:1;transition:opacity .2s ease}.sidebar.collapsed .sidebar-version{opacity:0}.sidebar.collapsed.hovered .sidebar-version{opacity:1}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spinning{animation:spin 1s linear infinite}.main-content{flex:1;margin-left:240px;padding:var(--space-8);min-height:100vh;background:var(--bg-primary);transition:margin-left .3s cubic-bezier(.4,0,.2,1)}.sidebar-collapsed .main-content{margin-left:72px}.card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);transition:all var(--transition-base)}.card:hover{background:var(--bg-card-hover);border-color:#ffffff1a}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)}.card-title{font-size:.875rem;font-weight:600;color:var(--text-secondary)}.team-logo{width:48px;height:48px;object-fit:contain}.team-logo-sm{width:32px;height:32px}.team-logo-lg{width:64px;height:64px}.team-logo-xl{width:80px;height:80px}.game-card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);margin-bottom:var(--space-4);transition:all var(--transition-base)}.game-card:hover{border-color:#ffffff1f;box-shadow:var(--shadow-sm)}.game-card.live{border-color:var(--accent-danger);box-shadow:0 0 0 1px var(--accent-danger)}.game-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4);padding-bottom:var(--space-3);border-bottom:var(--border-subtle)}.game-status{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.game-status.live{color:var(--accent-danger);display:flex;align-items:center;gap:var(--space-2)}.live-dot{width:6px;height:6px;background:var(--accent-danger);border-radius:50%;animation:pulse-dot 2s infinite}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}}.game-time{color:var(--text-muted);font-size:.75rem}.game-matchup{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:var(--space-6)}.team-block{display:flex;flex-direction:column;align-items:center;text-align:center}.team-block.away{align-items:flex-start;text-align:left}.team-block.home{align-items:flex-end;text-align:right}.team-info{display:flex;align-items:center;gap:var(--space-3)}.team-block.home .team-info{flex-direction:row-reverse}.team-details{display:flex;flex-direction:column}.team-name{font-size:1rem;font-weight:600;color:var(--text-primary)}.team-record{font-size:.75rem;color:var(--text-muted)}.team-score{font-size:2rem;font-weight:700;color:var(--text-primary);margin-top:var(--space-2)}.team-probability{font-size:1.5rem;font-weight:700;color:var(--accent-primary);margin-top:var(--space-2)}.game-center{display:flex;flex-direction:column;align-items:center;gap:var(--space-2)}.vs-divider{font-size:.875rem;color:var(--text-dim);font-weight:500}.prediction-indicator{background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);text-align:center}.prediction-label{font-size:.625rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--text-muted);margin-bottom:var(--space-1)}.prediction-pick{font-size:1rem;font-weight:700;color:var(--accent-primary)}.confidence-meter-container{margin-top:var(--space-2)}.confidence-meter{position:relative;display:flex;flex-direction:column;align-items:center}.confidence-meter svg{display:block}.confidence-value{position:absolute;top:28px;font-size:1.25rem;font-weight:700;text-shadow:0 0 10px currentColor}.confidence-label{font-size:.625rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;margin-top:-4px}.stats-comparison{display:flex;flex-direction:column;gap:var(--space-3)}.stat-comparison-row{display:grid;grid-template-columns:1fr auto 1fr;gap:var(--space-3);align-items:center}.stat-bar-container{display:flex;align-items:center;gap:var(--space-2);height:24px}.stat-bar-container.right{justify-content:flex-end}.stat-bar{height:8px;border-radius:4px;transition:width .3s ease}.stat-bar.left{margin-left:auto}.stat-value-label{font-size:.875rem;font-weight:600;min-width:40px}.stat-value-label.left{text-align:right}.stat-value-label.right{text-align:left}.stat-label-center{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);min-width:70px;text-align:center}.recent-form{display:flex;justify-content:center}.form-badges{display:flex;gap:var(--space-1)}.form-badge{width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700;border-radius:4px}.form-badge.win{background:#00d26a26;color:var(--accent-success)}.form-badge.loss{background:#ff3b3b26;color:var(--accent-danger)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--space-4);margin-bottom:var(--space-6)}.stat-card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);display:flex;flex-direction:column}.stat-label{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:var(--space-2)}.stat-value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1}.stat-value.accent{color:var(--accent-primary)}.stat-change{font-size:.75rem;margin-top:var(--space-2);display:flex;align-items:center;gap:var(--space-1)}.stat-change.positive{color:var(--accent-success)}.stat-change.negative{color:var(--accent-danger)}.form-group{margin-bottom:var(--space-4)}.form-label{display:block;font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:var(--space-2)}.form-select{width:100%;padding:var(--space-3) var(--space-4);background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);color:var(--text-primary);font-size:.875rem;font-weight:500;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--space-3) center;transition:all var(--transition-fast)}.form-select:hover{border-color:#ffffff26}.form-select:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 2px #00c8ff1a}.form-input{width:100%;padding:var(--space-3) var(--space-4);background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);color:var(--text-primary);font-size:.875rem;transition:all var(--transition-fast)}.form-input:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 2px #00c8ff1a}.form-input::placeholder{color:var(--text-dim)}.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-3) var(--space-5);border-radius:var(--radius-md);font-size:.875rem;font-weight:600;cursor:pointer;border:none;transition:all var(--transition-fast)}.btn-primary{background:var(--gradient-primary);color:#000}.btn-primary:hover{opacity:.9;box-shadow:var(--shadow-glow)}.btn-secondary{background:var(--bg-elevated);border:var(--border-subtle);color:var(--text-primary)}.btn-secondary:hover{background:var(--bg-tertiary);border-color:#ffffff26}.btn-ghost{background:transparent;color:var(--text-secondary)}.btn-ghost:hover{background:var(--bg-tertiary);color:var(--text-primary)}.btn-icon{width:36px;height:36px;padding:0;border-radius:var(--radius-md)}.btn-lg{padding:var(--space-4) var(--space-6);font-size:1rem}.btn-block{width:100%}.badge{display:inline-flex;align-items:center;padding:var(--space-1) var(--space-2);border-radius:var(--radius-xs);font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.badge-success{background:#00d26a26;color:var(--accent-success)}.badge-warning{background:#ffb80026;color:var(--accent-warning)}.badge-danger{background:#ff3b3b26;color:var(--accent-danger)}.badge-neutral{background:#ffffff14;color:var(--text-secondary)}.confidence-high{background:#00d26a26;color:var(--accent-success)}.confidence-medium{background:#ffb80026;color:var(--accent-warning)}.confidence-low{background:#ff3b3b26;color:var(--accent-danger)}.table-container{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);overflow:hidden}.data-table{width:100%;border-collapse:collapse}.data-table th{padding:var(--space-3) var(--space-4);text-align:left;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);background:var(--bg-secondary);border-bottom:var(--border-subtle)}.data-table td{padding:var(--space-4);border-bottom:var(--border-subtle);font-size:.875rem}.data-table tr:last-child td{border-bottom:none}.data-table tr:hover{background:var(--bg-card-hover)}.table-team{display:flex;align-items:center;gap:var(--space-3)}.loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--space-12)}.spinner{width:32px;height:32px;border:2px solid var(--bg-tertiary);border-top-color:var(--accent-primary);border-radius:50%;animation:spin .8s linear infinite}.loading-text{margin-top:var(--space-4);color:var(--text-muted);font-size:.875rem}@keyframes spin{to{transform:rotate(360deg)}}.skeleton{background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-elevated) 50%,var(--bg-tertiary) 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:var(--radius-sm)}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.empty-state{text-align:center;padding:var(--space-12);color:var(--text-muted)}.empty-state-icon{width:48px;height:48px;margin:0 auto var(--space-4);opacity:.3}.empty-state-title{font-size:1rem;font-weight:600;color:var(--text-secondary);margin-bottom:var(--space-2)}.empty-state-text{font-size:.875rem;color:var(--text-muted)}.page-header{margin-bottom:var(--space-8)}.page-title{font-size:1.75rem;font-weight:700;margin-bottom:var(--space-2)}.page-description{color:var(--text-secondary);font-size:.875rem}.probability-bar-container{margin-top:var(--space-4)}.probability-bar{height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden;display:flex}.probability-fill-away{height:100%;background:var(--accent-secondary);transition:width var(--transition-slow)}.probability-fill-home{height:100%;background:var(--accent-primary);transition:width var(--transition-slow)}.probability-labels{display:flex;justify-content:space-between;margin-top:var(--space-2);font-size:.75rem;color:var(--text-muted)}.section{margin-bottom:var(--space-8)}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)}.section-title{font-size:.875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .3s ease}.animate-slideUp{animation:slideUp .3s ease}.stagger>*:nth-child(1){animation-delay:.05s}.stagger>*:nth-child(2){animation-delay:.1s}.stagger>*:nth-child(3){animation-delay:.15s}.stagger>*:nth-child(4){animation-delay:.2s}.stagger>*:nth-child(5){animation-delay:.25s}@media(max-width:1024px){.sidebar{width:64px;padding:var(--space-3)}.sidebar-logo-text,.nav-section-title,.nav-item span:not(.nav-icon){display:none}.main-content{margin-left:64px;padding:var(--space-5)}}@media(max-width:768px){.sidebar{position:fixed;inset:auto 0 0;width:100%;height:64px;flex-direction:row;padding:var(--space-2);border-right:none;border-top:var(--border-subtle)}.sidebar-header{display:none}.sidebar-nav{flex-direction:row;justify-content:space-around;gap:0;width:100%}.nav-section{display:flex;margin:0}.nav-section-title{display:none}.main-content{margin-left:0;margin-bottom:64px;padding:var(--space-4)}.game-matchup{grid-template-columns:1fr;gap:var(--space-4)}.team-block.away,.team-block.home{align-items:center;text-align:center}.team-block.home .team-info{flex-direction:row}}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-tertiary);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--text-dim)}.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-2{gap:var(--space-2)}.gap-3{gap:var(--space-3)}.gap-4{gap:var(--space-4)}.mt-4{margin-top:var(--space-4)}.mb-4{margin-bottom:var(--space-4)}.text-right{text-align:right}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
|
|
| 1 |
+
*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #000000;--bg-secondary: #0d0d0d;--bg-tertiary: #141414;--bg-card: #0a0a0a;--bg-card-hover: #111111;--bg-elevated: #1a1a1a;--text-primary: #ffffff;--text-secondary: #b3b3b3;--text-muted: #666666;--text-dim: #444444;--accent-primary: #00c8ff;--accent-secondary: #ff6b35;--accent-success: #00d26a;--accent-warning: #ffb800;--accent-danger: #ff3b3b;--gradient-primary: linear-gradient(135deg, #00c8ff 0%, #0099cc 100%);--gradient-secondary: linear-gradient(135deg, #ff6b35 0%, #cc5529 100%);--gradient-subtle: linear-gradient(180deg, rgba(255, 255, 255, .03) 0%, rgba(255, 255, 255, 0) 100%);--border-subtle: 1px solid rgba(255, 255, 255, .06);--border-medium: 1px solid rgba(255, 255, 255, .1);--border-focus: 1px solid var(--accent-primary);--shadow-sm: 0 2px 8px rgba(0, 0, 0, .3);--shadow-md: 0 4px 16px rgba(0, 0, 0, .4);--shadow-lg: 0 8px 32px rgba(0, 0, 0, .5);--shadow-glow: 0 0 20px rgba(0, 200, 255, .15);--radius-xs: 4px;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-10: 40px;--space-12: 48px;--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono: "SF Mono", "Monaco", "Inconsolata", monospace;--transition-fast: .1s ease;--transition-base: .2s ease;--transition-slow: .3s ease}html{font-size:14px}body{font-family:var(--font-sans);background:var(--bg-primary);color:var(--text-primary);line-height:1.5;min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#root{min-height:100vh}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;letter-spacing:-.025em;color:var(--text-primary)}h1{font-size:2rem;font-weight:700}h2{font-size:1.5rem}h3{font-size:1.125rem}h4{font-size:1rem}.text-gradient{background:var(--gradient-primary);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.text-muted{color:var(--text-muted)}.text-secondary{color:var(--text-secondary)}.app-layout{display:flex;min-height:100vh;transition:all var(--transition-slow)}.app-layout.sidebar-collapsed .main-content{margin-left:72px}.sidebar{width:240px;background:var(--bg-secondary);border-right:var(--border-subtle);position:fixed;top:0;left:0;height:100vh;z-index:100;display:flex;flex-direction:column;padding:var(--space-5);transition:all .3s cubic-bezier(.4,0,.2,1);overflow:hidden}.sidebar.collapsed{width:72px;padding:var(--space-4) var(--space-3)}.sidebar.collapsed.hovered{width:240px;padding:var(--space-5);background:#0d0d0df2;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);box-shadow:0 8px 32px #00000080,0 0 0 1px #ffffff14;border-right:1px solid rgba(255,255,255,.1)}.sidebar-header{padding-bottom:var(--space-6);border-bottom:var(--border-subtle);margin-bottom:var(--space-4);display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)}.sidebar-logo{display:flex;align-items:center;gap:var(--space-3);min-width:0;flex:1}.sidebar-logo-icon{width:32px;height:32px;min-width:32px;background:var(--gradient-primary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;transition:transform .3s ease,box-shadow .3s ease}.sidebar:hover .sidebar-logo-icon{transform:scale(1.05);box-shadow:0 0 20px #00c8ff4d}.sidebar-logo-text{font-size:1.125rem;font-weight:700;letter-spacing:-.02em;white-space:nowrap;opacity:1;transition:opacity .2s ease .1s,transform .2s ease}.sidebar.collapsed .sidebar-logo-text{opacity:0;transform:translate(-10px);transition:opacity .1s ease,transform .1s ease}.sidebar.collapsed.hovered .sidebar-logo-text{opacity:1;transform:translate(0);transition:opacity .2s ease .1s,transform .2s ease .1s}.sidebar-toggle{width:28px;height:28px;min-width:28px;border-radius:var(--radius-sm);background:var(--bg-tertiary);border:var(--border-subtle);color:var(--text-secondary);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;opacity:0;transform:scale(.8)}.sidebar:hover .sidebar-toggle{opacity:1;transform:scale(1)}.sidebar-toggle:hover{background:var(--bg-elevated);color:var(--accent-primary);border-color:var(--accent-primary);box-shadow:0 0 12px #00c8ff33}.sidebar-toggle svg{width:16px;height:16px;transition:transform .3s ease}.sidebar-toggle:hover svg{transform:scale(1.1)}.sidebar-nav{flex:1;display:flex;flex-direction:column;gap:var(--space-1);overflow-y:auto;overflow-x:hidden}.nav-section{margin-bottom:var(--space-4)}.nav-section-title{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-dim);padding:var(--space-2) var(--space-3);margin-bottom:var(--space-1);white-space:nowrap;opacity:1;transition:opacity .2s ease}.sidebar.collapsed .nav-section-title{opacity:0;height:0;padding:0;margin:0;overflow:hidden}.sidebar.collapsed.hovered .nav-section-title{opacity:1;height:auto;padding:var(--space-2) var(--space-3);margin-bottom:var(--space-1)}.nav-item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);font-weight:500;font-size:.875rem;position:relative;overflow:hidden}.nav-item:before{content:"";position:absolute;inset:0;background:radial-gradient(circle at left center,rgba(0,200,255,.1) 0%,transparent 70%);opacity:0;transition:opacity .3s ease}.nav-item:hover:before{opacity:1}.nav-item:hover{background:var(--bg-tertiary);color:var(--text-primary);transform:translate(4px)}.nav-item.active{background:#00c8ff1f;color:var(--accent-primary)}.nav-item.active:after{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:3px;height:20px;background:var(--accent-primary);border-radius:0 2px 2px 0;box-shadow:0 0 10px #00c8ff80}.nav-icon{width:20px;height:20px;min-width:20px;opacity:.7;transition:all .2s ease}.nav-item:hover .nav-icon,.nav-item.active .nav-icon{opacity:1;transform:scale(1.1)}.nav-item.active .nav-icon{filter:drop-shadow(0 0 4px rgba(0,200,255,.5))}.nav-text{white-space:nowrap;opacity:1;transition:opacity .2s ease .1s}.sidebar.collapsed .nav-text{opacity:0;width:0;overflow:hidden;transition:opacity .1s ease}.sidebar.collapsed.hovered .nav-text{opacity:1;width:auto;transition:opacity .2s ease .15s}.sidebar.collapsed .nav-item{justify-content:center;padding:var(--space-3)}.sidebar.collapsed.hovered .nav-item{justify-content:flex-start}.sidebar-footer{margin-top:auto;padding-top:var(--space-4);border-top:var(--border-subtle)}.sidebar-footer .btn{justify-content:flex-start}.sidebar.collapsed .sidebar-footer .btn{justify-content:center;padding:var(--space-3)}.sidebar.collapsed.hovered .sidebar-footer .btn{justify-content:flex-start;padding:var(--space-3) var(--space-5)}.sidebar-version{margin-top:var(--space-3);font-size:.6875rem;color:var(--text-dim);text-align:center;opacity:1;transition:opacity .2s ease}.sidebar.collapsed .sidebar-version{opacity:0}.sidebar.collapsed.hovered .sidebar-version{opacity:1}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.spinning{animation:spin 1s linear infinite}.main-content{flex:1;margin-left:240px;padding:var(--space-8);min-height:100vh;background:var(--bg-primary);transition:margin-left .3s cubic-bezier(.4,0,.2,1)}.sidebar-collapsed .main-content{margin-left:72px}.card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);transition:all var(--transition-base)}.card:hover{background:var(--bg-card-hover);border-color:#ffffff1a}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)}.card-title{font-size:.875rem;font-weight:600;color:var(--text-secondary)}.team-logo{width:48px;height:48px;object-fit:contain}.team-logo-sm{width:32px;height:32px}.team-logo-lg{width:64px;height:64px}.team-logo-xl{width:80px;height:80px}.game-card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);margin-bottom:var(--space-4);transition:all var(--transition-base)}.game-card:hover{border-color:#ffffff1f;box-shadow:var(--shadow-sm)}.game-card.live{border-color:var(--accent-danger);box-shadow:0 0 0 1px var(--accent-danger)}.game-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4);padding-bottom:var(--space-3);border-bottom:var(--border-subtle)}.game-status{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.game-status.live{color:var(--accent-danger);display:flex;align-items:center;gap:var(--space-2)}.live-dot{width:6px;height:6px;background:var(--accent-danger);border-radius:50%;animation:pulse-dot 2s infinite}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}}.game-time{color:var(--text-muted);font-size:.75rem}.game-matchup{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:var(--space-6)}.team-block{display:flex;flex-direction:column;align-items:center;text-align:center}.team-block.away{align-items:flex-start;text-align:left}.team-block.home{align-items:flex-end;text-align:right}.team-info{display:flex;align-items:center;gap:var(--space-3)}.team-block.home .team-info{flex-direction:row-reverse}.team-details{display:flex;flex-direction:column}.team-name{font-size:1rem;font-weight:600;color:var(--text-primary)}.team-record{font-size:.75rem;color:var(--text-muted)}.team-score{font-size:2rem;font-weight:700;color:var(--text-primary);margin-top:var(--space-2)}.team-probability{font-size:1.5rem;font-weight:700;color:var(--accent-primary);margin-top:var(--space-2)}.game-center{display:flex;flex-direction:column;align-items:center;gap:var(--space-2)}.vs-divider{font-size:.875rem;color:var(--text-dim);font-weight:500}.prediction-indicator{background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);text-align:center}.prediction-label{font-size:.625rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--text-muted);margin-bottom:var(--space-1)}.prediction-pick{font-size:1rem;font-weight:700;color:var(--accent-primary)}.confidence-meter-container{margin-top:var(--space-2)}.confidence-meter{position:relative;display:flex;flex-direction:column;align-items:center}.confidence-meter svg{display:block}.confidence-value{position:absolute;top:28px;font-size:1.25rem;font-weight:700;text-shadow:0 0 10px currentColor}.confidence-label{font-size:.625rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;margin-top:-4px}.stats-comparison{display:flex;flex-direction:column;gap:var(--space-3)}.stat-comparison-row{display:grid;grid-template-columns:1fr auto 1fr;gap:var(--space-3);align-items:center}.stat-bar-container{display:flex;align-items:center;gap:var(--space-2);height:24px}.stat-bar-container.right{justify-content:flex-end}.stat-bar{height:8px;border-radius:4px;transition:width .3s ease}.stat-bar.left{margin-left:auto}.stat-value-label{font-size:.875rem;font-weight:600;min-width:40px}.stat-value-label.left{text-align:right}.stat-value-label.right{text-align:left}.stat-label-center{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);min-width:70px;text-align:center}.recent-form{display:flex;justify-content:center}.form-badges{display:flex;gap:var(--space-1)}.form-badge{width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:.625rem;font-weight:700;border-radius:4px}.form-badge.win{background:#00d26a26;color:var(--accent-success)}.form-badge.loss{background:#ff3b3b26;color:var(--accent-danger)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--space-4);margin-bottom:var(--space-6)}.stat-card{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);padding:var(--space-5);display:flex;flex-direction:column}.stat-label{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:var(--space-2)}.stat-value{font-size:2rem;font-weight:700;color:var(--text-primary);line-height:1}.stat-value.accent{color:var(--accent-primary)}.stat-change{font-size:.75rem;margin-top:var(--space-2);display:flex;align-items:center;gap:var(--space-1)}.stat-change.positive{color:var(--accent-success)}.stat-change.negative{color:var(--accent-danger)}.form-group{margin-bottom:var(--space-4)}.form-label{display:block;font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:var(--space-2)}.form-select{width:100%;padding:var(--space-3) var(--space-4);background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);color:var(--text-primary);font-size:.875rem;font-weight:500;cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--space-3) center;transition:all var(--transition-fast)}.form-select:hover{border-color:#ffffff26}.form-select:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 2px #00c8ff1a}.form-input{width:100%;padding:var(--space-3) var(--space-4);background:var(--bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);color:var(--text-primary);font-size:.875rem;transition:all var(--transition-fast)}.form-input:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 2px #00c8ff1a}.form-input::placeholder{color:var(--text-dim)}.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-3) var(--space-5);border-radius:var(--radius-md);font-size:.875rem;font-weight:600;cursor:pointer;border:none;transition:all var(--transition-fast)}.btn-primary{background:var(--gradient-primary);color:#000}.btn-primary:hover{opacity:.9;box-shadow:var(--shadow-glow)}.btn-secondary{background:var(--bg-elevated);border:var(--border-subtle);color:var(--text-primary)}.btn-secondary:hover{background:var(--bg-tertiary);border-color:#ffffff26}.btn-ghost{background:transparent;color:var(--text-secondary)}.btn-ghost:hover{background:var(--bg-tertiary);color:var(--text-primary)}.btn-icon{width:36px;height:36px;padding:0;border-radius:var(--radius-md)}.btn-lg{padding:var(--space-4) var(--space-6);font-size:1rem}.btn-block{width:100%}.badge{display:inline-flex;align-items:center;padding:var(--space-1) var(--space-2);border-radius:var(--radius-xs);font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.badge-success{background:#00d26a26;color:var(--accent-success)}.badge-warning{background:#ffb80026;color:var(--accent-warning)}.badge-danger{background:#ff3b3b26;color:var(--accent-danger)}.badge-neutral{background:#ffffff14;color:var(--text-secondary)}.confidence-high{background:#00d26a26;color:var(--accent-success)}.confidence-medium{background:#ffb80026;color:var(--accent-warning)}.confidence-low{background:#ff3b3b26;color:var(--accent-danger)}.table-container{background:var(--bg-card);border:var(--border-subtle);border-radius:var(--radius-lg);overflow:hidden}.data-table{width:100%;border-collapse:collapse}.data-table th{padding:var(--space-3) var(--space-4);text-align:left;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);background:var(--bg-secondary);border-bottom:var(--border-subtle)}.data-table td{padding:var(--space-4);border-bottom:var(--border-subtle);font-size:.875rem}.data-table tr:last-child td{border-bottom:none}.data-table tr:hover{background:var(--bg-card-hover)}.table-team{display:flex;align-items:center;gap:var(--space-3)}.loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--space-12)}.spinner{width:32px;height:32px;border:2px solid var(--bg-tertiary);border-top-color:var(--accent-primary);border-radius:50%;animation:spin .8s linear infinite}.loading-text{margin-top:var(--space-4);color:var(--text-muted);font-size:.875rem}@keyframes spin{to{transform:rotate(360deg)}}.skeleton{background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-elevated) 50%,var(--bg-tertiary) 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:var(--radius-sm)}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.empty-state{text-align:center;padding:var(--space-12);color:var(--text-muted)}.empty-state-icon{width:48px;height:48px;margin:0 auto var(--space-4);opacity:.3}.empty-state-title{font-size:1rem;font-weight:600;color:var(--text-secondary);margin-bottom:var(--space-2)}.empty-state-text{font-size:.875rem;color:var(--text-muted)}.page-header{margin-bottom:var(--space-8)}.page-title{font-size:1.75rem;font-weight:700;margin-bottom:var(--space-2)}.page-description{color:var(--text-secondary);font-size:.875rem}.probability-bar-container{margin-top:var(--space-4)}.probability-bar{height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden;display:flex}.probability-fill-away{height:100%;background:var(--accent-secondary);transition:width var(--transition-slow)}.probability-fill-home{height:100%;background:var(--accent-primary);transition:width var(--transition-slow)}.probability-labels{display:flex;justify-content:space-between;margin-top:var(--space-2);font-size:.75rem;color:var(--text-muted)}.section{margin-bottom:var(--space-8)}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)}.section-title{font-size:.875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.animate-fadeIn{animation:fadeIn .3s ease}.animate-slideUp{animation:slideUp .3s ease}.stagger>*:nth-child(1){animation-delay:.05s}.stagger>*:nth-child(2){animation-delay:.1s}.stagger>*:nth-child(3){animation-delay:.15s}.stagger>*:nth-child(4){animation-delay:.2s}.stagger>*:nth-child(5){animation-delay:.25s}@media(max-width:1024px){.sidebar{width:64px;padding:var(--space-3)}.sidebar-logo-text,.nav-section-title,.nav-item span:not(.nav-icon){display:none}.main-content{margin-left:64px;padding:var(--space-5)}}@media(max-width:768px){.sidebar{position:fixed;inset:auto 0 0;width:100%;height:64px;flex-direction:row;padding:var(--space-2);border-right:none;border-top:var(--border-subtle)}.sidebar-header{display:none}.sidebar-nav{flex-direction:row;justify-content:space-around;gap:0;width:100%}.nav-section{display:flex;margin:0}.nav-section-title{display:none}.main-content{margin-left:0;margin-bottom:64px;padding:var(--space-4)}.game-matchup{grid-template-columns:1fr;gap:var(--space-4)}.team-block.away,.team-block.home{align-items:center;text-align:center}.team-block.home .team-info{flex-direction:row}}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-tertiary);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--text-dim)}.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-2{gap:var(--space-2)}.gap-3{gap:var(--space-3)}.gap-4{gap:var(--space-4)}.mt-4{margin-top:var(--space-4)}.mb-4{margin-bottom:var(--space-4)}.text-right{text-align:right}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.analytics-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:var(--space-6)}.loading-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.loading-cell{width:20px;height:20px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));border-radius:4px;animation:loadingPulse 1.5s ease-in-out infinite}@keyframes loadingPulse{0%,to{opacity:.2;transform:scale(.8)}50%{opacity:1;transform:scale(1)}}.analytics-header{position:relative;padding:var(--space-8) var(--space-6);margin-bottom:var(--space-6);border-radius:var(--radius-xl);overflow:hidden;background:linear-gradient(135deg,#00c8ff1a,#a78bfa1a);border:1px solid rgba(255,255,255,.1)}.header-background{position:absolute;inset:0;overflow:hidden}.neural-grid{position:absolute;inset:0}.neural-node{position:absolute;width:4px;height:4px;background:var(--accent-primary);border-radius:50%;opacity:.3;animation:neuralPulse 3s ease-in-out infinite}@keyframes neuralPulse{0%,to{opacity:.1;transform:scale(1)}50%{opacity:.6;transform:scale(1.5)}}.header-content{position:relative;z-index:1}.analytics-title{display:flex;align-items:center;gap:var(--space-3);font-size:2rem;font-weight:700;margin-bottom:var(--space-2)}.title-icon{font-size:1.5rem}.title-badge{font-size:.65rem;font-weight:600;padding:4px 10px;background:linear-gradient(135deg,#00c8ff,#a78bfa);border-radius:20px;text-transform:uppercase;letter-spacing:.1em}.analytics-subtitle{color:var(--text-secondary);font-size:1rem}.analytics-tabs{display:flex;gap:var(--space-2);margin-bottom:var(--space-6);padding:var(--space-2);background:#0000004d;border-radius:var(--radius-lg);border:1px solid rgba(255,255,255,.05)}.analytics-tab{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3) var(--space-5);background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-secondary);font-size:.875rem;font-weight:500;cursor:pointer;transition:all .3s ease}.analytics-tab:hover{background:#ffffff0d;color:var(--text-primary)}.analytics-tab.active{background:linear-gradient(135deg,#00c8ff33,#a78bfa33);color:var(--text-primary);box-shadow:0 0 20px #00c8ff33}.hero-stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--space-4);margin-bottom:var(--space-6)}.analytics-stat-card{position:relative;padding:var(--space-5);background:#0006;border:1px solid rgba(255,255,255,.1);border-radius:var(--radius-lg);overflow:hidden;opacity:0;transform:translateY(20px);transition:all .5s ease}.analytics-stat-card.visible{opacity:1;transform:translateY(0)}.analytics-stat-card:hover{border-color:#fff3;transform:translateY(-2px)}.stat-glow{position:absolute;top:0;left:0;width:100%;height:3px;opacity:.8}.stat-icon{font-size:1.5rem;margin-bottom:var(--space-2)}.analytics-stat-card .stat-value{font-size:2rem;font-weight:700;margin-bottom:var(--space-1);text-shadow:0 0 30px currentColor}.analytics-stat-card .stat-label{font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em}.stat-pulse{position:absolute;bottom:10px;right:10px;width:8px;height:8px;border-radius:50%;animation:statPulse 2s ease-in-out infinite}@keyframes statPulse{0%,to{opacity:.3;transform:scale(1)}50%{opacity:.8;transform:scale(1.2)}}.charts-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:var(--space-6);margin-bottom:var(--space-6)}.chart-card{background:#0006;border:1px solid rgba(255,255,255,.08);border-radius:var(--radius-xl);padding:var(--space-5);transition:all .3s ease}.chart-card:hover{border-color:#ffffff26;box-shadow:0 8px 32px #0000004d}.chart-card.full-width{grid-column:1 / -1}.glass-effect{backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background:#0a0a0a99}.chart-header{display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-4);padding-bottom:var(--space-3);border-bottom:1px solid rgba(255,255,255,.05)}.chart-icon{font-size:1.25rem}.chart-header h3{flex:1;font-size:1rem;font-weight:600;color:var(--text-primary)}.chart-subtitle{font-size:.75rem;color:var(--text-muted)}.chart-badge{font-size:.625rem;font-weight:600;padding:3px 8px;border-radius:10px;background:#ffffff1a;text-transform:uppercase;letter-spacing:.05em}.chart-badge.live{background:#00d26a33;color:#00d26a;animation:livePulse 2s ease-in-out infinite}@keyframes livePulse{0%,to{opacity:.7}50%{opacity:1}}.chart-legend{display:flex;gap:var(--space-4);font-size:.75rem}.legend-item{display:flex;align-items:center;gap:var(--space-2);color:var(--text-muted)}.legend-item span:first-child{width:12px;height:3px;border-radius:2px}.chart-body{position:relative}.gauges-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-4);padding:var(--space-4)}.gauge-container{display:flex;flex-direction:column;align-items:center}.gauge-svg{width:120px;height:70px}.gauge-label{font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;margin-top:var(--space-1)}.streak-display{padding:var(--space-4)}.current-streak{display:flex;justify-content:center;margin-bottom:var(--space-5)}.streak-badge{display:flex;flex-direction:column;align-items:center;padding:var(--space-5) var(--space-8);border-radius:var(--radius-xl);background:linear-gradient(135deg,#00d26a33,#00d26a0d);border:2px solid rgba(0,210,106,.3)}.streak-badge.loss{background:linear-gradient(135deg,#ff3b3b33,#ff3b3b0d);border-color:#ff3b3b4d}.streak-count{font-size:3rem;font-weight:700;line-height:1}.streak-badge.win .streak-count{color:#00d26a}.streak-badge.loss .streak-count{color:#ff3b3b}.streak-type{font-size:1rem;font-weight:600;margin:var(--space-1) 0}.streak-badge.win .streak-type{color:#00d26a}.streak-badge.loss .streak-type{color:#ff3b3b}.streak-label{font-size:.65rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.1em}.streak-records{display:flex;justify-content:center;gap:var(--space-8);margin-bottom:var(--space-5)}.streak-record{text-align:center}.record-value{display:block;font-size:1.5rem;font-weight:700}.streak-record.win .record-value{color:#00d26a}.streak-record.loss .record-value{color:#ff3b3b}.record-label{font-size:.65rem;color:var(--text-muted);text-transform:uppercase}.streak-history{display:flex;align-items:flex-end;justify-content:center;gap:4px;height:70px;padding-top:var(--space-2)}.streak-bar{width:12px;border-radius:4px 4px 0 0;transition:all .3s ease}.streak-bar.win{background:linear-gradient(to top,#00d26a,#4ade80)}.streak-bar.loss{background:linear-gradient(to top,#ff3b3b,#ff6b6b)}.streak-bar:hover{transform:scaleY(1.1);filter:brightness(1.2)}.custom-tooltip{background:#000000e6;border:1px solid rgba(255,255,255,.2);border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);box-shadow:0 8px 32px #00000080}.tooltip-label{font-weight:600;margin-bottom:var(--space-2);color:var(--text-primary)}.custom-tooltip p{font-size:.825rem;margin:2px 0}.quick-stats-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-4);padding:var(--space-4)}.hexagon-stat{display:flex;justify-content:center}.hexagon-svg{width:100px;height:115px}.team-stats-table{overflow-x:auto}.team-stats-table table{width:100%;border-collapse:collapse}.team-stats-table th,.team-stats-table td{padding:var(--space-3) var(--space-4);text-align:left;border-bottom:1px solid rgba(255,255,255,.05)}.team-stats-table th{font-size:.7rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em}.team-stats-table td{font-size:.875rem}.team-stats-table tbody tr:hover{background:#ffffff08}.team-name-cell{display:flex;align-items:center;gap:var(--space-3)}.team-rank{width:24px;height:24px;display:flex;align-items:center;justify-content:center;background:#ffffff1a;border-radius:50%;font-size:.75rem;font-weight:600}.team-abbrev{font-weight:600}.accuracy-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-weight:600;font-size:.8rem}.accuracy-badge.high{background:#00d26a33;color:#00d26a}.accuracy-badge.medium{background:#ffb80033;color:#ffb800}.accuracy-badge.low{background:#ff3b3b33;color:#ff3b3b}.heatmap-container{padding:var(--space-4)}.heatmap-header,.heatmap-row{display:grid;grid-template-columns:60px repeat(7,1fr);gap:4px;margin-bottom:4px}.heatmap-day-label,.heatmap-team-label{display:flex;align-items:center;justify-content:center;font-size:.7rem;color:var(--text-muted);text-transform:uppercase}.heatmap-label{width:60px}.heatmap-cell{aspect-ratio:1;display:flex;align-items:center;justify-content:center;border-radius:4px;font-size:.7rem;font-weight:600;color:#000c;transition:all .2s ease}.heatmap-cell:hover{transform:scale(1.1);z-index:1}.heatmap-legend{display:flex;align-items:center;justify-content:center;gap:var(--space-3);margin-top:var(--space-4);padding-top:var(--space-4);border-top:1px solid rgba(255,255,255,.05)}.heatmap-legend .legend-label{font-size:.7rem;color:var(--text-muted)}.legend-gradient{width:120px;height:12px;background:linear-gradient(to right,#ff3b3b,#ffb800,#00d26a);border-radius:6px}.predictions-table{overflow-x:auto}.predictions-table table{width:100%;border-collapse:collapse}.predictions-table th,.predictions-table td{padding:var(--space-3) var(--space-4);text-align:left}.predictions-table th{font-size:.7rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid rgba(255,255,255,.1)}.predictions-table td{font-size:.875rem;border-bottom:1px solid rgba(255,255,255,.03)}.predictions-table tbody tr{transition:all .2s ease}.predictions-table tbody tr:hover{background:#ffffff08}.predictions-table tbody tr.correct{border-left:3px solid #00d26a}.predictions-table tbody tr.incorrect{border-left:3px solid #ff3b3b}.date-cell{color:var(--text-muted);font-size:.8rem}.matchup-cell{font-weight:500}.prediction-badge{display:inline-block;padding:2px 10px;background:#00c8ff26;border:1px solid rgba(0,200,255,.3);border-radius:var(--radius-sm);font-weight:600;font-size:.8rem;color:var(--accent-primary)}.confidence-cell{display:flex;align-items:center;gap:var(--space-2)}.confidence-bar-mini{width:60px;height:6px;background:#ffffff1a;border-radius:3px;overflow:hidden}.confidence-fill{height:100%;border-radius:3px;transition:width .5s ease}.result-badge{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;font-weight:700}.result-badge.win{background:#00d26a33;color:#00d26a}.result-badge.loss{background:#ff3b3b33;color:#ff3b3b}@media(max-width:768px){.analytics-header{padding:var(--space-5)}.analytics-title{font-size:1.5rem}.analytics-tabs{overflow-x:auto;-webkit-overflow-scrolling:touch}.analytics-tab{padding:var(--space-2) var(--space-3);font-size:.8rem;white-space:nowrap}.hero-stats-grid{grid-template-columns:repeat(2,1fr)}.charts-row{grid-template-columns:1fr}.gauges-grid{grid-template-columns:repeat(2,1fr)}.heatmap-container{overflow-x:auto}}
|
static/assets/index-pWNzHy8O.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
static/index-Cqd4wFSK.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
static/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 11 |
-
<script type="module" crossorigin src="/assets/index-
|
| 12 |
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="root"></div>
|
|
|
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 11 |
+
<script type="module" crossorigin src="/assets/index-pWNzHy8O.js"></script>
|
| 12 |
+
<link rel="stylesheet" crossorigin href="/assets/index-DJ5mbnNj.css">
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="root"></div>
|