SeaWolf-AI commited on
Commit
c2fd4fe
·
verified ·
1 Parent(s): 0e9de18

i18n EN/ZH/KO + browser auto-detect + language switcher

Browse files
Files changed (1) hide show
  1. index.html +319 -245
index.html CHANGED
@@ -1,245 +1,319 @@
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">
6
- <title>Open Quantum Challenge</title>
7
- <style>
8
- :root{--bg:#f6f8fc;--panel:#ffffff;--line:#e3e9f2;--ink:#131a26;--mut:#65748c;
9
- --acc:#0f7b6c;--acc-soft:#e6f5f2;--acc2:#0f7b6c;--gold:#b4740a;--gold-soft:#fdf4e3;--ok:#0f7b6c;--bad:#b3261e;
10
- --shadow:0 1px 2px rgba(19,26,38,.05),0 8px 24px rgba(19,26,38,.06);}
11
- *{box-sizing:border-box}
12
- html,body{margin:0;padding:0}
13
- body{background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,"Segoe UI",Roboto,"Malgun Gothic","Apple SD Gothic Neo",sans-serif;-webkit-font-smoothing:antialiased}
14
- a{color:var(--acc)}
15
- .wrap{max-width:1100px;margin:0 auto;padding:26px 20px 70px}
16
- .hero{background:linear-gradient(135deg,#ffffff 0%,#eef6f4 55%,#e7f0fb 100%);border:1px solid var(--line);border-radius:16px;padding:24px 26px;box-shadow:var(--shadow)}
17
- .hero-tag{display:inline-block;color:var(--acc);font-size:11.5px;font-weight:700;letter-spacing:2.2px;text-transform:uppercase;margin-bottom:6px}
18
- h1{margin:0;font-size:30px;letter-spacing:-.5px;font-weight:800}
19
- .sub{color:var(--mut);font-size:14px;margin:10px 0 0;max-width:72ch}
20
- .badges{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:16px 0 0}
21
- .badges img{height:20px}
22
- .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:18px 0}
23
- @media(max-width:720px){.grid{grid-template-columns:1fr}}
24
- .card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:16px;box-shadow:var(--shadow)}
25
- .card h3{margin:0 0 2px;font-size:17px;font-weight:800;color:#1c2a3d}
26
- .card .s{color:var(--acc);font-size:11px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase}
27
- .meta{color:var(--mut);font-size:13px;margin:6px 0 2px}
28
- code,.mono{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;background:#f0f4fa;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:#31465e}
29
- .pill{display:inline-block;font-size:10.5px;font-weight:800;border-radius:999px;padding:1px 9px;margin-left:6px;letter-spacing:.03em}
30
- .pill.open{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
31
- .pill.soon{background:#eef3fa;color:var(--mut);border:1px solid var(--line)}
32
- .prize{color:var(--gold);font-weight:800}
33
- .tabs{display:flex;gap:6px;margin:24px 0 10px;flex-wrap:wrap}
34
- .tab{padding:7px 14px;border:1px solid var(--line);border-radius:999px;background:#fff;cursor:pointer;color:var(--mut);font-weight:700;font-size:12.5px}
35
- .tab.on{color:#fff;border-color:var(--acc);background:var(--acc)}
36
- table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden;box-shadow:var(--shadow)}
37
- th,td{padding:11px 13px;text-align:left;border-bottom:1px solid var(--line);font-size:14px}
38
- th{background:#f3f7fc;color:var(--mut);font-size:11px;letter-spacing:.5px;text-transform:uppercase;font-weight:800}
39
- tr:last-child td{border-bottom:none}
40
- td.r,th.r{text-align:right;font-variant-numeric:tabular-nums}
41
- .rank{width:54px;text-align:center;font-weight:800;color:var(--acc)}
42
- .tagv{font-size:10.5px;font-weight:800;border-radius:6px;padding:1px 8px}
43
- .tagv.v{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
44
- .tagv.a{background:var(--gold-soft);color:var(--gold);border:1px solid #ecd9ad}
45
- h2{font-size:20px;margin:30px 0 6px;font-weight:800;letter-spacing:-.2px}
46
- .muted{color:var(--mut)}
47
- .steps{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin:12px 0}
48
- @media(max-width:720px){.steps{grid-template-columns:1fr}}
49
- .step{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px;box-shadow:var(--shadow)}
50
- .step b{color:var(--acc)}
51
- .form{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px;margin-top:10px;box-shadow:var(--shadow)}
52
- label{display:block;font-size:13px;color:var(--mut);margin:10px 0 4px;font-weight:600}
53
- input,select,textarea{width:100%;background:#fff;border:1px solid var(--line);border-radius:9px;color:var(--ink);padding:9px 10px;font:inherit}
54
- input:focus,select:focus,textarea:focus{outline:none;border-color:var(--acc)}
55
- textarea{min-height:120px;font-family:ui-monospace,monospace;font-size:12px}
56
- .btn{margin-top:14px;background:var(--acc);color:#fff;border:none;border-radius:10px;padding:11px 22px;font-weight:800;cursor:pointer}
57
- .btn:hover{background:#0c6558}
58
- .btn:disabled{opacity:.5;cursor:not-allowed}
59
- .note{margin-top:10px;font-size:13px;padding:10px 12px;border-radius:9px;display:none}
60
- .note.ok{display:block;background:var(--acc-soft);border:1px solid #bfe3da;color:#13503f}
61
- .note.err{display:block;background:#fdeceb;border:1px solid #f3c7c3;color:var(--bad)}
62
- .foot{margin-top:40px;color:var(--mut);font-size:12px;border-top:1px solid var(--line);padding-top:14px}
63
- .codeblock{position:relative;background:#0d1b2a;border:1px solid #16314a;border-radius:12px;padding:14px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;line-height:1.55;color:#d7e6ff;cursor:copy;white-space:pre}
64
- .codeblock:hover{border-color:var(--acc)}
65
- .codeblock::after{content:"click to copy";position:absolute;top:8px;right:10px;font-size:10px;color:#7f97b8}
66
- .codeblock.copied::after{content:"copied";color:#67e0b0}
67
- .authbtn{display:inline-flex;align-items:center;gap:6px;background:#fff;color:var(--acc);border:1px solid var(--acc);border-radius:999px;padding:4px 12px;font-weight:800;font-size:12px;text-decoration:none}
68
- .authuser{font-size:12px;color:var(--mut)}
69
- .authuser a{color:var(--acc)}
70
- </style>
71
- </head>
72
- <body>
73
- <div class="wrap">
74
- <div class="hero">
75
- <span class="hero-tag">FINAL-Bench · vendor-neutral · fully reproducible</span>
76
- <h1>Open Quantum Challenge <span style="color:var(--mut);font-size:16px">(OQC)</span></h1>
77
- <p class="sub">Contribute to the quantum ecosystem with <b>just a GPU and the open harness — no quantum
78
- hardware required.</b> Answers are held privately and every submission is auto-scored against a frozen
79
- ground truth, so the ranking is reproducible and hardware-independent. Two seasons, $1,000 prize each.</p>
80
- <div class="badges" id="badges">
81
- <a href="https://huggingface.co/spaces/FINAL-Bench/quantum-bench-leaderboard" target="_blank"><img alt="Leaderboard" src="https://img.shields.io/badge/%F0%9F%8F%86%20Leaderboard-HF%20Space-2563eb"></a>
82
- <a href="https://huggingface.co/datasets/FINAL-Bench/oqc-data" target="_blank"><img alt="Challenge data" src="https://img.shields.io/badge/%F0%9F%A7%AA%20OQC%20Data-oqc--data-06b6d4"></a>
83
- <a href="https://github.com/final-bench/quantum-bench-harness" target="_blank"><img alt="Harness" src="https://img.shields.io/badge/%F0%9F%92%BB%20Harness-GitHub-181717?logo=github"></a>
84
- <span id="auth" style="margin-left:auto"></span>
85
- </div>
86
- </div>
87
-
88
- <div class="grid" id="season-cards"></div>
89
-
90
- <h2>Live standings</h2>
91
- <div class="tabs" id="tabs"></div>
92
- <table id="lb"><thead><tr><th class="rank">#</th><th>Participant</th><th>Method</th>
93
- <th class="r" id="scorehead">Score</th><th>Status</th></tr></thead><tbody id="lbbody"></tbody></table>
94
- <p class="muted" id="lbmeta" style="font-size:12px;margin-top:8px"></p>
95
-
96
- <h2>How to enter</h2>
97
- <div class="steps">
98
- <div class="step"><b>1 · Get the inputs</b><br>Download from <a href="https://huggingface.co/datasets/FINAL-Bench/oqc-data" target="_blank">FINAL-Bench/oqc-data</a>.
99
- S1: <code>qsim/systems.json</code>. S2: <code>qec/detectors.npy</code> + <code>qec/dem.txt</code>.</div>
100
- <div class="step"><b>2 · Run on your GPU</b><br>Use the open harness
101
- <a href="https://github.com/final-bench/quantum-bench-harness" target="_blank">final-bench/quantum-bench-harness</a>
102
- (or any method) to produce a prediction per item.</div>
103
- <div class="step"><b>3 · Submit below</b><br>Sign in with Hugging Face, paste your predictions JSON. A worker
104
- scores it against the private key and the board updates automatically.</div>
105
- </div>
106
-
107
- <h2>Quick start — copy &amp; run</h2>
108
- <p class="muted" style="font-size:13px;margin:0 0 8px">Reproduce a baseline and build a valid submission file in a few lines. Click a block to copy.</p>
109
- <div class="tabs" id="gtabs">
110
- <div class="tab on" data-g="qsim">Season 1 · QSIM</div>
111
- <div class="tab" data-g="qec">Season 2 · QEC</div>
112
- </div>
113
- <pre class="codeblock" id="g-qsim" title="click to copy">pip install numpy scipy openfermion huggingface_hub
114
- # 1) get the public systems
115
- python - &lt;&lt;'PY'
116
- from huggingface_hub import hf_hub_download
117
- import json
118
- p = hf_hub_download("FINAL-Bench/oqc-data","qsim/systems.json",repo_type="dataset")
119
- sys = json.load(open(p))["systems"]
120
- # 2) compute each ground-state energy with YOUR method (example: exact via openfermion)
121
- from openfermion import fermi_hubbard, get_sparse_operator
122
- import scipy.sparse.linalg as sla
123
- pred = {}
124
- for sid, s in sys.items():
125
- x, y = s["lattice"]
126
- H = fermi_hubbard(x, y, tunneling=s["t"], coulomb=s["U_over_t"]*s["t"], periodic=False)
127
- pred[sid] = float(sla.eigsh(get_sparse_operator(H), k=1, which="SA", return_eigenvectors=False)[0])
128
- json.dump(pred, open("my_qsim.json","w")) # paste the contents of my_qsim.json into the Submit box
129
- print(pred)
130
- PY</pre>
131
- <pre class="codeblock" id="g-qec" style="display:none" title="click to copy">pip install numpy stim pymatching huggingface_hub
132
- python - &lt;&lt;'PY'
133
- from huggingface_hub import hf_hub_download
134
- import numpy as np, stim, pymatching, json
135
- det = np.load(hf_hub_download("FINAL-Bench/oqc-data","qec/detectors.npy",repo_type="dataset"))
136
- dem = stim.DetectorErrorModel(open(hf_hub_download("FINAL-Bench/oqc-data","qec/dem.txt",repo_type="dataset")).read())
137
- # decode each shot with YOUR decoder (example: MWPM baseline)
138
- m = pymatching.Matching.from_detector_error_model(dem)
139
- pred_bits = m.decode_batch(det)[:,0].astype(int)
140
- pred = {"shot_%05d"%i: int(pred_bits[i]) for i in range(det.shape[0])}
141
- json.dump(pred, open("my_qec.json","w")) # paste the contents of my_qec.json into the Submit box
142
- print("items", len(pred))
143
- PY</pre>
144
-
145
- <div class="form">
146
- <h3 style="margin:0 0 4px">Submit an entry</h3>
147
- <p class="muted" style="font-size:13px;margin:0">
148
- Format: <code>{ "&lt;item_id&gt;": value, ... }</code> — QSIM: energy per system id; QEC: 0/1 per <code>shot_00000</code>.</p>
149
- <label>Season / track</label>
150
- <select id="f-board"><option value="qsim">Season 1 · Quantum Simulation (QSIM)</option>
151
- <option value="qec">Season 2 · QEC Decoder (QEC)</option></select>
152
- <label>Display name</label>
153
- <input id="f-name" placeholder="your name, team or HF handle">
154
- <label>Method (short description)</label>
155
- <input id="f-method" placeholder="e.g. HVA-VQE 4 layers / BP+OSD decoder">
156
- <label>Predictions JSON</label>
157
- <textarea id="f-pred" placeholder='{"hub_1x2_U4": -1.0, "hub_1x3_U4": -2.0, ...}'></textarea>
158
- <button class="btn" id="f-submit">Submit entry</button>
159
- <div class="note" id="f-note"></div>
160
- </div>
161
-
162
- <div class="foot">
163
- Open Quantum Challenge · FINAL-Bench. The evaluation harness and frozen problems are public; the answer
164
- key and any competitive method internals are not. Code MIT · benchmark data CC-BY-NC-4.0.
165
- </div>
166
- </div>
167
-
168
- <script>
169
- const $=s=>document.querySelector(s), el=(t,c,h)=>{const e=document.createElement(t);if(c)e.className=c;if(h!=null)e.innerHTML=h;return e;};
170
- let SEASONS=[], CUR="qsim", ME={signed_in:false,oauth_configured:false};
171
-
172
- async function loadMe(){
173
- try{ME=await (await fetch('api/me')).json();}catch(e){ME={signed_in:false,oauth_configured:false};}
174
- const a=$('#auth');
175
- if(ME.signed_in){a.innerHTML='<span class="authuser">signed in as <b>'+esc(ME.user&&ME.user.name)+'</b> · <a href="logout">sign out</a></span>';
176
- const n=$('#f-name'); if(n){n.value=ME.user.name; n.readOnly=true;}}
177
- else if(ME.oauth_configured){a.innerHTML='<a class="authbtn" href="login">🤗 Sign in with Hugging Face</a>';}
178
- else{a.innerHTML='<span class="authuser">sign-in optional</span>';}
179
- }
180
- // guide tab switch
181
- document.querySelectorAll('#gtabs .tab').forEach(t=>t.onclick=()=>{
182
- document.querySelectorAll('#gtabs .tab').forEach(x=>x.classList.remove('on')); t.classList.add('on');
183
- const g=t.dataset.g; $('#g-qsim').style.display=(g==='qsim')?'block':'none'; $('#g-qec').style.display=(g==='qec')?'block':'none';
184
- });
185
- // click-to-copy code blocks
186
- document.querySelectorAll('.codeblock').forEach(b=>b.onclick=()=>{
187
- navigator.clipboard.writeText(b.textContent.replace(/\s*click to copy\s*$/,'')).then(()=>{
188
- b.classList.add('copied'); setTimeout(()=>b.classList.remove('copied'),1500);});
189
- });
190
-
191
- function card(s){
192
- const open=s.accepting, pill=open?'<span class="pill open">OPEN</span>':(s.open?'<span class="pill soon">CLOSED</span>':'<span class="pill soon">OPENS SOON</span>');
193
- const c=el('div','card');
194
- c.innerHTML=`<div class="s">SEASON ${s.number}</div><h3>${s.name} ${pill}</h3>
195
- <div class="meta">${s.title}</div>
196
- <div class="meta">Metric: ${s.metric}</div>
197
- <div class="meta">Opens ${s.opens} · <b>Deadline ${s.closes}</b> · <span class="prize">$${s.prize_usd}</span> (🥇600 / 🥈300 / 🥉100)</div>
198
- <div class="meta">Input: <code>${s.input}</code> — ${s.input_note}</div>`;
199
- return c;
200
- }
201
- function tab(s){
202
- const t=el('div','tab'+(s.board===CUR?' on':''),`S${s.number} · ${s.name}`);
203
- t.onclick=()=>{CUR=s.board;document.querySelectorAll('.tab').forEach(x=>x.classList.remove('on'));t.classList.add('on');loadBoard();};
204
- return t;
205
- }
206
- async function boot(){
207
- const d=await (await fetch('api/seasons')).json(); SEASONS=d.seasons;
208
- const cc=$('#season-cards'); cc.innerHTML=''; SEASONS.forEach(s=>cc.appendChild(card(s)));
209
- const tt=$('#tabs'); tt.innerHTML=''; SEASONS.forEach(s=>tt.appendChild(tab(s)));
210
- loadBoard();
211
- }
212
- async function loadBoard(){
213
- const s=SEASONS.find(x=>x.board===CUR)||{};
214
- $('#scorehead').textContent=s.col||'Score';
215
- const d=await (await fetch('api/leaderboard?board='+CUR)).json();
216
- const b=$('#lbbody'); b.innerHTML='';
217
- if(!d.entries||!d.entries.length){b.appendChild(el('tr',null,'<td colspan="5" class="muted" style="text-align:center;padding:20px">No entries yet — be the first.</td>'));}
218
- else d.entries.forEach(e=>{
219
- const tr=el('tr'); const v=e.verified?'<span class="tagv v">VERIFIED</span>':'<span class="tagv a">AUTO-SCORED</span>';
220
- const sc=(typeof e.score==='number')?e.score.toFixed(5):'—';
221
- tr.innerHTML=`<td class="rank">${e.rank}</td><td>${esc(e.participant)}</td><td class="muted">${esc(e.method||'—')}</td><td class="r">${sc}</td><td>${v}</td>`;
222
- b.appendChild(tr);
223
- });
224
- $('#lbmeta').textContent=d.updated?('Updated '+new Date(d.updated*1000).toLocaleString()+' · '+d.n+' ranked'):(d.n+' ranked');
225
- }
226
- function esc(x){return (x==null?'':(''+x)).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));}
227
-
228
- $('#f-submit').onclick=async()=>{
229
- const note=$('#f-note'); note.className='note';
230
- if(ME.oauth_configured && !ME.signed_in){note.className='note err';note.innerHTML='Please <a href="login">sign in with Hugging Face</a> to submit.';return;}
231
- let preds; try{preds=JSON.parse($('#f-pred').value);}catch(e){note.className='note err';note.textContent='Predictions must be valid JSON.';return;}
232
- const btn=$('#f-submit'); btn.disabled=true; btn.textContent='Submitting…';
233
- try{
234
- const r=await fetch('api/submit',{method:'POST',headers:{'Content-Type':'application/json'},
235
- body:JSON.stringify({board:$('#f-board').value,display_name:$('#f-name').value,method:$('#f-method').value,predictions:preds})});
236
- const j=await r.json();
237
- if(j.accepted){note.className='note ok';note.textContent='Accepted ('+j.submission_id+'). '+(j.note||'');loadBoard();}
238
- else{note.className='note err';note.textContent=(j.reasons||['rejected']).join(' · ');}
239
- }catch(e){note.className='note err';note.textContent='Network error — try again.';}
240
- btn.disabled=false; btn.textContent='Submit entry';
241
- };
242
- loadMe(); boot(); setInterval(loadBoard, 30000);
243
- </script>
244
- </body>
245
- </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">
6
+ <title>Open Quantum Challenge</title>
7
+ <style>
8
+ :root{--bg:#f6f8fc;--panel:#ffffff;--line:#e3e9f2;--ink:#131a26;--mut:#65748c;
9
+ --acc:#0f7b6c;--acc-soft:#e6f5f2;--gold:#b4740a;--gold-soft:#fdf4e3;--ok:#0f7b6c;--bad:#b3261e;
10
+ --shadow:0 1px 2px rgba(19,26,38,.05),0 8px 24px rgba(19,26,38,.06);}
11
+ *{box-sizing:border-box}
12
+ html,body{margin:0;padding:0}
13
+ body{background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,"Segoe UI",Roboto,"Malgun Gothic","Apple SD Gothic Neo","Microsoft YaHei",sans-serif;-webkit-font-smoothing:antialiased}
14
+ a{color:var(--acc)}
15
+ .wrap{max-width:1100px;margin:0 auto;padding:26px 20px 70px}
16
+ .hero{position:relative;background:linear-gradient(135deg,#ffffff 0%,#eef6f4 55%,#e7f0fb 100%);border:1px solid var(--line);border-radius:16px;padding:24px 26px;box-shadow:var(--shadow)}
17
+ .hero-tag{display:inline-block;color:var(--acc);font-size:11.5px;font-weight:700;letter-spacing:2.2px;text-transform:uppercase;margin-bottom:6px}
18
+ h1{margin:0;font-size:30px;letter-spacing:-.5px;font-weight:800}
19
+ .sub{color:var(--mut);font-size:14px;margin:10px 0 0;max-width:72ch}
20
+ #langbar{position:absolute;top:18px;right:18px;display:flex;gap:2px;background:#fff;border:1px solid var(--line);border-radius:9px;padding:3px;box-shadow:var(--shadow)}
21
+ #langbar button{border:0;background:transparent;color:var(--mut);font:inherit;font-size:12px;font-weight:700;padding:5px 10px;border-radius:6px;cursor:pointer}
22
+ #langbar button.on{background:var(--acc);color:#fff}
23
+ .badges{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:16px 0 0}
24
+ .badges img{height:20px}
25
+ .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:18px 0}
26
+ @media(max-width:720px){.grid{grid-template-columns:1fr}}
27
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:16px;box-shadow:var(--shadow)}
28
+ .card h3{margin:0 0 2px;font-size:17px;font-weight:800;color:#1c2a3d}
29
+ .card .s{color:var(--acc);font-size:11px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase}
30
+ .meta{color:var(--mut);font-size:13px;margin:6px 0 2px}
31
+ code,.mono{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;background:#f0f4fa;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:#31465e}
32
+ .pill{display:inline-block;font-size:10.5px;font-weight:800;border-radius:999px;padding:1px 9px;margin-left:6px;letter-spacing:.03em}
33
+ .pill.open{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
34
+ .pill.soon{background:#eef3fa;color:var(--mut);border:1px solid var(--line)}
35
+ .prize{color:var(--gold);font-weight:800}
36
+ .tabs{display:flex;gap:6px;margin:24px 0 10px;flex-wrap:wrap}
37
+ .tab{padding:7px 14px;border:1px solid var(--line);border-radius:999px;background:#fff;cursor:pointer;color:var(--mut);font-weight:700;font-size:12.5px}
38
+ .tab.on{color:#fff;border-color:var(--acc);background:var(--acc)}
39
+ table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden;box-shadow:var(--shadow)}
40
+ th,td{padding:11px 13px;text-align:left;border-bottom:1px solid var(--line);font-size:14px}
41
+ th{background:#f3f7fc;color:var(--mut);font-size:11px;letter-spacing:.5px;text-transform:uppercase;font-weight:800}
42
+ tr:last-child td{border-bottom:none}
43
+ td.r,th.r{text-align:right;font-variant-numeric:tabular-nums}
44
+ .rank{width:54px;text-align:center;font-weight:800;color:var(--acc)}
45
+ .tagv{font-size:10.5px;font-weight:800;border-radius:6px;padding:1px 8px}
46
+ .tagv.v{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
47
+ .tagv.a{background:var(--gold-soft);color:var(--gold);border:1px solid #ecd9ad}
48
+ h2{font-size:20px;margin:30px 0 6px;font-weight:800;letter-spacing:-.2px}
49
+ .muted{color:var(--mut)}
50
+ .steps{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin:12px 0}
51
+ @media(max-width:720px){.steps{grid-template-columns:1fr}}
52
+ .step{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px;box-shadow:var(--shadow)}
53
+ .step b{color:var(--acc)}
54
+ .form{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px;margin-top:10px;box-shadow:var(--shadow)}
55
+ label{display:block;font-size:13px;color:var(--mut);margin:10px 0 4px;font-weight:600}
56
+ input,select,textarea{width:100%;background:#fff;border:1px solid var(--line);border-radius:9px;color:var(--ink);padding:9px 10px;font:inherit}
57
+ input:focus,select:focus,textarea:focus{outline:none;border-color:var(--acc)}
58
+ textarea{min-height:120px;font-family:ui-monospace,monospace;font-size:12px}
59
+ .btn{margin-top:14px;background:var(--acc);color:#fff;border:none;border-radius:10px;padding:11px 22px;font-weight:800;cursor:pointer}
60
+ .btn:hover{background:#0c6558}
61
+ .btn:disabled{opacity:.5;cursor:not-allowed}
62
+ .note{margin-top:10px;font-size:13px;padding:10px 12px;border-radius:9px;display:none}
63
+ .note.ok{display:block;background:var(--acc-soft);border:1px solid #bfe3da;color:#13503f}
64
+ .note.err{display:block;background:#fdeceb;border:1px solid #f3c7c3;color:var(--bad)}
65
+ .foot{margin-top:40px;color:var(--mut);font-size:12px;border-top:1px solid var(--line);padding-top:14px}
66
+ .codeblock{position:relative;background:#0d1b2a;border:1px solid #16314a;border-radius:12px;padding:14px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;line-height:1.55;color:#d7e6ff;cursor:copy;white-space:pre}
67
+ .codeblock:hover{border-color:var(--acc)}
68
+ .codeblock::after{content:attr(data-copy);position:absolute;top:8px;right:10px;font-size:10px;color:#7f97b8}
69
+ .codeblock.copied::after{content:"OK";color:#67e0b0}
70
+ .authbtn{display:inline-flex;align-items:center;gap:6px;background:#fff;color:var(--acc);border:1px solid var(--acc);border-radius:999px;padding:4px 12px;font-weight:800;font-size:12px;text-decoration:none}
71
+ .authuser{font-size:12px;color:var(--mut)}
72
+ .authuser a{color:var(--acc)}
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div class="wrap">
77
+ <div class="hero">
78
+ <div id="langbar">
79
+ <button data-l="en">EN</button><button data-l="zh">中文</button><button data-l="ko">한국어</button>
80
+ </div>
81
+ <span class="hero-tag" data-i18n="eyebrow"></span>
82
+ <h1>Open Quantum Challenge <span style="color:var(--mut);font-size:16px">(OQC)</span></h1>
83
+ <p class="sub" data-i18n="sub"></p>
84
+ <div class="badges" id="badges">
85
+ <a href="https://huggingface.co/spaces/FINAL-Bench/quantum-bench-leaderboard" target="_blank"><img alt="Leaderboard" src="https://img.shields.io/badge/%F0%9F%8F%86%20Leaderboard-HF%20Space-0f7b6c"></a>
86
+ <a href="https://huggingface.co/datasets/FINAL-Bench/oqc-data" target="_blank"><img alt="OQC data" src="https://img.shields.io/badge/%F0%9F%A7%AA%20OQC%20Data-oqc--data-0ea5b7"></a>
87
+ <a href="https://github.com/final-bench/quantum-bench-harness" target="_blank"><img alt="Harness" src="https://img.shields.io/badge/%F0%9F%92%BB%20Harness-GitHub-24292f?logo=github"></a>
88
+ <span id="auth" style="margin-left:auto"></span>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="grid" id="season-cards"></div>
93
+
94
+ <h2 data-i18n="lb_h"></h2>
95
+ <div class="tabs" id="tabs"></div>
96
+ <table id="lb"><thead><tr><th class="rank">#</th><th data-i18n="th_part"></th><th data-i18n="th_method"></th>
97
+ <th class="r" id="scorehead">Score</th><th data-i18n="th_status"></th></tr></thead><tbody id="lbbody"></tbody></table>
98
+ <p class="muted" id="lbmeta" style="font-size:12px;margin-top:8px"></p>
99
+
100
+ <h2 data-i18n="how_h"></h2>
101
+ <div class="steps">
102
+ <div class="step"><b data-i18n="step1_t"></b><br><span data-i18n="step1_b"></span></div>
103
+ <div class="step"><b data-i18n="step2_t"></b><br><span data-i18n="step2_b"></span></div>
104
+ <div class="step"><b data-i18n="step3_t"></b><br><span data-i18n="step3_b"></span></div>
105
+ </div>
106
+
107
+ <h2 data-i18n="qs_h"></h2>
108
+ <p class="muted" data-i18n="qs_d" style="font-size:13px;margin:0 0 8px"></p>
109
+ <div class="tabs" id="gtabs">
110
+ <div class="tab on" data-g="qsim">Season 1 · QSIM</div>
111
+ <div class="tab" data-g="qec">Season 2 · QEC</div>
112
+ </div>
113
+ <pre class="codeblock" id="g-qsim" data-copy="click to copy">pip install numpy scipy openfermion huggingface_hub
114
+ python - &lt;&lt;'PY'
115
+ from huggingface_hub import hf_hub_download
116
+ import json
117
+ p = hf_hub_download("FINAL-Bench/oqc-data","qsim/systems.json",repo_type="dataset")
118
+ sysd = json.load(open(p))["systems"]
119
+ from openfermion import fermi_hubbard, get_sparse_operator
120
+ import scipy.sparse.linalg as sla
121
+ pred = {}
122
+ for sid, s in sysd.items():
123
+ x, y = s["lattice"]
124
+ H = fermi_hubbard(x, y, tunneling=s["t"], coulomb=s["U_over_t"]*s["t"], periodic=False)
125
+ pred[sid] = float(sla.eigsh(get_sparse_operator(H), k=1, which="SA", return_eigenvectors=False)[0])
126
+ json.dump(pred, open("my_qsim.json","w")) # paste my_qsim.json into the Submit box
127
+ print(pred)
128
+ PY</pre>
129
+ <pre class="codeblock" id="g-qec" style="display:none" data-copy="click to copy">pip install numpy stim pymatching huggingface_hub
130
+ python - &lt;&lt;'PY'
131
+ from huggingface_hub import hf_hub_download
132
+ import numpy as np, stim, pymatching, json
133
+ det = np.load(hf_hub_download("FINAL-Bench/oqc-data","qec/detectors.npy",repo_type="dataset"))
134
+ dem = stim.DetectorErrorModel(open(hf_hub_download("FINAL-Bench/oqc-data","qec/dem.txt",repo_type="dataset")).read())
135
+ m = pymatching.Matching.from_detector_error_model(dem)
136
+ bits = m.decode_batch(det)[:,0].astype(int)
137
+ pred = {"shot_%05d"%i: int(bits[i]) for i in range(det.shape[0])}
138
+ json.dump(pred, open("my_qec.json","w")) # paste my_qec.json into the Submit box
139
+ print("items", len(pred))
140
+ PY</pre>
141
+
142
+ <div class="form">
143
+ <h3 style="margin:0 0 4px" data-i18n="submit_h"></h3>
144
+ <p class="muted" data-i18n="f_fmt" style="font-size:13px;margin:0"></p>
145
+ <label data-i18n="l_board"></label>
146
+ <select id="f-board"><option value="qsim">Season 1 · Quantum Simulation (QSIM)</option>
147
+ <option value="qec">Season 2 · QEC Decoder (QEC)</option></select>
148
+ <label data-i18n="l_name"></label>
149
+ <input id="f-name" data-i18n-ph="ph_name">
150
+ <label data-i18n="l_method"></label>
151
+ <input id="f-method" data-i18n-ph="ph_method">
152
+ <label data-i18n="l_pred"></label>
153
+ <textarea id="f-pred" placeholder='{"hub_1x2_U4": -1.0, "hub_1x3_U4": -2.0}'></textarea>
154
+ <button class="btn" id="f-submit" data-i18n="btn_submit"></button>
155
+ <div class="note" id="f-note"></div>
156
+ </div>
157
+
158
+ <div class="foot" data-i18n="foot"></div>
159
+ </div>
160
+
161
+ <script>
162
+ const $=s=>document.querySelector(s), el=(t,c,h)=>{const e=document.createElement(t);if(c)e.className=c;if(h!=null)e.innerHTML=h;return e;};
163
+ const I18N={
164
+ en:{eyebrow:"FINAL-Bench · vendor-neutral · fully reproducible",
165
+ sub:"Contribute to the quantum ecosystem with just a GPU and the open harness — no quantum hardware required. Answers are held privately and every submission is auto-scored against a frozen ground truth, so the ranking is reproducible and hardware-independent. Two seasons, $1,000 prize each.",
166
+ lb_h:"Live standings", th_part:"Participant", th_method:"Method", th_status:"Status",
167
+ empty:"No entries yet — be the first.", updated:"Updated", ranked:"ranked",
168
+ how_h:"How to enter",
169
+ step1_t:"1 · Get the inputs", step1_b:"Download from <a href='https://huggingface.co/datasets/FINAL-Bench/oqc-data' target='_blank'>FINAL-Bench/oqc-data</a>. S1: <code>qsim/systems.json</code>. S2: <code>qec/detectors.npy</code> + <code>qec/dem.txt</code>.",
170
+ step2_t:"2 · Run on your GPU", step2_b:"Use the open harness <a href='https://github.com/final-bench/quantum-bench-harness' target='_blank'>final-bench/quantum-bench-harness</a> (or any method) to produce a prediction per item.",
171
+ step3_t:"3 · Submit below", step3_b:"Sign in with Hugging Face, paste your predictions JSON. A worker scores it against the private key and the board updates automatically.",
172
+ qs_h:"Quick start — copy & run", qs_d:"Reproduce a baseline and build a valid submission in a few lines. Click a block to copy.",
173
+ submit_h:"Submit an entry", f_fmt:"Format: <code>{ \"&lt;item_id&gt;\": value, ... }</code> — QSIM: energy per system id; QEC: 0/1 per <code>shot_00000</code>.",
174
+ l_board:"Season / track", l_name:"Display name", l_method:"Method (short description)", l_pred:"Predictions JSON",
175
+ ph_name:"your name, team or HF handle", ph_method:"e.g. HVA-VQE 4 layers / BP+OSD decoder", btn_submit:"Submit entry",
176
+ foot:"Open Quantum Challenge · FINAL-Bench. The evaluation harness and frozen problems are public; the answer key and any competitive method internals are not. Code MIT · benchmark data CC-BY-NC-4.0.",
177
+ auth_signin:"🤗 Sign in with Hugging Face", auth_optional:"sign-in optional", auth_as:"signed in as", auth_out:"sign out",
178
+ c_season:"SEASON", c_metric:"Metric:", c_opens:"Opens", c_deadline:"Deadline", c_prize:"Prize", c_input:"Input:",
179
+ p_open:"OPEN", p_closed:"CLOSED", p_soon:"OPENS SOON", copy:"click to copy",
180
+ need_login:"Please <a href='login'>sign in with Hugging Face</a> to submit.", bad_json:"Predictions must be valid JSON.",
181
+ net_err:"Network error — try again.", submitting:"Submitting…",
182
+ seasons:{qsim:{name:"Quantum Simulation",title:"Fermi-Hubbard ground-state energy",metric:"MAE of reported ground energy vs exact (lower wins)",note:"qsim/systems.json — lattice, U/t and qubit count per system"},
183
+ qec:{name:"QEC Decoder",title:"Rotated surface code d=5, p=0.005 (circuit-level)",metric:"logical error rate (lower wins)",note:"qec/detectors.npy (shots × detectors) + qec/dem.txt"}}},
184
+ zh:{eyebrow:"FINAL-Bench · 厂商中立 · 完全可复现",
185
+ sub:"只需一块 GPU 和开放测评工具即可为量子生态做贡献——无需量子硬件。答案保密,每次提交都会对照冻结的标准答案自动评分,因此排名可复现、与硬件无关。两个赛季,各 $1,000 奖金。",
186
+ lb_h:"实时榜单", th_part:"参赛者", th_method:"方法", th_status:"状态",
187
+ empty:"暂无提交——成为第一个。", updated:"更新于", ranked:"已���名",
188
+ how_h:"参赛方式",
189
+ step1_t:"1 · 获取输入", step1_b:"从 <a href='https://huggingface.co/datasets/FINAL-Bench/oqc-data' target='_blank'>FINAL-Bench/oqc-data</a> 下载。S1:<code>qsim/systems.json</code>。S2:<code>qec/detectors.npy</code> + <code>qec/dem.txt</code>。",
190
+ step2_t:"2 · 在你的 GPU 上运行", step2_b:"使用开放测评工具 <a href='https://github.com/final-bench/quantum-bench-harness' target='_blank'>final-bench/quantum-bench-harness</a>(或任意方法)为每个条目生成预测。",
191
+ step3_t:"3 · 在下方提交", step3_b:"用 Hugging Face 登录,粘贴你的预测 JSON。评分程序会对照私有答案评分,榜单自动更新。",
192
+ qs_h:"快速开始 — 复制运行", qs_d:"几行代码即可复现基线并生成有效提交。点击代码块复制。",
193
+ submit_h:"提交参赛", f_fmt:"格式:<code>{ \"&lt;item_id&gt;\": value, ... }</code> — QSIM:每个系统的能量;QEC:每个 <code>shot_00000</code> 的 0/1。",
194
+ l_board:"赛季 / 赛道", l_name:"显示名称", l_method:"方法(简短描述)", l_pred:"预测 JSON",
195
+ ph_name:"你的名字、团队或 HF 账号", ph_method:"例如 HVA-VQE 4 层 / BP+OSD 解码器", btn_submit:"提交",
196
+ foot:"Open Quantum Challenge · FINAL-Bench。测评工具与冻结题目公开;答案与任何竞赛方法内部实现不公开。代码 MIT · 基准数据 CC-BY-NC-4.0。",
197
+ auth_signin:"🤗 使用 Hugging Face 登录", auth_optional:"可选登录", auth_as:"已登录:", auth_out:"退出",
198
+ c_season:"赛季", c_metric:"指标:", c_opens:"开始", c_deadline:"截止", c_prize:"奖金", c_input:"输入:",
199
+ p_open:"进行中", p_closed:"已截止", p_soon:"即将开始", copy:"点击复制",
200
+ need_login:"请先用 <a href='login'>Hugging Face 登录</a> 再提交。", bad_json:"预测必须是有效的 JSON。",
201
+ net_err:"网络错误——请重试。", submitting:"提交中…",
202
+ seasons:{qsim:{name:"量子模拟",title:"费米-哈伯德基态能量",metric:"报告基态能量与精确值的 MAE(越低越好)",note:"qsim/systems.json — 晶格、U/t 与量子比特数"},
203
+ qec:{name:"量子纠错解码器",title:"旋转表面码 d=5, p=0.005(电路级)",metric:"逻辑错误率(越低越好)",note:"qec/detectors.npy(样本 × 探测器)+ qec/dem.txt"}}},
204
+ ko:{eyebrow:"FINAL-Bench · 벤더 중립 · 완전 재현 가능",
205
+ sub:"GPU와 공개 하네스만으로 양자 생태계에 기여하세요 — 양자 하드웨어가 필요 없습니다. 정답은 비공개로 보관되며 모든 제출은 동결된 정답과 자동 채점되어, 순위가 재현 가능하고 하드웨어에 무관합니다. 2개 시즌, 각 $1,000 상금.",
206
+ lb_h:"실시간 순위", th_part:"참가자", th_method:"방법", th_status:"상태",
207
+ empty:"아직 제출이 없습니다 — 첫 주자가 되세요.", updated:"업데이트", ranked:"순위",
208
+ how_h:"참가 방법",
209
+ step1_t:"1 · 입력 받기", step1_b:"<a href='https://huggingface.co/datasets/FINAL-Bench/oqc-data' target='_blank'>FINAL-Bench/oqc-data</a>에서 받으세요. S1: <code>qsim/systems.json</code>. S2: <code>qec/detectors.npy</code> + <code>qec/dem.txt</code>.",
210
+ step2_t:"2 · 내 GPU에서 실행", step2_b:"공개 하네스 <a href='https://github.com/final-bench/quantum-bench-harness' target='_blank'>final-bench/quantum-bench-harness</a>(또는 임의 방법)로 항목마다 예측을 만드세요.",
211
+ step3_t:"3 · 아래에서 제출", step3_b:"Hugging Face로 로그인하고 예측 JSON을 붙여넣으세요. 워커가 비공개 정답과 채점하고 순위가 자동 갱신됩니다.",
212
+ qs_h:"퀵스타트 — 복사 실행", qs_d:"몇 줄로 베이스라인을 재현하고 유효한 제출을 만듭니다. 블록을 클릭하면 복사됩니다.",
213
+ submit_h:"항목 제출", f_fmt:"형식: <code>{ \"&lt;item_id&gt;\": value, ... }</code> — QSIM: 시스템별 에너지, QEC: <code>shot_00000</code>별 0/1.",
214
+ l_board:"시즌 / 트랙", l_name:"표시 이름", l_method:"방법 (간단 설명)", l_pred:"예측 JSON",
215
+ ph_name:"이름, 팀 또는 HF 핸들", ph_method:"예: HVA-VQE 4층 / BP+OSD 디코더", btn_submit:"제출",
216
+ foot:"Open Quantum Challenge · FINAL-Bench. 평가 하네스와 동결 문제는 공개, 정답 키와 경쟁 기법 내부는 비공개. 코드 MIT · 벤치마크 데이터 CC-BY-NC-4.0.",
217
+ auth_signin:"🤗 Hugging Face로 로그인", auth_optional:"로그인 선택", auth_as:"로그인:", auth_out:"로그아웃",
218
+ c_season:"시즌", c_metric:"지표:", c_opens:"개시", c_deadline:"마감", c_prize:"상금", c_input:"입력:",
219
+ p_open:"접수중", p_closed:"마감", p_soon:"개시 예정", copy:"클릭하여 복사",
220
+ need_login:"제출하려면 <a href='login'>Hugging Face로 로그인</a>하세요.", bad_json:"예측은 유효한 JSON이어야 합니다.",
221
+ net_err:"네트워크 오류 — 다시 시도하세요.", submitting:"제출 중…",
222
+ seasons:{qsim:{name:"양자 시뮬레이션",title:"페르미-허버드 바닥상태 에너지",metric:"보고 에너지와 정확해의 MAE (낮을수록 우승)",note:"qsim/systems.json — 격자, U/t, 큐빗 수"},
223
+ qec:{name:"QEC 디코더",title:"회전 표면코드 d=5, p=0.005 (회로수준)",metric:"논리 오류율 (낮을수록 우승)",note:"qec/detectors.npy (샷 × 디텍터) + qec/dem.txt"}}}
224
+ };
225
+ let LANG=(localStorage.getItem('oqc_lang')||(navigator.language||'en').slice(0,2).toLowerCase());
226
+ if(!I18N[LANG]) LANG='en';
227
+ function t(k){return (I18N[LANG]&&I18N[LANG][k])||I18N.en[k]||k;}
228
+ function ts(board,field){const s=(I18N[LANG].seasons||{})[board];return (s&&s[field])||(I18N.en.seasons[board]||{})[field]||'';}
229
+ function esc(x){return (x==null?'':(''+x)).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));}
230
+
231
+ let SEASONS=[], CUR="qsim", ME={signed_in:false,oauth_configured:false};
232
+
233
+ function applyLang(){
234
+ document.querySelectorAll('[data-i18n]').forEach(e=>e.innerHTML=t(e.dataset.i18n));
235
+ document.querySelectorAll('[data-i18n-ph]').forEach(e=>e.placeholder=t(e.dataset.i18nPh));
236
+ document.querySelectorAll('.codeblock').forEach(e=>e.setAttribute('data-copy',t('copy')));
237
+ document.documentElement.lang=LANG;
238
+ document.querySelectorAll('#langbar button').forEach(b=>b.classList.toggle('on',b.dataset.l===LANG));
239
+ renderCards(); renderTabs(); loadBoard(); renderAuth();
240
+ }
241
+ function setLang(l){LANG=l;try{localStorage.setItem('oqc_lang',l);}catch(e){} applyLang();}
242
+ document.querySelectorAll('#langbar button').forEach(b=>b.onclick=()=>setLang(b.dataset.l));
243
+
244
+ function renderAuth(){
245
+ const a=$('#auth');
246
+ if(ME.signed_in){a.innerHTML='<span class="authuser">'+t('auth_as')+' <b>'+esc(ME.user&&ME.user.name)+'</b> · <a href="logout">'+t('auth_out')+'</a></span>';
247
+ const n=$('#f-name'); if(n){n.value=ME.user.name; n.readOnly=true;}}
248
+ else if(ME.oauth_configured){a.innerHTML='<a class="authbtn" href="login">'+t('auth_signin')+'</a>';}
249
+ else{a.innerHTML='<span class="authuser">'+t('auth_optional')+'</span>';}
250
+ }
251
+ async function loadMe(){try{ME=await (await fetch('api/me')).json();}catch(e){} renderAuth();}
252
+
253
+ function renderCards(){
254
+ const cc=$('#season-cards'); if(!cc) return; cc.innerHTML='';
255
+ SEASONS.forEach(s=>{
256
+ const open=s.accepting, pill=open?'<span class="pill open">'+t('p_open')+'</span>':(s.open?'<span class="pill soon">'+t('p_closed')+'</span>':'<span class="pill soon">'+t('p_soon')+'</span>');
257
+ const c=el('div','card');
258
+ c.innerHTML=`<div class="s">${t('c_season')} ${s.number}</div><h3>${ts(s.board,'name')} ${pill}</h3>
259
+ <div class="meta">${ts(s.board,'title')}</div>
260
+ <div class="meta">${t('c_metric')} ${ts(s.board,'metric')}</div>
261
+ <div class="meta">${t('c_opens')} ${s.opens} · <b>${t('c_deadline')} ${s.closes}</b> · <span class="prize">$${s.prize_usd}</span> (🥇600 / 🥈300 / 🥉100)</div>
262
+ <div class="meta">${t('c_input')} <code>${s.input}</code> — ${ts(s.board,'note')}</div>`;
263
+ cc.appendChild(c);
264
+ });
265
+ }
266
+ function renderTabs(){
267
+ const tt=$('#tabs'); if(!tt) return; tt.innerHTML='';
268
+ SEASONS.forEach(s=>{
269
+ const b=el('div','tab'+(s.board===CUR?' on':''),`S${s.number} · ${ts(s.board,'name')}`);
270
+ b.onclick=()=>{CUR=s.board;renderTabs();loadBoard();};
271
+ tt.appendChild(b);
272
+ });
273
+ }
274
+ async function loadBoard(){
275
+ const s=SEASONS.find(x=>x.board===CUR)||{};
276
+ $('#scorehead').textContent=s.col||'Score';
277
+ let d; try{d=await (await fetch('api/leaderboard?board='+CUR)).json();}catch(e){return;}
278
+ const b=$('#lbbody'); b.innerHTML='';
279
+ if(!d.entries||!d.entries.length){b.appendChild(el('tr',null,'<td colspan="5" class="muted" style="text-align:center;padding:20px">'+t('empty')+'</td>'));}
280
+ else d.entries.forEach(e=>{
281
+ const tr=el('tr'); const v=e.verified?'<span class="tagv v">VERIFIED</span>':'<span class="tagv a">AUTO-SCORED</span>';
282
+ const sc=(typeof e.score==='number')?e.score.toFixed(5):'—';
283
+ tr.innerHTML=`<td class="rank">${e.rank}</td><td>${esc(e.participant)}</td><td class="muted">${esc(e.method||'—')}</td><td class="r">${sc}</td><td>${v}</td>`;
284
+ b.appendChild(tr);
285
+ });
286
+ $('#lbmeta').textContent=d.updated?(t('updated')+' '+new Date(d.updated*1000).toLocaleString()+' · '+d.n+' '+t('ranked')):(d.n+' '+t('ranked'));
287
+ }
288
+
289
+ document.querySelectorAll('#gtabs .tab').forEach(x=>x.onclick=()=>{
290
+ document.querySelectorAll('#gtabs .tab').forEach(y=>y.classList.remove('on')); x.classList.add('on');
291
+ const g=x.dataset.g; $('#g-qsim').style.display=(g==='qsim')?'block':'none'; $('#g-qec').style.display=(g==='qec')?'block':'none';
292
+ });
293
+ document.querySelectorAll('.codeblock').forEach(bl=>bl.onclick=()=>{
294
+ navigator.clipboard.writeText(bl.textContent).then(()=>{bl.classList.add('copied');setTimeout(()=>bl.classList.remove('copied'),1500);});
295
+ });
296
+
297
+ $('#f-submit').onclick=async()=>{
298
+ const note=$('#f-note'); note.className='note';
299
+ if(ME.oauth_configured && !ME.signed_in){note.className='note err';note.innerHTML=t('need_login');return;}
300
+ let preds; try{preds=JSON.parse($('#f-pred').value);}catch(e){note.className='note err';note.textContent=t('bad_json');return;}
301
+ const btn=$('#f-submit'); btn.disabled=true; btn.textContent=t('submitting');
302
+ try{
303
+ const r=await fetch('api/submit',{method:'POST',headers:{'Content-Type':'application/json'},
304
+ body:JSON.stringify({board:$('#f-board').value,display_name:$('#f-name').value,method:$('#f-method').value,predictions:preds})});
305
+ const j=await r.json();
306
+ if(j.accepted){note.className='note ok';note.textContent='✓ '+(j.submission_id||'')+' · '+(j.note||'');loadBoard();}
307
+ else{note.className='note err';note.textContent=(j.reasons||['rejected']).join(' · ');}
308
+ }catch(e){note.className='note err';note.textContent=t('net_err');}
309
+ btn.disabled=false; btn.textContent=t('btn_submit');
310
+ };
311
+
312
+ async function boot(){
313
+ try{const d=await (await fetch('api/seasons')).json(); SEASONS=d.seasons;}catch(e){SEASONS=[];}
314
+ loadMe(); applyLang();
315
+ }
316
+ boot(); setInterval(loadBoard, 30000);
317
+ </script>
318
+ </body>
319
+ </html>