Spaces:
Running
Running
One 'output not in trace' measure on the leaderboard; gaps table grouped; row page two-column layout
Browse files
home.js
CHANGED
|
@@ -44,7 +44,6 @@
|
|
| 44 |
const h = r.scan.holes, m = h.models, out = [];
|
| 45 |
if (m.mismatch) out.push(["bad", "served " + shortModel(m.served[0].model)]);
|
| 46 |
else if (m.foreignTrials) out.push(["warn", "fallback " + m.served.filter(s => !s.configured).map(s => shortModel(s.model).replace(/^claude-/, "")).join(", ") + " " + pct0(m.foreignStepShare)]);
|
| 47 |
-
if (h.text.uncounted) out.push(["bad", "output ≥" + pct0(h.text.uncounted) + " unbilled"]);
|
| 48 |
const off = h.lbCost && Math.abs(h.costSum - h.lbCost) / h.lbCost > 0.02;
|
| 49 |
if (off) out.push(["warn", "trial costs " + money(h.costSum) + "/" + money(h.lbCost)]);
|
| 50 |
else if (h.uncosted / r.scan.trials > 0.05) out.push(["warn", h.uncosted + " uncosted"]);
|
|
@@ -57,17 +56,15 @@
|
|
| 57 |
const leadsOnly = $("leads-only");
|
| 58 |
const SORTS = {
|
| 59 |
rank: r => r.rank, score: r => -r.lb.accuracy, leads: r => -r.scan.leads, hacks: r => -(r.lb.rewardHacks || 0),
|
| 60 |
-
trace: r =>
|
| 61 |
};
|
| 62 |
function board() {
|
| 63 |
const cols = [
|
| 64 |
{ name: "89 tasks × 5 attempts, easiest first", cls: "l" },
|
| 65 |
{ name: "Score", sort: "score", help: "leaderboard accuracy ± 95% CI half-width" },
|
| 66 |
-
{ name: "Rewarded", help: "rewarded trials of the row's listed trials" },
|
| 67 |
{ name: "Leads", sort: "leads", help: "rewarded trials with an unexcused high or critical finding" },
|
| 68 |
{ name: "LB hacks", sort: "hacks", help: "leaderboard's own reward-hack rate" },
|
| 69 |
-
{ name: "
|
| 70 |
-
{ name: "Reasoning", help: "how much reasoning the trajectories hold" },
|
| 71 |
{ name: "Cost", sort: "cost", help: "leaderboard total" },
|
| 72 |
{ name: "Notes", cls: "l" },
|
| 73 |
];
|
|
@@ -78,18 +75,19 @@
|
|
| 78 |
const h = r.scan.holes, tr = el("tr");
|
| 79 |
tr.append(rowHead(r));
|
| 80 |
const s = el("td", "l"); s.append(strip(r, { leadsOnly: leadsOnly.checked })); tr.append(s);
|
| 81 |
-
const sc = el("td"); sc.append(num(fmt1(r.lb.accuracy), "n big"), el("span", "pm", "±" + fmt1(r.lb.ci95)));
|
| 82 |
-
|
| 83 |
const ld = el("td");
|
| 84 |
if (r.scan.leads) ld.append(link(rowPath(r) + "highlights/index.html", String(r.scan.leads), "n big hot"));
|
| 85 |
else ld.append(el("span", "z", "0"));
|
| 86 |
tr.append(ld);
|
| 87 |
tr.append(el("td", "n" + (r.lb.rewardHacks >= 1 ? " hot" : ""), fmt1(r.lb.rewardHacks) + "%"));
|
| 88 |
-
const
|
| 89 |
-
const bar = el("span", "bar"); const fill = el("i"); fill.style.width = Math.round(100 *
|
| 90 |
-
|
|
|
|
|
|
|
| 91 |
tr.append(tc);
|
| 92 |
-
const rz = el("td", "rz rz--" + h.text.form, RZ[h.text.form]); rz.title = RZ_HELP[h.text.form]; tr.append(rz);
|
| 93 |
tr.append(el("td", "n", money(r.lb.cost)));
|
| 94 |
const nt = el("td", "l notes");
|
| 95 |
flags(r).forEach(([lvl, text]) => nt.append(el("span", "fl fl--" + lvl, text)));
|
|
@@ -106,31 +104,38 @@
|
|
| 106 |
}
|
| 107 |
leadsOnly.addEventListener("change", board);
|
| 108 |
|
| 109 |
-
/* 2. Gaps & accounting. */
|
| 110 |
function gaps() {
|
| 111 |
-
const
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
{ name: "Trial cost / LB", help: "sum of per-trial recorded cost against the leaderboard total" },
|
| 127 |
-
{ name: "Uncosted", help: "trials without a recorded cost" },
|
| 128 |
-
{ name: "Output unbilled", help: "recorded reasoning not counted in output tokens, as a share of reported output" },
|
| 129 |
-
{ name: "Undecided", help: "trials with at least one behavioural check atif-scan couldn't decide" },
|
| 130 |
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
const t = $("gaps-table");
|
| 132 |
-
t.replaceChildren(
|
| 133 |
const tb = el("tbody");
|
|
|
|
| 134 |
D.rows.forEach(r => {
|
| 135 |
const h = r.scan.holes, n = r.scan.trials, m = h.models, tr = el("tr");
|
| 136 |
tr.append(rowHead(r));
|
|
@@ -138,24 +143,26 @@
|
|
| 138 |
m.served.filter(x => x.share >= 0.005).slice(0, 3).forEach(x =>
|
| 139 |
md.append(el("span", x.configured ? "mdl" : "mdl off", shortModel(x.model) + (m.served.length > 1 ? " " + pct0(x.share) : ""))));
|
| 140 |
if (m.foreignTrials) md.append(link(holeHref(r, "foreign"), m.foreignTrials + " trials", "mlink"));
|
| 141 |
-
tr.append(md);
|
| 142 |
-
|
| 143 |
-
tr.append(el("td", "n", String(h.text.allCpt)));
|
| 144 |
-
const vis = visibleShare(h), vc = el("td", "n", pct0(vis)); heat(vc, 1 - (vis ?? 1), 1, "cool"); tr.append(vc);
|
| 145 |
tr.append(el("td", "n", h.text.reasoningShare !== null ? pct0(h.text.reasoningShare) : "–"));
|
| 146 |
-
tr.append(countCell(r, "unseen", h.unseen, n, { scale: 1
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
["webUnresolved", "unpaired", "callsMissing", "endsOnCall", "compacted", "noTrajectory", "timestamps"]
|
| 149 |
.forEach(k => tr.append(countCell(r, k, h[k], n)));
|
|
|
|
| 150 |
const off = h.lbCost ? h.costSum - h.lbCost : 0;
|
| 151 |
-
|
| 152 |
-
Math.abs(off) >= 0.5 ? money(h.costSum) + " / " + money(h.lbCost) : money(h.costSum) + " =")
|
| 153 |
tr.append(countCell(r, "uncosted", h.uncosted, n, { scale: 0.3 }));
|
| 154 |
-
const
|
| 155 |
-
|
| 156 |
-
else ub.append(el("span", "z", "·"));
|
| 157 |
-
tr.append(ub);
|
| 158 |
-
const und = el("td", "n", String(h.evidenceIncomplete)); heat(und, h.evidenceIncomplete / n, 1.5, "cool"); tr.append(und);
|
| 159 |
tb.append(tr);
|
| 160 |
});
|
| 161 |
t.append(tb);
|
|
|
|
| 44 |
const h = r.scan.holes, m = h.models, out = [];
|
| 45 |
if (m.mismatch) out.push(["bad", "served " + shortModel(m.served[0].model)]);
|
| 46 |
else if (m.foreignTrials) out.push(["warn", "fallback " + m.served.filter(s => !s.configured).map(s => shortModel(s.model).replace(/^claude-/, "")).join(", ") + " " + pct0(m.foreignStepShare)]);
|
|
|
|
| 47 |
const off = h.lbCost && Math.abs(h.costSum - h.lbCost) / h.lbCost > 0.02;
|
| 48 |
if (off) out.push(["warn", "trial costs " + money(h.costSum) + "/" + money(h.lbCost)]);
|
| 49 |
else if (h.uncosted / r.scan.trials > 0.05) out.push(["warn", h.uncosted + " uncosted"]);
|
|
|
|
| 56 |
const leadsOnly = $("leads-only");
|
| 57 |
const SORTS = {
|
| 58 |
rank: r => r.rank, score: r => -r.lb.accuracy, leads: r => -r.scan.leads, hacks: r => -(r.lb.rewardHacks || 0),
|
| 59 |
+
trace: r => -(r.scan.holes.text.unseen ?? 0), cost: r => -(r.lb.cost || 0),
|
| 60 |
};
|
| 61 |
function board() {
|
| 62 |
const cols = [
|
| 63 |
{ name: "89 tasks × 5 attempts, easiest first", cls: "l" },
|
| 64 |
{ name: "Score", sort: "score", help: "leaderboard accuracy ± 95% CI half-width" },
|
|
|
|
| 65 |
{ name: "Leads", sort: "leads", help: "rewarded trials with an unexcused high or critical finding" },
|
| 66 |
{ name: "LB hacks", sort: "hacks", help: "leaderboard's own reward-hack rate" },
|
| 67 |
+
{ name: "Output not in trace", sort: "trace", cls: "l", help: "share of reported output tokens with no text in the trace, and why: the reasoning's form" },
|
|
|
|
| 68 |
{ name: "Cost", sort: "cost", help: "leaderboard total" },
|
| 69 |
{ name: "Notes", cls: "l" },
|
| 70 |
];
|
|
|
|
| 75 |
const h = r.scan.holes, tr = el("tr");
|
| 76 |
tr.append(rowHead(r));
|
| 77 |
const s = el("td", "l"); s.append(strip(r, { leadsOnly: leadsOnly.checked })); tr.append(s);
|
| 78 |
+
const sc = el("td"); sc.append(num(fmt1(r.lb.accuracy), "n big"), el("span", "pm", "±" + fmt1(r.lb.ci95)));
|
| 79 |
+
sc.title = r.scan.rewarded + " of " + r.scan.trials + " listed trials rewarded"; tr.append(sc);
|
| 80 |
const ld = el("td");
|
| 81 |
if (r.scan.leads) ld.append(link(rowPath(r) + "highlights/index.html", String(r.scan.leads), "n big hot"));
|
| 82 |
else ld.append(el("span", "z", "0"));
|
| 83 |
tr.append(ld);
|
| 84 |
tr.append(el("td", "n" + (r.lb.rewardHacks >= 1 ? " hot" : ""), fmt1(r.lb.rewardHacks) + "%"));
|
| 85 |
+
const miss = h.text.unseen ?? 0, tc = el("td", "vis l");
|
| 86 |
+
const bar = el("span", "bar bar--miss"); const fill = el("i"); fill.style.width = Math.round(100 * miss) + "%"; bar.append(fill);
|
| 87 |
+
const rz = el("span", "rz rz--" + h.text.form, RZ[h.text.form]); rz.title = RZ_HELP[h.text.form];
|
| 88 |
+
tc.append(bar, el("span", "n pc", pct0(miss)), rz);
|
| 89 |
+
if (h.text.uncounted) tc.append(el("span", "bad unb", " · ≥" + pct0(h.text.uncounted) + " unbilled"));
|
| 90 |
tr.append(tc);
|
|
|
|
| 91 |
tr.append(el("td", "n", money(r.lb.cost)));
|
| 92 |
const nt = el("td", "l notes");
|
| 93 |
flags(r).forEach(([lvl, text]) => nt.append(el("span", "fl fl--" + lvl, text)));
|
|
|
|
| 104 |
}
|
| 105 |
leadsOnly.addEventListener("change", board);
|
| 106 |
|
| 107 |
+
/* 2. Gaps & accounting: grouped columns, each analysis once. */
|
| 108 |
function gaps() {
|
| 109 |
+
const groups = [
|
| 110 |
+
["Models", [["Served, by share of steps", "l", "red: not the configured model; the count opens the trials"]]],
|
| 111 |
+
["Output vs recorded text", [
|
| 112 |
+
["Chars / output token", "", "recorded agent text (reasoning, messages, tool arguments) per reported output token; ≈3.2 when the trace holds all output"],
|
| 113 |
+
["Reasoning tokens", "", "reasoning tokens as a share of output, where the harness reports them separately"],
|
| 114 |
+
["Trials < 80% text", "", "trials whose recorded text covers under 80% of their output tokens"],
|
| 115 |
+
["Unbilled", "", "recorded reasoning on top of output tokens the visible text already fills: missing from the reported output and cost"]]],
|
| 116 |
+
["Trace gaps · trials", [
|
| 117 |
+
["Web results", "", "trials with web activity whose results aren't in the trace / trials with web activity"],
|
| 118 |
+
["Web prov.", "", "web input or reference provenance unresolved"], ["Unpaired results", "", "tool results not paired to their calls"],
|
| 119 |
+
["Calls missing", "", "token totals exceed the recorded model calls"], ["Ends on call", "", "trace ends on a tool call with no result"],
|
| 120 |
+
["Compacted", "", "history compacted into a summary"], ["No traj.", "", "listed trial without a trajectory"],
|
| 121 |
+
["Timestamps", "", "missing or out of order"]]],
|
| 122 |
+
["Cost", [["Trials / LB", "", "sum of per-trial recorded cost against the leaderboard total"], ["Uncosted", "", "trials without a recorded cost"]]],
|
| 123 |
+
["Evidence", [["Undecided", "", "trials with a behavioural check atif-scan couldn't decide"]]],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
];
|
| 125 |
+
const g = el("tr", "groups"), hr = el("tr");
|
| 126 |
+
const g0 = el("th", "rowh", ""); g0.rowSpan = 1; g.append(g0);
|
| 127 |
+
hr.append(el("th", "rowh", "Row"));
|
| 128 |
+
groups.forEach(([name, cols], i) => {
|
| 129 |
+
const th = el("th", "grp" + (i ? " gstart" : ""), name); th.colSpan = cols.length; g.append(th);
|
| 130 |
+
cols.forEach(([cname, cls, help], j) => {
|
| 131 |
+
const c = el("th", (cls || "") + (j === 0 && i ? " gstart" : ""), cname); c.title = help; hr.append(c);
|
| 132 |
+
});
|
| 133 |
+
});
|
| 134 |
+
const thead = el("thead"); thead.append(g, hr);
|
| 135 |
const t = $("gaps-table");
|
| 136 |
+
t.replaceChildren(thead);
|
| 137 |
const tb = el("tbody");
|
| 138 |
+
const start = td => { td.classList.add("gstart"); return td; };
|
| 139 |
D.rows.forEach(r => {
|
| 140 |
const h = r.scan.holes, n = r.scan.trials, m = h.models, tr = el("tr");
|
| 141 |
tr.append(rowHead(r));
|
|
|
|
| 143 |
m.served.filter(x => x.share >= 0.005).slice(0, 3).forEach(x =>
|
| 144 |
md.append(el("span", x.configured ? "mdl" : "mdl off", shortModel(x.model) + (m.served.length > 1 ? " " + pct0(x.share) : ""))));
|
| 145 |
if (m.foreignTrials) md.append(link(holeHref(r, "foreign"), m.foreignTrials + " trials", "mlink"));
|
| 146 |
+
tr.append(start(md));
|
| 147 |
+
|
| 148 |
+
tr.append(start(el("td", "n", String(h.text.allCpt))));
|
|
|
|
| 149 |
tr.append(el("td", "n", h.text.reasoningShare !== null ? pct0(h.text.reasoningShare) : "–"));
|
| 150 |
+
tr.append(countCell(r, "unseen", h.unseen, n, { scale: 1 }));
|
| 151 |
+
const ub = el("td", "c");
|
| 152 |
+
if (h.text.uncounted) ub.append(link(holeHref(r, "uncounted"), "≥" + pct0(h.text.uncounted), "bad"));
|
| 153 |
+
else ub.append(el("span", "z", "·"));
|
| 154 |
+
tr.append(ub);
|
| 155 |
+
|
| 156 |
+
tr.append(start(h.webTrials ? countCell(r, "webUnrecorded", h.webUnrecorded, n, { of: h.webTrials, scale: 1 }) : el("td", "c z", "no web")));
|
| 157 |
["webUnresolved", "unpaired", "callsMissing", "endsOnCall", "compacted", "noTrajectory", "timestamps"]
|
| 158 |
.forEach(k => tr.append(countCell(r, k, h[k], n)));
|
| 159 |
+
|
| 160 |
const off = h.lbCost ? h.costSum - h.lbCost : 0;
|
| 161 |
+
tr.append(start(el("td", "n" + (Math.abs(off) / (h.lbCost || 1) > 0.02 ? " bad" : ""),
|
| 162 |
+
Math.abs(off) >= 0.5 ? money(h.costSum) + " / " + money(h.lbCost) : money(h.costSum) + " =")));
|
| 163 |
tr.append(countCell(r, "uncosted", h.uncosted, n, { scale: 0.3 }));
|
| 164 |
+
const und = el("td", "n", String(h.evidenceIncomplete)); heat(und, h.evidenceIncomplete / n, 1.5, "cool");
|
| 165 |
+
tr.append(start(und));
|
|
|
|
|
|
|
|
|
|
| 166 |
tb.append(tr);
|
| 167 |
});
|
| 168 |
t.append(tb);
|
index.html
CHANGED
|
@@ -48,12 +48,19 @@
|
|
| 48 |
<p class="key"><i class="sq sq--1"></i>rewarded <i class="sq sq--0"></i>failed <i class="sq sq--t"></i>timed out
|
| 49 |
<i class="sq sq--e"></i>errored · corner: highest finding <i class="sq sq--1 sq--c"></i>critical
|
| 50 |
<i class="sq sq--1 sq--h"></i>high <i class="sq sq--1 sq--m"></i>medium · <b>Leads</b>: rewarded trials whose
|
| 51 |
-
highest unexcused finding is high or critical
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
</section>
|
| 53 |
|
| 54 |
<section id="gaps" class="block">
|
| 55 |
<header class="block-head"><h2>Gaps & accounting</h2>
|
| 56 |
-
<span class="muted small">trials per row unless marked · shading is the share · click a
|
|
|
|
|
|
|
|
|
|
| 57 |
<div class="scroll"><table id="gaps-table" class="grid grid--gaps"></table></div>
|
| 58 |
</section>
|
| 59 |
|
|
|
|
| 48 |
<p class="key"><i class="sq sq--1"></i>rewarded <i class="sq sq--0"></i>failed <i class="sq sq--t"></i>timed out
|
| 49 |
<i class="sq sq--e"></i>errored · corner: highest finding <i class="sq sq--1 sq--c"></i>critical
|
| 50 |
<i class="sq sq--1 sq--h"></i>high <i class="sq sq--1 sq--m"></i>medium · <b>Leads</b>: rewarded trials whose
|
| 51 |
+
highest unexcused finding is high or critical.
|
| 52 |
+
<b>Output not in trace</b>: share of reported output tokens with no text in the trace, and why —
|
| 53 |
+
<span class="rz rz--hidden">hidden</span> encrypted, tokens reported · <span class="rz rz--unrecorded">unrecorded</span> no text
|
| 54 |
+
or count · <span class="rz rz--dropped">dropped</span> lost converting the session · <span class="rz rz--summary">summary</span> ·
|
| 55 |
+
<span class="rz rz--full">full text</span>.</p>
|
| 56 |
</section>
|
| 57 |
|
| 58 |
<section id="gaps" class="block">
|
| 59 |
<header class="block-head"><h2>Gaps & accounting</h2>
|
| 60 |
+
<span class="muted small">trials per row unless marked · shading is the share · click a figure for its trials</span></header>
|
| 61 |
+
<p class="explain-line">The leaderboard's <b>output not in trace</b> comes from this group: recorded text per reported
|
| 62 |
+
output token, ≈3.2 when the trace holds everything. Where reasoning tokens are reported, their share is shown; trials under 80%
|
| 63 |
+
open from their count. <b>Unbilled</b> is the reverse gap: recorded reasoning missing from the output tokens, and so the cost.</p>
|
| 64 |
<div class="scroll"><table id="gaps-table" class="grid grid--gaps"></table></div>
|
| 65 |
</section>
|
| 66 |
|
row.js
CHANGED
|
@@ -69,7 +69,7 @@
|
|
| 69 |
stats.append(
|
| 70 |
stat("Score", fmt1(r.lb.accuracy) + "%", "± " + fmt1(r.lb.ci95) + " · " + r.scan.rewarded + " of " + n + " trials rewarded"),
|
| 71 |
stat("Review leads", String(r.scan.leads), "leaderboard reward hacks " + fmt1(r.lb.rewardHacks) + "%", r.scan.leads ? "stat--hot" : ""),
|
| 72 |
-
stat("
|
| 73 |
stat("Cost", money(r.lb.cost), "leaderboard · trials sum to " + money(h.costSum)));
|
| 74 |
const actions = el("div", "actions");
|
| 75 |
actions.append(link(rowPath(r) + "highlights/index.html", "Highlights", "btn btn--primary"),
|
|
@@ -150,8 +150,12 @@
|
|
| 150 |
if (h.uncosted) cost.append(trials("uncosted", "Trials without a recorded cost"));
|
| 151 |
if (!h.text.uncounted && !h.uncosted && Math.abs(gap) < 0.5) cost.append(el("p", "muted small", "Every trial costed; the trials add up to the leaderboard's total."));
|
| 152 |
|
|
|
|
| 153 |
const grid = el("div", "panels");
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
| 155 |
main.replaceChildren(head, stats, actions, stripWrap, grid);
|
| 156 |
|
| 157 |
// #k=<gap> (from the home page's tables) opens that list of trials; #found etc. scroll to a panel.
|
|
|
|
| 69 |
stats.append(
|
| 70 |
stat("Score", fmt1(r.lb.accuracy) + "%", "± " + fmt1(r.lb.ci95) + " · " + r.scan.rewarded + " of " + n + " trials rewarded"),
|
| 71 |
stat("Review leads", String(r.scan.leads), "leaderboard reward hacks " + fmt1(r.lb.rewardHacks) + "%", r.scan.leads ? "stat--hot" : ""),
|
| 72 |
+
stat("Output not in trace", pct0(h.text.unseen), "reasoning " + T.RZ[h.text.form] + (h.text.uncounted ? " · ≥" + pct0(h.text.uncounted) + " unbilled" : "")),
|
| 73 |
stat("Cost", money(r.lb.cost), "leaderboard · trials sum to " + money(h.costSum)));
|
| 74 |
const actions = el("div", "actions");
|
| 75 |
actions.append(link(rowPath(r) + "highlights/index.html", "Highlights", "btn btn--primary"),
|
|
|
|
| 150 |
if (h.uncosted) cost.append(trials("uncosted", "Trials without a recorded cost"));
|
| 151 |
if (!h.text.uncounted && !h.uncosted && Math.abs(gap) < 0.5) cost.append(el("p", "muted small", "Every trial costed; the trials add up to the leaderboard's total."));
|
| 152 |
|
| 153 |
+
// Two columns that fill independently: findings on the left; what ran, the trace and the cost on the right.
|
| 154 |
const grid = el("div", "panels");
|
| 155 |
+
const left = el("div", "pcol"), right = el("div", "pcol");
|
| 156 |
+
left.append(found);
|
| 157 |
+
right.append(ran, trace, cost);
|
| 158 |
+
grid.append(left, right);
|
| 159 |
main.replaceChildren(head, stats, actions, stripWrap, grid);
|
| 160 |
|
| 161 |
// #k=<gap> (from the home page's tables) opens that list of trials; #found etc. scroll to a panel.
|
style.css
CHANGED
|
@@ -72,7 +72,8 @@ a.sq:hover { outline: 2px solid var(--ink); }
|
|
| 72 |
.btn:hover { background: var(--inset); color: var(--ink); }
|
| 73 |
.strip-wrap { padding: 14px 18px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--card); overflow-x: auto; }
|
| 74 |
.strip-wrap p { margin: 0 0 8px; }
|
| 75 |
-
.panels { display: grid; grid-template-columns:
|
|
|
|
| 76 |
.panel { padding: 18px 20px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--card); scroll-margin-top: 70px; }
|
| 77 |
.panel h2 { font-size: 24px; margin: 0 0 4px; }
|
| 78 |
.checks, .models, .trial-list { list-style: none; padding: 0; margin: 0; }
|
|
@@ -192,3 +193,18 @@ details.trials summary .label { font-weight: 650; }
|
|
| 192 |
.grid--fam td { min-width: 96px; }
|
| 193 |
.grid th, .grid td { padding: 5px 6px; }
|
| 194 |
.grid .notes { max-width: 220px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
.btn:hover { background: var(--inset); color: var(--ink); }
|
| 73 |
.strip-wrap { padding: 14px 18px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--card); overflow-x: auto; }
|
| 74 |
.strip-wrap p { margin: 0 0 8px; }
|
| 75 |
+
.panels { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 22px; margin-top: 22px; align-items: start; }
|
| 76 |
+
.pcol { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
|
| 77 |
.panel { padding: 18px 20px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--card); scroll-margin-top: 70px; }
|
| 78 |
.panel h2 { font-size: 24px; margin: 0 0 4px; }
|
| 79 |
.checks, .models, .trial-list { list-style: none; padding: 0; margin: 0; }
|
|
|
|
| 193 |
.grid--fam td { min-width: 96px; }
|
| 194 |
.grid th, .grid td { padding: 5px 6px; }
|
| 195 |
.grid .notes { max-width: 220px; }
|
| 196 |
+
|
| 197 |
+
/* Grouped headers. */
|
| 198 |
+
.grid thead tr.groups th { position: static; border-bottom: 0; padding-bottom: 0; font-size: 10px; color: var(--ink); text-align: left; }
|
| 199 |
+
.grid .gstart { border-left: 1.5px solid var(--line-soft); }
|
| 200 |
+
.grid thead tr.groups th.gstart { border-left: 1.5px solid var(--line); }
|
| 201 |
+
.explain-line { margin: 0 0 10px; font-size: 13.5px; color: var(--muted); max-width: 1100px; }
|
| 202 |
+
.explain-line b { color: var(--ink); }
|
| 203 |
+
.vis .pc { display: inline-block; min-width: 36px; margin-right: 6px; }
|
| 204 |
+
.grid td .rz { display: block; font-size: 10.5px; }
|
| 205 |
+
.vis .rz { display: inline; font-size: 12px; }
|
| 206 |
+
.grid td.vis .rz { display: inline; font-size: 12px; }
|
| 207 |
+
.bar--miss { background: color-mix(in srgb, var(--ink) 10%, transparent); }
|
| 208 |
+
.bar--miss i { background: var(--hl-h); }
|
| 209 |
+
.unb { font-size: 12px; }
|
| 210 |
+
.vis .bar { width: 40px; }
|