SeaWolf-AI's picture
S1 revision: lowest-energy ranking, larger systems, validity gate, anchors/metric updated
d421899 verified
Raw History Blame Contribute Delete
26.5 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Open Quantum Challenge</title>
<style>
:root{--bg:#f6f8fc;--panel:#ffffff;--line:#e3e9f2;--ink:#131a26;--mut:#65748c;
--acc:#0f7b6c;--acc-soft:#e6f5f2;--gold:#b4740a;--gold-soft:#fdf4e3;--ok:#0f7b6c;--bad:#b3261e;
--shadow:0 1px 2px rgba(19,26,38,.05),0 8px 24px rgba(19,26,38,.06);}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
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}
a{color:var(--acc)}
.wrap{max-width:1100px;margin:0 auto;padding:26px 20px 70px}
.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)}
.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}
h1{margin:0;font-size:30px;letter-spacing:-.5px;font-weight:800}
.sub{color:var(--mut);font-size:14px;margin:10px 0 0;max-width:72ch}
#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)}
#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}
#langbar button.on{background:var(--acc);color:#fff}
.badges{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:16px 0 0}
.badges img{height:20px}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:18px 0}
@media(max-width:720px){.grid{grid-template-columns:1fr}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:16px;box-shadow:var(--shadow)}
.card h3{margin:0 0 2px;font-size:17px;font-weight:800;color:#1c2a3d}
.card .s{color:var(--acc);font-size:11px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase}
.meta{color:var(--mut);font-size:13px;margin:6px 0 2px}
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}
.pill{display:inline-block;font-size:10.5px;font-weight:800;border-radius:999px;padding:1px 9px;margin-left:6px;letter-spacing:.03em}
.pill.open{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
.pill.soon{background:#eef3fa;color:var(--mut);border:1px solid var(--line)}
.prize{color:var(--gold);font-weight:800}
.tabs{display:flex;gap:6px;margin:24px 0 10px;flex-wrap:wrap}
.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}
.tab.on{color:#fff;border-color:var(--acc);background:var(--acc)}
table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden;box-shadow:var(--shadow)}
th,td{padding:11px 13px;text-align:left;border-bottom:1px solid var(--line);font-size:14px}
th{background:#f3f7fc;color:var(--mut);font-size:11px;letter-spacing:.5px;text-transform:uppercase;font-weight:800}
tr:last-child td{border-bottom:none}
td.r,th.r{text-align:right;font-variant-numeric:tabular-nums}
.rank{width:54px;text-align:center;font-weight:800;color:var(--acc)}
.tagv{font-size:10.5px;font-weight:800;border-radius:6px;padding:1px 8px}
.tagv.v{background:var(--acc-soft);color:var(--acc);border:1px solid #bfe3da}
.tagv.a{background:var(--gold-soft);color:var(--gold);border:1px solid #ecd9ad}
h2{font-size:20px;margin:30px 0 6px;font-weight:800;letter-spacing:-.2px}
.muted{color:var(--mut)}
.steps{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin:12px 0}
@media(max-width:720px){.steps{grid-template-columns:1fr}}
.step{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px;box-shadow:var(--shadow)}
.step b{color:var(--acc)}
.form{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px;margin-top:10px;box-shadow:var(--shadow)}
label{display:block;font-size:13px;color:var(--mut);margin:10px 0 4px;font-weight:600}
input,select,textarea{width:100%;background:#fff;border:1px solid var(--line);border-radius:9px;color:var(--ink);padding:9px 10px;font:inherit}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--acc)}
textarea{min-height:120px;font-family:ui-monospace,monospace;font-size:12px}
.btn{margin-top:14px;background:var(--acc);color:#fff;border:none;border-radius:10px;padding:11px 22px;font-weight:800;cursor:pointer}
.btn:hover{background:#0c6558}
.btn:disabled{opacity:.5;cursor:not-allowed}
.note{margin-top:10px;font-size:13px;padding:10px 12px;border-radius:9px;display:none}
.note.ok{display:block;background:var(--acc-soft);border:1px solid #bfe3da;color:#13503f}
.note.err{display:block;background:#fdeceb;border:1px solid #f3c7c3;color:var(--bad)}
.foot{margin-top:40px;color:var(--mut);font-size:12px;border-top:1px solid var(--line);padding-top:14px}
.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}
.codeblock:hover{border-color:var(--acc)}
.codeblock::after{content:attr(data-copy);position:absolute;top:8px;right:10px;font-size:10px;color:#7f97b8}
.codeblock.copied::after{content:"OK";color:#67e0b0}
.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}
.authuser{font-size:12px;color:var(--mut)}
.authuser a{color:var(--acc)}
.hf-user{color:var(--acc);text-decoration:none;font-weight:700}
.hf-user:hover{text-decoration:underline}
</style>
</head>
<body>
<div class="wrap">
<div class="hero">
<div id="langbar">
<button data-l="en">EN</button><button data-l="zh">中文</button><button data-l="ko">한국어</button>
</div>
<span class="hero-tag" data-i18n="eyebrow"></span>
<h1>Open Quantum Challenge <span style="color:var(--mut);font-size:16px">(OQC)</span></h1>
<p class="sub" data-i18n="sub"></p>
<div class="badges" id="badges">
<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>
<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>
<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>
<span id="auth" style="margin-left:auto"></span>
</div>
</div>
<div class="grid" id="season-cards"></div>
<h2 data-i18n="lb_h"></h2>
<div class="tabs" id="tabs"></div>
<table id="lb"><thead><tr><th class="rank">#</th><th data-i18n="th_part"></th><th data-i18n="th_method"></th>
<th class="r" id="scorehead">Score</th><th data-i18n="th_status"></th></tr></thead><tbody id="lbbody"></tbody></table>
<p class="muted" id="lbmeta" style="font-size:12px;margin-top:8px"></p>
<h2 data-i18n="how_h"></h2>
<div class="steps">
<div class="step"><b data-i18n="step1_t"></b><br><span data-i18n="step1_b"></span></div>
<div class="step"><b data-i18n="step2_t"></b><br><span data-i18n="step2_b"></span></div>
<div class="step"><b data-i18n="step3_t"></b><br><span data-i18n="step3_b"></span></div>
</div>
<h2 data-i18n="qs_h"></h2>
<p class="muted" data-i18n="qs_d" style="font-size:13px;margin:0 0 8px"></p>
<div class="tabs" id="gtabs">
<div class="tab on" data-g="qsim">Season 1 · QSIM</div>
<div class="tab" data-g="qec">Season 2 · QEC</div>
</div>
<pre class="codeblock" id="g-qsim" data-copy="click to copy">pip install numpy scipy openfermion huggingface_hub
python - &lt;&lt;'PY'
from huggingface_hub import hf_hub_download
import json
p = hf_hub_download("FINAL-Bench/oqc-data","qsim/systems.json",repo_type="dataset")
sysd = json.load(open(p))["systems"]
from openfermion import fermi_hubbard, get_sparse_operator
import scipy.sparse.linalg as sla
pred = {}
for sid, s in sysd.items():
x, y = s["lattice"]
H = fermi_hubbard(x, y, tunneling=s["t"], coulomb=s["U_over_t"]*s["t"], periodic=False)
pred[sid] = float(sla.eigsh(get_sparse_operator(H), k=1, which="SA", return_eigenvectors=False)[0])
json.dump(pred, open("my_qsim.json","w")) # report EVERY system; lowest total energy wins; ties by submission time
print(pred)
PY</pre>
<pre class="codeblock" id="g-qec" style="display:none" data-copy="click to copy">pip install numpy stim pymatching huggingface_hub
python - &lt;&lt;'PY'
from huggingface_hub import hf_hub_download
import numpy as np, stim, pymatching, json
det = np.load(hf_hub_download("FINAL-Bench/oqc-data","qec/detectors.npy",repo_type="dataset"))
dem = stim.DetectorErrorModel(open(hf_hub_download("FINAL-Bench/oqc-data","qec/dem.txt",repo_type="dataset")).read())
m = pymatching.Matching.from_detector_error_model(dem)
bits = m.decode_batch(det)[:,0].astype(int)
pred = {"shot_%05d"%i: int(bits[i]) for i in range(det.shape[0])}
json.dump(pred, open("my_qec.json","w")) # paste my_qec.json into the Submit box
print("items", len(pred))
PY</pre>
<div class="form">
<h3 style="margin:0 0 4px" data-i18n="submit_h"></h3>
<p class="muted" data-i18n="f_fmt" style="font-size:13px;margin:0"></p>
<label data-i18n="l_board"></label>
<select id="f-board"><option value="qsim">Season 1 · Quantum Simulation (QSIM)</option>
<option value="qec">Season 2 · QEC Decoder (QEC)</option></select>
<label data-i18n="l_name"></label>
<input id="f-name" data-i18n-ph="ph_name">
<label data-i18n="l_method"></label>
<input id="f-method" data-i18n-ph="ph_method">
<label data-i18n="l_pred"></label>
<textarea id="f-pred" placeholder='{"hub_1x2_U4": -1.0, "hub_1x3_U4": -2.0}'></textarea>
<button class="btn" id="f-submit" data-i18n="btn_submit"></button>
<div class="note" id="f-note"></div>
</div>
<div class="foot" data-i18n="foot"></div>
</div>
<script>
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;};
const I18N={
en:{eyebrow:"FINAL-Bench · vendor-neutral · fully reproducible",
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.",
lb_h:"Live standings", th_part:"Participant", th_method:"Method", th_status:"Status",
empty:"No entries yet — be the first.", updated:"Updated", ranked:"ranked",
how_h:"How to enter",
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>.",
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.",
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.",
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.",
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>.",
l_board:"Season / track", l_name:"Display name", l_method:"Method (short description)", l_pred:"Predictions JSON",
ph_name:"your name, team or HF handle", ph_method:"e.g. HVA-VQE 4 layers / BP+OSD decoder", btn_submit:"Submit entry",
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.",
auth_signin:"🤗 Sign in with Hugging Face", auth_optional:"sign-in optional", auth_as:"signed in as", auth_out:"sign out",
c_season:"SEASON", c_metric:"Metric:", c_opens:"Opens", c_deadline:"Deadline", c_prize:"Prize", c_input:"Input:",
p_open:"OPEN", p_closed:"CLOSED", p_soon:"OPENS SOON", copy:"click to copy", ref_tag:"REFERENCE",
need_login:"Please <a href='login'>sign in with Hugging Face</a> to submit.", bad_json:"Predictions must be valid JSON.",
net_err:"Network error — try again.", submitting:"Submitting…",
seasons:{qsim:{name:"Quantum Simulation",title:"Fermi-Hubbard ground-state energy",metric:"lowest total energy over all systems (lower wins)",note:"qsim/systems.json — lattice, U/t and qubit count per system"},
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"}}},
zh:{eyebrow:"FINAL-Bench · 厂商中立 · 完全可复现",
sub:"只需一块 GPU 和开放测评工具即可为量子生态做贡献——无需量子硬件。答案保密,每次提交都会对照冻结的标准答案自动评分,因此排名可复现、与硬件无关。两个赛季,各 $1,000 奖金。",
lb_h:"实时榜单", th_part:"参赛者", th_method:"方法", th_status:"状态",
empty:"暂无提交——成为第一个。", updated:"更新于", ranked:"已排名",
how_h:"参赛方式",
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>。",
step2_t:"2 · 在你的 GPU 上运行", step2_b:"使用开放测评工具 <a href='https://github.com/final-bench/quantum-bench-harness' target='_blank'>final-bench/quantum-bench-harness</a>(或任意方法)为每个条目生成预测。",
step3_t:"3 · 在下方提交", step3_b:"用 Hugging Face 登录,粘贴你的预测 JSON。评分程序会对照私有答案评分,榜单自动更新。",
qs_h:"快速开始 — 复制运行", qs_d:"几行代码即可复现基线并生成有效提交。点击代码块复制。",
submit_h:"提交参赛", f_fmt:"格式:<code>{ \"&lt;item_id&gt;\": value, ... }</code> — QSIM:每个系统的能量;QEC:每个 <code>shot_00000</code> 的 0/1。",
l_board:"赛季 / 赛道", l_name:"显示名称", l_method:"方法(简短描述)", l_pred:"预测 JSON",
ph_name:"你的名字、团队或 HF 账号", ph_method:"例如 HVA-VQE 4 层 / BP+OSD 解码器", btn_submit:"提交",
foot:"Open Quantum Challenge · FINAL-Bench。测评工具与冻结题目公开;答案与任何竞赛方法内部实现不公开。代码 MIT · 基准数据 CC-BY-NC-4.0。",
auth_signin:"🤗 使用 Hugging Face 登录", auth_optional:"可选登录", auth_as:"已登录:", auth_out:"退出",
c_season:"赛季", c_metric:"指标:", c_opens:"开始", c_deadline:"截止", c_prize:"奖金", c_input:"输入:",
p_open:"进行中", p_closed:"已截止", p_soon:"即将开始", copy:"点击复制", ref_tag:"参考基准",
need_login:"请先用 <a href='login'>Hugging Face 登录</a> 再提交。", bad_json:"预测必须是有效的 JSON。",
net_err:"网络错误——请重试。", submitting:"提交中…",
seasons:{qsim:{name:"量子模拟",title:"费米-哈伯德基态能量",metric:"全部系统的最低总基态能量(越低越好)",note:"qsim/systems.json — 晶格、U/t 与量子比特数"},
qec:{name:"量子纠错解码器",title:"旋转表面码 d=5, p=0.005(电路级)",metric:"逻辑错误率(越低越好)",note:"qec/detectors.npy(样本 × 探测器)+ qec/dem.txt"}}},
ko:{eyebrow:"FINAL-Bench · 벤더 중립 · 완전 재현 가능",
sub:"GPU와 공개 하네스만으로 양자 생태계에 기여하세요 — 양자 하드웨어가 필요 없습니다. 정답은 비공개로 보관되며 모든 제출은 동결된 정답과 자동 채점되어, 순위가 재현 가능하고 하드웨어에 무관합니다. 2개 시즌, 각 $1,000 상금.",
lb_h:"실시간 순위", th_part:"참가자", th_method:"방법", th_status:"상태",
empty:"아직 제출이 없습니다 — 첫 주자가 되세요.", updated:"업데이트", ranked:"순위",
how_h:"참가 방법",
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>.",
step2_t:"2 · 내 GPU에서 실행", step2_b:"공개 하네스 <a href='https://github.com/final-bench/quantum-bench-harness' target='_blank'>final-bench/quantum-bench-harness</a>(또는 임의 방법)로 항목마다 예측을 만드세요.",
step3_t:"3 · 아래에서 제출", step3_b:"Hugging Face로 로그인하고 예측 JSON을 붙여넣으세요. 워커가 비공개 정답과 채점하고 순위가 자동 갱신됩니다.",
qs_h:"퀵스타트 — 복사 실행", qs_d:"몇 줄로 베이스라인을 재현하고 유효한 제출을 만듭니다. 블록을 클릭하면 복사됩니다.",
submit_h:"항목 제출", f_fmt:"형식: <code>{ \"&lt;item_id&gt;\": value, ... }</code> — QSIM: 시스템별 에너지, QEC: <code>shot_00000</code>별 0/1.",
l_board:"시즌 / 트랙", l_name:"표시 이름", l_method:"방법 (간단 설명)", l_pred:"예측 JSON",
ph_name:"이름, 팀 또는 HF 핸들", ph_method:"예: HVA-VQE 4층 / BP+OSD 디코더", btn_submit:"제출",
foot:"Open Quantum Challenge · FINAL-Bench. 평가 하네스와 동결 문제는 공개, 정답 키와 경쟁 기법 내부는 비공개. 코드 MIT · 벤치마크 데이터 CC-BY-NC-4.0.",
auth_signin:"🤗 Hugging Face로 로그인", auth_optional:"로그인 선택", auth_as:"로그인:", auth_out:"로그아웃",
c_season:"시즌", c_metric:"지표:", c_opens:"개시", c_deadline:"마감", c_prize:"상금", c_input:"입력:",
p_open:"접수중", p_closed:"마감", p_soon:"개시 예정", copy:"클릭하여 복사", ref_tag:"기준선",
need_login:"제출하려면 <a href='login'>Hugging Face로 로그인</a>하세요.", bad_json:"예측은 유효한 JSON이어야 합니다.",
net_err:"네트워크 오류 — 다시 시도하세요.", submitting:"제출 중…",
seasons:{qsim:{name:"양자 시뮬레이션",title:"페르미-허버드 바닥상태 에너지",metric:"전 시스템 최저 총 바닥에너지 (낮을수록 우승)",note:"qsim/systems.json — 격자, U/t, 큐빗 수"},
qec:{name:"QEC 디코더",title:"회전 표면코드 d=5, p=0.005 (회로수준)",metric:"논리 오류율 (낮을수록 우승)",note:"qec/detectors.npy (샷 × 디텍터) + qec/dem.txt"}}}
};
let LANG=(localStorage.getItem('oqc_lang')||(navigator.language||'en').slice(0,2).toLowerCase());
if(!I18N[LANG]) LANG='en';
function t(k){return (I18N[LANG]&&I18N[LANG][k])||I18N.en[k]||k;}
function ts(board,field){const s=(I18N[LANG].seasons||{})[board];return (s&&s[field])||(I18N.en.seasons[board]||{})[field]||'';}
function esc(x){return (x==null?'':(''+x)).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));}
function hfProfile(u){if(!u)return '&mdash;';const url='https://huggingface.co/'+encodeURIComponent(u);
return '<a class="hf-user" href="'+url+'" target="_blank" rel="noopener noreferrer" title="Hugging Face: '+esc(u)+'">🤗 '+esc(u)+'</a>';}
let SEASONS=[], CUR="qsim", ME={signed_in:false,oauth_configured:false};
function applyLang(){
document.querySelectorAll('[data-i18n]').forEach(e=>e.innerHTML=t(e.dataset.i18n));
document.querySelectorAll('[data-i18n-ph]').forEach(e=>e.placeholder=t(e.dataset.i18nPh));
document.querySelectorAll('.codeblock').forEach(e=>e.setAttribute('data-copy',t('copy')));
document.documentElement.lang=LANG;
document.querySelectorAll('#langbar button').forEach(b=>b.classList.toggle('on',b.dataset.l===LANG));
renderCards(); renderTabs(); loadBoard(); renderAuth();
}
function setLang(l){LANG=l;try{localStorage.setItem('oqc_lang',l);}catch(e){} applyLang();}
document.querySelectorAll('#langbar button').forEach(b=>b.onclick=()=>setLang(b.dataset.l));
function renderAuth(){
const a=$('#auth');
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>';
const n=$('#f-name'); if(n){n.value=ME.user.name; n.readOnly=true;}}
else if(ME.oauth_configured){a.innerHTML='<a class="authbtn" href="login">'+t('auth_signin')+'</a>';}
else{a.innerHTML='<span class="authuser">'+t('auth_optional')+'</span>';}
}
async function loadMe(){try{ME=await (await fetch('api/me')).json();}catch(e){} renderAuth();}
function renderCards(){
const cc=$('#season-cards'); if(!cc) return; cc.innerHTML='';
SEASONS.forEach(s=>{
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>');
const c=el('div','card');
c.innerHTML=`<div class="s">${t('c_season')} ${s.number}</div><h3>${ts(s.board,'name')} ${pill}</h3>
<div class="meta">${ts(s.board,'title')}</div>
<div class="meta">${t('c_metric')} ${ts(s.board,'metric')}</div>
<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>
<div class="meta">${t('c_input')} <code>${s.input}</code> — ${ts(s.board,'note')}</div>`;
cc.appendChild(c);
});
}
function renderTabs(){
const tt=$('#tabs'); if(!tt) return; tt.innerHTML='';
SEASONS.forEach(s=>{
const b=el('div','tab'+(s.board===CUR?' on':''),`S${s.number} · ${ts(s.board,'name')}`);
b.onclick=()=>{CUR=s.board;renderTabs();loadBoard();};
tt.appendChild(b);
});
}
async function loadBoard(){
const s=SEASONS.find(x=>x.board===CUR)||{};
$('#scorehead').textContent=s.col||'Score';
let d; try{d=await (await fetch('api/leaderboard?board='+CUR)).json();}catch(e){return;}
const b=$('#lbbody'); b.innerHTML='';
(d.anchors||[]).forEach(e=>{
const tr=el('tr'); tr.style.background='#f8fbfa';
const sc=(typeof e.score==='number')?e.score.toFixed(5):'—';
tr.innerHTML=`<td class="rank" style="color:var(--mut)">—</td><td>${esc(e.participant)}</td><td class="muted">${esc(e.method||'—')}</td><td class="r">${sc}</td><td><span class="tagv a">${t('ref_tag')}</span></td>`;
b.appendChild(tr);
});
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>'));}
else d.entries.forEach(e=>{
const tr=el('tr'); const v=e.verified?'<span class="tagv v">VERIFIED</span>':'<span class="tagv a">AUTO-SCORED</span>';
const sc=(typeof e.score==='number')?e.score.toFixed(5):'—';
tr.innerHTML=`<td class="rank">${e.rank}</td><td>${hfProfile(e.participant)}</td><td class="muted">${esc(e.method||'—')}</td><td class="r">${sc}</td><td>${v}</td>`;
b.appendChild(tr);
});
$('#lbmeta').textContent=d.updated?(t('updated')+' '+new Date(d.updated*1000).toLocaleString()+' · '+d.n+' '+t('ranked')):(d.n+' '+t('ranked'));
}
document.querySelectorAll('#gtabs .tab').forEach(x=>x.onclick=()=>{
document.querySelectorAll('#gtabs .tab').forEach(y=>y.classList.remove('on')); x.classList.add('on');
const g=x.dataset.g; $('#g-qsim').style.display=(g==='qsim')?'block':'none'; $('#g-qec').style.display=(g==='qec')?'block':'none';
});
document.querySelectorAll('.codeblock').forEach(bl=>bl.onclick=()=>{
navigator.clipboard.writeText(bl.textContent).then(()=>{bl.classList.add('copied');setTimeout(()=>bl.classList.remove('copied'),1500);});
});
$('#f-pred').addEventListener('input',()=>{
try{const p=JSON.parse($('#f-pred').value);const k=Object.keys(p);if(!k.length)return;
if(k[0].indexOf('shot_')===0)$('#f-board').value='qec';
else if(k[0].indexOf('hub_')===0)$('#f-board').value='qsim';
}catch(e){}
});
$('#f-submit').onclick=async()=>{
const note=$('#f-note'); note.className='note';
if(ME.oauth_configured && !ME.signed_in){note.className='note err';note.innerHTML=t('need_login');return;}
let preds; try{preds=JSON.parse($('#f-pred').value);}catch(e){note.className='note err';note.textContent=t('bad_json');return;}
const btn=$('#f-submit'); btn.disabled=true; btn.textContent=t('submitting');
try{
const r=await fetch('api/submit',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({board:$('#f-board').value,display_name:$('#f-name').value,method:$('#f-method').value,predictions:preds})});
const j=await r.json();
if(j.accepted){note.className='note ok';note.textContent='✓ '+(j.submission_id||'')+' · '+(j.note||'');loadBoard();}
else{note.className='note err';note.textContent=(j.reasons||['rejected']).join(' · ');}
}catch(e){note.className='note err';note.textContent=t('net_err');}
btn.disabled=false; btn.textContent=t('btn_submit');
};
async function boot(){
try{const d=await (await fetch('api/seasons')).json(); SEASONS=d.seasons;}catch(e){SEASONS=[];}
loadMe(); applyLang();
}
boot(); setInterval(loadBoard, 30000);
</script>
</body>
</html>