Mike-BM commited on
Commit
9b1fe6c
·
verified ·
1 Parent(s): 9aa09e2

# =====================================================

Browse files

# Economic News Sentiment + Forecasting Script
# =====================================================

# Install packages if not already installed:
# pip install transformers requests beautifulsoup4 pandas prophet tweepy streamlit

# -------------------------------
# 1. Imports
# -------------------------------
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
from transformers import pipeline
from prophet import Prophet
import json
import tweepy # For Twitter data

# -------------------------------
# 2. Hugging Face Sentiment Model
# -------------------------------
sentiment_model = pipeline("sentiment-analysis", model="yiyanghkust/finbert-tone")

def sentiment_score(text):
"""Return numeric sentiment score for text"""
result = sentiment_model(text)[0]
if result['label'] == 'positive':
return 1
elif result['label'] == 'neutral':
return 0
else:
return -1

# -------------------------------
# 3. Fetch News Headlines (RSS)
# -------------------------------
def fetch_rss_news(rss_url):
response = requests.get(rss_url)
soup = BeautifulSoup(response.content, 'xml')
items = soup.find_all('item')
data = []
for item in items:
title = item.title.text
pub_date = datetime.strptime(item.pubDate.text, '%a, %d %b %Y %H:%M:%S %Z')
data.append({'date': pub_date, 'headline': title})
return pd.DataFrame(data)

news_sources = [
'https://www.reuters.com/finance/economy/rss',
# Add more RSS links here if needed
]

news_df = pd.concat([fetch_rss_news(url) for url in news_sources])
news_df['sentiment'] = news_df['headline'].apply(sentiment_score)

# -------------------------------
# 4. Fetch Twitter Data
# -------------------------------
# You need Twitter API keys
# consumer_key, consumer_secret, access_token, access_token_secret

# Example placeholder (fill your own keys)
consumer_key = 'YOUR_KEY'
consumer_secret = 'YOUR_SECRET'
access_token = 'YOUR_ACCESS_TOKEN'
access_token_secret = 'YOUR_ACCESS_SECRET'

auth = tweepy.OAuth1UserHandler(consumer_key, consumer_secret, access_token, access_token_secret)
api = tweepy.API(auth)

def fetch_tweets(hashtag, count=50):
tweets = api.search_tweets(q=hashtag, count=count, lang='en', tweet_mode='extended')
data = []
for t in tweets:
date = t.created_at
text = t.full_text
data.append({'date': date, 'headline': text})
return pd.DataFrame(data)

hashtags = ['#KenyaEconomy', '#Inflation', '#GDP', '#InterestRates']
tweets_df = pd.concat([fetch_tweets(tag) for tag in hashtags])
tweets_df['sentiment'] = tweets_df['headline'].apply(sentiment_score)

# -------------------------------
# 5. Merge News + Tweets
# -------------------------------
all_texts = pd.concat([news_df, tweets_df])
all_texts['date_only'] = all_texts['date'].dt.date
daily_sentiment = all_texts.groupby('date_only')['sentiment'].mean().reset_index()
daily_sentiment.rename(columns={'date_only':'ds','sentiment':'y_sentiment'}, inplace=True)

# -------------------------------
# 6. Load Historical Economic Indicators
# -------------------------------
# CSV should have columns: date,inflation,GDP,interest_rate
historical = pd.read_csv('historical_economics.csv')
historical['ds'] = pd.to_datetime(historical['date'])
historical = historical[['ds','inflation','GDP','interest_rate']]

# Merge with sentiment
data = historical.merge(daily_sentiment, on='ds', how='left')
data['y_sentiment'].fillna(0, inplace=True)

# -------------------------------
# 7. Forecasting with Prophet
# -------------------------------
def forecast_indicator(df, indicator_col, sentiment_col='y_sentiment', periods=30):
df_prophet = df[['ds', indicator_col, sentiment_col]].rename(columns={indicator_col:'y'})
m = Prophet()
m.add_regressor(sentiment_col)
m.fit(df_prophet)

