replication-radar 0.3.3__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {replication_radar-0.3.3 → replication_radar-0.3.4}/DEMO.md +26 -2
- {replication_radar-0.3.3 → replication_radar-0.3.4}/PKG-INFO +1 -1
- {replication_radar-0.3.3 → replication_radar-0.3.4}/pyproject.toml +1 -1
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/app.js +61 -10
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/style.css +19 -0
- replication_radar-0.3.4/src/replication_radar/data/verdicts.json +584 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/src/replication_radar/network.py +37 -1
- {replication_radar-0.3.3 → replication_radar-0.3.4}/src/replication_radar/openaire.py +1 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/src/replication_radar/radar.py +2 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/src/replication_radar/server.py +9 -2
- replication_radar-0.3.4/src/replication_radar/verdicts.py +128 -0
- replication_radar-0.3.3/src/replication_radar/data/verdicts.json +0 -394
- replication_radar-0.3.3/src/replication_radar/verdicts.py +0 -56
- {replication_radar-0.3.3 → replication_radar-0.3.4}/.github/workflows/publish-pypi.yml +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/.gitignore +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/CLAUDE.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/LICENSE +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/README.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/STORY.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/demo_sdm.py +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/docs/app-ui.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/docs/link-types.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/docs/next-layers-plan.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/docs/openaire-mcp.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/docs/readiness-scoring-plan.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/netlify.toml +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/scripts/build_verdicts.py +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/README.md +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/curated.json +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/index.html +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/methodology.html +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/methodology.json +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/site/verdicts.json +0 -0
- {replication_radar-0.3.3 → replication_radar-0.3.4}/src/replication_radar/__init__.py +0 -0
|
@@ -61,7 +61,7 @@ demo lands with just `replication-radar`.
|
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
64
|
-
## 3 · The recording —
|
|
64
|
+
## 3 · The recording — three beats
|
|
65
65
|
|
|
66
66
|
### Beat 1 — the money shot: verify + cite (≈35 s)
|
|
67
67
|
|
|
@@ -81,7 +81,31 @@ confirmed, 1 qualifies it; here are the signed verdicts to cite,"* with links.
|
|
|
81
81
|
"settled"; the MCP shows it's been checked 5 times and hands you **signed, citable verdicts** —
|
|
82
82
|
including the one that *qualifies* it. That's the difference between paraphrasing and citing.
|
|
83
83
|
|
|
84
|
-
### Beat 2 — the
|
|
84
|
+
### Beat 2 — abstract → atomic claim → verdict: the two-project synergy (≈30 s)
|
|
85
|
+
|
|
86
|
+
This is the strongest AI beat: the agent turns a paper's **text into a structured claim** and
|
|
87
|
+
then **verifies** it, in one turn — your two hackathon projects composing (Jean's Hackaweek
|
|
88
|
+
claim-*extraction* pipeline + the Radar's claim-*verification* layer). Needs the MCP **≥ 0.3.3**
|
|
89
|
+
(it now returns the paper's abstract).
|
|
90
|
+
|
|
91
|
+
**Type this prompt:**
|
|
92
|
+
|
|
93
|
+
> Read the abstract of Soroye et al. 2020 (DOI 10.1126/science.aax8591), extract its single
|
|
94
|
+
> central finding as one atomic AIDA sentence, and then tell me whether that exact claim has
|
|
95
|
+
> been independently replicated.
|
|
96
|
+
|
|
97
|
+
**What to expect:** the agent calls **`replication_status("10.1126/science.aax8591")`**, which now
|
|
98
|
+
returns the **abstract** alongside the verdicts. The agent reads the abstract, **writes the atomic
|
|
99
|
+
claim itself** (something like *"An increasing frequency of unusually hot days raises local
|
|
100
|
+
extinction and lowers site occupancy of bumble bees, independent of land-use change"*), and then
|
|
101
|
+
reports it has been **independently replicated 5× (4 confirm, 1 qualifies)** with the signed
|
|
102
|
+
nanopubs.
|
|
103
|
+
|
|
104
|
+
**The point to land:** *from a paper's text → a structured, atomic claim → a signed, citable
|
|
105
|
+
verdict, in one turn.* Generation meets verification — the full "graph of verified knowledge" arc,
|
|
106
|
+
live.
|
|
107
|
+
|
|
108
|
+
### Beat 3 — the discovery side (≈20 s, optional)
|
|
85
109
|
|
|
86
110
|
**Type this prompt:**
|
|
87
111
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: replication-radar
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: MCP server that turns the OpenAIRE Graph into a ranked replication queue — impact-ranked targets and the Science Live verification overlay (retraction/supersession-aware).
|
|
5
5
|
Project-URL: Homepage, https://github.com/ScienceLiveHub/replication-radar
|
|
6
6
|
Project-URL: Repository, https://github.com/ScienceLiveHub/replication-radar
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "replication-radar"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.4"
|
|
4
4
|
description = "MCP server that turns the OpenAIRE Graph into a ranked replication queue — impact-ranked targets and the Science Live verification overlay (retraction/supersession-aware)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -122,6 +122,12 @@ const outcomeLinks = (outs) => {
|
|
|
122
122
|
return ` · ${lead} ` + outs.map((o) =>
|
|
123
123
|
`<a class="onp ${verdictClass(o.verdict)}" href="${o.np}" target="_blank" rel="noopener" title="${esc(o.verdict)} — open the signed nanopub">${verdictLabel(o.verdict)}</a>`).join(" ");
|
|
124
124
|
};
|
|
125
|
+
// "See all replications" → the Science Live constellation page: every signed replication of this
|
|
126
|
+
// paper's claims, grouped by claim, with each verdict + the full chain. (Switch the host to
|
|
127
|
+
// platform.sciencelive4all.org once the page is deployed to prod.)
|
|
128
|
+
const CONSTELLATION = "https://platform-dev.sciencelive4all.org/np/replications?doi=";
|
|
129
|
+
const constellationLink = (doi, n) => !doi ? "" :
|
|
130
|
+
` · <a class="seeall" href="${CONSTELLATION}${encodeURIComponent(doi)}" target="_blank" rel="noopener">see ${n === 1 ? "the replication" : `all ${n} replications`} →</a>`;
|
|
125
131
|
// Agreement pattern across the independent replication verdicts — many-agree ≠ disagree.
|
|
126
132
|
const agreementOf = (doi) => {
|
|
127
133
|
const vs = outcomesFor(doi).map((o) => o.verdict);
|
|
@@ -188,6 +194,15 @@ const subjectsOf = (rec, scheme) => [...new Set((rec.subjects || []).filter((s)
|
|
|
188
194
|
const cleanFos = (arr) => [...new Set(arr.map((v) => v.replace(/^\d+\s+/, "")).filter((v) => v && !/^\d+$/.test(v)))];
|
|
189
195
|
const oaOf = (rec) => rec.openAccessColor || ((rec.bestAccessRight || {}).label || "").toLowerCase();
|
|
190
196
|
const urlOf = (rec) => (rec.instances && rec.instances[0] && rec.instances[0].urls && rec.instances[0].urls[0]) || null;
|
|
197
|
+
// Paper abstract from OpenAIRE `descriptions` — strip JATS/HTML tags, decode entities, collapse
|
|
198
|
+
// whitespace (mirrors the Python _abstract). Gives an OPEN target the context of what it claims.
|
|
199
|
+
const abstractOf = (rec) => {
|
|
200
|
+
let t = (rec.descriptions || []).filter((d) => typeof d === "string").join(" ");
|
|
201
|
+
if (!t) return "";
|
|
202
|
+
t = t.replace(/<[^>]+>/g, " ");
|
|
203
|
+
const d = document.createElement("textarea"); d.innerHTML = t; t = d.value; // decode entities
|
|
204
|
+
return t.replace(/\s+/g, " ").trim().replace(/^abstract[\s:.\-–—]*/i, ""); // drop redundant "Abstract" prefix
|
|
205
|
+
};
|
|
191
206
|
|
|
192
207
|
// ---------- software FAIR + usage assessment (grounded, live from GitHub + SWH) ----------
|
|
193
208
|
// Computes the fair-software.eu 5 recommendations + usage from authoritative sources
|
|
@@ -371,7 +386,7 @@ async function loadCurated() {
|
|
|
371
386
|
if (rec) {
|
|
372
387
|
c.title = rec.mainTitle || doi; c.citations = impact(rec).citationCount || 0;
|
|
373
388
|
c.cls = impact(rec).citationClass; c.infl = impact(rec).influenceClass;
|
|
374
|
-
c.impl = impact(rec).impulseClass; c.year = yearOf(rec);
|
|
389
|
+
c.impl = impact(rec).impulseClass; c.year = yearOf(rec); c.abstract = abstractOf(rec);
|
|
375
390
|
} else { c.title = c.title || doi; }
|
|
376
391
|
}));
|
|
377
392
|
}
|
|
@@ -412,6 +427,7 @@ async function radar(topic) {
|
|
|
412
427
|
const readiness = readinessFrom(mat.score, impactScore, momentum); // computed for ALL (incl. verified)
|
|
413
428
|
return {
|
|
414
429
|
title: p.mainTitle || "", doi, citations: impact(p).citationCount || 0,
|
|
430
|
+
abstract: abstractOf(p),
|
|
415
431
|
cls: impact(p).citationClass, infl: impact(p).influenceClass,
|
|
416
432
|
year: yearOf(p), impl: impact(p).impulseClass || null,
|
|
417
433
|
mat, parts: { mat: mat.score, impact: impactScore, momentum },
|
|
@@ -490,7 +506,7 @@ async function radar(topic) {
|
|
|
490
506
|
const matScore = c.state === "rocrate" ? 1.0 : 0.6;
|
|
491
507
|
targets.push({
|
|
492
508
|
title: c.title, doi, citations: c.citations || 0, cls: c.cls, infl: c.infl,
|
|
493
|
-
year: c.year || null, impl: c.impl || null,
|
|
509
|
+
year: c.year || null, impl: c.impl || null, abstract: c.abstract || "",
|
|
494
510
|
mat: { score: matScore, state: c.state, code: c.code || null, resolved: true, data: c.data || [], source: c.source || "the paper", lang: c.lang },
|
|
495
511
|
parts: { mat: matScore, impact: impactScore, momentum },
|
|
496
512
|
status: VERDICTS[doi] ? "VERIFIED" : "OPEN",
|
|
@@ -519,6 +535,36 @@ const esc = (s) => (s || "").replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<
|
|
|
519
535
|
const PER_PAGE = 10;
|
|
520
536
|
let _targets = [], _tpage = 0, _tfilter = new Set();
|
|
521
537
|
|
|
538
|
+
// FAIR-software block: a fold that expands to the 5 fair-software.eu recommendations (met/missing),
|
|
539
|
+
// with a live Software Heritage link (browse the archived snapshot, or Save Code Now if not yet).
|
|
540
|
+
const FAIR_REC = { repository: "public repository", license: "open licence", registry: "in a registry", citation: "citable (CITATION.cff / DOI)", quality: "quality artefacts" };
|
|
541
|
+
// Software Heritage status: archived → browse the snapshot; not yet → a one-click "archive it"
|
|
542
|
+
// button that POSTs to the SWH Save-Code-Now API (anonymous, CORS-open, no account needed).
|
|
543
|
+
const swhHtml = (repo, archived) => {
|
|
544
|
+
if (archived) {
|
|
545
|
+
const b = repo ? `https://archive.softwareheritage.org/browse/origin/directory/?origin_url=${encodeURIComponent(repo)}` : null;
|
|
546
|
+
return b ? `<a class="swhok" href="${b}" target="_blank" rel="noopener" title="Browse the archived snapshot in Software Heritage">in Software Heritage</a>` : `<span class="swhok">in Software Heritage</span>`;
|
|
547
|
+
}
|
|
548
|
+
if (!repo) return `<a class="swhno" href="https://archive.softwareheritage.org/save/" target="_blank" rel="noopener">not yet archived</a>`;
|
|
549
|
+
return `<button type="button" class="swhsave" data-repo="${esc(repo)}" onclick="swhSave(this)" title="Open Software Heritage Save Code Now and copy this repo's URL ready to paste">not yet archived — archive it →</button>`;
|
|
550
|
+
};
|
|
551
|
+
// SWH's save API can't be called cross-origin from a static page (the repo URL sits in the request
|
|
552
|
+
// path and the form can't be pre-filled), so do the reliable thing: copy the repo URL to the
|
|
553
|
+
// clipboard and open the Save Code Now form — one click, then paste (Cmd/Ctrl-V) and submit.
|
|
554
|
+
window.swhSave = async (btn) => {
|
|
555
|
+
const repo = btn.dataset.repo || "";
|
|
556
|
+
try { if (repo && navigator.clipboard) { await navigator.clipboard.writeText(repo); btn.classList.add("done"); btn.innerHTML = `${ICON.check}repo URL copied — paste it into the form`; } }
|
|
557
|
+
catch (e) { /* clipboard blocked — the form still opens */ }
|
|
558
|
+
window.open("https://archive.softwareheritage.org/save/", "_blank", "noopener");
|
|
559
|
+
};
|
|
560
|
+
function fairBlock(f, repo) {
|
|
561
|
+
const recs = Object.entries(f.recs || {}).map(([k, ok]) =>
|
|
562
|
+
`<span class="${ok ? "rok" : "rno"}">${ok ? ICON.check : ICON.x}${FAIR_REC[k] || k}</span>`).join("");
|
|
563
|
+
// stop the SWH link's click from toggling the fold
|
|
564
|
+
const swh = ` · <span onclick="event.stopPropagation()">${swhHtml(repo, f.swh)}</span>`;
|
|
565
|
+
return `<details class="tfair"><summary>FAIR software <b>${f.score}/5</b> · ${ICON.star}${f.stars}${swh}</summary><div class="fairrecs">${recs}</div></details>`;
|
|
566
|
+
}
|
|
567
|
+
|
|
522
568
|
function targetRow(t) {
|
|
523
569
|
const p = t.parts || {};
|
|
524
570
|
const scoreTitle = t.status === "VERIFIED"
|
|
@@ -543,22 +589,27 @@ function targetRow(t) {
|
|
|
543
589
|
const cl = t.status === "VERIFIED" ? claimFor(t.outcome_np) : null;
|
|
544
590
|
const claimLine = (cl && (cl.aida || cl.label))
|
|
545
591
|
? `<div class="tclaim"><span class="claimlbl">claim:</span> <span class="claimq">“${esc(cl.aida || cl.label)}”</span>${cl.type ? ` <span class="badge ctype" title="FORRT claim type">${esc(cl.type)}</span>` : ""}</div>` : "";
|
|
592
|
+
// OPEN targets have no verdict-chain claim yet — show the abstract for context on what you'd
|
|
593
|
+
// test. Long ones fold (click to expand the full text) so the card stays compact.
|
|
594
|
+
const absLine = (t.status !== "VERIFIED" && t.abstract)
|
|
595
|
+
? (t.abstract.length <= 240
|
|
596
|
+
? `<div class="tabstract"><span class="claimlbl">abstract</span> ${esc(t.abstract)}</div>`
|
|
597
|
+
: `<details class="tabstract"><summary><span class="claimlbl">abstract</span> <span class="absnip">${esc(t.abstract.slice(0, 200))}…</span></summary><div class="absfull">${esc(t.abstract)}</div></details>`)
|
|
598
|
+
: "";
|
|
546
599
|
const outs = t.status === "VERIFIED" ? outcomesFor(t.doi).filter((o) => o.np) : [];
|
|
547
600
|
const verdictLink = (t.status === "VERIFIED")
|
|
548
|
-
? `<div class="tverdict">independently checked by <a href="https://sciencelive4all.org" target="_blank" rel="noopener">Science Live</a> — <b>${esc(agreementOf(t.doi).why)}</b>${
|
|
601
|
+
? `<div class="tverdict">independently checked by <a href="https://sciencelive4all.org" target="_blank" rel="noopener">Science Live</a> — <b>${esc(agreementOf(t.doi).why)}</b>${constellationLink(t.doi, outs.length)}</div>` : "";
|
|
549
602
|
const resolvedNote = (t.mat && t.mat.resolved)
|
|
550
603
|
? `<div class="tresolved">↳ materials resolved from ${esc(t.mat.source || "the paper")} (not in OpenAIRE): <a href="${esc(t.mat.code || "")}" target="_blank" rel="noopener">code repo</a>${(t.mat.data && t.mat.data.length) ? ` · data: ${t.mat.data.map(esc).join(", ")}` : ""}</div>`
|
|
551
604
|
: "";
|
|
552
|
-
const fairNote = t.fair
|
|
553
|
-
? `<div class="tfair">FAIR software <b>${t.fair.score}/5</b> · ${ICON.star}${t.fair.stars}${t.fair.swh ? " · in Software Heritage" : ""}</div>`
|
|
554
|
-
: "";
|
|
605
|
+
const fairNote = t.fair ? fairBlock(t.fair, t.mat && t.mat.code) : "";
|
|
555
606
|
// OPEN targets get a next step: discovery here → the FORRT template handles the nanopub chain.
|
|
556
607
|
const replicateCTA = (t.status !== "VERIFIED")
|
|
557
608
|
? `<div class="treplicate"><a href="https://github.com/ScienceLiveHub/forrt-replication-template" target="_blank" rel="noopener" title="Start a replication from the FORRT template — it scaffolds the repo and the signed nanopub chain (Claim · Study · Outcome)">▷ Replicate this with the template →</a></div>`
|
|
558
609
|
: "";
|
|
559
610
|
return `<div class="target ${t.status === "VERIFIED" ? "verified" : ""}">
|
|
560
611
|
${score}
|
|
561
|
-
<div class="t-main">${badge}<br><b>${esc(t.title)}</b>${meta}${claimLine}${verdictLink}${resolvedNote}${fairNote}${replicateCTA}</div>
|
|
612
|
+
<div class="t-main">${badge}<br><b>${esc(t.title)}</b>${meta}${claimLine}${absLine}${verdictLink}${resolvedNote}${fairNote}${replicateCTA}</div>
|
|
562
613
|
<div class="t-right">${t.citations.toLocaleString()} cites<br>${link}</div>
|
|
563
614
|
</div>`;
|
|
564
615
|
}
|
|
@@ -623,8 +674,8 @@ function renderVerified(inField) {
|
|
|
623
674
|
if (v.repl) {
|
|
624
675
|
const f = v.repl.fair;
|
|
625
676
|
const fairBadge = f
|
|
626
|
-
? `<div class="fairrecs"><b>FAIR software (${f.score}/5):</b> ${Object.entries(f.recs).map(([k, ok]) => `<span class="${ok ? "rok" : "rno"}">${ok ? ICON.check : ICON.x}${k}</span>`).join("")}</div>
|
|
627
|
-
<div class="fairline">${ICON.star}${f.stars} stars · ${ICON.fork}${f.forks} forks · ${f.swh
|
|
677
|
+
? `<div class="fairrecs"><b>FAIR software (${f.score}/5):</b> ${Object.entries(f.recs).map(([k, ok]) => `<span class="${ok ? "rok" : "rno"}">${ok ? ICON.check : ICON.x}${FAIR_REC[k] || k}</span>`).join("")}</div>
|
|
678
|
+
<div class="fairline">${ICON.star}${f.stars} stars · ${ICON.fork}${f.forks} forks · ${swhHtml(v.repl.code, f.swh)}</div>`
|
|
628
679
|
: "";
|
|
629
680
|
const nodeHref = v.repl.code && v.repl.code.includes("github") ? v.repl.code : v.repl.url;
|
|
630
681
|
repl = `<div class="vrepl">↳ replication is an OpenAIRE node: <a href="${nodeHref}" target="_blank" rel="noopener">${esc(v.repl.title).slice(0, 44) || v.repl.doi}</a> <span class="ochip type">${esc(v.repl.type)}</span></div>${fairBadge}`;
|
|
@@ -640,7 +691,7 @@ function renderVerified(inField) {
|
|
|
640
691
|
<span class="vt">${esc(v.title).slice(0, 82)}</span>
|
|
641
692
|
<div class="ochips">${chips}</div>
|
|
642
693
|
${vClaimLine}
|
|
643
|
-
<div class="vline"><span class="vv ${partialOf(v) ? "partial" : ""}">${v.verdicts.join(", ")}</span> — independently checked by <a href="https://sciencelive4all.org" target="_blank" rel="noopener">Science Live</a> ${vouts.length ?
|
|
694
|
+
<div class="vline"><span class="vv ${partialOf(v) ? "partial" : ""}">${v.verdicts.join(", ")}</span> — independently checked by <a href="https://sciencelive4all.org" target="_blank" rel="noopener">Science Live</a> ${vouts.length ? constellationLink(v.doi, vouts.length) : (v.cito_np ? `· <a href="${v.cito_np}" target="_blank" rel="noopener">verdict chain →</a>` : "")}</div>
|
|
644
695
|
${repl}
|
|
645
696
|
</li>`;
|
|
646
697
|
};
|
|
@@ -98,10 +98,19 @@ header .sub{margin:0;color:#aebfdc;font-size:14px}
|
|
|
98
98
|
.badge .ic{margin-right:3px}
|
|
99
99
|
.tfair .ic,.fairline .ic,.fairrecs .ic{margin-right:2px}
|
|
100
100
|
.tclaim{font-size:12.5px;color:var(--ink);margin-top:6px;line-height:1.5}
|
|
101
|
+
.tabstract{font-size:12px;color:var(--mut);margin-top:6px;line-height:1.5}
|
|
102
|
+
.tabstract .claimlbl{color:var(--navy2)}
|
|
103
|
+
details.tabstract > summary{cursor:pointer;list-style:none}
|
|
104
|
+
details.tabstract > summary::-webkit-details-marker{display:none}
|
|
105
|
+
details.tabstract > summary::after{content:" ▸ more";color:var(--magenta);font-weight:600;font-size:11px}
|
|
106
|
+
details.tabstract[open] > summary::after{content:" ▾ less"}
|
|
107
|
+
details.tabstract[open] .absnip{display:none}
|
|
108
|
+
details.tabstract .absfull{margin-top:3px}
|
|
101
109
|
.claimlbl{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--green)}
|
|
102
110
|
.claimq{font-style:italic}
|
|
103
111
|
.badge.ctype{background:#e7edf6;color:var(--navy2);text-transform:capitalize}
|
|
104
112
|
.tverdict{font-size:12px;color:var(--mut);margin-top:4px}
|
|
113
|
+
.seeall{font-weight:600}
|
|
105
114
|
.onp{display:inline-block;min-width:18px;text-align:center;border:1px solid var(--line);border-radius:4px;padding:0 6px;margin:0 2px;font-weight:700;font-size:11px;line-height:1.6}
|
|
106
115
|
.onp:hover{text-decoration:none}
|
|
107
116
|
/* white body so the chip is visible on tinted (verified) cards; verdict lives in border+text+hover */
|
|
@@ -116,6 +125,11 @@ header .sub{margin:0;color:#aebfdc;font-size:14px}
|
|
|
116
125
|
.tresolved a{font-weight:600}
|
|
117
126
|
.tfair{font-size:12px;color:var(--mut);margin-top:3px}
|
|
118
127
|
.tfair b{color:var(--navy2)}
|
|
128
|
+
details.tfair > summary{cursor:pointer;list-style:none}
|
|
129
|
+
details.tfair > summary::-webkit-details-marker{display:none}
|
|
130
|
+
details.tfair > summary::after{content:" ▸";color:var(--navy2)}
|
|
131
|
+
details.tfair[open] > summary::after{content:" ▾"}
|
|
132
|
+
details.tfair .fairrecs{margin-top:5px}
|
|
119
133
|
.treplicate{margin-top:7px}
|
|
120
134
|
.treplicate a{font-size:12px;font-weight:700;background:var(--magenta-soft);color:#a32a6a;padding:4px 11px;border-radius:7px;display:inline-block}
|
|
121
135
|
.treplicate a:hover{background:var(--magenta);color:#fff;text-decoration:none}
|
|
@@ -154,6 +168,11 @@ header .sub{margin:0;color:#aebfdc;font-size:14px}
|
|
|
154
168
|
.fairbar i{display:block;height:100%;background:var(--green)}
|
|
155
169
|
.swhok{color:var(--green);font-weight:600}
|
|
156
170
|
.swhno{color:#8a5804}
|
|
171
|
+
.swhsave{background:none;border:0;padding:0;margin:0;font:inherit;font-weight:600;color:#8a5804;cursor:pointer;text-decoration:underline}
|
|
172
|
+
.swhsave:hover:not(:disabled){color:#6f560f}
|
|
173
|
+
.swhsave:disabled{cursor:default;text-decoration:none}
|
|
174
|
+
.swhsave.done{color:var(--green);text-decoration:none}
|
|
175
|
+
.swhsave .ic{margin-right:3px}
|
|
157
176
|
.ochip.wait{background:#fdecc8;color:#8a5804}
|
|
158
177
|
.vnone{color:#a32a6a;background:var(--magenta-soft);padding:12px 14px;border-radius:8px;font-size:13.5px}
|
|
159
178
|
.vmore-stat{margin-top:14px;padding-top:12px;border-top:1px solid var(--line);font-size:12.5px;color:var(--mut)}
|