/* ════════════════════════════════════════════════════════════════════
IndiaFinBench — main.js
Chart, tables, canvas, scroll choreography, live RAG, explorer, submit.
Vanilla JS, no dependencies.
════════════════════════════════════════════════════════════════════ */
(function () {
'use strict';
var MODELS = window.IFB_MODELS || [];
var HUMAN = window.IFB_HUMAN || null;
var CLAUDE = window.IFB_CLAUDE || null;
var DIFF = window.IFB_DIFF || [];
var TASKS = window.IFB_TASKS || [];
var REDUCED = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var $ = function (s, c) { return (c || document).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
var esc = function (s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (ch) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[ch];
});
};
/* ── Deep links: jump instantly on load instead of animating from top ─ */
if (location.hash) {
var deepTarget = document.getElementById(location.hash.slice(1));
if (deepTarget) {
requestAnimationFrame(function () {
deepTarget.scrollIntoView({ behavior: 'instant', block: 'start' });
});
}
}
/* ── Masthead drawer ──────────────────────────────────────────────── */
var menuBtn = $('#menuBtn');
var drawer = $('#menuDrawer');
if (menuBtn && drawer) {
menuBtn.addEventListener('click', function () {
var open = drawer.classList.toggle('open');
menuBtn.setAttribute('aria-expanded', String(open));
});
$$('a', drawer).forEach(function (a) {
a.addEventListener('click', function () {
drawer.classList.remove('open');
menuBtn.setAttribute('aria-expanded', 'false');
});
});
}
/* ── Scroll reveal ────────────────────────────────────────────────── */
var revealIO = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
e.target.classList.add('shown');
revealIO.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
function watch(el) { if (el) revealIO.observe(el); }
$$('.reveal').forEach(watch);
/* ── Count-up numbers ─────────────────────────────────────────────── */
function countUp(el) {
var target = parseFloat(el.dataset.count);
var dec = parseInt(el.dataset.dec || '0', 10);
var suffix = el.dataset.suffix || '';
if (REDUCED) { el.textContent = target.toFixed(dec) + suffix; return; }
var t0 = null, DUR = 1400;
function frame(t) {
if (!t0) t0 = t;
var p = Math.min((t - t0) / DUR, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = (target * eased).toFixed(dec) + suffix;
if (p < 1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var countIO = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { countUp(e.target); countIO.unobserve(e.target); }
});
}, { threshold: 0.4 });
$$('[data-count]').forEach(function (el) { countIO.observe(el); });
/* ── Progress rail ────────────────────────────────────────────────── */
var railFill = $('#railFill');
var railItems = $$('.rail-list li');
var chapterIds = railItems.map(function (li) { return li.dataset.ch; });
window.addEventListener('scroll', function () {
if (!railFill) return;
var h = document.documentElement.scrollHeight - window.innerHeight;
railFill.style.height = (h > 0 ? (window.scrollY / h) * 100 : 0) + '%';
}, { passive: true });
var chapterIO = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
railItems.forEach(function (li) {
li.classList.toggle('on', li.dataset.ch === e.target.id);
});
$$('.mast-nav a').forEach(function (a) {
a.classList.toggle('active', a.getAttribute('href') === '#' + e.target.id);
});
}
});
}, { rootMargin: '-30% 0px -60% 0px' });
chapterIds.forEach(function (id) {
var sec = document.getElementById(id);
if (sec) chapterIO.observe(sec);
});
/* ── §02 Corpus: 192 document marks ───────────────────────────────── */
(function docDots() {
var holder = $('#docDots');
if (!holder) return;
var frag = document.createDocumentFragment();
for (var i = 0; i < 192; i++) {
var el = document.createElement('i');
el.className = i < 92 ? 'sebi' : 'rbi';
el.style.setProperty('--d', (i * 7) + 'ms');
frag.appendChild(el);
}
holder.appendChild(frag);
watchShown(holder);
})();
function watchShown(el) {
new IntersectionObserver(function (entries, io) {
entries.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('shown'); io.unobserve(e.target); }
});
}, { threshold: 0.25 }).observe(el);
}
/* ── §03 Task cards ───────────────────────────────────────────────── */
(function taskCards() {
var grid = $('#taskGrid');
if (!grid || !TASKS.length) return;
grid.innerHTML = TASKS.map(function (t) {
var share = (t.n / 406) * 100;
return '' +
'' + esc(t.code) + '' +
'' + esc(t.label) + '
' +
'' + esc(t.desc) + '
' +
'' + t.n + ' items · ' + share.toFixed(1) + '% of benchmark
' +
'
' +
'';
}).join('');
$$('.task-card', grid).forEach(function (card) { watch(card); watchShown(card); });
})();
/* ── §03 difficulty strip + diff segments need .shown ─────────────── */
$$('.diff-strip, .rag-diagram').forEach(watchShown);
/* ── §04 Chart ────────────────────────────────────────────────────── */
var METRIC_META = {
overall: { title: 'Overall accuracy', desc: 'All 406 items · zero-shot · 95% Wilson confidence intervals on hover' },
reg: { title: 'Regulatory Interpretation', desc: '174 items · precision reading of rules, thresholds and applicability' },
num: { title: 'Numerical Reasoning', desc: '92 items · arithmetic over figures embedded in regulatory prose' },
con: { title: 'Contradiction Detection', desc: '62 items · do two passages conflict on the stated issue?' },
tmp: { title: 'Temporal Reasoning', desc: '78 items · supersession chains and operative-date resolution' }
};
var chartEl = $('#barChart');
var chartShown = false;
function tipHTML(m) {
return '' + esc(m.label) + '' +
'' +
'REG' + m.reg.toFixed(1) + '%' +
'NUM' + m.num.toFixed(1) + '%' +
'CON' + m.con.toFixed(1) + '%' +
'TMP' + m.tmp.toFixed(1) + '%' +
'Overall' + m.overall.toFixed(1) + '%' +
'' +
(m.ci ? '95% CI ' + esc(m.ci) + ' · n = ' + m.n_items + '' : '');
}
function buildChart(metric) {
if (!chartEl) return;
var rows = MODELS.slice();
if (HUMAN) rows.push(HUMAN);
rows.sort(function (a, b) { return b[metric] - a[metric]; });
var html = rows.map(function (m, idx) {
var v = m[metric];
var cls = m.is_human ? 'human' : ('t' + (m.tier || 2));
return '
' +
'
' +
'' + (m.is_human ? '—' : (idx + 1)) + '' +
'' + esc(m.label) + '' +
'
' +
'
' +
'
' + v.toFixed(1) + '%
' +
'
' + tipHTML(m) + '
' +
'
' +
'
';
}).join('');
html += 'human baseline · ' +
(HUMAN ? HUMAN[metric].toFixed(1) : '—') + '%
';
chartEl.innerHTML = html;
function animate() {
$$('.crow-fill', chartEl).forEach(function (f) {
f.style.width = f.dataset.w + '%';
f.classList.add('shown');
});
positionBaseline(metric);
}
if (chartShown) {
requestAnimationFrame(function () { requestAnimationFrame(animate); });
} else {
new IntersectionObserver(function (entries, io) {
if (entries[0].isIntersecting) {
chartShown = true;
animate();
io.disconnect();
}
}, { threshold: 0.15 }).observe(chartEl);
}
}
function positionBaseline(metric) {
var line = $('#chartBaseline');
var track = $('.crow-track', chartEl);
if (!line || !track || !HUMAN) return;
var v = HUMAN[metric];
var left = track.offsetLeft + (track.offsetWidth * v / 100);
line.style.left = left + 'px';
}
window.addEventListener('resize', function () {
var active = $('.tab.active');
if (chartShown) positionBaseline(active ? active.dataset.t : 'overall');
});
$$('#taskTabs .tab').forEach(function (tab) {
tab.addEventListener('click', function () {
$$('#taskTabs .tab').forEach(function (t) {
t.classList.toggle('active', t === tab);
t.setAttribute('aria-selected', String(t === tab));
});
var k = tab.dataset.t;
$('#chartTitle').textContent = METRIC_META[k].title;
$('#chartDesc').textContent = METRIC_META[k].desc;
buildChart(k);
});
});
buildChart('overall');
/* ── §04 Results table ────────────────────────────────────────────── */
var sortKey = 'overall', sortAsc = false;
function scoreCell(v, isBest) {
var cls = v >= 85 ? 's-hi' : v >= 70 ? 's-md' : 's-lo';
return '' + v.toFixed(1) + ' | ';
}
function buildTable() {
var body = $('#tBody');
if (!body) return;
var rows = MODELS.slice();
rows.sort(function (a, b) {
var x = a[sortKey], y = b[sortKey];
if (sortKey === 'params') { x = parseFloat(x) || 0; y = parseFloat(y) || 0; }
if (sortKey === 'rank') return sortAsc ? a.rank - b.rank : b.rank - a.rank;
return sortAsc ? x - y : y - x;
});
var best = {};
['reg', 'num', 'con', 'tmp', 'overall'].forEach(function (k) {
best[k] = Math.max.apply(null, MODELS.map(function (m) { return m[k]; }));
});
var html = rows.map(function (m) {
return '' +
'| ' + m.rank + ' | ' +
'' + esc(m.label) + ' ' + esc(m.hf_id) + ' | ' +
'' + esc(m.params) + ' | ' +
'' + esc(m.type) + ' | ' +
scoreCell(m.reg, m.reg === best.reg) +
scoreCell(m.num, m.num === best.num) +
scoreCell(m.con, m.con === best.con) +
scoreCell(m.tmp, m.tmp === best.tmp) +
scoreCell(m.overall, m.overall === best.overall) +
'' + esc(m.ci) + ' | ' +
'
';
}).join('');
if (HUMAN) {
html += '' +
'| — | ' +
'' + esc(HUMAN.label) + ' ' + esc(HUMAN.hf_id) + ' | ' +
'— | Human baseline | ' +
scoreCell(HUMAN.reg) + scoreCell(HUMAN.num) + scoreCell(HUMAN.con) + scoreCell(HUMAN.tmp) + scoreCell(HUMAN.overall) +
'' + esc(HUMAN.ci) + ' |
';
}
if (CLAUDE) {
html += '' +
'| † | ' +
'' + esc(CLAUDE.label.replace('†', '')) + ' ' + esc(CLAUDE.hf_id) + ' | ' +
'— | 150-item subset | ' +
scoreCell(CLAUDE.reg) + scoreCell(CLAUDE.num) + scoreCell(CLAUDE.con) + scoreCell(CLAUDE.tmp) + scoreCell(CLAUDE.overall) +
'' + esc(CLAUDE.ci) + ' |
';
}
body.innerHTML = html;
}
$$('#resultsTable th.sortable').forEach(function (th) {
th.addEventListener('click', function () {
var k = th.dataset.k;
if (sortKey === k) sortAsc = !sortAsc;
else { sortKey = k; sortAsc = (k === 'rank'); }
$$('#resultsTable th').forEach(function (h) { h.classList.toggle('sorted', h === th); });
buildTable();
});
});
buildTable();
/* ── §04 Difficulty table ─────────────────────────────────────────── */
(function diffTable() {
var body = $('#diffBody');
if (!body || !DIFF.length) return;
body.innerHTML = DIFF.map(function (m) {
var d = m.hard - m.easy;
var sign = d >= 0 ? '+' : '−';
var cls = d >= 0 ? 'delta-up' : 'delta-down';
return '' +
'' + esc(m.label) + ' | ' +
'' + m.easy.toFixed(1) + '% | ' +
'' + m.med.toFixed(1) + '% | ' +
'' + m.hard.toFixed(1) + '% | ' +
'' + sign + Math.abs(d).toFixed(1) + ' | ' +
'
';
}).join('');
})();
/* ── §06 Live RAG ─────────────────────────────────────────────────── */
(function rag() {
var input = $('#ragQ'), btn = $('#ragBtn');
var out = $('#ragOut'), status = $('#ragStatus'), statusText = $('#ragStatusText');
var answerEl = $('#ragAnswer'), sourcesEl = $('#ragSources');
if (!input || !btn) return;
var PHASES = ['Encoding query…', 'Searching 4,347 chunks…', 'Fusing dense and sparse ranks…', 'Drafting cited answer…'];
var phaseTimer = null;
function startPhases() {
var i = 0;
statusText.textContent = PHASES[0];
phaseTimer = setInterval(function () {
i = Math.min(i + 1, PHASES.length - 1);
statusText.textContent = PHASES[i];
}, 1500);
}
function stopPhases() { clearInterval(phaseTimer); }
function run() {
var q = input.value.trim();
if (!q) return;
btn.disabled = true;
out.hidden = false;
status.hidden = false;
answerEl.textContent = '';
sourcesEl.innerHTML = '';
startPhases();
fetch('/api/rag', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: q })
}).then(function (r) { return r.json(); }).then(function (data) {
stopPhases();
status.hidden = true;
if (data.error) {
answerEl.textContent = '⚠ ' + data.error;
} else {
answerEl.textContent = data.answer || '';
(data.sources || []).forEach(function (s, i) {
var card = document.createElement('div');
card.className = 'rag-src';
card.innerHTML =
'Source ' + (i + 1) + ' · ' + esc(s.source || s.doc_id || '') + '
' +
'' + esc(s.title || s.doc_id || 'Document') + '
' +
'' + esc(s.text || '') + '
' +
'' +
'RRF ' + (s.rrf_score != null ? Number(s.rrf_score).toFixed(4) : '—') + '' +
'dense ' + (s.dense_score != null ? Number(s.dense_score).toFixed(3) : '—') + '' +
'BM25 ' + (s.bm25_score != null ? Number(s.bm25_score).toFixed(2) : '—') + '' +
'
';
sourcesEl.appendChild(card);
});
}
btn.disabled = false;
}).catch(function (e) {
stopPhases();
status.hidden = true;
answerEl.textContent = '⚠ Network error: ' + e.message;
btn.disabled = false;
});
}
btn.addEventListener('click', run);
input.addEventListener('keydown', function (e) { if (e.key === 'Enter') run(); });
$$('.rag-examples .chip').forEach(function (chip) {
chip.addEventListener('click', function () {
input.value = chip.dataset.q || chip.textContent.trim();
run();
});
});
})();
/* ── §07 Specimen explorer ────────────────────────────────────────── */
(function explorer() {
var btn = $('#exBtn');
if (!btn) return;
btn.addEventListener('click', function () {
btn.disabled = true;
var task = encodeURIComponent($('#exTask').value);
var diff = encodeURIComponent($('#exDiff').value);
fetch('/api/example?task=' + task + '&diff=' + diff)
.then(function (r) { return r.json(); })
.then(function (q) {
btn.disabled = false;
var card = $('#exCard');
if (q.error) {
card.hidden = false;
$('#exId').textContent = '';
$('#exTaskBadge').textContent = '';
$('#exDiffBadge').textContent = '';
$('#exContext').textContent = q.error;
$('#exQuestion').textContent = '';
$('#exReveal').hidden = true;
$('#exAnswer').hidden = true;
return;
}
card.hidden = false;
$('#exId').textContent = q.id || '';
$('#exTaskBadge').textContent = q.task_type || '';
$('#exDiffBadge').textContent = q.difficulty || '';
$('#exContext').textContent = q.context || '';
$('#exQuestion').textContent = q.question || '';
$('#exReveal').hidden = false;
$('#exAnswer').hidden = true;
$('#exAnswerText').textContent = q.answer || '';
})
.catch(function () { btn.disabled = false; });
});
$('#exReveal').addEventListener('click', function () {
$('#exAnswer').hidden = false;
this.hidden = true;
});
})();
/* ── §07 Submit ───────────────────────────────────────────────────── */
(function submit() {
var btn = $('#subBtn');
if (!btn) return;
btn.addEventListener('click', function () {
var hfId = $('#hfId').value.trim();
var box = $('#statusBox');
box.hidden = false;
box.className = 'status';
if (!hfId || hfId.indexOf('/') < 1) {
box.classList.add('err');
box.textContent = 'Enter a valid HuggingFace model ID (org/model).';
return;
}
btn.disabled = true;
box.textContent = 'Preparing submission…';
fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
hf_id: hfId,
label: $('#dispName').value.trim(),
params: $('#modelParams').value.trim(),
model_type: $('#mtype').value
})
}).then(function (r) { return r.json(); }).then(function (data) {
btn.disabled = false;
if (data.issue_url) {
box.classList.add('ok');
box.textContent = 'Submission issue opened in a new tab — submit it on GitHub and the model joins the evaluation queue.';
window.open(data.issue_url, '_blank', 'noopener');
} else {
box.classList.add('err');
box.textContent = data.error || 'Submission failed.';
}
}).catch(function (e) {
btn.disabled = false;
box.classList.add('err');
box.textContent = 'Network error: ' + e.message;
});
});
})();
/* ── §07 Copy citation ────────────────────────────────────────────── */
(function cite() {
var btn = $('#copyBtn');
if (!btn) return;
btn.addEventListener('click', function () {
navigator.clipboard.writeText($('#citeText').textContent).then(function () {
btn.textContent = 'Copied';
setTimeout(function () { btn.textContent = 'Copy'; }, 1600);
});
});
})();
})();