durgexh commited on
Commit
d8d4264
·
verified ·
1 Parent(s): a5c7ef2

Upload 9 files

Browse files

all files for ipl-prediction site

Files changed (9) hide show
  1. Dockerfile +20 -0
  2. app.js +133 -0
  3. history.html +51 -0
  4. history.js +5 -0
  5. index.html +146 -0
  6. main.py +36 -0
  7. requirements.txt +4 -0
  8. scraper.py +45 -0
  9. style.css +333 -0
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.10-slim
3
+
4
+ # Set the working directory directly in the Hugging Face container
5
+ WORKDIR /app
6
+
7
+ # Copy the requirements file first to build the environment
8
+ COPY requirements.txt .
9
+
10
+ # Install the Python hacker packages we used
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Copy all the HTML, CSS, JS, and Python scripts into the container
14
+ COPY . .
15
+
16
+ # Expose the standard port used by Hugging Face Spaces
17
+ EXPOSE 7860
18
+
19
+ # Command to run the FastAPI app via Uvicorn
20
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
app.js ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Real-time Mock Engine Outcomes
2
+ const liveOutcomes = [
3
+ { delivery: "Inswinger, Good Length", outcome: "Defends - No Run", probD: 82, probO: 76 },
4
+ { delivery: "Yorker, Middle & Leg", outcome: "Squeezes to Point - 1 Run", probD: 65, probO: 58 },
5
+ { delivery: "Bouncer, At the Body", outcome: "Hook Shot - SIX over Fine Leg!!!", probD: 45, probO: 32 },
6
+ { delivery: "Slower Ball, Outside Off", outcome: "Swings and Misses - Dot Ball", probD: 78, probO: 81 },
7
+ { delivery: "Outswinger, Full Length", outcome: "Cover Drive - FOUR Runs!", probD: 60, probO: 55 },
8
+ { delivery: "Wide Yorker", outcome: "Slashes to Third Man - FOUR Runs", probD: 53, probO: 44 }
9
+ ];
10
+
11
+ document.addEventListener('DOMContentLoaded', () => {
12
+ // Top Bar Elements
13
+ const statusBadge = document.getElementById('matchStatusBadge');
14
+ const team1El = document.getElementById('team1Text');
15
+ const team2El = document.getElementById('team2Text');
16
+ const scoreEl = document.getElementById('liveScoreText');
17
+ const overEl = document.getElementById('liveOverText');
18
+ const apiIndicator = document.getElementById('apiStatus');
19
+
20
+ // Context Panels
21
+ const venueEl = document.getElementById('venueText');
22
+ const subEl = document.getElementById('matchSubtitle');
23
+
24
+ // UI Panes
25
+ const liveBox = document.getElementById('livePredictorBox');
26
+ const upcBox = document.getElementById('upcomingPredictorBox');
27
+ const titleEl = document.getElementById('predictionTitle');
28
+
29
+ // Instantly bypass external API and load reliable presentation data
30
+ apiIndicator.innerText = "LOCAL ENGINE: ACTIVE";
31
+ apiIndicator.style.color = "var(--accent-green)";
32
+
33
+ // Set Presentation Match Data directly
34
+ const t1 = "LSG";
35
+ const t2 = "KKR";
36
+
37
+ // Choose mode: true for Live Dashboard, false for Upcoming Dashboard
38
+ const IS_LIVE = true;
39
+
40
+ if (IS_LIVE) {
41
+ statusBadge.innerText = "🔥 MATCH IN PROGRESS";
42
+ statusBadge.style.color = "#ef4444";
43
+ statusBadge.style.borderColor = "#ef4444";
44
+ statusBadge.style.backgroundColor = "rgba(239, 68, 68, 0.1)";
45
+
46
+ team1El.innerText = t1;
47
+ team2El.innerText = t2;
48
+ venueEl.innerText = "Ekana Sports City";
49
+ subEl.innerText = "IPL Presentation Match";
50
+
51
+ scoreEl.innerText = "142 / 4";
52
+ overEl.innerText = "15.2 Overs";
53
+ scoreEl.classList.remove('hidden');
54
+ overEl.classList.remove('hidden');
55
+
56
+ document.getElementById('contextLabel1').innerText = "Striker";
57
+ document.getElementById('contextValue1').innerText = "KL Rahul (58* off 42)";
58
+ document.getElementById('contextLabel2').innerText = "Bowler";
59
+ document.getElementById('contextValue2').innerText = "Sunil Narine (1/24 in 3 ov)";
60
+ document.getElementById('pitchText').innerText = "Slow, Turning Track";
61
+
62
+ titleEl.innerText = "LIVE BALL PREDICTIONS ACTIVE";
63
+ liveBox.classList.remove('hidden');
64
+ upcBox.classList.add('hidden');
65
+
66
+ cycleLivePredictions();
67
+ } else {
68
+ statusBadge.innerText = "⏳ UPCOMING FIXTURE";
69
+ statusBadge.style.color = "var(--accent-cyan)";
70
+
71
+ team1El.innerText = t1;
72
+ team2El.innerText = t2;
73
+ venueEl.innerText = "Ekana Sports City";
74
+ subEl.innerText = "Upcoming Schedule";
75
+
76
+ document.getElementById('pitchText').innerText = "AI awaiting toss data...";
77
+
78
+ liveBox.classList.add('hidden');
79
+ upcBox.classList.remove('hidden');
80
+ titleEl.innerText = "UPCOMING MATCH AI OVERVIEW";
81
+
82
+ const p1 = document.getElementById('winProbBar');
83
+ const fT = document.getElementById('favoredTeamText');
84
+ const pT = document.getElementById('winProbText');
85
+
86
+ const winProb = 64;
87
+ fT.innerText = t1;
88
+ pT.innerText = `${winProb}% Probability to Win`;
89
+
90
+ setTimeout(() => { p1.style.width = `${winProb}%`; }, 300);
91
+
92
+ document.getElementById('factor1').innerText = `AI historical data shows ${t1} has a +15% dominance matrix on home turf.`;
93
+ document.getElementById('factor2').innerText = `Weather and ground toss dynamics inherently favor the chasing team by 14%.`;
94
+ }
95
+
96
+ function cycleLivePredictions() {
97
+ const dOut = document.getElementById('deliveryOut');
98
+ const oOut = document.getElementById('outcomeOut');
99
+ const dProbBar = document.getElementById('delProbBar');
100
+ const oProbBar = document.getElementById('outProbBar');
101
+ const dProbText = document.getElementById('delProbText');
102
+ const oProbText = document.getElementById('outProbText');
103
+
104
+ // Let's increment balls visually
105
+ let curScore = 142;
106
+ let balls = 2; // starting 15.2
107
+
108
+ setInterval(() => {
109
+ dProbBar.style.width = '0%';
110
+ oProbBar.style.width = '0%';
111
+ dOut.innerHTML = "<span style='color: var(--text-muted); font-size: 1.2rem'>AI Re-calculating Next Ball...</span>";
112
+ oOut.innerHTML = "...";
113
+
114
+ setTimeout(() => {
115
+ const p = liveOutcomes[Math.floor(Math.random() * liveOutcomes.length)];
116
+
117
+ // update fake score
118
+ balls++;
119
+ if (balls > 6) { balls = 1; }
120
+ overEl.innerText = `15.${balls} Overs`;
121
+
122
+ dOut.innerText = p.delivery;
123
+ oOut.innerText = p.outcome;
124
+
125
+ dProbBar.style.width = `${p.probD}%`;
126
+ oProbBar.style.width = `${p.probO}%`;
127
+
128
+ dProbText.innerText = `${p.probD}% Probability`;
129
+ oProbText.innerText = `${p.probO}% Probability`;
130
+ }, 1000);
131
+ }, 5000); // Cycles continuously every 5 seconds
132
+ }
133
+ });
history.html ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Match History | IPL AI Predictor</title>
8
+ <link rel="stylesheet" href="style.css">
9
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
10
+ </head>
11
+
12
+ <body>
13
+ <nav class="navbar">
14
+ <div class="logo">
15
+ <span class="ai-glow">Predict</span>IPL
16
+ </div>
17
+ <ul class="nav-links">
18
+ <li><a href="index.html">Live Prediction</a></li>
19
+ <li><a href="history.html" class="active">Match History</a></li>
20
+ </ul>
21
+ </nav>
22
+
23
+ <main class="dashboard history-dashboard">
24
+ <header class="section-header">
25
+ <h2>AI Prediction History</h2>
26
+ <p>Session initialized today. Awaiting current match completion.</p>
27
+ </header>
28
+
29
+ <section class="history-container" style="display: flex; justify-content: center; width: 100%;">
30
+ <div class="ball-history-panel panel"
31
+ style="width: 100%; max-width: 800px; text-align: center; padding: 4rem;">
32
+ <h3 style="margin-bottom: 1.5rem; color: var(--accent-cyan); font-size: 1.5rem; border: none;">
33
+ Initialize Sequence: Day 1</h3>
34
+ <p style="color: var(--text-main); font-size: 1.1rem; line-height: 1.8;">
35
+ The prediction model's memory state was wiped and initialized <strong>today</strong>.<br>
36
+ There are currently <span style="color: #ef4444; font-weight: bold;">0</span> past matches recorded
37
+ in the system.<br><br>
38
+ </p>
39
+ <p style="color: var(--text-muted); font-size: 0.95rem;">
40
+ Live ball-by-ball accuracy tracking will begin saving to the database dynamically as soon as today's
41
+ live predictions conclude.
42
+ </p>
43
+ <div class="spinner" style="margin: 3rem auto; border-top-color: var(--accent-green);"></div>
44
+ </div>
45
+ </section>
46
+ </main>
47
+
48
+ <script src="history.js"></script>
49
+ </body>
50
+
51
+ </html>
history.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ // Emptied because the model was initialized today
2
+ // Real history will be generated after today's logic.
3
+ document.addEventListener('DOMContentLoaded', () => {
4
+ // Memory wiped
5
+ });
index.html ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>IPL AI Predictor | Live Match</title>
8
+ <link rel="stylesheet" href="style.css">
9
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
10
+ </head>
11
+
12
+ <body>
13
+ <nav class="navbar">
14
+ <div class="logo">
15
+ <span class="ai-glow">Predict</span>IPL
16
+ </div>
17
+ <ul class="nav-links">
18
+ <li><a href="index.html" class="active">Live Prediction</a></li>
19
+ <li><a href="history.html">Match History</a></li>
20
+ </ul>
21
+ </nav>
22
+
23
+ <main class="dashboard">
24
+ <header class="match-header">
25
+ <div class="team" id="team1Text">--</div>
26
+ <div class="score-container">
27
+ <div id="matchStatusBadge" class="status-badge">FETCHING LIVE API DATA...</div>
28
+ <h1 id="liveScoreText" class="hidden">-- / --</h1>
29
+ <p id="liveOverText" class="hidden">-- Overs</p>
30
+ <div class="run-rate hidden" id="matchRunRateText">CRR: --</div>
31
+ </div>
32
+ <div class="team" id="team2Text">--</div>
33
+ <div id="apiStatus" class="api-status">API: CONNECTING</div>
34
+ </header>
35
+
36
+ <div class="grid-container">
37
+ <!-- Left Column: Context -->
38
+ <section class="context-panel panel">
39
+ <h3>Match Context</h3>
40
+ <div class="stats-row">
41
+ <span class="label">Match Subtitle</span>
42
+ <span class="value" id="matchSubtitle">Scanning...</span>
43
+ </div>
44
+ <div class="stats-row">
45
+ <span class="label" id="contextLabel1">-</span>
46
+ <span class="value" id="contextValue1">-</span>
47
+ </div>
48
+ <div class="stats-row">
49
+ <span class="label" id="contextLabel2">-</span>
50
+ <span class="value" id="contextValue2">-</span>
51
+ </div>
52
+ <hr>
53
+ <div class="stats-row">
54
+ <span class="label">Ground</span>
55
+ <span class="value" id="venueText">Predicting Database Venue...</span>
56
+ </div>
57
+ <div class="stats-row">
58
+ <span class="label">Pitch Condition</span>
59
+ <span class="value highlight" id="pitchText">Analysing AI Model...</span>
60
+ </div>
61
+ </section>
62
+
63
+ <!-- Center Column: Prediction Box -->
64
+ <section class="prediction-panel panel emphasis">
65
+ <div class="live-indicator">
66
+ <div class="pulse" id="pulseIndicator"></div>
67
+ <span id="predictionTitle">INITIALIZING MODEL NETWORK...</span>
68
+ </div>
69
+
70
+ <!-- LIVE PREDICTION UI -->
71
+ <div id="livePredictorBox" class="prediction-box hidden">
72
+ <div id="predictionResult" class="result">
73
+ <h3 class="upcoming-ball-badge">⚡ NEXT BALL PREDICTION ⚡</h3>
74
+ <div class="prediction-item">
75
+ <h4>Model's Expected Delivery</h4>
76
+ <h2 class="cyan-text" id="deliveryOut">...</h2>
77
+ <div class="prob-bar">
78
+ <div class="fill cyan" id="delProbBar" style="width: 0%"></div>
79
+ </div>
80
+ <span class="prob-text" id="delProbText">--% Probability</span>
81
+ </div>
82
+ <div class="prediction-item mt-2">
83
+ <h4>Batter Reaction & Outcome</h4>
84
+ <h2 class="green-text" id="outcomeOut">...</h2>
85
+ <div class="prob-bar">
86
+ <div class="fill green" id="outProbBar" style="width: 0%"></div>
87
+ </div>
88
+ <span class="prob-text" id="outProbText">--% Probability</span>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <!-- UPCOMING MATCH PREDICTION UI -->
94
+ <div id="upcomingPredictorBox" class="prediction-box hidden">
95
+ <div class="result">
96
+ <div class="prediction-item">
97
+ <h4>AI Overall Match Winner Prediction</h4>
98
+ <h2 class="cyan-text" id="favoredTeamText">...</h2>
99
+ <div class="prob-bar">
100
+ <div class="fill cyan" id="winProbBar" style="width: 0%"></div>
101
+ </div>
102
+ <span class="prob-text" id="winProbText">--% Win Probability</span>
103
+ </div>
104
+ <div class="prediction-item mt-2">
105
+ <h4>Key Deciding Variables</h4>
106
+ <div
107
+ style="text-align: left; margin-top: 15px; color: var(--text-main); font-size: 1.1rem; line-height: 1.5;">
108
+ 🏏 <span id="factor1">Calculating team head-to-head metrics...</span><br><br>
109
+ 🏟️ <span id="factor2">Scanning stadium ground history...</span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </section>
115
+
116
+ <!-- Right Column: Model Stats -->
117
+ <section class="model-stats-panel panel">
118
+ <h3>Model Health</h3>
119
+ <div class="accuracy-circle">
120
+ <svg viewBox="0 0 36 36" class="circular-chart green">
121
+ <path class="circle-bg"
122
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
123
+ <path class="circle" stroke-dasharray="87, 100"
124
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
125
+ <text x="18" y="20.35" class="percentage">94%</text>
126
+ </svg>
127
+ </div>
128
+ <p class="accuracy-desc">Current Dataset Confidence</p>
129
+ <div class="mini-stats">
130
+ <div class="mini-stat">
131
+ <span class="val">Live</span>
132
+ <span class="lbl">Hook State</span>
133
+ </div>
134
+ <div class="mini-stat">
135
+ <span class="val" id="modelLat">12ms</span>
136
+ <span class="lbl">Latency</span>
137
+ </div>
138
+ </div>
139
+ </section>
140
+ </div>
141
+ </main>
142
+
143
+ <script src="app.js"></script>
144
+ </body>
145
+
146
+ </html>
main.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.staticfiles import StaticFiles
3
+ import uvicorn
4
+ import threading
5
+ import time
6
+
7
+ # You can import the bypass function from your scraper.py when you're ready
8
+ # from scraper import bypass_firewall
9
+
10
+ app = FastAPI()
11
+
12
+ # This tells the server to statically serve your HTML, CSS, and JS exactly as they are without breaking the design.
13
+ app.mount("/", StaticFiles(directory=".", html=True), name="static")
14
+
15
+ # ==============================================================================
16
+ # HUGGING FACE BACKGROUND TASK ARCHITECTURE
17
+ # When you activate this, the cloud computer will automatically run your hacker script
18
+ # in the background 24/7 independently of the website viewers.
19
+ # ==============================================================================
20
+ def autonomous_data_heist():
21
+ while True:
22
+ try:
23
+ print("Background Cloud Server: Attempting ESPNcricinfo Bypass Check...")
24
+ # bypass_firewall() # Trigger the scraper
25
+ except Exception as e:
26
+ print("Routine network reset.")
27
+
28
+ time.sleep(30) # Rips the database every 30 seconds to prevent bans
29
+
30
+ # Start the background task as a ghost thread
31
+ threading.Thread(target=autonomous_data_heist, daemon=True).start()
32
+
33
+ if __name__ == "__main__":
34
+ # Hugging Face Spaces strictly requires servers to broadcast on port 7860
35
+ print("Initializing Hugging Face Server on Port 7860...")
36
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi==0.109.2
2
+ uvicorn==0.27.1
3
+ curl_cffi==0.15.0
4
+ beautifulsoup4==4.12.3
scraper.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from curl_cffi import requests
2
+ from bs4 import BeautifulSoup
3
+ import json
4
+
5
+ LIVE_MATCH_URL = "https://www.espncricinfo.com/series/indian-premier-league-2024-1411166/chennai-super-kings-vs-royal-challengers-bengaluru-1st-match-1422119/live-cricket-score"
6
+
7
+ def bypass_firewall():
8
+ print("========================================")
9
+ print(" TLS FINGERPRINT SPOOFER ENGAGED ")
10
+ print("========================================")
11
+ print("Deploying curl-cffi to impersonate Chrome 120 TLS handshake at the C-level...")
12
+ try:
13
+ # Instead of launching a whole physical browser, we use the specific encryption
14
+ # signature of Chrome 120. Cloudflare looks at the raw binary packet and is tricked.
15
+ response = requests.get(
16
+ LIVE_MATCH_URL,
17
+ impersonate="chrome120",
18
+ headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}
19
+ )
20
+
21
+ soup = BeautifulSoup(response.text, 'html.parser')
22
+ title = soup.find('title')
23
+
24
+ if title:
25
+ print(f"\n[TARGET TAB VISUAL]: {title.text}")
26
+
27
+ if "Access Denied" in response.text or "Cloudflare" in response.text:
28
+ print("\n[!] TLS Spoofing failed. Our datacenter IP is likely blocked completely.")
29
+ else:
30
+ print("\n[+] SUCCESS! WE BROKE THE WALL.")
31
+ print("[+] Checking for Next.js internal JSON database...")
32
+
33
+ next_data = soup.find('script', id='__NEXT_DATA__')
34
+ if next_data:
35
+ print("[+] INTERNAL DATABASE LOCATED!")
36
+ live_json = json.loads(next_data.string)
37
+ print("[+] RAW DATA ACQUIRED.")
38
+ else:
39
+ print("[-] We broke the wall, but they moved the data. HTML parsing required.")
40
+
41
+ except Exception as e:
42
+ print(f"\n[!] Critical Error: {e}")
43
+
44
+ if __name__ == "__main__":
45
+ bypass_firewall()
style.css ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg-dark: #0f172a;
3
+ --panel-bg: #1e293b;
4
+ --accent-cyan: #06b6d4;
5
+ --accent-cyan-glow: rgba(6, 182, 212, 0.4);
6
+ --accent-green: #10b981;
7
+ --text-main: #f8fafc;
8
+ --text-muted: #94a3b8;
9
+ --border-color: #334155;
10
+ }
11
+
12
+ * {
13
+ margin: 0;
14
+ padding: 0;
15
+ box-sizing: border-box;
16
+ font-family: 'Outfit', sans-serif;
17
+ }
18
+
19
+ body {
20
+ background-color: var(--bg-dark);
21
+ color: var(--text-main);
22
+ min-height: 100vh;
23
+ }
24
+
25
+ /* Navbar */
26
+ .navbar {
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ padding: 1rem 3rem;
31
+ background-color: rgba(15, 23, 42, 0.8);
32
+ backdrop-filter: blur(10px);
33
+ border-bottom: 1px solid var(--border-color);
34
+ position: sticky;
35
+ top: 0;
36
+ z-index: 100;
37
+ }
38
+
39
+ .logo {
40
+ font-size: 1.5rem;
41
+ font-weight: 800;
42
+ letter-spacing: 1px;
43
+ }
44
+
45
+ .ai-glow {
46
+ color: var(--accent-cyan);
47
+ text-shadow: 0 0 10px var(--accent-cyan-glow);
48
+ }
49
+
50
+ .nav-links {
51
+ list-style: none;
52
+ display: flex;
53
+ gap: 2rem;
54
+ }
55
+
56
+ .nav-links a {
57
+ color: var(--text-muted);
58
+ text-decoration: none;
59
+ font-weight: 600;
60
+ transition: color 0.3s;
61
+ }
62
+
63
+ .nav-links a:hover,
64
+ .nav-links a.active {
65
+ color: var(--accent-cyan);
66
+ }
67
+
68
+ /* Dashboard Layout */
69
+ .dashboard {
70
+ padding: 2rem 3rem;
71
+ max-width: 1400px;
72
+ margin: 0 auto;
73
+ }
74
+
75
+ .match-header {
76
+ display: flex;
77
+ justify-content: center;
78
+ align-items: center;
79
+ gap: 4rem;
80
+ margin-bottom: 3rem;
81
+ }
82
+
83
+ .team {
84
+ font-size: 2.5rem;
85
+ font-weight: 800;
86
+ color: var(--text-muted);
87
+ }
88
+
89
+ .score-container {
90
+ text-align: center;
91
+ }
92
+
93
+ .score-container h1 {
94
+ font-size: 3.5rem;
95
+ color: var(--text-main);
96
+ }
97
+
98
+ .score-container p {
99
+ color: var(--accent-cyan);
100
+ font-size: 1.2rem;
101
+ font-weight: 600;
102
+ }
103
+
104
+ .run-rate {
105
+ margin-top: 0.5rem;
106
+ font-size: 0.9rem;
107
+ color: var(--text-muted);
108
+ }
109
+
110
+ .grid-container {
111
+ display: grid;
112
+ grid-template-columns: 1fr 2fr 1fr;
113
+ gap: 1.5rem;
114
+ }
115
+
116
+ .panel {
117
+ background-color: var(--panel-bg);
118
+ border-radius: 16px;
119
+ padding: 1.5rem;
120
+ border: 1px solid var(--border-color);
121
+ }
122
+
123
+ .panel h3 {
124
+ font-size: 1.2rem;
125
+ margin-bottom: 1.5rem;
126
+ color: var(--text-muted);
127
+ border-bottom: 1px solid var(--border-color);
128
+ padding-bottom: 0.5rem;
129
+ }
130
+
131
+ /* Context Panel */
132
+ .stats-row {
133
+ display: flex;
134
+ flex-direction: column;
135
+ margin-bottom: 1rem;
136
+ }
137
+
138
+ .stats-row .label {
139
+ font-size: 0.85rem;
140
+ color: var(--text-muted);
141
+ }
142
+
143
+ .stats-row .value {
144
+ font-size: 1.1rem;
145
+ font-weight: 600;
146
+ }
147
+
148
+ .stats-row .highlight {
149
+ color: var(--accent-cyan);
150
+ }
151
+
152
+ hr {
153
+ border: 0;
154
+ border-top: 1px solid var(--border-color);
155
+ margin: 1.5rem 0;
156
+ }
157
+
158
+ /* Prediction Panel */
159
+ .prediction-panel {
160
+ position: relative;
161
+ display: flex;
162
+ flex-direction: column;
163
+ align-items: center;
164
+ }
165
+
166
+ .prediction-panel.emphasis {
167
+ background: linear-gradient(180deg, var(--panel-bg) 0%, rgba(30, 41, 59, 0.4) 100%);
168
+ border: 1px solid var(--accent-cyan);
169
+ box-shadow: 0 0 30px rgba(6, 182, 212, 0.1);
170
+ }
171
+
172
+ .live-indicator {
173
+ display: flex;
174
+ align-items: center;
175
+ gap: 0.5rem;
176
+ color: var(--accent-cyan);
177
+ font-weight: 600;
178
+ margin-bottom: 2rem;
179
+ letter-spacing: 1px;
180
+ }
181
+
182
+ .pulse {
183
+ width: 10px;
184
+ height: 10px;
185
+ background-color: var(--accent-cyan);
186
+ border-radius: 50%;
187
+ box-shadow: 0 0 10px var(--accent-cyan);
188
+ animation: pulse 1.5s infinite;
189
+ }
190
+
191
+ @keyframes pulse {
192
+ 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(6, 182, 212, 0.7); }
193
+ 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(6, 182, 212, 0); }
194
+ 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(6, 182, 212, 0); }
195
+ }
196
+
197
+ .prediction-box {
198
+ width: 100%;
199
+ min-height: 250px;
200
+ display: flex;
201
+ justify-content: center;
202
+ align-items: center;
203
+ text-align: center;
204
+ }
205
+
206
+ .processing {
207
+ display: flex;
208
+ flex-direction: column;
209
+ align-items: center;
210
+ gap: 1rem;
211
+ }
212
+
213
+ .spinner {
214
+ width: 40px;
215
+ height: 40px;
216
+ border: 4px solid var(--border-color);
217
+ border-top: 4px solid var(--accent-cyan);
218
+ border-radius: 50%;
219
+ animation: spin 1s linear infinite;
220
+ }
221
+
222
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
223
+
224
+ .result {
225
+ width: 100%;
226
+ animation: fadein 0.5s ease;
227
+ }
228
+
229
+ @keyframes fadein { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
230
+
231
+ .hidden { display: none !important; }
232
+
233
+ .prediction-item {
234
+ background: rgba(0, 0, 0, 0.2);
235
+ padding: 1.5rem;
236
+ border-radius: 12px;
237
+ }
238
+
239
+ .prediction-item h4 {
240
+ color: var(--text-muted);
241
+ font-weight: 400;
242
+ margin-bottom: 0.5rem;
243
+ }
244
+
245
+ .prediction-item h2 {
246
+ font-size: 1.8rem;
247
+ margin-bottom: 1rem;
248
+ }
249
+
250
+ .cyan-text { color: var(--accent-cyan); }
251
+ .green-text { color: var(--accent-green); }
252
+ .mt-2 { margin-top: 1rem; }
253
+
254
+ .prob-bar {
255
+ width: 100%;
256
+ height: 6px;
257
+ background: var(--border-color);
258
+ border-radius: 3px;
259
+ overflow: hidden;
260
+ margin-bottom: 0.5rem;
261
+ }
262
+
263
+ .prob-bar .fill {
264
+ height: 100%;
265
+ border-radius: 3px;
266
+ transition: width 1s ease-out;
267
+ }
268
+
269
+ .fill.cyan { background-color: var(--accent-cyan); box-shadow: 0 0 10px var(--accent-cyan); }
270
+ .fill.green { background-color: var(--accent-green); box-shadow: 0 0 10px var(--accent-green); }
271
+
272
+ .prob-text {
273
+ font-size: 0.8rem;
274
+ color: var(--text-muted);
275
+ }
276
+
277
+ .action-btn {
278
+ margin-top: 2rem;
279
+ padding: 0.8rem 2rem;
280
+ background: transparent;
281
+ border: 1px solid var(--accent-cyan);
282
+ color: var(--accent-cyan);
283
+ border-radius: 30px;
284
+ cursor: pointer;
285
+ font-weight: 600;
286
+ transition: all 0.3s;
287
+ }
288
+
289
+ .action-btn:hover {
290
+ background: var(--accent-cyan);
291
+ color: var(--bg-dark);
292
+ box-shadow: 0 0 15px var(--accent-cyan-glow);
293
+ }
294
+
295
+ /* Model stats panel */
296
+ .accuracy-circle { margin: 2rem auto; width: 120px; }
297
+ .circular-chart { display: block; max-height: 250px; }
298
+ .circle-bg { fill: none; stroke: var(--border-color); stroke-width: 2.5; }
299
+ .circle { fill: none; stroke-width: 2.5; stroke-linecap: round; animation: progress 1s ease-out forwards; }
300
+ @keyframes progress { 0% { stroke-dasharray: 0 100; } }
301
+ .circular-chart.green .circle { stroke: var(--accent-green); }
302
+ .percentage { fill: #fff; font-family: sans-serif; font-size: 0.5em; text-anchor: middle; font-weight: bold; }
303
+
304
+ .accuracy-desc { text-align: center; color: var(--text-muted); font-size: 0.9rem; margin-bottom: 2rem; }
305
+ .mini-stats { display: flex; justify-content: space-around; }
306
+ .mini-stat { display: flex; flex-direction: column; align-items: center; }
307
+ .mini-stat .val { font-size: 1.2rem; font-weight: 600; color: var(--text-main); }
308
+ .mini-stat .lbl { font-size: 0.8rem; color: var(--text-muted); }
309
+
310
+ /* History Page specific */
311
+ .section-header { text-align: center; margin-bottom: 3rem; }
312
+ .section-header h2 { font-size: 2.5rem; }
313
+ .section-header p { color: var(--text-muted); margin-top: 0.5rem; }
314
+
315
+ .history-container { display: grid; grid-template-columns: 300px 1fr; gap: 2rem; }
316
+ .match-card { background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 12px; padding: 1.2rem; margin-bottom: 1rem; cursor: pointer; transition: all 0.3s; }
317
+ .match-card:hover { border-color: var(--accent-cyan); }
318
+ .match-card.active { border-color: var(--accent-cyan); background: rgba(30, 41, 59, 0.8); }
319
+ .match-info h3 { font-size: 1.1rem; color: var(--text-main); margin-bottom: 0.3rem; }
320
+ .match-info p { font-size: 0.85rem; color: var(--text-muted); }
321
+ .match-accuracy { margin-top: 1rem; font-size: 0.9rem; color: var(--accent-green); }
322
+
323
+ .history-table { width: 100%; border-collapse: collapse; text-align: left; }
324
+ .history-table th, .history-table td { padding: 1rem; border-bottom: 1px solid var(--border-color); }
325
+ .history-table th { color: var(--text-muted); font-weight: 600; font-size: 0.9rem; }
326
+ .history-table td { font-size: 0.95rem; }
327
+ .history-table tr:last-child td { border-bottom: none; }
328
+ .match-yes { color: var(--accent-green); font-weight: bold; }
329
+ .match-no { color: #ef4444; font-weight: bold; }
330
+
331
+ @media (max-width: 1024px) {
332
+ .grid-container, .history-container { grid-template-columns: 1fr; }
333
+ }