future = m.make_future_dataframe(periods=periods)
future = future.merge(df[['ds', sentiment_col]], on='ds', how='left')
future[sentiment_col].fillna(0, inplace=True)

forecast = m.predict(future)
forecast = forecast[['ds','yhat','yhat_lower','yhat_upper']]
forecast.rename(columns={'yhat':f'{indicator_col}_pred',
'yhat_lower':f'{indicator_col}_lower',
'yhat_upper':f'{indicator_col}_upper'}, inplace=True)
return forecast

forecast_inflation = forecast_indicator(data, 'inflation')
forecast_gdp = forecast_indicator(data, 'GDP')
forecast_interest = forecast_indicator(data, 'interest_rate')

# -------------------------------
# 8. Combine Forecasts + Sentiment into JSON
# -------------------------------
forecast_json = []
for i, row in data.iterrows():
f_row = {
"date": row['ds'].strftime('%Y-%m-%d'),
"sentiment_score": float(row['y_sentiment']),
"forecast": {
"inflation": {
"predicted_value": float(forecast_inflation.loc[i,'inflation_pred']),
"lower_ci": float(forecast_inflation.loc[i,'inflation_lower']),
"upper_ci": float(forecast_inflation.loc[i,'inflation_upper'])
},
"GDP": {
"predicted_value": float(forecast_gdp.loc[i,'GDP_pred']),
"lower_ci": float(forecast_gdp.loc[i,'GDP_lower']),
"upper_ci": float(forecast_gdp.loc[i,'GDP_upper'])
},
"interest_rate": {
"predicted_value": float(forecast_interest.loc[i,'interest_rate_pred']),
"lower_ci": float(forecast_interest.loc[i,'interest_rate_lower']),
"upper_ci": float(forecast_interest.loc[i,'interest_rate_upper'])
}
}
}
forecast_json.append(f_row)

# Save to JSON file
with open('economic_forecast.json','w') as f:
json.dump(forecast_json, f, indent=4)

print("✅ Economic sentiment + forecast JSON generated: economic_forecast.json")

Files changed (4) hide show
  1. README.md +8 -5
  2. index.html +104 -19
  3. script.js +301 -0
  4. style.css +87 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Suepcaf
