AlicanKiraz0 commited on
Commit
2d4e8d6
·
verified ·
1 Parent(s): 5022881

Upload 8 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ combined_benchmark_cleaned.png filter=lfs diff=lfs merge=lfs -text
37
+ mcq_benchmark_cleaned.png filter=lfs diff=lfs merge=lfs -text
38
+ saq_benchmark_cleaned.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,12 +1,41 @@
1
- ---
2
- title: Seneca Trbench
3
- emoji: 📊
4
- colorFrom: purple
5
- colorTo: pink
6
- sdk: static
7
- pinned: false
8
- license: apache-2.0
9
- short_description: Turkish LLM Benchmark Leaderboard
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LLM Leaderboard (Yerel)
2
+
3
+ Bu depo, `leaderboard_data.csv` dosyasındaki metriklere göre bir LLM leaderboard arayüzü sağlar. Esinlenilen kaynak: [HF Space – llm_leaderboard_fr](https://huggingface.co/spaces/fr-gouv-coordination-ia/llm_leaderboard_fr/tree/main).
4
+
5
+ ## Özellikler
6
+ - Sütun bazlı sıralama (varsayılan: Combined Score azalan)
7
+ - Arama kutusu (model adına göre)
8
+ - Sağlayıcı filtresi (model adından türetilir, örn. `deepseek-ai`, `openai`, `anthropic`, `google`, `meta`)
9
+ - İlk 3 modelin görsel vurgusu (🥇🥈🥉)
10
+ - Karanlık tema, duyarlı (responsive) tasarım
11
+
12
+ ## Dosyalar
13
+ - `index.html` – Uygulama HTML iskeleti
14
+ - `styles.css` – UI stilleri
15
+ - `app.js` – CSV okuma, ayrıştırma, sıralama/filtreleme ve render
16
+ - `leaderboard_data.csv` – Veriler (sağlanmış)
17
+
18
+ ## Yerelde Çalıştırma
19
+ Statik dosyaları bir HTTP sunucusuyla servis etmelisiniz (file:// ile fetch kısıtlıdır). Örnek:
20
+
21
+ ```bash
22
+ cd "/Users/alicankiraz/Desktop/Cursor Projects/HF-Space"
23
+ python3 -m http.server 8000
24
+ ```
25
+
26
+ Ardından tarayıcıdan `http://localhost:8000/` adresine gidin.
27
+
28
+ ## CSV Güncelleme
29
+ - Yeni skorlar için `leaderboard_data.csv` dosyasını aynı şemayla güncelleyin.
30
+ - Sütunlar: `Rank, Model, MCQ Score, SAQ Score, Combined Score`
31
+ - Uygulama Combined sütununu kullanır; yoksa MCQ/SAQ’tan türetmek için kodu genişletebilirsiniz.
32
+
33
+ ## Hugging Face Spaces’e Yayınlama (Statik)
34
+ - Space Type: Static
35
+ - Dosyalar: `index.html`, `styles.css`, `app.js`, `leaderboard_data.csv`
36
+ - Giriş noktası: `index.html`
37
+
38
+ ## Lisans
39
+ Bu proje örnek niteliğindedir; üstveri ve veriler için ilgili sağlayıcıların lisanslarına uyun.
40
+
41
+
app.js ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+
3
+ // Basit ama sağlam CSV ayrıştırıcı (çift tırnak ve yeni satırları destekler)
4
+ function parseCSV(text) {
5
+ const rows = [];
6
+ let row = [];
7
+ let field = "";
8
+ let inQuotes = false;
9
+ let i = 0;
10
+ while (i < text.length) {
11
+ const char = text[i];
12
+ if (inQuotes) {
13
+ if (char === '"') {
14
+ const next = text[i + 1];
15
+ if (next === '"') { // kaçışlı çift tırnak
16
+ field += '"';
17
+ i += 2;
18
+ continue;
19
+ } else {
20
+ inQuotes = false;
21
+ i += 1;
22
+ continue;
23
+ }
24
+ } else {
25
+ field += char;
26
+ i += 1;
27
+ continue;
28
+ }
29
+ } else {
30
+ if (char === '"') {
31
+ inQuotes = true;
32
+ i += 1;
33
+ continue;
34
+ }
35
+ if (char === ',') {
36
+ row.push(field);
37
+ field = "";
38
+ i += 1;
39
+ continue;
40
+ }
41
+ if (char === '\n') {
42
+ row.push(field);
43
+ rows.push(row);
44
+ row = [];
45
+ field = "";
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (char === '\r') {
50
+ // CRLF desteği
51
+ i += 1;
52
+ continue;
53
+ }
54
+ field += char;
55
+ i += 1;
56
+ }
57
+ }
58
+ // Son alan
59
+ if (field.length > 0 || row.length > 0) {
60
+ row.push(field);
61
+ rows.push(row);
62
+ }
63
+ if (rows.length === 0) return { headers: [], records: [] };
64
+ const headers = rows[0].map(h => (h || "").trim());
65
+ const records = rows.slice(1).filter(r => r.length && r.some(c => (c || "").trim().length > 0));
66
+ return { headers, records };
67
+ }
68
+
69
+ function normalizeHeader(header) {
70
+ const h = header.toLowerCase().trim();
71
+ if (h.includes("rank")) return "rank";
72
+ if (h.includes("model")) return "model";
73
+ if (h.includes("mcq")) return "mcq";
74
+ if (h.includes("saq")) return "saq";
75
+ if (h.includes("combined")) return "combined";
76
+ return header.toLowerCase().replace(/[^a-z0-9]+/g, "_");
77
+ }
78
+
79
+ function detectProvider(modelName) {
80
+ const name = String(modelName || "");
81
+ if (name.includes('/')) {
82
+ return name.split('/')[0];
83
+ }
84
+ const lower = name.toLowerCase();
85
+ if (lower.startsWith("gpt")) return "openai";
86
+ if (lower.startsWith("claude")) return "anthropic";
87
+ if (lower.startsWith("gemini")) return "google";
88
+ if (lower.startsWith("gemma")) return "google";
89
+ if (lower.startsWith("llama")) return "meta";
90
+ return "diğer";
91
+ }
92
+
93
+ function toNumber(value) {
94
+ const n = parseFloat(String(value).replace(",", "."));
95
+ return Number.isFinite(n) ? n : null;
96
+ }
97
+
98
+ function formatScore(n) {
99
+ return n == null ? "-" : n.toFixed(2);
100
+ }
101
+
102
+ const state = {
103
+ search: "",
104
+ provider: "",
105
+ sortKey: "combined",
106
+ sortDir: "desc",
107
+ };
108
+
109
+ let fullData = [];
110
+
111
+ async function loadData() {
112
+ const res = await fetch("./leaderboard_data.csv", { cache: "no-cache" });
113
+ const text = await res.text();
114
+ const { headers, records } = parseCSV(text);
115
+ const keys = headers.map(normalizeHeader);
116
+ const data = records.map(cols => {
117
+ const obj = {};
118
+ for (let i = 0; i < keys.length; i += 1) {
119
+ obj[keys[i]] = cols[i];
120
+ }
121
+ const mcq = toNumber(obj.mcq);
122
+ const saq = toNumber(obj.saq);
123
+ const combined = toNumber(obj.combined);
124
+ const rank = toNumber(obj.rank);
125
+ const model = obj.model;
126
+ const provider = detectProvider(model);
127
+ return { model, provider, mcq, saq, combined, csvRank: rank };
128
+ });
129
+ return data;
130
+ }
131
+
132
+ function uniqueProviders(data) {
133
+ const s = new Set(data.map(d => d.provider));
134
+ return Array.from(s).sort((a, b) => a.localeCompare(b));
135
+ }
136
+
137
+ function applyFilters(data) {
138
+ let out = data.slice(0);
139
+ if (state.search) {
140
+ const q = state.search.toLowerCase();
141
+ out = out.filter(d => d.model.toLowerCase().includes(q));
142
+ }
143
+ if (state.provider) {
144
+ out = out.filter(d => d.provider === state.provider);
145
+ }
146
+ return out;
147
+ }
148
+
149
+ function sortData(data) {
150
+ const key = state.sortKey;
151
+ const dir = state.sortDir === "asc" ? 1 : -1;
152
+ const copy = data.slice(0);
153
+ copy.sort((a, b) => {
154
+ const av = a[key];
155
+ const bv = b[key];
156
+ if (av == null && bv == null) return 0;
157
+ if (av == null) return 1; // null'lar alta
158
+ if (bv == null) return -1;
159
+ if (typeof av === "number" && typeof bv === "number") {
160
+ return (av - bv) * dir * -1; // büyükten küçüğe için -1 çevirimi
161
+ }
162
+ return String(av).localeCompare(String(bv)) * dir;
163
+ });
164
+ return copy;
165
+ }
166
+
167
+ function clearSortIndicators() {
168
+ const ths = document.querySelectorAll("thead th[data-key]");
169
+ ths.forEach(th => th.setAttribute("aria-sort", "none"));
170
+ }
171
+
172
+ function setSortIndicator() {
173
+ const th = document.querySelector(`thead th[data-key="${state.sortKey}"]`);
174
+ if (th) th.setAttribute("aria-sort", state.sortDir === "asc" ? "ascending" : "descending");
175
+ }
176
+
177
+ function render(data) {
178
+ const tbody = document.getElementById("tableBody");
179
+ const sorted = sortData(applyFilters(data));
180
+ let html = "";
181
+ sorted.forEach((d, idx) => {
182
+ const rank = idx + 1;
183
+ const trophy = rank === 1 ? "🥇" : rank === 2 ? "🥈" : rank === 3 ? "🥉" : "";
184
+ html += `
185
+ <tr class="rank-${rank}">
186
+ <td class="rank-cell" title="CSV rank: ${d.csvRank ?? '-'}">${rank} ${trophy}</td>
187
+ <td>${d.model}</td>
188
+ <td>${formatScore(d.mcq)}</td>
189
+ <td>${formatScore(d.saq)}</td>
190
+ <td>${formatScore(d.combined)}</td>
191
+ </tr>
192
+ `;
193
+ });
194
+ tbody.innerHTML = html;
195
+
196
+ // İlk 3 için sınıf koruması (fazladan koruma)
197
+ const trs = tbody.querySelectorAll("tr");
198
+ trs.forEach((tr, i) => {
199
+ tr.classList.toggle("rank-1", i === 0);
200
+ tr.classList.toggle("rank-2", i === 1);
201
+ tr.classList.toggle("rank-3", i === 2);
202
+ });
203
+
204
+ clearSortIndicators();
205
+ setSortIndicator();
206
+ const stats = document.getElementById("summaryStats");
207
+ stats.textContent = `${sorted.length} model listelendi${state.provider ? ` • Sağlayıcı: ${state.provider}` : ""}${state.search ? ` • Arama: "${state.search}"` : ""}`;
208
+ }
209
+
210
+ function populateProviders(data) {
211
+ const select = document.getElementById("providerSelect");
212
+ const providers = uniqueProviders(data);
213
+ // mevcut seçenekleri koru (ilk tüm sağlayıcılar)
214
+ providers.forEach(p => {
215
+ const opt = document.createElement("option");
216
+ opt.value = p;
217
+ opt.textContent = p;
218
+ select.appendChild(opt);
219
+ });
220
+ }
221
+
222
+ function updateURL() {
223
+ const params = new URLSearchParams();
224
+ if (state.search) params.set("q", state.search);
225
+ if (state.provider) params.set("p", state.provider);
226
+ if (state.sortKey !== "combined" || state.sortDir !== "desc") {
227
+ params.set("s", `${state.sortKey}:${state.sortDir}`);
228
+ }
229
+ const qs = params.toString();
230
+ const url = qs ? `?${qs}` : location.pathname;
231
+ history.replaceState(null, "", url);
232
+ }
233
+
234
+ function restoreFromURL() {
235
+ const params = new URLSearchParams(location.search);
236
+ state.search = params.get("q") || "";
237
+ state.provider = params.get("p") || "";
238
+ const s = params.get("s");
239
+ if (s && s.includes(":")) {
240
+ const [k, d] = s.split(":");
241
+ state.sortKey = k || state.sortKey;
242
+ state.sortDir = d === "asc" ? "asc" : "desc";
243
+ }
244
+ document.getElementById("searchInput").value = state.search;
245
+ // provider seçeneği data yüklenince set edilecek
246
+ }
247
+
248
+ function attachEvents() {
249
+ const searchInput = document.getElementById("searchInput");
250
+ const providerSelect = document.getElementById("providerSelect");
251
+ const resetBtn = document.getElementById("resetFilters");
252
+ const ths = document.querySelectorAll("thead th[data-key]");
253
+
254
+ searchInput.addEventListener("input", () => {
255
+ state.search = searchInput.value.trim();
256
+ updateURL();
257
+ render(fullData);
258
+ });
259
+
260
+ providerSelect.addEventListener("change", () => {
261
+ state.provider = providerSelect.value;
262
+ updateURL();
263
+ render(fullData);
264
+ });
265
+
266
+ resetBtn.addEventListener("click", () => {
267
+ state.search = "";
268
+ state.provider = "";
269
+ state.sortKey = "combined";
270
+ state.sortDir = "desc";
271
+ document.getElementById("searchInput").value = "";
272
+ providerSelect.value = "";
273
+ updateURL();
274
+ render(fullData);
275
+ });
276
+
277
+ ths.forEach(th => {
278
+ th.addEventListener("click", () => {
279
+ const key = th.getAttribute("data-key");
280
+ if (state.sortKey === key) {
281
+ state.sortDir = state.sortDir === "asc" ? "desc" : "asc";
282
+ } else {
283
+ state.sortKey = key;
284
+ state.sortDir = key === "model" ? "asc" : "desc";
285
+ }
286
+ updateURL();
287
+ render(fullData);
288
+ });
289
+ });
290
+ }
291
+
292
+ document.addEventListener("DOMContentLoaded", async () => {
293
+ restoreFromURL();
294
+ fullData = await loadData();
295
+ populateProviders(fullData);
296
+ // URL'den gelen provider varsa select'e uygula
297
+ if (state.provider) {
298
+ const sel = document.getElementById("providerSelect");
299
+ sel.value = state.provider;
300
+ }
301
+ attachEvents();
302
+ render(fullData);
303
+ });
304
+
305
+
combined_benchmark_cleaned.png ADDED

Git LFS Details

  • SHA256: a33d8cbac14f1a8b2a9969eb685af4070cf04b00675c77bc3da01e6b153dc0df
  • Pointer size: 131 Bytes
  • Size of remote file: 494 kB
index.html CHANGED
@@ -1,19 +1,64 @@
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="tr">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>LLM Leaderboard (Local)</title>
7
+ <link rel="stylesheet" href="./styles.css">
8
+ </head>
9
+ <body>
10
+ <header class="topbar">
11
+ <div class="topbar__left">
12
+ <h1 class="app-title">LLM Leaderboard</h1>
13
+ </div>
14
+ <div class="topbar__right">
15
+ <span id="datasetInfo" class="muted">Kaynak: leaderboard_data.csv</span>
16
+ <a class="link" target="_blank" rel="noopener noreferrer" href="https://huggingface.co/spaces/fr-gouv-coordination-ia/llm_leaderboard_fr/tree/main">Referans Space</a>
17
+ </div>
18
+ </header>
19
+
20
+ <main class="container">
21
+ <section class="controls">
22
+ <div class="control">
23
+ <input id="searchInput" type="search" placeholder="Model ara…" aria-label="Model ara">
24
+ </div>
25
+ <div class="control">
26
+ <select id="providerSelect" aria-label="Sağlayıcı filtresi">
27
+ <option value="">Tüm Sağlayıcılar</option>
28
+ </select>
29
+ </div>
30
+ <div class="control">
31
+ <button id="resetFilters" type="button">Sıfırla</button>
32
+ </div>
33
+ </section>
34
+
35
+ <section class="table-wrap">
36
+ <table id="leaderboardTable" class="table" aria-describedby="tableCaption">
37
+ <caption id="tableCaption" class="visually-hidden">LLM Leaderboard tablosu</caption>
38
+ <thead>
39
+ <tr>
40
+ <th scope="col" data-key="rank" aria-sort="none">Sıra</th>
41
+ <th scope="col" data-key="model" aria-sort="none">Model</th>
42
+ <th scope="col" data-key="mcq" aria-sort="none">MCQ</th>
43
+ <th scope="col" data-key="saq" aria-sort="none">SAQ</th>
44
+ <th scope="col" data-key="combined" aria-sort="descending">Genel</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody id="tableBody"></tbody>
48
+ </table>
49
+ </section>
50
+
51
+ <section class="footer">
52
+ <div id="summaryStats" class="muted"></div>
53
+ <div class="disclaimer">
54
+ Bu leaderboard, <code>leaderboard_data.csv</code> dosyasındaki metriklere göre oluşturulmuştur. Kaynak:
55
+ <a class="link" href="https://huggingface.co/spaces/fr-gouv-coordination-ia/llm_leaderboard_fr/tree/main" target="_blank" rel="noopener noreferrer">HF Space</a>.
56
+ </div>
57
+ </section>
58
+ </main>
59
+
60
+ <script src="./app.js"></script>
61
+ </body>
62
+ </html>
63
+
64
+
leaderboard_data.csv ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Rank,Model,MCQ Score,SAQ Score,Combined Score
2
+ 1,gpt-5,94.66,92.35,93.5
3
+ 2,gpt-5-nano,93.13,92.67,92.9
4
+ 3,gpt-5-mini,93.13,91.67,92.4
5
+ 4,claude-opus-4-1-20250805,90.08,90.04,90.06
6
+ 5,claude-sonnet-4-5-20250929,87.79,89.77,88.78
7
+ 6,deepseek-ai/DeepSeek-V3,89.31,85.98,87.65
8
+ 7,openai/gpt-oss-120b,88.55,84.47,86.51
9
+ 8,moonshotai/Kimi-K2-Instruct-0905,89.31,83.29,86.3
10
+ 9,claude-haiku-4-5-20251001,85.5,86.92,86.21
11
+ 10,deepseek-ai/DeepSeek-R1,86.26,85.4,85.83
12
+ 11,gemini-2.5-flash-lite,89.31,79.23,84.27
13
+ 12,gemini-2.5-flash,80.92,85.73,83.33
14
+ 13,Gemma-3-27b-it,84.73,81.37,83.05
15
+ 14,Qwen/Qwen3-Next-80B-A3B-Instruct,83.21,82.63,82.92
16
+ 15,gpt-4o-mini,87.79,76.65,82.22
17
+ 16,zai-org/GLM-4.5-Air-FP8,80.15,82.77,81.46
18
+ 17,meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8,81.68,77.86,79.77
19
+ 18,meta-llama/Llama-3.3-70B-Instruct-Turbo,74.05,73.16,73.6
20
+ 19,google/gemma-3n-E4B-it,71.76,74.18,72.97
21
+ 20,Qwen/Qwen3-235B-A22B-Thinking-2507,62.6,76.3,69.45
mcq_benchmark_cleaned.png ADDED

Git LFS Details

  • SHA256: 6f29817da926de925e4839facbcd061ee31b2d156709ceb82eba8609d884ec93
  • Pointer size: 131 Bytes
  • Size of remote file: 462 kB
saq_benchmark_cleaned.png ADDED

Git LFS Details

  • SHA256: b45bc5cb4cd0566be0a98d87e741c95b054d0fec468ac2ad7bf67debb26a89de
  • Pointer size: 131 Bytes
  • Size of remote file: 458 kB
styles.css ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #0b0f17;
3
+ --panel: #121826;
4
+ --text: #e5e7eb;
5
+ --muted: #9ca3af;
6
+ --accent: #8b5cf6;
7
+ --border: #1f2937;
8
+ --row-hover: #111827;
9
+ --gold: #ffe27a33;
10
+ --silver: #c0c0c033;
11
+ --bronze: #cd7f3233;
12
+ }
13
+
14
+ * { box-sizing: border-box; }
15
+ html, body { height: 100%; }
16
+ body {
17
+ margin: 0;
18
+ background: var(--bg);
19
+ color: var(--text);
20
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif;
21
+ line-height: 1.45;
22
+ }
23
+
24
+ .container {
25
+ max-width: 1100px;
26
+ margin: 0 auto;
27
+ padding: 16px;
28
+ }
29
+
30
+ .topbar {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: space-between;
34
+ padding: 12px 16px;
35
+ border-bottom: 1px solid var(--border);
36
+ background: var(--panel);
37
+ }
38
+ .topbar__left { display: flex; align-items: center; gap: 12px; }
39
+ .topbar__right { display: flex; align-items: center; gap: 12px; }
40
+ .app-title { font-size: 20px; margin: 0; }
41
+ .muted { color: var(--muted); }
42
+ .link { color: var(--accent); text-decoration: none; }
43
+ .link:hover { text-decoration: underline; }
44
+
45
+ .controls {
46
+ display: flex;
47
+ gap: 8px;
48
+ align-items: center;
49
+ flex-wrap: wrap;
50
+ margin: 16px 0 8px;
51
+ }
52
+ .control input[type="search"],
53
+ .control select {
54
+ background: var(--panel);
55
+ color: var(--text);
56
+ border: 1px solid var(--border);
57
+ border-radius: 8px;
58
+ padding: 8px 10px;
59
+ }
60
+ .control button {
61
+ background: var(--accent);
62
+ color: #fff;
63
+ border: none;
64
+ border-radius: 8px;
65
+ padding: 8px 12px;
66
+ cursor: pointer;
67
+ }
68
+ .control button:hover { opacity: 0.95; }
69
+
70
+ .table-wrap { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--panel); }
71
+ table.table { width: 100%; border-collapse: separate; border-spacing: 0; }
72
+ thead th {
73
+ text-align: left;
74
+ font-weight: 600;
75
+ padding: 12px;
76
+ border-bottom: 1px solid var(--border);
77
+ background: #0f1524;
78
+ position: sticky;
79
+ top: 0;
80
+ z-index: 1;
81
+ cursor: pointer;
82
+ }
83
+ tbody td { padding: 12px; border-bottom: 1px solid var(--border); }
84
+ tbody tr:hover { background: var(--row-hover); }
85
+
86
+ /* Sıralama göstergeleri */
87
+ th[aria-sort="ascending"]::after,
88
+ th[aria-sort="descending"]::after {
89
+ content: '';
90
+ display: inline-block;
91
+ margin-left: 6px;
92
+ width: 0; height: 0;
93
+ border-left: 5px solid transparent;
94
+ border-right: 5px solid transparent;
95
+ }
96
+ th[aria-sort="ascending"]::after { border-bottom: 6px solid var(--muted); }
97
+ th[aria-sort="descending"]::after { border-top: 6px solid var(--muted); }
98
+
99
+ /* İlk 3 vurgusu */
100
+ tr.rank-1 { background: var(--gold); }
101
+ tr.rank-2 { background: var(--silver); }
102
+ tr.rank-3 { background: var(--bronze); }
103
+ td.rank-cell { font-weight: 700; }
104
+
105
+ .footer { display: flex; align-items: center; justify-content: space-between; padding: 12px 0 0; gap: 12px; }
106
+ .disclaimer { color: var(--muted); font-size: 14px; }
107
+
108
+ .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
109
+
110
+ @media (max-width: 720px) {
111
+ thead th:nth-child(3),
112
+ thead th:nth-child(4) { display: none; }
113
+ tbody td:nth-child(3),
114
+ tbody td:nth-child(4) { display: none; }
115
+ }
116
+
117
+