3
- emoji: 🌖
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: ---------------------suepcaf
3
+ colorFrom: yellow
4
+ colorTo: green
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
index.html CHANGED
@@ -1,19 +1,104 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>EconoPulse Predictor | Economic Sentiment Dashboard</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
12
+ <script src="components/navbar.js"></script>
13
+ <script src="components/footer.js"></script>
14
+ </head>
15
+ <body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-300">
16
+ <custom-navbar></custom-navbar>
17
+
18
+ <main class="container mx-auto px-4 py-8">
19
+ <!-- Hero Section -->
20
+ <section class="mb-16">
21
+ <div class="text-center">
22
+ <h1 class="text-4xl md:text-6xl font-bold text-gray-800 dark:text-white mb-4">
23
+ Economic <span class="text-indigo-600">Sentiment</span> Intelligence
24
+ </h1>
25
+ <p class="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
26
+ Real-time economic sentiment analysis and forecasting powered by AI and social data
27
+ </p>
28
+ </div>
29
+ </section>
30
+
31
+ <!-- Dashboard Overview -->
32
+ <section class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
33
+ <!-- Sentiment Card -->
34
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
35
+ <div class="flex items-center justify-between mb-4">
36
+ <h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">Current Sentiment</h3>
37
+ <i data-feather="activity" class="text-indigo-500"></i>
38
+ </div>
39
+ <div id="sentimentGauge" class="h-40 mb-4"></div>
40
+ <p class="text-gray-600 dark:text-gray-300 text-sm" id="sentimentText">Loading sentiment data...</p>
41
+ </div>
42
+
43
+ <!-- Inflation Card -->
44
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
45
+ <div class="flex items-center justify-between mb-4">
46
+ <h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">Inflation Forecast</h3>
47
+ <i data-feather="trending-up" class="text-red-500"></i>
48
+ </div>
49
+ <div class="h-40 mb-4">
50
+ <canvas id="inflationChart"></canvas>
51
+ </div>
52
+ <p class="text-gray-600 dark:text-gray-300 text-sm" id="inflationText">Loading inflation data...</p>
53
+ </div>
54
+
55
+ <!-- GDP Card -->
56
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
57
+ <div class="flex items-center justify-between mb-4">
58
+ <h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">GDP Growth</h3>
59
+ <i data-feather="dollar-sign" class="text-green-500"></i>
60
+ </div>
61
+ <div class="h-40 mb-4">
62
+ <canvas id="gdpChart"></canvas>
63
+ </div>
64
+ <p class="text-gray-600 dark:text-gray-300 text-sm" id="gdpText">Loading GDP data...</p>
65
+ </div>
66
+ </section>
67
+
68
+ <!-- Detailed Charts Section -->
69
+ <section class="mb-12">
70
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6">
71
+ <h2 class="text-xl font-semibold text-gray-700 dark:text-gray-200 mb-6">Economic Indicators Timeline</h2>
72
+ <div class="h-96">
73
+ <canvas id="fullChart"></canvas>
74
+ </div>
75
+ </div>
76
+ </section>
77
+
78
+ <!-- News Feed Section -->
79
+ <section>
80
+ <h2 class="text-2xl font-bold text-gray-800 dark:text-white mb-6">Latest Economic Headlines</h2>
81
+ <div id="newsFeed" class="space-y-4">
82
+ <!-- News items will be loaded here -->
83
+ <div class="animate-pulse flex space-x-4">
84
+ <div class="flex-1 space-y-4 py-1">
85
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4"></div>
86
+ <div class="space-y-2">
87
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded"></div>
88
+ <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-5/6"></div>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </section>
94
+ </main>
95
+
96
+ <custom-footer></custom-footer>
97
+
98
+ <script src="script.js"></script>
99
+ <script>
100
+ feather.replace();
101
+ </script>
102
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
103
+ </body>
104
+ </html>
script.js ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Initialize charts with sample data
3
+ initCharts();
4
+ loadNewsFeed();
5
+ setupDarkModeToggle();
6
+ });
7
+
8
+ function initCharts() {
9
+ // Sample data - in a real app, this would come from your API
10
+ const dates = ['2023-01', '2023-02', '2023-03', '2023-04', '2023-05', '2023-06'];
11
+ const inflationData = [5.2, 5.5, 5.8, 6.1, 6.0, 5.9];
12
+ const gdpData = [2.1, 2.3, 2.5, 2.7, 2.9, 3.1];
13
+ const sentimentData = [0.2, -0.1, 0.3, -0.2, 0.1, 0.4];
14
+
15
+ // Inflation Chart
16
+ const inflationCtx = document.getElementById('inflationChart').getContext('2d');
17
+ new Chart(inflationCtx, {
18
+ type: 'line',
19
+ data: {
20
+ labels: dates,
21
+ datasets: [{
22
+ label: 'Inflation Rate',
23
+ data: inflationData,
24
+ borderColor: '#ef4444',
25
+ backgroundColor: 'rgba(239, 68, 68, 0.1)',
26
+ tension: 0.3,
27
+ fill: true
28
+ }]
29
+ },
30
+ options: getChartOptions('Inflation Rate (%)')
31
+ });
32
+
33
+ // GDP Chart
34
+ const gdpCtx = document.getElementById('gdpChart').getContext('2d');
35
+ new Chart(gdpCtx, {
36
+ type: 'line',
37
+ data: {
38
+ labels: dates,
39
+ datasets: [{
40
+ label: 'GDP Growth',
41
+ data: gdpData,
42
+ borderColor: '#10b981',
43
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
44
+ tension: 0.3,
45
+ fill: true
46
+ }]
47
+ },
48
+ options: getChartOptions('GDP Growth (%)')
49
+ });
50
+
51
+ // Full Chart
52
+ const fullCtx = document.getElementById('fullChart').getContext('2d');
53
+ new Chart(fullCtx, {
54
+ type: 'line',
55
+ data: {
56
+ labels: dates,
57
+ datasets: [
58
+ {
59
+ label: 'Inflation',
60
+ data: inflationData,
61
+ borderColor: '#ef4444',
62
+ backgroundColor: 'rgba(239, 68, 68, 0.1)',
63
+ yAxisID: 'y',
64
+ tension: 0.3
65
+ },
66
+ {
67
+ label: 'GDP Growth',
68
+ data: gdpData,
69
+ borderColor: '#10b981',
70
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
71
+ yAxisID: 'y',
72
+ tension: 0.3
73
+ },
74
+ {
75
+ label: 'Sentiment',
76
+ data: sentimentData,
77
+ borderColor: '#6366f1',
78
+ backgroundColor: 'rgba(99, 102, 241, 0.1)',
79
+ yAxisID: 'y1',
80
+ tension: 0.3
81
+ }
82
+ ]
83
+ },
84
+ options: {
85
+ responsive: true,
86
+ interaction: {
87
+ mode: 'index',
88
+ intersect: false,
89
+ },
90
+ plugins: {
91
+ title: {
92
+ display: true,
93
+ text: 'Economic Indicators Over Time',
94
+ color: '#6b7280',
95
+ font: {
96
+ size: 16
97
+ }
98
+ },
99
+ legend: {
100
+ position: 'top',
101
+ labels: {
102
+ color: '#6b7280'
103
+ }
104
+ },
105
+ tooltip: {
106
+ mode: 'index',
107
+ intersect: false
108
+ }
109
+ },
110
+ scales: {
111
+ x: {
112
+ grid: {
113
+ color: 'rgba(209, 213, 219, 0.2)'
114
+ },
115
+ ticks: {
116
+ color: '#6b7280'
117
+ }
118
+ },
119
+ y: {
120
+ type: 'linear',
121
+ display: true,
122
+ position: 'left',
123
+ grid: {
124
+ color: 'rgba(209, 213, 219, 0.2)'
125
+ },
126
+ ticks: {
127
+ color: '#6b7280'
128
+ }
129
+ },
130
+ y1: {
131
+ type: 'linear',
132
+ display: true,
133
+ position: 'right',
134
+ grid: {
135
+ drawOnChartArea: false,
136
+ color: 'rgba(209, 213, 219, 0.2)'
137
+ },
138
+ ticks: {
139
+ color: '#6b7280'
140
+ }
141
+ }
142
+ }
143
+ }
144
+ });
145
+
146
+ // Update text elements
147
+ document.getElementById('sentimentText').textContent = `Current sentiment: Positive (0.42)`;
148
+ document.getElementById('inflationText').textContent = `Forecast: 5.9% (↓ 0.1% from last month)`;
149
+ document.getElementById('gdpText').textContent = `Forecast: 3.1% (↑ 0.2% from last quarter)`;
150
+
151
+ // Initialize sentiment gauge
152
+ updateSentimentGauge(0.42);
153
+ }
154
+
155
+ function getChartOptions(title) {
156
+ return {
157
+ responsive: true,
158
+ maintainAspectRatio: false,
159
+ plugins: {
160
+ legend: {
161
+ display: false
162
+ },
163
+ title: {
164
+ display: false
165
+ },
166
+ tooltip: {
167
+ enabled: true,
168
+ mode: 'index',
169
+ intersect: false
170
+ }
171
+ },
172
+ scales: {
173
+ x: {
174
+ grid: {
175
+ display: false
176
+ },
177
+ ticks: {
178
+ display: false
179
+ }
180
+ },
181
+ y: {
182
+ grid: {
183
+ display: false
184
+ },
185
+ ticks: {
186
+ display: false
187
+ }
188
+ }
189
+ },
190
+ elements: {
191
+ point: {
192
+ radius: 0
193
+ }
194
+ }
195
+ };
196
+ }
197
+
198
+ function updateSentimentGauge(value) {
199
+ // Value between -1 and 1
200
+ const gaugeFill = document.createElement('div');
201
+ gaugeFill.className = 'gauge-fill';
202
+ gaugeFill.style.transform = `rotate(${value * 0.5}turn)`;
203
+
204
+ const gaugeCover = document.createElement('div');
205
+ gaugeCover.className = 'gauge-cover';
206
+ gaugeCover.textContent = value > 0 ? '+' + value.toFixed(2) : value.toFixed(2);
207
+
208
+ const gaugeBody = document.createElement('div');
209
+ gaugeBody.className = 'gauge-body';
210
+ gaugeBody.appendChild(gaugeFill);
211
+
212
+ const gaugeContainer = document.createElement('div');
213
+ gaugeContainer.className = 'gauge-container';
214
+ gaugeContainer.appendChild(gaugeBody);
215
+ gaugeContainer.appendChild(gaugeCover);
216
+
217
+ document.getElementById('sentimentGauge').innerHTML = '';
218
+ document.getElementById('sentimentGauge').appendChild(gaugeContainer);
219
+ }
220
+
221
+ async function loadNewsFeed() {
222
+ try {
223
+ // In a real app, you would fetch this from your API
224
+ const mockNews = [
225
+ {
226
+ title: "Central Bank Announces Interest Rate Hike to Combat Inflation",
227
+ source: "Financial Times",
228
+ date: "2023-06-15",
229
+ sentiment: 0.42
230
+ },
231
+ {
232
+ title: "GDP Growth Exceeds Expectations in Q2",
233
+ source: "Bloomberg",
234
+ date: "2023-06-10",
235
+ sentiment: 0.78
236
+ },
237
+ {
238
+ title: "Unemployment Rates Rise Slightly Amid Economic Slowdown",
239
+ source: "Reuters",
240
+ date: "2023-06-05",
241
+ sentiment: -0.35
242
+ },
243
+ {
244
+ title: "Government Announces New Stimulus Package to Boost Economy",
245
+ source: "Wall Street Journal",
246
+ date: "2023-05-28",
247
+ sentiment: 0.65
248
+ }
249
+ ];
250
+
251
+ const newsFeed = document.getElementById('newsFeed');
252
+ newsFeed.innerHTML = '';
253
+
254
+ mockNews.forEach(item => {
255
+ const sentimentColor = item.sentiment > 0.5 ? 'text-green-500' :
256
+ item.sentiment < -0.5 ? 'text-red-500' : 'text-yellow-500';
257
+ const sentimentIcon = item.sentiment > 0.5 ? 'trending-up' :
258
+ item.sentiment < -0.5 ? 'trending-down' : 'minus';
259
+
260
+ const newsItem = document.createElement('div');
261
+ newsItem.className = 'news-item bg-white dark:bg-gray-800 rounded-lg shadow p-4';
262
+ newsItem.innerHTML = `
263
+ <div class="flex justify-between items-start">
264
+ <div class="flex-1">
265
+ <h3 class="font-medium text-gray-800 dark:text-gray-200">${item.title}</h3>
266
+ <div class="flex items-center mt-2 text-sm text-gray-500 dark:text-gray-400">
267
+ <span>${item.source}</span>
268
+ <span class="mx-2">•</span>
269
+ <span>${item.date}</span>
270
+ </div>
271
+ </div>
272
+ <div class="flex items-center ml-4 ${sentimentColor}">
273
+ <i data-feather="${sentimentIcon}" class="w-5 h-5 mr-1"></i>
274
+ <span>${item.sentiment > 0 ? '+' : ''}${item.sentiment.toFixed(2)}</span>
275
+ </div>
276
+ </div>
277
+ `;
278
+ newsFeed.appendChild(newsItem);
279
+ });
280
+
281
+ feather.replace();
282
+ } catch (error) {
283
+ console.error('Error loading news feed:', error);
284
+ document.getElementById('newsFeed').innerHTML = `
285
+ <div class="text-center py-8 text-gray-500 dark:text-gray-400">
286
+ <i data-feather="alert-circle" class="w-12 h-12 mx-auto mb-4"></i>
287
+ <p>Failed to load news feed. Please try again later.</p>
288
+ </div>
289
+ `;
290
+ feather.replace();
291
+ }
292
+ }
293
+
294
+ function setupDarkModeToggle() {
295
+ const darkModeToggle = document.createElement('button');
296
+ darkModeToggle.className = 'flex items-center justify-center w-10 h-10 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200';
297
+ darkModeToggle.innerHTML = '<i data-feather="moon"></i>';
298
+ darkModeToggle.addEventListener('click', () => {
299
+ document.documentElement.classList.toggle('dark');
300
+ localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
301
+ feather.replace();
style.css CHANGED
@@ -1,28 +1,97 @@
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
  body {
4
+ font-family: 'Inter', sans-serif;
5
+ }
6
+
7
+ /* Gauge styles */
8
+ .gauge-container {
9
+ width: 100%;
10
+ height: 100%;
11
+ position: relative;
12
+ }
13
+
14
+ .gauge-body {
15
+ width: 100%;
16
+ height: 0;
17
+ padding-bottom: 50%;
18
+ position: relative;
19
+ border-top-left-radius: 100% 200%;
20
+ border-top-right-radius: 100% 200%;
21
+ overflow: hidden;
22
+ background: #e5e7eb;
23
+ }
24
+
25
+ .gauge-fill {
26
+ position: absolute;
27
+ top: 100%;
28
+ left: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ background: linear-gradient(to right, #ef4444, #f59e0b, #10b981);
32
+ transform-origin: center top;
33
+ transform: rotate(0.5turn);
34
+ transition: transform 1s ease-out;
35
+ }
36
+
37
+ .gauge-cover {
38
+ width: 75%;
39
+ height: 150%;
40
+ background: white;
41
+ border-radius: 50%;
42
+ position: absolute;
43
+ top: 25%;
44
+ left: 50%;
45
+ transform: translateX(-50%);
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ font-size: 2rem;
50
+ font-weight: bold;
51
+ color: #1f2937;
52
+ }
53
+
54
+ .dark .gauge-cover {
55
+ background: #1f2937;
56
+ color: #f3f4f6;
57
+ }
58
+
59
+ /* Animation for news items */
60
+ .news-item {
61
+ transition: all 0.3s ease;
62
+ }
63
+
64
+ .news-item:hover {
65
+ transform: translateY(-2px);
66
  }
67
 
68
+ /* Custom scrollbar */
69
+ ::-webkit-scrollbar {
70
+ width: 8px;
71
+ height: 8px;
72
  }
73
 
74
+ ::-webkit-scrollbar-track {
75
+ background: #f1f1f1;
 
 
 
76
  }
77
 
78
+ ::-webkit-scrollbar-thumb {
79
+ background: #888;
80
+ border-radius: 4px;
 
 
 
81
  }
82
 
83
+ ::-webkit-scrollbar-thumb:hover {
84
+ background: #555;
85
  }
86
+
87
+ .dark ::-webkit-scrollbar-track {
88
+ background: #374151;
89
+ }
90
+
91
+ .dark ::-webkit-scrollbar-thumb {
92
+ background: #6b7280;
93
+ }
94
+
95
+ .dark ::-webkit-scrollbar-thumb:hover {
96
+ background: #9ca3af;
97
+ }