@opacedev/astro-ai-content-checker 0.3.1
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.
- package/CHANGELOG.md +138 -0
- package/LICENSE +9 -0
- package/README.md +236 -0
- package/SBOM.cdx.json +172 -0
- package/SECURITY.md +9 -0
- package/THIRD_PARTY_NOTICES.md +36 -0
- package/dist/build-report-html.d.ts +3 -0
- package/dist/highlight.js +155 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +43 -0
- package/dist/options.d.ts +21 -0
- package/dist/options.js +6 -0
- package/dist/receipt.d.ts +15 -0
- package/dist/receipt.js +100 -0
- package/dist/report.d.ts +55 -0
- package/dist/report.js +12 -0
- package/dist/sections.js +181 -0
- package/dist/share.d.ts +47 -0
- package/dist/share.js +72 -0
- package/dist/shared-EX54KI6H.js +1503 -0
- package/dist/shared-MMXWTX2U.js +43 -0
- package/dist/toolbar.js +13478 -0
- package/node_modules/@opacedev/ai-content-checker-browser/LICENSE +21 -0
- package/node_modules/@opacedev/ai-content-checker-browser/README.md +145 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.d.ts +14 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.js +47 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/index.d.ts +3 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/index.js +99 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.d.ts +12 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.js +17 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.d.ts +1 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.js +3984 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.d.ts +30 -0
- package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.js +1 -0
- package/node_modules/@opacedev/ai-content-checker-browser/package.json +60 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/LICENSE +21 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/README.md +93 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.d.ts +58 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.js +17 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/package.json +57 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-request.schema.d.ts +44 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-result.schema.d.ts +122 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/candidate.schema.d.ts +81 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/capabilities.schema.d.ts +24 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/checker-result.schema.d.ts +342 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/common.schema.d.ts +10 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/envelope.schema.d.ts +14 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/gate-result.schema.d.ts +19 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/integrity-receipt.schema.d.ts +122 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/job.schema.d.ts +188 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/method-result.schema.d.ts +39 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/pattern-finding.schema.d.ts +26 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/protected-span.schema.d.ts +35 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/rewrite-request.schema.d.ts +65 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/index.ts +63 -0
- package/node_modules/@opacedev/ai-content-checker-contracts/src/semantic-validation.mjs +32 -0
- package/node_modules/@opacedev/ai-content-checker-core/LICENSE +21 -0
- package/node_modules/@opacedev/ai-content-checker-core/README.md +186 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/bundle.js +4744 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.d.ts +16 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.js +7 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.d.ts +17 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.js +76 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.d.ts +27 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.js +47 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.d.ts +14 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.js +16 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/index.d.ts +15 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/index.js +15 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.d.ts +8 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.js +84 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.d.ts +12 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.js +68 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.d.ts +73 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.js +499 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.d.ts +76 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.js +1067 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.d.ts +165 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.js +614 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.d.ts +19 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.js +542 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.d.ts +40 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.js +40 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.d.ts +138 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.js +200 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.d.ts +71 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.js +388 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.d.ts +17 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.js +75 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.d.ts +3 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.js +17 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.d.ts +55 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.js +86 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.d.ts +48 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.js +61 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.d.ts +241 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.js +419 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.d.ts +34 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.js +51 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.d.ts +3 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.js +58 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.d.ts +12 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.js +30 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.d.ts +22 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.js +137 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.d.ts +16 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.js +84 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.d.ts +162 -0
- package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.js +501 -0
- package/node_modules/@opacedev/ai-content-checker-core/package.json +62 -0
- package/node_modules/canonicalize/LICENSE +201 -0
- package/node_modules/canonicalize/README.md +65 -0
- package/node_modules/canonicalize/bin/canonicalize.js +17 -0
- package/node_modules/canonicalize/lib/canonicalize.d.ts +2 -0
- package/node_modules/canonicalize/lib/canonicalize.js +74 -0
- package/node_modules/canonicalize/package.json +64 -0
- package/node_modules/entities/LICENSE +11 -0
- package/node_modules/entities/lib/decode.d.ts +211 -0
- package/node_modules/entities/lib/decode.d.ts.map +1 -0
- package/node_modules/entities/lib/decode.js +536 -0
- package/node_modules/entities/lib/decode.js.map +1 -0
- package/node_modules/entities/lib/decode_codepoint.d.ts +19 -0
- package/node_modules/entities/lib/decode_codepoint.d.ts.map +1 -0
- package/node_modules/entities/lib/decode_codepoint.js +76 -0
- package/node_modules/entities/lib/decode_codepoint.js.map +1 -0
- package/node_modules/entities/lib/encode.d.ts +22 -0
- package/node_modules/entities/lib/encode.d.ts.map +1 -0
- package/node_modules/entities/lib/encode.js +77 -0
- package/node_modules/entities/lib/encode.js.map +1 -0
- package/node_modules/entities/lib/escape.d.ts +43 -0
- package/node_modules/entities/lib/escape.d.ts.map +1 -0
- package/node_modules/entities/lib/escape.js +122 -0
- package/node_modules/entities/lib/escape.js.map +1 -0
- package/node_modules/entities/lib/esm/decode.d.ts +211 -0
- package/node_modules/entities/lib/esm/decode.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/decode.js +496 -0
- package/node_modules/entities/lib/esm/decode.js.map +1 -0
- package/node_modules/entities/lib/esm/decode_codepoint.d.ts +19 -0
- package/node_modules/entities/lib/esm/decode_codepoint.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/decode_codepoint.js +71 -0
- package/node_modules/entities/lib/esm/decode_codepoint.js.map +1 -0
- package/node_modules/entities/lib/esm/encode.d.ts +22 -0
- package/node_modules/entities/lib/esm/encode.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/encode.js +69 -0
- package/node_modules/entities/lib/esm/encode.js.map +1 -0
- package/node_modules/entities/lib/esm/escape.d.ts +43 -0
- package/node_modules/entities/lib/esm/escape.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/escape.js +116 -0
- package/node_modules/entities/lib/esm/escape.js.map +1 -0
- package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts +3 -0
- package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/generated/decode-data-html.js +7 -0
- package/node_modules/entities/lib/esm/generated/decode-data-html.js.map +1 -0
- package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts +3 -0
- package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/generated/decode-data-xml.js +7 -0
- package/node_modules/entities/lib/esm/generated/decode-data-xml.js.map +1 -0
- package/node_modules/entities/lib/esm/generated/encode-html.d.ts +8 -0
- package/node_modules/entities/lib/esm/generated/encode-html.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/generated/encode-html.js +10 -0
- package/node_modules/entities/lib/esm/generated/encode-html.js.map +1 -0
- package/node_modules/entities/lib/esm/index.d.ts +96 -0
- package/node_modules/entities/lib/esm/index.d.ts.map +1 -0
- package/node_modules/entities/lib/esm/index.js +99 -0
- package/node_modules/entities/lib/esm/index.js.map +1 -0
- package/node_modules/entities/lib/esm/package.json +1 -0
- package/node_modules/entities/lib/generated/decode-data-html.d.ts +3 -0
- package/node_modules/entities/lib/generated/decode-data-html.d.ts.map +1 -0
- package/node_modules/entities/lib/generated/decode-data-html.js +9 -0
- package/node_modules/entities/lib/generated/decode-data-html.js.map +1 -0
- package/node_modules/entities/lib/generated/decode-data-xml.d.ts +3 -0
- package/node_modules/entities/lib/generated/decode-data-xml.d.ts.map +1 -0
- package/node_modules/entities/lib/generated/decode-data-xml.js +9 -0
- package/node_modules/entities/lib/generated/decode-data-xml.js.map +1 -0
- package/node_modules/entities/lib/generated/encode-html.d.ts +8 -0
- package/node_modules/entities/lib/generated/encode-html.d.ts.map +1 -0
- package/node_modules/entities/lib/generated/encode-html.js +12 -0
- package/node_modules/entities/lib/generated/encode-html.js.map +1 -0
- package/node_modules/entities/lib/index.d.ts +96 -0
- package/node_modules/entities/lib/index.d.ts.map +1 -0
- package/node_modules/entities/lib/index.js +126 -0
- package/node_modules/entities/lib/index.js.map +1 -0
- package/node_modules/entities/package.json +90 -0
- package/node_modules/entities/readme.md +122 -0
- package/package.json +91 -0
|
@@ -0,0 +1,1503 @@
|
|
|
1
|
+
// src/report.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { lstat, mkdir, readFile, readdir, realpath, writeFile } from "node:fs/promises";
|
|
4
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { inspect, prefixedSha256 } from "@opacedev/ai-content-checker-core";
|
|
7
|
+
import { decodeHTML } from "entities";
|
|
8
|
+
|
|
9
|
+
// ../../shared/evidence/document-tells.mjs
|
|
10
|
+
var phrase = (text, aiPer1000, humanPer1000, aiDocs, humanDocs, ratio, confirmingRatio) => ({
|
|
11
|
+
phrase: text,
|
|
12
|
+
aiPer1000,
|
|
13
|
+
humanPer1000,
|
|
14
|
+
aiDocs,
|
|
15
|
+
humanDocs,
|
|
16
|
+
ratio,
|
|
17
|
+
confirmingRatio,
|
|
18
|
+
smallSample: aiDocs < 10 || humanDocs < 10
|
|
19
|
+
});
|
|
20
|
+
var CURATED_PHRASE_TELLS = [
|
|
21
|
+
phrase("robust", 60.26, 16.17, 242, 67, 3.7, 3.7),
|
|
22
|
+
phrase("whether you're", 43.82, 8.69, 176, 36, 5, 3.1),
|
|
23
|
+
phrase("seamless", 22.41, 5.79, 90, 24, 3.8, 3.6),
|
|
24
|
+
phrase("seamlessly", 11.21, 5.07, 45, 21, 2.2, 2.2),
|
|
25
|
+
phrase("streamline", 10.21, 4.34, 41, 18, 2.3, 4.4),
|
|
26
|
+
phrase("underscores", 5.48, 2.41, 22, 10, 2.2, 3.5),
|
|
27
|
+
phrase("in an era", 4.48, 1.21, 18, 5, 3.5, 3.3),
|
|
28
|
+
phrase("at its core", 3.24, 1.45, 13, 6, 2.1, 2.8),
|
|
29
|
+
phrase("game-changer", 4.23, 1.69, 17, 7, 2.4, 3.4),
|
|
30
|
+
phrase("streamlining", 4.23, 1.93, 17, 8, 2.1, 4),
|
|
31
|
+
phrase("is not just about", 3.49, 1.21, 14, 5, 2.7, 2.5),
|
|
32
|
+
phrase("a testament to", 3.24, 1.21, 13, 5, 2.5, 13.9),
|
|
33
|
+
phrase("elevate your", 2.99, 0.72, 12, 3, 3.7, 2.8),
|
|
34
|
+
phrase("here's the thing", 2.24, 0.24, 9, 1, 6.5, 5.8),
|
|
35
|
+
phrase("key takeaways", 1.99, 0.72, 8, 3, 2.5, 2.2),
|
|
36
|
+
phrase("paving the way", 1.74, 0.24, 7, 1, 5.2, 5.8),
|
|
37
|
+
phrase("plays a crucial role", 1.25, 0.48, 5, 2, 2.3, 2.6),
|
|
38
|
+
phrase("tapestry", 0.75, 0, 3, 0, 7.2, 13.4),
|
|
39
|
+
phrase("in today's digital", 0.75, 0, 3, 0, 7.2, 2.5),
|
|
40
|
+
phrase("final thoughts", 0.75, 0.24, 3, 1, 2.4, 2.3),
|
|
41
|
+
phrase("a beacon of", 0.25, 0, 1, 0, 3.1, 6.2)
|
|
42
|
+
];
|
|
43
|
+
var COHESION_STOPWORDS = new Set(`a about above after again against all am an and any are aren't as at be because been
|
|
44
|
+
before being below between both but by can cannot could couldn't did didn't do does doesn't doing don't
|
|
45
|
+
down during each few for from further had hadn't has hasn't have haven't having he her here hers herself
|
|
46
|
+
him himself his how i if in into is isn't it its itself let's me more most mustn't my myself no nor not
|
|
47
|
+
of off on once only or other ought our ours ourselves out over own same shan't she should shouldn't so
|
|
48
|
+
some such than that the their theirs them themselves then there these they this those through to too
|
|
49
|
+
under until up very was wasn't we were weren't what when where which while who whom why with won't would
|
|
50
|
+
wouldn't you your yours yourself yourselves`.split(/\s+/));
|
|
51
|
+
|
|
52
|
+
// ../../shared/evidence/phrase-ratios.mjs
|
|
53
|
+
var WORDS = {
|
|
54
|
+
colour: "color",
|
|
55
|
+
colours: "colors",
|
|
56
|
+
coloured: "colored",
|
|
57
|
+
behaviour: "behavior",
|
|
58
|
+
behaviours: "behaviors",
|
|
59
|
+
behavioural: "behavioral",
|
|
60
|
+
favour: "favor",
|
|
61
|
+
favours: "favors",
|
|
62
|
+
favoured: "favored",
|
|
63
|
+
favourable: "favorable",
|
|
64
|
+
labour: "labor",
|
|
65
|
+
honour: "honor",
|
|
66
|
+
neighbour: "neighbor",
|
|
67
|
+
neighbours: "neighbors",
|
|
68
|
+
centre: "center",
|
|
69
|
+
centres: "centers",
|
|
70
|
+
theatre: "theater",
|
|
71
|
+
metre: "meter",
|
|
72
|
+
metres: "meters",
|
|
73
|
+
fibre: "fiber",
|
|
74
|
+
litre: "liter",
|
|
75
|
+
defence: "defense",
|
|
76
|
+
offence: "offense",
|
|
77
|
+
licence: "license",
|
|
78
|
+
practise: "practice",
|
|
79
|
+
programme: "program",
|
|
80
|
+
programmes: "programs",
|
|
81
|
+
analyse: "analyze",
|
|
82
|
+
analysed: "analyzed",
|
|
83
|
+
analysing: "analyzing",
|
|
84
|
+
catalogue: "catalog",
|
|
85
|
+
dialogue: "dialog",
|
|
86
|
+
travelled: "traveled",
|
|
87
|
+
travelling: "traveling",
|
|
88
|
+
modelled: "modeled",
|
|
89
|
+
modelling: "modeling",
|
|
90
|
+
labelled: "labeled",
|
|
91
|
+
labelling: "labeling",
|
|
92
|
+
whilst: "while",
|
|
93
|
+
amongst: "among",
|
|
94
|
+
towards: "toward",
|
|
95
|
+
learnt: "learned",
|
|
96
|
+
spelt: "spelled",
|
|
97
|
+
burnt: "burned",
|
|
98
|
+
sceptical: "skeptical",
|
|
99
|
+
sceptic: "skeptic",
|
|
100
|
+
grey: "gray",
|
|
101
|
+
storey: "story",
|
|
102
|
+
kerb: "curb"
|
|
103
|
+
};
|
|
104
|
+
var WORD_KEYS = Object.keys(WORDS).sort((a, b) => b.length - a.length);
|
|
105
|
+
var WORD_RE = new RegExp(`\\b(${WORD_KEYS.join("|")})\\b`, "g");
|
|
106
|
+
|
|
107
|
+
// ../../shared/presentation/checker-result-presentation.mjs
|
|
108
|
+
var CHECKER_LEVEL_LABELS = Object.freeze({
|
|
109
|
+
"signal-strongly-ai": "Strongly AI",
|
|
110
|
+
"signal-likely-ai": "Likely AI",
|
|
111
|
+
"signal-potentially-ai": "Potentially AI",
|
|
112
|
+
"signal-unclear": "Unclear",
|
|
113
|
+
"signal-likely-human": "Likely human"
|
|
114
|
+
});
|
|
115
|
+
var LEVEL_IDS = new Set(Object.keys(CHECKER_LEVEL_LABELS));
|
|
116
|
+
var METHOD_STATE_LABEL = Object.freeze({
|
|
117
|
+
pass: "No issue found",
|
|
118
|
+
attention: "Review evidence",
|
|
119
|
+
fail: "Failed",
|
|
120
|
+
inconclusive: "Inconclusive",
|
|
121
|
+
unsupported: "Unavailable",
|
|
122
|
+
not_configured: "Not configured",
|
|
123
|
+
not_run: "Not run",
|
|
124
|
+
error: "Error"
|
|
125
|
+
});
|
|
126
|
+
function escapeResultHtml(value) {
|
|
127
|
+
return String(value).replace(/[&<>"']/gu, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character] ?? character);
|
|
128
|
+
}
|
|
129
|
+
var PRODUCT_LOGO_DATA_URI = "data:image/png;base64,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";
|
|
130
|
+
var PRODUCT_NAME = "Opace AI Content Checker & Detector";
|
|
131
|
+
var PRODUCT_TAGLINE = "Evidence, not guarantees";
|
|
132
|
+
var CHECKER_GAUGE_ORDER = Object.freeze([
|
|
133
|
+
"signal-likely-human",
|
|
134
|
+
"signal-unclear",
|
|
135
|
+
"signal-potentially-ai",
|
|
136
|
+
"signal-likely-ai",
|
|
137
|
+
"signal-strongly-ai"
|
|
138
|
+
]);
|
|
139
|
+
var CHECKER_LEVEL_MEANINGS = Object.freeze({
|
|
140
|
+
"signal-strongly-ai": "The model found a very strong match to AI writing patterns. This does not prove authorship; review the scored sections and separate writing observations.",
|
|
141
|
+
"signal-likely-ai": "The model found a strong match to AI writing patterns. This does not prove authorship; review the scored sections and separate writing observations.",
|
|
142
|
+
"signal-potentially-ai": "The model found some similarity to AI writing patterns. This does not prove authorship; review the scored sections and separate writing observations.",
|
|
143
|
+
"signal-unclear": "The model did not find a clear enough match to favour human or AI writing. The passage scores and any examples below show what was measured, without settling who wrote it.",
|
|
144
|
+
"signal-likely-human": "The model found a closer match to human writing than to AI writing. This is not proof of authorship. Any writing-pattern examples below are separate observations."
|
|
145
|
+
});
|
|
146
|
+
var CHECKER_MEANING_PANEL = Object.freeze({
|
|
147
|
+
meansTitle: "What this means",
|
|
148
|
+
means: Object.freeze([
|
|
149
|
+
"Parts of the writing match patterns common in AI text.",
|
|
150
|
+
"The marked sections carry the strongest match and are worth a careful read."
|
|
151
|
+
]),
|
|
152
|
+
notTitle: "What this does not mean",
|
|
153
|
+
not: Object.freeze([
|
|
154
|
+
"It does not prove who wrote the draft.",
|
|
155
|
+
"It says nothing about whether the content is accurate or good.",
|
|
156
|
+
"Human writing, including writing edited with AI assistance, can receive an AI-leaning reading."
|
|
157
|
+
])
|
|
158
|
+
});
|
|
159
|
+
var OVERLAP_NORMS = Object.freeze({ machineMedian: 2.1, humanMedian: 6.3, corpus: "670 register-and-length-matched pairs of long-form documents" });
|
|
160
|
+
var INTEGRITY_READINGS = Object.freeze({
|
|
161
|
+
clean: "No hidden or lookalike characters found",
|
|
162
|
+
attention: "Review",
|
|
163
|
+
manipulated: "Planted pattern",
|
|
164
|
+
inconclusive: "No clear answer",
|
|
165
|
+
error: "Error"
|
|
166
|
+
});
|
|
167
|
+
var EDITORIAL_READINGS = Object.freeze({
|
|
168
|
+
none: "No selected writing rules matched",
|
|
169
|
+
some: "A few patterns",
|
|
170
|
+
many: "Several patterns",
|
|
171
|
+
not_assessed: "Not assessed",
|
|
172
|
+
error: "Error"
|
|
173
|
+
});
|
|
174
|
+
var ROUTE_KIND_LABELS = Object.freeze({
|
|
175
|
+
browser_model: "On this device, in the browser",
|
|
176
|
+
eu_server: "Opace EU server",
|
|
177
|
+
wordpress_same_site: "This WordPress site",
|
|
178
|
+
loopback_engine: "Local engine on this machine",
|
|
179
|
+
deterministic_only: "Named rule checks only"
|
|
180
|
+
});
|
|
181
|
+
var TRANSFER_LABELS = Object.freeze({
|
|
182
|
+
none: "The text stayed on this device.",
|
|
183
|
+
same_site: "The text went to this site and nowhere else.",
|
|
184
|
+
loopback: "The text went to the local engine on this machine and nowhere else.",
|
|
185
|
+
eu_server: "The text was sent once to the Opace EU server for this request."
|
|
186
|
+
});
|
|
187
|
+
var CONSENT_LABELS = Object.freeze({
|
|
188
|
+
not_required: "No transfer, so no consent was needed.",
|
|
189
|
+
explicit: "You agreed to this route before the run."
|
|
190
|
+
});
|
|
191
|
+
var PASSAGE_SIGNAL_REFERENCES = Object.freeze({
|
|
192
|
+
adjacent_overlap: Object.freeze({
|
|
193
|
+
label: "Word re-use between neighbouring sentences",
|
|
194
|
+
aiMedian: 2.1,
|
|
195
|
+
humanMedian: 6.3,
|
|
196
|
+
auroc: 0.912,
|
|
197
|
+
basis: "medians over 670 matched pairs of long-form documents; this is the signal that separates the two populations best"
|
|
198
|
+
}),
|
|
199
|
+
vocabulary_variety: Object.freeze({
|
|
200
|
+
label: "Vocabulary variety across the passage",
|
|
201
|
+
aiMedian: 0.776,
|
|
202
|
+
humanMedian: 0.694,
|
|
203
|
+
auroc: 0.911,
|
|
204
|
+
basis: "moving-average type-token ratio over 100-word windows; medians over the same 670 matched pairs"
|
|
205
|
+
}),
|
|
206
|
+
sentence_length_cv: Object.freeze({
|
|
207
|
+
label: "Sentence-length evenness",
|
|
208
|
+
aiMedian: null,
|
|
209
|
+
humanMedian: null,
|
|
210
|
+
auroc: 0.521,
|
|
211
|
+
basis: "measured on 5,935 matched pairs: AUROC 0.521 against 0.500 for chance, catching 2.5% of machine documents at a 1% false-positive budget"
|
|
212
|
+
})
|
|
213
|
+
});
|
|
214
|
+
var LEAN_PHRASES = Object.freeze({
|
|
215
|
+
adjacent_overlap: { ai: "it re-uses fewer words between neighbouring sentences than people typically do", human: "it re-uses words between neighbouring sentences the way people typically do" },
|
|
216
|
+
vocabulary_variety: { ai: "its vocabulary is more varied for its length than people typically write", human: "its vocabulary is about as varied for its length as people typically write" }
|
|
217
|
+
});
|
|
218
|
+
var CHECK_GROUP_BY_CATEGORY = Object.freeze({
|
|
219
|
+
detector: "ai",
|
|
220
|
+
watermark: "ai",
|
|
221
|
+
unicode: "integrity",
|
|
222
|
+
provenance: "integrity",
|
|
223
|
+
fidelity: "integrity",
|
|
224
|
+
pattern: "editorial"
|
|
225
|
+
});
|
|
226
|
+
var CHECK_GROUP_BY_ID = Object.freeze([
|
|
227
|
+
[/^(?:detector|model|classifier)\b/u, "ai"],
|
|
228
|
+
[/^watermark\b/u, "ai"],
|
|
229
|
+
[/^(?:unicode|homoglyph|invisible)\b/u, "integrity"],
|
|
230
|
+
[/^(?:c2pa|provenance|credential)/u, "integrity"],
|
|
231
|
+
[/^(?:pattern|rule|editorial|writing)\b/u, "editorial"]
|
|
232
|
+
]);
|
|
233
|
+
var CHECK_GROUP_LABELS = Object.freeze({
|
|
234
|
+
ai: "AI-pattern reading",
|
|
235
|
+
integrity: "Text integrity",
|
|
236
|
+
editorial: "Editorial signals",
|
|
237
|
+
other: "Other named checks"
|
|
238
|
+
});
|
|
239
|
+
var CHECK_GROUP_ORDER = Object.freeze(["ai", "integrity", "editorial", "other"]);
|
|
240
|
+
var CHECK_SUBJECTS = Object.freeze({
|
|
241
|
+
detector: "patterns in this draft that match AI writing",
|
|
242
|
+
watermark: "an invisible watermark that some AI systems add to their own text",
|
|
243
|
+
unicode: "invisible or lookalike characters in this draft",
|
|
244
|
+
provenance: "Content Credentials attached to this draft or the file it came from",
|
|
245
|
+
fidelity: "whether a suggested fix would change what the draft says",
|
|
246
|
+
pattern: "phrasing and structure a person might want to edit"
|
|
247
|
+
});
|
|
248
|
+
var CHECK_SUBJECT_BY_ID = Object.freeze({
|
|
249
|
+
"unicode.invisible": "characters in this draft that carry no mark of their own, such as zero-width joiners and other hidden controls",
|
|
250
|
+
"unicode.homoglyph": "letters from other alphabets that look like ordinary ones, such as a Cyrillic \u201C\u0430\u201D standing in for a Latin \u201Ca\u201D"
|
|
251
|
+
});
|
|
252
|
+
var CHECK_OUTCOMES = Object.freeze({
|
|
253
|
+
pass: "it ran on this draft and found nothing to raise",
|
|
254
|
+
attention: "it ran on this draft and found something worth your eye, shown above",
|
|
255
|
+
fail: "it ran but did not finish cleanly, so nothing from it counts here",
|
|
256
|
+
inconclusive: "it ran on this draft but could not settle on an answer",
|
|
257
|
+
unsupported: "it is not available on this route, so it did not look at your draft",
|
|
258
|
+
not_configured: "it is not set up on this route, so it did not look at your draft",
|
|
259
|
+
not_run: "it did not run on this draft, and nothing is assumed from that",
|
|
260
|
+
error: "it stopped with an error, so nothing from it counts here"
|
|
261
|
+
});
|
|
262
|
+
var CHECK_RAN = Object.freeze(/* @__PURE__ */ new Set(["pass", "attention", "fail", "inconclusive", "error"]));
|
|
263
|
+
var CHECK_NAME_BY_ID = Object.freeze({
|
|
264
|
+
"unicode.invisible": "Invisible and hidden characters",
|
|
265
|
+
"unicode.homoglyph": "Lookalike (homoglyph) characters",
|
|
266
|
+
"watermark.anthropic": "Anthropic official watermark verifier"
|
|
267
|
+
});
|
|
268
|
+
var LIMITATION_CONTRADICTIONS = Object.freeze([
|
|
269
|
+
{
|
|
270
|
+
id: "model-did-run",
|
|
271
|
+
when: (result) => result.axes.ai_pattern.assessment_status === "assessed",
|
|
272
|
+
match: /no trained model (?:ran|was run)|ai-pattern reading is not assessed|cannot supply an ai-pattern reading|no ai-pattern reading is available/iu
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: "full-checker-parity",
|
|
276
|
+
when: (result) => result.profile === "full_checker",
|
|
277
|
+
match: /not full-checker parity/iu
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: "sections-were-scored",
|
|
281
|
+
when: (result) => result.sections.length > 0,
|
|
282
|
+
match: /no section (?:was|were) scored|no scored passages? (?:is|are) available/iu
|
|
283
|
+
}
|
|
284
|
+
]);
|
|
285
|
+
var LIMITATION_CONSTANTS = Object.freeze([
|
|
286
|
+
"A check that did not run is never counted as a pass.",
|
|
287
|
+
"No result proves who wrote a text."
|
|
288
|
+
]);
|
|
289
|
+
var LIMITATION_THEMES = Object.freeze([
|
|
290
|
+
{ id: "authorship", match: /prov(?:e|es|ing|en)\b[^.]*\b(?:who wrote|authorship)|not proof of authorship|proves? who wrote/iu },
|
|
291
|
+
{ id: "not-a-percentage", match: /not a percentage/iu },
|
|
292
|
+
{ id: "absence-is-not-evidence", match: /absence of [^.]* is not evidence/iu }
|
|
293
|
+
]);
|
|
294
|
+
var SHAREABLE_LEVELS = Object.freeze([
|
|
295
|
+
"signal-likely-human",
|
|
296
|
+
"signal-unclear",
|
|
297
|
+
"signal-potentially-ai",
|
|
298
|
+
"signal-likely-ai",
|
|
299
|
+
"signal-strongly-ai"
|
|
300
|
+
]);
|
|
301
|
+
var SHARE_SHEET_COPY = Object.freeze({
|
|
302
|
+
eyebrow: "Share result",
|
|
303
|
+
title: "Choose where to share",
|
|
304
|
+
intro: "These options work across Mac, Windows, iPhone and Android. Which apps appear still depends on the device and the browser.",
|
|
305
|
+
copy: "Copy result link",
|
|
306
|
+
email: "Email",
|
|
307
|
+
device: "More apps on this device",
|
|
308
|
+
directly: "Share directly",
|
|
309
|
+
close: "Close sharing options",
|
|
310
|
+
privacy: "Only the reading summary and result link are shared. Your checked text is never included.",
|
|
311
|
+
copied: "Result link copied.",
|
|
312
|
+
copyFailed: "The link could not be copied. You can use Email or a social option instead.",
|
|
313
|
+
shared: "Result shared.",
|
|
314
|
+
cancelled: "Sharing cancelled.",
|
|
315
|
+
shareFailed: "Share options were unavailable, so the link was copied instead.",
|
|
316
|
+
unavailable: "Could not share or copy the link. Use one of the options above."
|
|
317
|
+
});
|
|
318
|
+
var SOCIAL_ORDER = Object.freeze([
|
|
319
|
+
["linkedin", "LinkedIn"],
|
|
320
|
+
["facebook", "Facebook"],
|
|
321
|
+
["x", "X"],
|
|
322
|
+
["whatsapp", "WhatsApp"]
|
|
323
|
+
]);
|
|
324
|
+
|
|
325
|
+
// ../../shared/presentation/checker-ui-css.mjs
|
|
326
|
+
var CHECKER_UI_CSS = `/*
|
|
327
|
+
Opace AI Content Checker & Detector \u2014 shared result presentation stylesheet.
|
|
328
|
+
|
|
329
|
+
One stylesheet for every surface: the WordPress Lab (inside WP admin base
|
|
330
|
+
styles), the Chrome side panel (about 360-420 px wide), the Astro dev toolbar
|
|
331
|
+
(about 400 px) and any full-width page or print sheet.
|
|
332
|
+
|
|
333
|
+
Rules that hold throughout:
|
|
334
|
+
|
|
335
|
+
- Every class is prefixed \`oaci-\`. Nothing here styles a bare element outside
|
|
336
|
+
the \`.oaci-result\` scope.
|
|
337
|
+
- Layout responds to the WIDTH OF THE COMPONENT, not the viewport, so a
|
|
338
|
+
400 px panel inside a 1440 px window gets the narrow layout. Container
|
|
339
|
+
queries do that; a viewport media query is kept as the fallback.
|
|
340
|
+
- No network request. No stylesheet fetch, no font file, no image URL. Surfaces may
|
|
341
|
+
bundle the Outfit and Plus Jakarta Sans OFL woff2 files themselves; every
|
|
342
|
+
family here ends in a full system fallback stack, so the component is
|
|
343
|
+
complete without them.
|
|
344
|
+
- Colour is never the only carrier. Every band prints its own name.
|
|
345
|
+
- The five band colours are the website scale values
|
|
346
|
+
(src/styles/content-integrity-signal-scale.css).
|
|
347
|
+
*/
|
|
348
|
+
|
|
349
|
+
.oaci-result .oaci-reasons > h2,
|
|
350
|
+
.oaci-result .oaci-reasons > h3,
|
|
351
|
+
.oaci-result .oaci-reasons > h4 { margin: 0; }
|
|
352
|
+
.oaci-result .oaci-reasons__scope { color: var(--oaci-note); font-size: var(--oaci-text-sm); }
|
|
353
|
+
.oaci-result .oaci-reason { padding: 16px 0; border-top: 1px solid var(--oaci-line); overflow-wrap: anywhere; }
|
|
354
|
+
.oaci-result .oaci-reason > b { display: block; color: var(--oaci-ink); font-size: var(--oaci-text-md); }
|
|
355
|
+
.oaci-result .oaci-reason blockquote { margin: 10px 0 !important; padding: 10px 14px !important; border-left: 3px solid var(--oaci-blue); background: var(--oaci-soft); color: var(--oaci-ink); font-style: normal; white-space: pre-wrap; }
|
|
356
|
+
.oaci-result .oaci-reason p { margin: 8px 0 !important; }
|
|
357
|
+
.oaci-result .oaci-reasons__title { font-size: var(--oaci-text-xl); }
|
|
358
|
+
.oaci-result .oaci-reason summary { cursor: pointer; color: var(--oaci-blue-deep); font-weight: 600; padding: 6px 0; }
|
|
359
|
+
.oaci-result .oaci-reason details p,
|
|
360
|
+
.oaci-result .oaci-reasons__boundary { font-size: var(--oaci-text-sm); color: var(--oaci-note); }
|
|
361
|
+
.oaci-result .oaci-reason summary:focus-visible { outline: 2px solid var(--oaci-focus); outline-offset: 3px; }
|
|
362
|
+
.oaci-result .oaci-axis[data-axis="integrity"][data-state="clean"] .oaci-axis__reading,
|
|
363
|
+
.oaci-result .oaci-axis[data-axis="editorial"][data-state="none"] .oaci-axis__reading { color: var(--oaci-ink-soft); }
|
|
364
|
+
@media print { .oaci-result .oaci-reason { break-inside: avoid; } }
|
|
365
|
+
|
|
366
|
+
/* ---------------------------------------------------------------- tokens -- */
|
|
367
|
+
|
|
368
|
+
.oaci-result {
|
|
369
|
+
/* Type. The families are declared, never fetched. */
|
|
370
|
+
--oaci-font-display: "Outfit", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
371
|
+
--oaci-font-body: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
372
|
+
--oaci-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
|
373
|
+
--oaci-text-xs: 11.5px;
|
|
374
|
+
--oaci-text-sm: 12.5px;
|
|
375
|
+
--oaci-text-base: 14px;
|
|
376
|
+
--oaci-text-md: 15px;
|
|
377
|
+
--oaci-text-lg: 17px;
|
|
378
|
+
--oaci-text-xl: 21px;
|
|
379
|
+
--oaci-text-2xl: 30px;
|
|
380
|
+
|
|
381
|
+
/* Surface. Light paper canvas, white panels, near-black ink. */
|
|
382
|
+
--oaci-paper: #f2ede6;
|
|
383
|
+
--oaci-panel: #ffffff;
|
|
384
|
+
--oaci-inset: #e9e2d8;
|
|
385
|
+
--oaci-soft: #f6f2eb;
|
|
386
|
+
--oaci-ink: #0f1115;
|
|
387
|
+
--oaci-ink-soft: #3d4344;
|
|
388
|
+
--oaci-note: #505758;
|
|
389
|
+
--oaci-line: #d9d0c5;
|
|
390
|
+
--oaci-line-strong: #b9ae9f;
|
|
391
|
+
|
|
392
|
+
/* Opace identity. */
|
|
393
|
+
--oaci-orange: #fb700a;
|
|
394
|
+
--oaci-orange-ink: #8b3f0b;
|
|
395
|
+
--oaci-blue: #0068b3;
|
|
396
|
+
--oaci-blue-deep: #00456f;
|
|
397
|
+
--oaci-focus: #0b7285;
|
|
398
|
+
|
|
399
|
+
/* The five bands, low to high. */
|
|
400
|
+
--oaci-band-human: #1c6e46; --oaci-band-human-bg: #d9f2e5;
|
|
401
|
+
--oaci-band-unclear: #5c6360; --oaci-band-unclear-bg: #e6e0d6;
|
|
402
|
+
--oaci-band-potential: #8a5a00; --oaci-band-potential-bg: #f8e5c7;
|
|
403
|
+
--oaci-band-likely: #a84a08; --oaci-band-likely-bg: #f9dcc4;
|
|
404
|
+
--oaci-band-strong: #96261b; --oaci-band-strong-bg: #f6d7d0;
|
|
405
|
+
--oaci-band-none: #5c6360; --oaci-band-none-bg: #e6e0d6;
|
|
406
|
+
|
|
407
|
+
/* Status chips. */
|
|
408
|
+
--oaci-ok: #185c3b; --oaci-ok-bg: #d9f2e5;
|
|
409
|
+
--oaci-watch: #70400e; --oaci-watch-bg: #f8e5c7;
|
|
410
|
+
--oaci-bad: #702d28; --oaci-bad-bg: #f6dcd9;
|
|
411
|
+
|
|
412
|
+
--oaci-radius-lg: 16px;
|
|
413
|
+
--oaci-radius: 14px;
|
|
414
|
+
--oaci-radius-sm: 10px;
|
|
415
|
+
--oaci-radius-pill: 999px;
|
|
416
|
+
--oaci-shadow: 0 1px 3px rgb(16 20 22 / 6%);
|
|
417
|
+
--oaci-gap: 16px;
|
|
418
|
+
|
|
419
|
+
/* One spacing scale for the result and for the shell around it, so a surface
|
|
420
|
+
that draws its own chrome sits on the same rhythm as the component. Every
|
|
421
|
+
step is a multiple of four; nothing here is used by the result itself
|
|
422
|
+
except through the names already above, so adding it changes no pixel. */
|
|
423
|
+
--oaci-space-1: 4px;
|
|
424
|
+
--oaci-space-2: 8px;
|
|
425
|
+
--oaci-space-3: 12px;
|
|
426
|
+
--oaci-space-4: 16px;
|
|
427
|
+
--oaci-space-5: 20px;
|
|
428
|
+
--oaci-space-6: 24px;
|
|
429
|
+
--oaci-space-7: 32px;
|
|
430
|
+
--oaci-space-8: 40px;
|
|
431
|
+
--oaci-space-9: 56px;
|
|
432
|
+
|
|
433
|
+
/* Elevation, low to high. The result stays flat; a shell uses these for the
|
|
434
|
+
panels it draws around it. */
|
|
435
|
+
--oaci-elev-1: 0 1px 3px rgb(16 20 22 / 6%);
|
|
436
|
+
--oaci-elev-2: 0 6px 18px rgb(15 17 21 / 6%);
|
|
437
|
+
--oaci-elev-3: 0 18px 44px rgb(15 17 21 / 8%);
|
|
438
|
+
|
|
439
|
+
container-type: inline-size;
|
|
440
|
+
container-name: oaci;
|
|
441
|
+
display: block;
|
|
442
|
+
max-width: 100%;
|
|
443
|
+
margin: 0;
|
|
444
|
+
padding: 0;
|
|
445
|
+
background: var(--oaci-paper);
|
|
446
|
+
color: var(--oaci-ink);
|
|
447
|
+
font-family: var(--oaci-font-body);
|
|
448
|
+
font-size: var(--oaci-text-base);
|
|
449
|
+
line-height: 1.6;
|
|
450
|
+
text-align: left;
|
|
451
|
+
overflow-wrap: break-word;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/* Dark mode. The default "system" setting only sets prefers-color-scheme, so
|
|
455
|
+
the media block carries it; an explicit [data-theme] on the component or on
|
|
456
|
+
any ancestor wins in both directions. */
|
|
457
|
+
@media (prefers-color-scheme: dark) {
|
|
458
|
+
.oaci-result:not([data-theme="light"]),
|
|
459
|
+
[data-theme="dark"] .oaci-result:not([data-theme="light"]) {
|
|
460
|
+
--oaci-paper: #14171a;
|
|
461
|
+
--oaci-panel: #1c2126;
|
|
462
|
+
--oaci-inset: #242a30;
|
|
463
|
+
--oaci-soft: #21262b;
|
|
464
|
+
--oaci-ink: #f2efe9;
|
|
465
|
+
--oaci-ink-soft: #cbd2d3;
|
|
466
|
+
--oaci-note: #a8b0b1;
|
|
467
|
+
--oaci-line: #39424a;
|
|
468
|
+
--oaci-line-strong: #55606a;
|
|
469
|
+
--oaci-orange-ink: #ffa76b;
|
|
470
|
+
--oaci-blue: #6fb6ec;
|
|
471
|
+
--oaci-blue-deep: #a8d5f7;
|
|
472
|
+
--oaci-focus: #66d0e0;
|
|
473
|
+
--oaci-band-human: #6fd6a2; --oaci-band-human-bg: #10382a;
|
|
474
|
+
--oaci-band-unclear: #b8bfbc; --oaci-band-unclear-bg: #2c3238;
|
|
475
|
+
--oaci-band-potential: #f0bb5c; --oaci-band-potential-bg: #3d2f12;
|
|
476
|
+
--oaci-band-likely: #ff9f68; --oaci-band-likely-bg: #43230f;
|
|
477
|
+
--oaci-band-strong: #ff9083; --oaci-band-strong-bg: #451a17;
|
|
478
|
+
--oaci-band-none: #b8bfbc; --oaci-band-none-bg: #2c3238;
|
|
479
|
+
--oaci-ok: #6fd6a2; --oaci-ok-bg: #10382a;
|
|
480
|
+
--oaci-watch: #f0bb5c; --oaci-watch-bg: #3d2f12;
|
|
481
|
+
--oaci-bad: #ff9083; --oaci-bad-bg: #451a17;
|
|
482
|
+
--oaci-shadow: 0 1px 3px rgb(0 0 0 / 40%);
|
|
483
|
+
--oaci-elev-1: 0 1px 3px rgb(0 0 0 / 40%);
|
|
484
|
+
--oaci-elev-2: 0 6px 18px rgb(0 0 0 / 44%);
|
|
485
|
+
--oaci-elev-3: 0 18px 44px rgb(0 0 0 / 50%);
|
|
486
|
+
color-scheme: dark;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.oaci-result[data-theme="dark"],
|
|
491
|
+
[data-theme="dark"] .oaci-result:not([data-theme="light"]) {
|
|
492
|
+
--oaci-paper: #14171a;
|
|
493
|
+
--oaci-panel: #1c2126;
|
|
494
|
+
--oaci-inset: #242a30;
|
|
495
|
+
--oaci-soft: #21262b;
|
|
496
|
+
--oaci-ink: #f2efe9;
|
|
497
|
+
--oaci-ink-soft: #cbd2d3;
|
|
498
|
+
--oaci-note: #a8b0b1;
|
|
499
|
+
--oaci-line: #39424a;
|
|
500
|
+
--oaci-line-strong: #55606a;
|
|
501
|
+
--oaci-orange-ink: #ffa76b;
|
|
502
|
+
--oaci-blue: #6fb6ec;
|
|
503
|
+
--oaci-blue-deep: #a8d5f7;
|
|
504
|
+
--oaci-focus: #66d0e0;
|
|
505
|
+
--oaci-band-human: #6fd6a2; --oaci-band-human-bg: #10382a;
|
|
506
|
+
--oaci-band-unclear: #b8bfbc; --oaci-band-unclear-bg: #2c3238;
|
|
507
|
+
--oaci-band-potential: #f0bb5c; --oaci-band-potential-bg: #3d2f12;
|
|
508
|
+
--oaci-band-likely: #ff9f68; --oaci-band-likely-bg: #43230f;
|
|
509
|
+
--oaci-band-strong: #ff9083; --oaci-band-strong-bg: #451a17;
|
|
510
|
+
--oaci-band-none: #b8bfbc; --oaci-band-none-bg: #2c3238;
|
|
511
|
+
--oaci-ok: #6fd6a2; --oaci-ok-bg: #10382a;
|
|
512
|
+
--oaci-watch: #f0bb5c; --oaci-watch-bg: #3d2f12;
|
|
513
|
+
--oaci-bad: #ff9083; --oaci-bad-bg: #451a17;
|
|
514
|
+
--oaci-shadow: 0 1px 3px rgb(0 0 0 / 40%);
|
|
515
|
+
--oaci-elev-1: 0 1px 3px rgb(0 0 0 / 40%);
|
|
516
|
+
--oaci-elev-2: 0 6px 18px rgb(0 0 0 / 44%);
|
|
517
|
+
--oaci-elev-3: 0 18px 44px rgb(0 0 0 / 50%);
|
|
518
|
+
color-scheme: dark;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* ------------------------------------------------- host-proof base rules -- */
|
|
522
|
+
/* WordPress admin, Astro's toolbar shadow host and a bare page all set their
|
|
523
|
+
own element defaults. These reset only inside the component. */
|
|
524
|
+
|
|
525
|
+
.oaci-result,
|
|
526
|
+
.oaci-result *,
|
|
527
|
+
.oaci-result *::before,
|
|
528
|
+
.oaci-result *::after { box-sizing: border-box; }
|
|
529
|
+
|
|
530
|
+
/* \`:where()\` keeps the reset at the specificity of \`.oaci-result\` alone, so the
|
|
531
|
+
component's own class rules below still win, while \`!important\` keeps the
|
|
532
|
+
host's element styles out. */
|
|
533
|
+
.oaci-result :where(h1, .oaci-result h2, .oaci-result h3, .oaci-result h4, .oaci-result h5, .oaci-result h6, .oaci-result p, .oaci-result ul, .oaci-result ol, .oaci-result li, .oaci-result dl, .oaci-result dd, .oaci-result dt, .oaci-result figure, .oaci-result blockquote) {
|
|
534
|
+
margin: 0 !important;
|
|
535
|
+
padding: 0 !important;
|
|
536
|
+
color: inherit;
|
|
537
|
+
font-family: inherit;
|
|
538
|
+
text-transform: none;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.oaci-result :where(h1, .oaci-result h2, .oaci-result h3, .oaci-result h4, .oaci-result h5, .oaci-result h6) {
|
|
542
|
+
font-family: var(--oaci-font-display) !important;
|
|
543
|
+
font-weight: 800;
|
|
544
|
+
line-height: 1.2;
|
|
545
|
+
letter-spacing: -0.01em;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.oaci-result :where(ul, .oaci-result ol) { list-style: none !important; }
|
|
549
|
+
.oaci-result :where(button) { margin: 0; font: inherit; color: inherit; text-align: left; }
|
|
550
|
+
.oaci-result :where(img) { max-width: 100%; height: auto; }
|
|
551
|
+
.oaci-result :where(svg) { display: block; max-width: 100%; }
|
|
552
|
+
.oaci-result [hidden] { display: none !important; }
|
|
553
|
+
|
|
554
|
+
.oaci-result :is(button, .oaci-result a, .oaci-result summary, .oaci-result input):focus-visible {
|
|
555
|
+
outline: 3px solid var(--oaci-focus);
|
|
556
|
+
outline-offset: 3px;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.oaci-result .oaci-sr {
|
|
560
|
+
position: absolute;
|
|
561
|
+
width: 1px;
|
|
562
|
+
height: 1px;
|
|
563
|
+
margin: -1px;
|
|
564
|
+
padding: 0;
|
|
565
|
+
border: 0;
|
|
566
|
+
overflow: hidden;
|
|
567
|
+
clip: rect(0 0 0 0);
|
|
568
|
+
white-space: nowrap;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.oaci-result .oaci-kicker {
|
|
572
|
+
font-family: var(--oaci-font-body) !important;
|
|
573
|
+
font-size: var(--oaci-text-xs) !important;
|
|
574
|
+
font-weight: 800;
|
|
575
|
+
letter-spacing: 0.09em;
|
|
576
|
+
text-transform: uppercase !important;
|
|
577
|
+
color: var(--oaci-note);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.oaci-result .oaci-note { color: var(--oaci-note); font-size: var(--oaci-text-sm); }
|
|
581
|
+
|
|
582
|
+
/* Wide content scrolls inside its own box. The page never scrolls sideways. */
|
|
583
|
+
.oaci-result .oaci-scroll { max-width: 100%; overflow-x: auto; }
|
|
584
|
+
|
|
585
|
+
/* ------------------------------------------------------------ the shell -- */
|
|
586
|
+
|
|
587
|
+
.oaci-result__body {
|
|
588
|
+
display: grid;
|
|
589
|
+
gap: 20px;
|
|
590
|
+
padding: clamp(16px, 3cqi, 30px);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.oaci-result .oaci-panel {
|
|
594
|
+
padding: clamp(16px, 2.6cqi, 26px);
|
|
595
|
+
border: 1px solid var(--oaci-line);
|
|
596
|
+
border-radius: var(--oaci-radius);
|
|
597
|
+
background: var(--oaci-panel);
|
|
598
|
+
box-shadow: var(--oaci-shadow);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* ----------------------------------------------------------- the masthead */
|
|
602
|
+
|
|
603
|
+
.oaci-result .oaci-mast {
|
|
604
|
+
display: flex;
|
|
605
|
+
flex-wrap: wrap;
|
|
606
|
+
align-items: center;
|
|
607
|
+
gap: 12px 16px;
|
|
608
|
+
padding: clamp(14px, 2.4cqi, 22px) clamp(16px, 3cqi, 30px);
|
|
609
|
+
border-bottom: 3px solid var(--oaci-orange);
|
|
610
|
+
background: var(--oaci-panel);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.oaci-result .oaci-mast__logo {
|
|
614
|
+
flex: 0 0 auto;
|
|
615
|
+
display: grid;
|
|
616
|
+
place-items: center;
|
|
617
|
+
width: 44px;
|
|
618
|
+
height: 44px;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.oaci-result .oaci-mast__logo img,
|
|
622
|
+
.oaci-result .oaci-mast__logo svg { width: 100%; height: 100%; object-fit: contain; }
|
|
623
|
+
|
|
624
|
+
.oaci-result .oaci-mast__identity { min-width: 0; flex: 1 1 200px; }
|
|
625
|
+
|
|
626
|
+
.oaci-result .oaci-mast__product {
|
|
627
|
+
font-family: var(--oaci-font-display) !important;
|
|
628
|
+
font-size: var(--oaci-text-lg) !important;
|
|
629
|
+
font-weight: 800;
|
|
630
|
+
line-height: 1.15;
|
|
631
|
+
letter-spacing: -0.015em;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.oaci-result .oaci-mast__tagline {
|
|
635
|
+
margin-top: 2px !important;
|
|
636
|
+
color: var(--oaci-note);
|
|
637
|
+
font-size: var(--oaci-text-sm) !important;
|
|
638
|
+
font-weight: 700;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.oaci-result .oaci-mast__meta {
|
|
642
|
+
margin-left: auto;
|
|
643
|
+
color: var(--oaci-note);
|
|
644
|
+
font-size: var(--oaci-text-xs) !important;
|
|
645
|
+
text-align: right;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.oaci-result .oaci-mast__meta span { display: block; }
|
|
649
|
+
|
|
650
|
+
/* ---------------------------------------------------------- the actions -- */
|
|
651
|
+
|
|
652
|
+
.oaci-result .oaci-actions {
|
|
653
|
+
display: flex;
|
|
654
|
+
flex-wrap: wrap;
|
|
655
|
+
gap: 8px;
|
|
656
|
+
padding: 10px clamp(16px, 3cqi, 30px);
|
|
657
|
+
border-bottom: 1px solid var(--oaci-line);
|
|
658
|
+
background: var(--oaci-panel);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.oaci-result .oaci-actions:empty { display: none; }
|
|
662
|
+
|
|
663
|
+
.oaci-result .oaci-action {
|
|
664
|
+
display: inline-flex;
|
|
665
|
+
align-items: center;
|
|
666
|
+
gap: 7px;
|
|
667
|
+
min-height: 38px;
|
|
668
|
+
padding: 8px 14px;
|
|
669
|
+
border: 1px solid var(--oaci-line);
|
|
670
|
+
border-radius: 999px;
|
|
671
|
+
background: var(--oaci-panel);
|
|
672
|
+
color: var(--oaci-ink);
|
|
673
|
+
font-size: var(--oaci-text-sm);
|
|
674
|
+
font-weight: 700;
|
|
675
|
+
cursor: pointer;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.oaci-result .oaci-action:hover:not(:disabled) { border-color: var(--oaci-ink); }
|
|
679
|
+
.oaci-result .oaci-action:disabled { color: var(--oaci-note); background: var(--oaci-inset); cursor: not-allowed; }
|
|
680
|
+
.oaci-result .oaci-action__glyph { font-style: normal; }
|
|
681
|
+
.oaci-result .oaci-actions__status { align-self: center; margin-left: auto; color: var(--oaci-note); font-size: var(--oaci-text-sm); font-weight: 700; }
|
|
682
|
+
.oaci-result .oaci-actions__status:empty { display: none; }
|
|
683
|
+
|
|
684
|
+
/* ---------------------------------------------------------- the verdict -- */
|
|
685
|
+
|
|
686
|
+
.oaci-result .oaci-verdict { text-align: center; }
|
|
687
|
+
.oaci-result .oaci-verdict > .oaci-kicker { display: block; text-align: left; }
|
|
688
|
+
|
|
689
|
+
.oaci-result .oaci-dial { max-width: 360px; margin: 12px auto 0; }
|
|
690
|
+
.oaci-result .oaci-dial svg { width: 100%; height: auto; }
|
|
691
|
+
.oaci-result .oaci-dial__seg { fill: none; stroke-width: 15; stroke-linecap: round; opacity: 0.3; }
|
|
692
|
+
.oaci-result .oaci-dial__seg[data-level="signal-likely-human"] { stroke: var(--oaci-band-human); }
|
|
693
|
+
.oaci-result .oaci-dial__seg[data-level="signal-unclear"] { stroke: var(--oaci-band-unclear); }
|
|
694
|
+
.oaci-result .oaci-dial__seg[data-level="signal-potentially-ai"] { stroke: var(--oaci-band-potential); }
|
|
695
|
+
.oaci-result .oaci-dial__seg[data-level="signal-likely-ai"] { stroke: var(--oaci-band-likely); }
|
|
696
|
+
.oaci-result .oaci-dial__seg[data-level="signal-strongly-ai"] { stroke: var(--oaci-band-strong); }
|
|
697
|
+
.oaci-result .oaci-dial__seg[data-active="true"] { opacity: 1; stroke-width: 19; }
|
|
698
|
+
.oaci-result .oaci-dial__needle { stroke: var(--oaci-ink); stroke-width: 3.5; stroke-linecap: round; }
|
|
699
|
+
.oaci-result .oaci-dial__hub { fill: var(--oaci-ink); }
|
|
700
|
+
|
|
701
|
+
.oaci-result .oaci-dial__labels { display: flex; gap: 6px; margin-top: 14px; }
|
|
702
|
+
.oaci-result .oaci-dial__labels span {
|
|
703
|
+
flex: 1;
|
|
704
|
+
min-width: 0;
|
|
705
|
+
color: var(--oaci-note);
|
|
706
|
+
font-size: var(--oaci-text-xs);
|
|
707
|
+
line-height: 1.25;
|
|
708
|
+
text-align: center;
|
|
709
|
+
/* A level name is a name: it wraps between its words and never inside one. */
|
|
710
|
+
overflow-wrap: normal;
|
|
711
|
+
word-break: keep-all;
|
|
712
|
+
hyphens: none;
|
|
713
|
+
}
|
|
714
|
+
.oaci-result .oaci-dial__labels span[data-active="true"] { color: var(--oaci-ink); font-weight: 800; }
|
|
715
|
+
|
|
716
|
+
.oaci-result .oaci-verdict__level {
|
|
717
|
+
margin-top: 18px !important;
|
|
718
|
+
font-family: var(--oaci-font-display) !important;
|
|
719
|
+
font-size: var(--oaci-text-2xl) !important;
|
|
720
|
+
font-weight: 800;
|
|
721
|
+
line-height: 1.1;
|
|
722
|
+
letter-spacing: -0.02em;
|
|
723
|
+
}
|
|
724
|
+
.oaci-result .oaci-verdict__level[data-level="signal-likely-human"] { color: var(--oaci-band-human); }
|
|
725
|
+
.oaci-result .oaci-verdict__level[data-level="signal-unclear"] { color: var(--oaci-ink-soft); }
|
|
726
|
+
.oaci-result .oaci-verdict__level[data-level="signal-potentially-ai"] { color: var(--oaci-band-potential); }
|
|
727
|
+
.oaci-result .oaci-verdict__level[data-level="signal-likely-ai"] { color: var(--oaci-band-likely); }
|
|
728
|
+
.oaci-result .oaci-verdict__level[data-level="signal-strongly-ai"] { color: var(--oaci-band-strong); }
|
|
729
|
+
|
|
730
|
+
.oaci-result .oaci-verdict__meaning {
|
|
731
|
+
max-width: 46ch;
|
|
732
|
+
margin: 10px auto 0 !important;
|
|
733
|
+
color: var(--oaci-ink-soft);
|
|
734
|
+
font-size: var(--oaci-text-md) !important;
|
|
735
|
+
line-height: 1.55;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.oaci-result .oaci-verdict__score {
|
|
739
|
+
margin-top: 12px !important;
|
|
740
|
+
color: var(--oaci-note);
|
|
741
|
+
font-size: var(--oaci-text-sm) !important;
|
|
742
|
+
font-variant-numeric: tabular-nums;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.oaci-result .oaci-verdict[data-assessed="false"] { text-align: left; }
|
|
746
|
+
.oaci-result .oaci-verdict[data-assessed="false"] .oaci-verdict__meaning { max-width: none; margin-left: 0 !important; }
|
|
747
|
+
|
|
748
|
+
.oaci-result .oaci-state {
|
|
749
|
+
display: inline-block;
|
|
750
|
+
margin-top: 12px;
|
|
751
|
+
padding: 5px 12px;
|
|
752
|
+
border-radius: 999px;
|
|
753
|
+
background: var(--oaci-inset);
|
|
754
|
+
color: var(--oaci-note);
|
|
755
|
+
font-size: var(--oaci-text-sm);
|
|
756
|
+
font-weight: 700;
|
|
757
|
+
}
|
|
758
|
+
.oaci-result .oaci-state--withheld { background: var(--oaci-watch-bg); color: var(--oaci-watch); }
|
|
759
|
+
.oaci-result .oaci-state--error { background: var(--oaci-bad-bg); color: var(--oaci-bad); }
|
|
760
|
+
|
|
761
|
+
/* --------------------------------------------------- the section scores -- */
|
|
762
|
+
|
|
763
|
+
.oaci-result .oaci-strip__head {
|
|
764
|
+
display: flex;
|
|
765
|
+
flex-wrap: wrap;
|
|
766
|
+
align-items: baseline;
|
|
767
|
+
justify-content: space-between;
|
|
768
|
+
gap: 6px 12px;
|
|
769
|
+
margin-bottom: 12px;
|
|
770
|
+
}
|
|
771
|
+
.oaci-result .oaci-strip__title { font-size: var(--oaci-text-lg) !important; }
|
|
772
|
+
.oaci-result .oaci-strip__head p { color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
773
|
+
|
|
774
|
+
.oaci-result .oaci-strip__list { display: grid; gap: 6px; }
|
|
775
|
+
|
|
776
|
+
.oaci-result .oaci-strip__bar {
|
|
777
|
+
display: grid;
|
|
778
|
+
grid-template-columns: 96px minmax(0, 1fr) 52px minmax(104px, auto) 14px;
|
|
779
|
+
gap: 12px;
|
|
780
|
+
align-items: center;
|
|
781
|
+
width: 100%;
|
|
782
|
+
padding: 11px 14px;
|
|
783
|
+
border: 1px solid var(--oaci-line);
|
|
784
|
+
border-radius: var(--oaci-radius-sm);
|
|
785
|
+
background: var(--oaci-panel);
|
|
786
|
+
cursor: pointer;
|
|
787
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
|
|
788
|
+
}
|
|
789
|
+
.oaci-result .oaci-strip__bar:hover { border-color: var(--oaci-line-strong); transform: translateY(-1px); }
|
|
790
|
+
.oaci-result .oaci-strip__bar[data-strongest="true"] { border-color: var(--oaci-ink); box-shadow: 0 0 0 2px rgb(16 20 22 / 12%); }
|
|
791
|
+
.oaci-result .oaci-strip__name { font-size: var(--oaci-text-sm); font-weight: 700; white-space: nowrap; }
|
|
792
|
+
.oaci-result .oaci-strip__track { display: block; height: 10px; border-radius: 999px; background: var(--oaci-inset); overflow: hidden; }
|
|
793
|
+
.oaci-result .oaci-strip__fill { display: block; height: 100%; border-radius: 999px; background: var(--oaci-band-unclear); }
|
|
794
|
+
.oaci-result .oaci-strip__fill[data-level="signal-likely-human"] { background: var(--oaci-band-human); }
|
|
795
|
+
.oaci-result .oaci-strip__fill[data-level="signal-unclear"] { background: var(--oaci-band-unclear); }
|
|
796
|
+
.oaci-result .oaci-strip__fill[data-level="signal-potentially-ai"] { background: var(--oaci-band-potential); }
|
|
797
|
+
.oaci-result .oaci-strip__fill[data-level="signal-likely-ai"] { background: var(--oaci-band-likely); }
|
|
798
|
+
.oaci-result .oaci-strip__fill[data-level="signal-strongly-ai"] { background: var(--oaci-band-strong); }
|
|
799
|
+
.oaci-result .oaci-strip__score { font-size: var(--oaci-text-sm); font-variant-numeric: tabular-nums; text-align: right; }
|
|
800
|
+
.oaci-result .oaci-strip__band { font-size: var(--oaci-text-sm); font-weight: 700; text-align: right; white-space: nowrap; }
|
|
801
|
+
.oaci-result .oaci-strip__band[data-level="signal-likely-human"] { color: var(--oaci-band-human); }
|
|
802
|
+
.oaci-result .oaci-strip__band[data-level="signal-unclear"] { color: var(--oaci-ink-soft); }
|
|
803
|
+
.oaci-result .oaci-strip__band[data-level="signal-potentially-ai"] { color: var(--oaci-band-potential); }
|
|
804
|
+
.oaci-result .oaci-strip__band[data-level="signal-likely-ai"] { color: var(--oaci-band-likely); }
|
|
805
|
+
.oaci-result .oaci-strip__band[data-level="signal-strongly-ai"] { color: var(--oaci-band-strong); }
|
|
806
|
+
.oaci-result .oaci-strip__go { color: var(--oaci-line-strong); font-size: 18px; font-weight: 700; text-align: right; transition: transform 0.15s ease; }
|
|
807
|
+
.oaci-result .oaci-strip__bar[aria-expanded="true"] .oaci-strip__go { transform: rotate(90deg); }
|
|
808
|
+
.oaci-result .oaci-strip__foot { margin-top: 10px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
809
|
+
|
|
810
|
+
/* ------------------------------------------------------- the deep dives -- */
|
|
811
|
+
|
|
812
|
+
.oaci-result .oaci-dives { display: grid; gap: 16px; }
|
|
813
|
+
.oaci-result .oaci-dives__intro { max-width: 62ch; color: var(--oaci-ink-soft); font-size: var(--oaci-text-base) !important; line-height: 1.6; }
|
|
814
|
+
|
|
815
|
+
.oaci-result .oaci-dive { padding: clamp(16px, 2.6cqi, 26px); border: 1px solid var(--oaci-line); border-radius: var(--oaci-radius-lg); background: var(--oaci-panel); box-shadow: var(--oaci-shadow); }
|
|
816
|
+
.oaci-result .oaci-dive[data-strongest="true"] { box-shadow: inset 0 0 0 2px rgb(16 20 22 / 14%); }
|
|
817
|
+
.oaci-result .oaci-dive__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; }
|
|
818
|
+
.oaci-result .oaci-dive__title { font-size: var(--oaci-text-lg) !important; }
|
|
819
|
+
.oaci-result .oaci-dive__sub { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 8px 0 16px; }
|
|
820
|
+
|
|
821
|
+
.oaci-result .oaci-chip {
|
|
822
|
+
display: inline-block;
|
|
823
|
+
padding: 3px 11px;
|
|
824
|
+
border-radius: 999px;
|
|
825
|
+
background: var(--oaci-band-none);
|
|
826
|
+
color: #ffffff;
|
|
827
|
+
font-size: var(--oaci-text-sm);
|
|
828
|
+
font-weight: 800;
|
|
829
|
+
}
|
|
830
|
+
.oaci-result .oaci-chip[data-level="signal-likely-human"] { background: var(--oaci-band-human); }
|
|
831
|
+
.oaci-result .oaci-chip[data-level="signal-unclear"] { background: var(--oaci-band-unclear); }
|
|
832
|
+
.oaci-result .oaci-chip[data-level="signal-potentially-ai"] { background: var(--oaci-band-potential); }
|
|
833
|
+
.oaci-result .oaci-chip[data-level="signal-likely-ai"] { background: var(--oaci-band-likely); }
|
|
834
|
+
.oaci-result .oaci-chip[data-level="signal-strongly-ai"] { background: var(--oaci-band-strong); }
|
|
835
|
+
@media (prefers-color-scheme: dark) { .oaci-result:not([data-theme="light"]) .oaci-chip { color: #10141a; } }
|
|
836
|
+
.oaci-result[data-theme="dark"] .oaci-chip,
|
|
837
|
+
[data-theme="dark"] .oaci-result:not([data-theme="light"]) .oaci-chip { color: #10141a; }
|
|
838
|
+
|
|
839
|
+
.oaci-result .oaci-dive__score { color: var(--oaci-note); font-size: var(--oaci-text-sm); font-variant-numeric: tabular-nums; }
|
|
840
|
+
|
|
841
|
+
.oaci-result .oaci-quote {
|
|
842
|
+
margin: 0 0 22px !important;
|
|
843
|
+
padding: 2px 0 2px 18px !important;
|
|
844
|
+
border-left: 3px solid var(--oaci-line-strong);
|
|
845
|
+
color: var(--oaci-ink-soft);
|
|
846
|
+
font-size: var(--oaci-text-md) !important;
|
|
847
|
+
font-style: italic;
|
|
848
|
+
line-height: 1.6;
|
|
849
|
+
max-height: 170px;
|
|
850
|
+
overflow-y: auto;
|
|
851
|
+
overscroll-behavior: contain;
|
|
852
|
+
}
|
|
853
|
+
.oaci-result .oaci-quote[data-level="signal-likely-human"] { border-left-color: var(--oaci-band-human); }
|
|
854
|
+
.oaci-result .oaci-quote[data-level="signal-potentially-ai"] { border-left-color: var(--oaci-band-potential); }
|
|
855
|
+
.oaci-result .oaci-quote[data-level="signal-likely-ai"] { border-left-color: var(--oaci-band-likely); }
|
|
856
|
+
.oaci-result .oaci-quote[data-level="signal-strongly-ai"] { border-left-color: var(--oaci-band-strong); }
|
|
857
|
+
|
|
858
|
+
.oaci-result .oaci-locator { margin: 0 0 18px !important; color: var(--oaci-note); font-family: var(--oaci-font-mono); font-size: var(--oaci-text-sm) !important; }
|
|
859
|
+
|
|
860
|
+
/* ------------------------------------------------- the evidence measure -- */
|
|
861
|
+
|
|
862
|
+
/* "What the model measured": the meters, then the paragraph that names which of
|
|
863
|
+
them lean the way the reading went. */
|
|
864
|
+
.oaci-result .oaci-measured { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--oaci-line); }
|
|
865
|
+
.oaci-result .oaci-measured__title { font-size: var(--oaci-text-md) !important; }
|
|
866
|
+
.oaci-result .oaci-measured__intro { margin: 4px 0 0 !important; max-width: 74ch; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; line-height: 1.55; }
|
|
867
|
+
.oaci-result .oaci-measured .oaci-measure + .oaci-measure { margin-top: 26px; padding-top: 22px; border-top: 1px dashed var(--oaci-line); }
|
|
868
|
+
.oaci-result .oaci-measured .oaci-measure:first-of-type { margin-top: 22px; }
|
|
869
|
+
.oaci-result .oaci-measure__basis { margin: 6px 0 0 !important; max-width: 74ch; color: var(--oaci-note); font-size: var(--oaci-text-xs) !important; line-height: 1.5; }
|
|
870
|
+
/* A signal we measured and found at chance keeps its scale and loses its emphasis. */
|
|
871
|
+
.oaci-result .oaci-measure[data-oaci-informative="false"] .oaci-measure__label { color: var(--oaci-note); }
|
|
872
|
+
.oaci-result .oaci-measured__why { margin-top: 22px; padding: 14px 18px; border-radius: var(--oaci-radius-sm); background: var(--oaci-soft); }
|
|
873
|
+
.oaci-result .oaci-measured__why b { display: block; margin-bottom: 6px; font-size: var(--oaci-text-sm); }
|
|
874
|
+
.oaci-result .oaci-measured__why p { margin: 0 !important; max-width: 74ch; font-size: var(--oaci-text-base) !important; line-height: 1.6; }
|
|
875
|
+
.oaci-result .oaci-measure { margin: 0; }
|
|
876
|
+
.oaci-result .oaci-measure__label { display: block; font-size: var(--oaci-text-base); font-weight: 700; margin-bottom: 30px; }
|
|
877
|
+
.oaci-result .oaci-measure__scale {
|
|
878
|
+
position: relative;
|
|
879
|
+
height: 10px;
|
|
880
|
+
margin: 0 44px 42px 40px;
|
|
881
|
+
border-radius: 999px;
|
|
882
|
+
background: linear-gradient(90deg, var(--oaci-band-strong-bg), var(--oaci-band-potential-bg) 40%, var(--oaci-band-human-bg));
|
|
883
|
+
}
|
|
884
|
+
/* Which end of the scale is the AI end is a property of the signal, not of the
|
|
885
|
+
bar. Word re-use runs machine-low to human-high; vocabulary variety runs the
|
|
886
|
+
other way, and a bar that always tinted the left red would say the opposite
|
|
887
|
+
of the markers printed on it. A signal with no measured separation gets no
|
|
888
|
+
tint at all. */
|
|
889
|
+
.oaci-result .oaci-measure__scale[data-direction="ai-high"] {
|
|
890
|
+
background: linear-gradient(90deg, var(--oaci-band-human-bg), var(--oaci-band-potential-bg) 60%, var(--oaci-band-strong-bg));
|
|
891
|
+
}
|
|
892
|
+
.oaci-result .oaci-measure__scale[data-direction="none"] { background: var(--oaci-inset); }
|
|
893
|
+
.oaci-result .oaci-measure__mark { position: absolute; top: -4px; transform: translateX(-50%); }
|
|
894
|
+
.oaci-result .oaci-measure__mark i { display: block; width: 3px; height: 18px; margin: 0 auto; border-radius: 2px; background: var(--oaci-line-strong); }
|
|
895
|
+
.oaci-result .oaci-measure__mark small { position: absolute; top: -20px; left: 50%; transform: translateX(-50%); color: var(--oaci-note); font-size: var(--oaci-text-xs); white-space: nowrap; }
|
|
896
|
+
/* The short label is the narrow-panel form of the same fact. One or the other
|
|
897
|
+
is shown, never both, and neither is ever dropped: the scale has to say which
|
|
898
|
+
end is which at any width. The scale is aria-hidden and the same numbers are
|
|
899
|
+
read out by the visually hidden sentence beneath it, so carrying both forms
|
|
900
|
+
in the markup costs assistive technology nothing. */
|
|
901
|
+
.oaci-result .oaci-measure__brief { display: none; }
|
|
902
|
+
/* A label centred on a mark near either end hangs off the panel. Anchored, it
|
|
903
|
+
starts (or ends) at the mark instead of overhanging it: nothing is ever
|
|
904
|
+
clipped, at any width. */
|
|
905
|
+
.oaci-result .oaci-measure__mark[data-anchor="start"] small { left: 0; transform: none; }
|
|
906
|
+
.oaci-result .oaci-measure__mark[data-anchor="end"] small { left: auto; right: 0; transform: none; }
|
|
907
|
+
.oaci-result .oaci-measure__mark--this i { width: 12px; height: 12px; margin-top: 3px; border-radius: 50%; background: var(--oaci-ink); }
|
|
908
|
+
.oaci-result .oaci-measure__mark--this small { top: auto; bottom: -22px; color: var(--oaci-ink); font-weight: 800; }
|
|
909
|
+
.oaci-result .oaci-measure__reading { max-width: 62ch; margin: 0 0 10px !important; font-size: var(--oaci-text-base) !important; line-height: 1.6; }
|
|
910
|
+
.oaci-result .oaci-measure__example { margin: 16px 0 0; padding: 2px 0 2px 18px; border-left: 3px solid var(--oaci-line); }
|
|
911
|
+
.oaci-result .oaci-measure__example p { padding: 0 !important; }
|
|
912
|
+
.oaci-result .oaci-measure__example b { display: block; margin-bottom: 8px; color: var(--oaci-note); font-size: var(--oaci-text-sm); font-weight: 700; }
|
|
913
|
+
.oaci-result .oaci-measure__example p { margin: 6px 0 !important; color: var(--oaci-ink-soft); font-size: var(--oaci-text-sm) !important; font-style: italic; line-height: 1.55; }
|
|
914
|
+
.oaci-result .oaci-measure__example .oaci-measure__note { font-style: normal; }
|
|
915
|
+
.oaci-result .oaci-measure__note { color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
916
|
+
|
|
917
|
+
/* ------------------------------------------------------ editing advice -- */
|
|
918
|
+
|
|
919
|
+
.oaci-result .oaci-advice { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--oaci-line); }
|
|
920
|
+
.oaci-result .oaci-advice__title { font-size: var(--oaci-text-md) !important; }
|
|
921
|
+
.oaci-result .oaci-advice__note { margin: 3px 0 10px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
922
|
+
.oaci-result .oaci-advice__card { margin: 10px 0; padding: 15px 18px; border-radius: var(--oaci-radius-sm); background: var(--oaci-soft); }
|
|
923
|
+
.oaci-result .oaci-advice__card b { padding: 0; }
|
|
924
|
+
.oaci-result .oaci-advice__card b { display: block; margin-bottom: 6px; font-size: var(--oaci-text-sm); }
|
|
925
|
+
.oaci-result .oaci-advice__try { display: inline-block; padding: 5px 12px; border-radius: 999px; background: var(--oaci-ok-bg); color: var(--oaci-ok); font-size: var(--oaci-text-sm); font-weight: 700; }
|
|
926
|
+
.oaci-result .oaci-advice__why { margin: 8px 0 0 !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; line-height: 1.55; }
|
|
927
|
+
.oaci-result .oaci-advice__none { color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
928
|
+
.oaci-result .oaci-advice__more { margin-top: 10px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
929
|
+
|
|
930
|
+
/* ----------------------------------------------------------- the axes ---- */
|
|
931
|
+
|
|
932
|
+
.oaci-result .oaci-axes { display: grid; gap: 12px; grid-template-columns: 1fr; }
|
|
933
|
+
.oaci-result .oaci-axis { padding: 16px 18px; border: 1px solid var(--oaci-line); border-left: 4px solid var(--oaci-line-strong); border-radius: var(--oaci-radius-sm); background: var(--oaci-panel); }
|
|
934
|
+
.oaci-result .oaci-axis[data-axis="ai"] { border-left-color: var(--oaci-blue); }
|
|
935
|
+
.oaci-result .oaci-axis[data-axis="integrity"] { border-left-color: var(--oaci-ok); }
|
|
936
|
+
.oaci-result .oaci-axis[data-axis="editorial"] { border-left-color: var(--oaci-line-strong); }
|
|
937
|
+
.oaci-result .oaci-axis__label { font-family: var(--oaci-font-body) !important; font-size: var(--oaci-text-sm) !important; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase !important; color: var(--oaci-note); }
|
|
938
|
+
.oaci-result .oaci-axis__reading { margin-top: 6px !important; font-family: var(--oaci-font-display) !important; font-size: var(--oaci-text-xl) !important; font-weight: 800; line-height: 1.15; }
|
|
939
|
+
.oaci-result .oaci-axis__reason { margin-top: 6px !important; color: var(--oaci-ink-soft); font-size: var(--oaci-text-sm) !important; line-height: 1.55; }
|
|
940
|
+
/* The editorial axis carries no severity colour, ever: a coloured writing note
|
|
941
|
+
reads as a detection result whatever the caption says. */
|
|
942
|
+
.oaci-result .oaci-axis[data-axis="editorial"] .oaci-axis__reading { color: var(--oaci-note); }
|
|
943
|
+
|
|
944
|
+
/* --------------------------------------------------------- named checks -- */
|
|
945
|
+
/* One row per check, grouped by the reading it feeds. The card grid it replaced
|
|
946
|
+
put the name, the id, the route and a limitation into a box and asked the
|
|
947
|
+
reader to compare eight of them side by side. */
|
|
948
|
+
|
|
949
|
+
.oaci-result .oaci-checks__title { font-size: var(--oaci-text-lg) !important; }
|
|
950
|
+
.oaci-result .oaci-checks__intro { margin: 4px 0 16px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; line-height: 1.55; }
|
|
951
|
+
.oaci-result .oaci-checks__group + .oaci-checks__group { margin-top: 18px; }
|
|
952
|
+
.oaci-result .oaci-checks__group-title { font-family: var(--oaci-font-body) !important; font-size: var(--oaci-text-xs) !important; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase !important; color: var(--oaci-note); margin-bottom: 8px !important; }
|
|
953
|
+
.oaci-result .oaci-checks__list { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
|
|
954
|
+
.oaci-result .oaci-check { display: block; padding: 16px !important; border: 1px solid var(--oaci-line); border-radius: var(--oaci-radius-sm); background: var(--oaci-soft); }
|
|
955
|
+
.oaci-result .oaci-check__row { display: flex; align-items: flex-start; flex-wrap: wrap; justify-content: space-between; gap: 8px 12px; width: 100%; }
|
|
956
|
+
.oaci-result .oaci-check__name { font-size: var(--oaci-text-md); font-weight: 700; line-height: 1.35; }
|
|
957
|
+
.oaci-result .oaci-check__means { margin: 5px 0 0 !important; color: var(--oaci-ink-soft); font-size: var(--oaci-text-sm) !important; line-height: 1.55; max-width: 74ch; }
|
|
958
|
+
.oaci-result .oaci-check__details { margin-top: 12px; border-top: 1px solid var(--oaci-line); }
|
|
959
|
+
.oaci-result .oaci-check__details > summary { display: flex; align-items: center; gap: 8px; min-height: 44px; color: var(--oaci-blue-deep); font-size: var(--oaci-text-sm); font-weight: 700; cursor: pointer; list-style: none; }
|
|
960
|
+
.oaci-result .oaci-check__details > summary::-webkit-details-marker { display: none; }
|
|
961
|
+
.oaci-result .oaci-check__details > summary::before { content: ''; width: 7px; height: 7px; flex: 0 0 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); }
|
|
962
|
+
.oaci-result .oaci-check__details[open] > summary::before { transform: rotate(45deg); }
|
|
963
|
+
.oaci-result .oaci-check[data-method^="detector."] .oaci-status { color: var(--oaci-blue-deep); background: var(--oaci-panel); }
|
|
964
|
+
.oaci-result .oaci-check__details > summary:focus-visible { outline: 3px solid var(--oaci-focus); outline-offset: 2px; }
|
|
965
|
+
.oaci-result .oaci-check__facts { display: grid; gap: 8px 16px; grid-template-columns: auto minmax(0, 1fr); margin: 0 !important; padding: 12px !important; border-radius: 6px; background: var(--oaci-panel); }
|
|
966
|
+
.oaci-result .oaci-check__facts > div { display: contents; }
|
|
967
|
+
.oaci-result .oaci-check__facts dt { color: var(--oaci-note); font-size: var(--oaci-text-xs); font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; }
|
|
968
|
+
.oaci-result .oaci-check__facts dd { margin: 0 !important; font-family: var(--oaci-font-mono); font-size: var(--oaci-text-xs); overflow-wrap: anywhere; }
|
|
969
|
+
.oaci-result .oaci-status { display: inline-block; flex: none; padding: 3px 11px; border-radius: 999px; background: var(--oaci-inset); color: var(--oaci-note); font-size: var(--oaci-text-sm); font-weight: 700; white-space: nowrap; }
|
|
970
|
+
.oaci-result .oaci-status[data-status="pass"] { background: var(--oaci-ok-bg); color: var(--oaci-ok); }
|
|
971
|
+
.oaci-result .oaci-status[data-status="attention"] { background: var(--oaci-watch-bg); color: var(--oaci-watch); }
|
|
972
|
+
.oaci-result .oaci-status[data-status="fail"], .oaci-result .oaci-status[data-status="error"] { background: var(--oaci-bad-bg); color: var(--oaci-bad); }
|
|
973
|
+
.oaci-result .oaci-limits { margin-top: 16px !important; padding-left: 18px !important; color: var(--oaci-ink-soft); font-size: var(--oaci-text-sm) !important; list-style: disc !important; }
|
|
974
|
+
.oaci-result .oaci-limits li { margin: 5px 0 !important; }
|
|
975
|
+
|
|
976
|
+
/* The quiet panel the honest caveats live in, once each. */
|
|
977
|
+
.oaci-result .oaci-goodtoknow { margin-top: 20px; padding: 14px 18px; border-radius: var(--oaci-radius-sm); background: var(--oaci-inset); }
|
|
978
|
+
.oaci-result .oaci-goodtoknow__title { font-family: var(--oaci-font-body) !important; font-size: var(--oaci-text-xs) !important; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase !important; color: var(--oaci-note); }
|
|
979
|
+
.oaci-result .oaci-goodtoknow .oaci-limits { margin-top: 8px !important; }
|
|
980
|
+
.oaci-result .oaci-goodtoknow__more { margin-top: 8px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; }
|
|
981
|
+
|
|
982
|
+
/* -------------------------------------------------- means / not-means ---- */
|
|
983
|
+
|
|
984
|
+
.oaci-result .oaci-meaning { display: grid; gap: 18px 28px; grid-template-columns: 1fr; padding: clamp(16px, 2.6cqi, 26px); border-radius: var(--oaci-radius-lg); background: var(--oaci-inset); }
|
|
985
|
+
.oaci-result .oaci-meaning__title { font-family: var(--oaci-font-body) !important; font-size: var(--oaci-text-sm) !important; font-weight: 800; letter-spacing: 0.03em; text-transform: uppercase !important; color: var(--oaci-note); margin-bottom: 6px !important; }
|
|
986
|
+
.oaci-result .oaci-meaning p { margin: 5px 0 !important; font-size: var(--oaci-text-base) !important; line-height: 1.55; }
|
|
987
|
+
|
|
988
|
+
/* ---------------------------------------------------------- certainty ---- */
|
|
989
|
+
|
|
990
|
+
.oaci-result .oaci-certainty { padding: 14px 16px; border: 1px solid var(--oaci-line); border-radius: var(--oaci-radius-sm); background: var(--oaci-panel); }
|
|
991
|
+
.oaci-result .oaci-certainty > summary { color: var(--oaci-orange-ink); font-weight: 800; cursor: pointer; }
|
|
992
|
+
.oaci-result .oaci-certainty p { margin: 10px 0 0 !important; font-size: var(--oaci-text-base) !important; line-height: 1.6; }
|
|
993
|
+
.oaci-result .oaci-certainty__plain { padding: 12px 14px !important; border-radius: var(--oaci-radius-sm); background: var(--oaci-inset); }
|
|
994
|
+
.oaci-result .oaci-certainty__raw { color: var(--oaci-note); font-family: var(--oaci-font-mono); font-size: var(--oaci-text-sm) !important; overflow-wrap: anywhere; }
|
|
995
|
+
.oaci-result .oaci-certainty__meter { display: block; height: 10px; margin: 12px 0 6px; border-radius: 999px; background: var(--oaci-inset); overflow: hidden; }
|
|
996
|
+
.oaci-result .oaci-certainty__meter i { display: block; height: 100%; border-radius: 999px; background: var(--oaci-blue); }
|
|
997
|
+
|
|
998
|
+
/* --------------------------------------------------------- run record ---- */
|
|
999
|
+
|
|
1000
|
+
.oaci-result .oaci-run { padding: clamp(16px, 2.6cqi, 26px); border-radius: var(--oaci-radius-lg); background: var(--oaci-inset); }
|
|
1001
|
+
.oaci-result .oaci-run__title { font-size: var(--oaci-text-md) !important; }
|
|
1002
|
+
.oaci-result .oaci-run dl { display: grid; gap: 8px 20px; grid-template-columns: 1fr; margin-top: 12px !important; }
|
|
1003
|
+
.oaci-result .oaci-run dt { color: var(--oaci-note); font-size: var(--oaci-text-xs) !important; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase !important; }
|
|
1004
|
+
.oaci-result .oaci-run dd { margin: 2px 0 0 !important; font-size: var(--oaci-text-sm) !important; overflow-wrap: anywhere; }
|
|
1005
|
+
|
|
1006
|
+
/* -------------------------------------------------------- share sheet ---- */
|
|
1007
|
+
/* The dialog is usually appended to \`document.body\`, outside the result, so its
|
|
1008
|
+
own root carries \`.oaci-result\` as well: that is what brings the colour tokens
|
|
1009
|
+
and this scope with it. Every measurement is the website's toolbar chooser
|
|
1010
|
+
(src/styles/content-integrity-share.css, \`.ci-share-picker\`). */
|
|
1011
|
+
|
|
1012
|
+
.oaci-result.oaci-share__scrim {
|
|
1013
|
+
position: fixed;
|
|
1014
|
+
inset: 0;
|
|
1015
|
+
z-index: 2147483000;
|
|
1016
|
+
display: grid;
|
|
1017
|
+
place-items: center;
|
|
1018
|
+
padding: 16px;
|
|
1019
|
+
overflow: auto;
|
|
1020
|
+
background: rgb(9 11 14 / 62%);
|
|
1021
|
+
container-type: normal;
|
|
1022
|
+
}
|
|
1023
|
+
.oaci-result .oaci-share {
|
|
1024
|
+
box-sizing: border-box;
|
|
1025
|
+
width: min(430px, 100%);
|
|
1026
|
+
max-height: calc(100dvh - 32px);
|
|
1027
|
+
padding: 22px;
|
|
1028
|
+
overflow: auto;
|
|
1029
|
+
border: 1px solid var(--oaci-line);
|
|
1030
|
+
border-top: 4px solid var(--oaci-orange);
|
|
1031
|
+
border-radius: var(--oaci-radius);
|
|
1032
|
+
background: var(--oaci-panel);
|
|
1033
|
+
color: var(--oaci-ink);
|
|
1034
|
+
box-shadow: var(--oaci-elev-3);
|
|
1035
|
+
text-align: left;
|
|
1036
|
+
}
|
|
1037
|
+
.oaci-result .oaci-share__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
|
1038
|
+
.oaci-result .oaci-share__eyebrow { margin: 0 0 3px !important; color: var(--oaci-orange-ink); font-size: var(--oaci-text-xs) !important; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; }
|
|
1039
|
+
.oaci-result .oaci-share__title { margin: 0 !important; font-family: var(--oaci-font-display) !important; font-size: var(--oaci-text-lg) !important; font-weight: 800; line-height: 1.25; }
|
|
1040
|
+
.oaci-result .oaci-share__close {
|
|
1041
|
+
display: grid;
|
|
1042
|
+
flex: 0 0 40px;
|
|
1043
|
+
place-items: center;
|
|
1044
|
+
width: 40px;
|
|
1045
|
+
height: 40px;
|
|
1046
|
+
margin: -4px -4px 0 0;
|
|
1047
|
+
padding: 0;
|
|
1048
|
+
border: 1px solid var(--oaci-line);
|
|
1049
|
+
border-radius: var(--oaci-radius-sm);
|
|
1050
|
+
background: var(--oaci-soft);
|
|
1051
|
+
color: var(--oaci-ink-soft);
|
|
1052
|
+
font: 500 22px/1 var(--oaci-font-body);
|
|
1053
|
+
cursor: pointer;
|
|
1054
|
+
}
|
|
1055
|
+
.oaci-result .oaci-share__intro { margin: 12px 0 16px !important; color: var(--oaci-note); font-size: var(--oaci-text-sm) !important; line-height: 1.5; }
|
|
1056
|
+
.oaci-result .oaci-share__quick,
|
|
1057
|
+
.oaci-result .oaci-share__social { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
1058
|
+
.oaci-result .oaci-share__action {
|
|
1059
|
+
display: flex;
|
|
1060
|
+
box-sizing: border-box;
|
|
1061
|
+
align-items: center;
|
|
1062
|
+
justify-content: center;
|
|
1063
|
+
min-width: 0;
|
|
1064
|
+
min-height: 44px;
|
|
1065
|
+
padding: 9px 12px;
|
|
1066
|
+
border: 1px solid var(--oaci-line);
|
|
1067
|
+
border-radius: var(--oaci-radius-sm);
|
|
1068
|
+
background: var(--oaci-soft);
|
|
1069
|
+
color: var(--oaci-ink);
|
|
1070
|
+
font-family: var(--oaci-font-body);
|
|
1071
|
+
font-size: var(--oaci-text-sm);
|
|
1072
|
+
font-weight: 700;
|
|
1073
|
+
line-height: 1.25;
|
|
1074
|
+
text-align: center;
|
|
1075
|
+
text-decoration: none;
|
|
1076
|
+
cursor: pointer;
|
|
1077
|
+
}
|
|
1078
|
+
/* The ink orange, not the fill orange: white on #fb700a measures 2.9:1. */
|
|
1079
|
+
.oaci-result .oaci-share__action--copy { border-color: var(--oaci-orange-ink); background: var(--oaci-orange-ink); color: var(--oaci-panel); }
|
|
1080
|
+
.oaci-result .oaci-share__action--copy[data-oaci-copied="true"] { border-color: var(--oaci-ok); background: var(--oaci-ok-bg); color: var(--oaci-ok); }
|
|
1081
|
+
.oaci-result .oaci-share__action--device { grid-column: 1 / -1; border-color: var(--oaci-ink); background: var(--oaci-ink); color: var(--oaci-paper); }
|
|
1082
|
+
.oaci-result .oaci-share__action:hover,
|
|
1083
|
+
.oaci-result .oaci-share__close:hover { border-color: var(--oaci-orange); }
|
|
1084
|
+
.oaci-result .oaci-share__action:focus-visible,
|
|
1085
|
+
.oaci-result .oaci-share__close:focus-visible { outline: 3px solid var(--oaci-focus); outline-offset: 2px; }
|
|
1086
|
+
.oaci-result .oaci-share__label { margin: 18px 0 8px !important; color: var(--oaci-note); font-size: var(--oaci-text-xs) !important; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
1087
|
+
.oaci-result .oaci-share__status { margin: 12px 0 0 !important; color: var(--oaci-ok); font-size: var(--oaci-text-sm) !important; font-weight: 700; min-height: 1.2em; }
|
|
1088
|
+
.oaci-result .oaci-share__privacy { margin: 14px 0 0 !important; padding: 10px 12px !important; border-radius: var(--oaci-radius-sm); background: var(--oaci-inset); color: var(--oaci-ink-soft); font-size: var(--oaci-text-sm) !important; line-height: 1.5; }
|
|
1089
|
+
|
|
1090
|
+
/* ------------------------------------------------------------ responsive - */
|
|
1091
|
+
/* Component-width driven, so a 400 px panel in a wide window gets this. */
|
|
1092
|
+
|
|
1093
|
+
@container oaci (max-width: 560px) {
|
|
1094
|
+
/* The token override lands on the children, not on \`.oaci-result\` itself: an
|
|
1095
|
+
element is never matched by its own container query, so a rule on the
|
|
1096
|
+
container silently does nothing. The children inherit it downwards. */
|
|
1097
|
+
.oaci-result > * { --oaci-text-2xl: 25px; --oaci-text-xl: 19px; }
|
|
1098
|
+
.oaci-result .oaci-mast__meta { margin-left: 0; text-align: left; }
|
|
1099
|
+
/* The gauge stays 320 px, but its legend does not have to. Sharing the
|
|
1100
|
+
gauge's width gave each of the five band names a 47 px cell at 375, and
|
|
1101
|
+
"Potentially" is 51 px at this size and may not break inside a word \u2014 so it
|
|
1102
|
+
ran out of its cell and sat against "Likely AI", which read as one string.
|
|
1103
|
+
The legend takes the panel's own width; the drawing keeps its cap. */
|
|
1104
|
+
.oaci-result .oaci-dial { max-width: none; }
|
|
1105
|
+
.oaci-result .oaci-dial svg { display: block; margin: 0 auto; max-width: 320px; }
|
|
1106
|
+
/* The legend is the one row that needs the whole panel: five band names in
|
|
1107
|
+
five equal cells under a 320 px arc. Sharing the arc's width gave each name
|
|
1108
|
+
a 46 px cell, and "Potentially" is 51 px at this size and may not break
|
|
1109
|
+
inside a word \u2014 so it ran out of its cell and sat against "Likely AI",
|
|
1110
|
+
which read as one string. It takes the panel's full width instead, the same
|
|
1111
|
+
full-width legend row the printable report uses, and drops half a point so
|
|
1112
|
+
the longest name has room to spare rather than exactly enough. */
|
|
1113
|
+
.oaci-result .oaci-dial__labels {
|
|
1114
|
+
gap: 4px;
|
|
1115
|
+
margin-inline: calc(-1 * clamp(16px, 2.6cqi, 26px));
|
|
1116
|
+
padding-inline: 2px;
|
|
1117
|
+
}
|
|
1118
|
+
.oaci-result .oaci-dial__labels span { font-size: 9.5px; }
|
|
1119
|
+
.oaci-result .oaci-strip__bar { grid-template-columns: minmax(0, 1fr) auto 14px; grid-template-rows: auto auto auto; row-gap: 7px; }
|
|
1120
|
+
.oaci-result .oaci-strip__name { grid-column: 1; grid-row: 1; white-space: normal; }
|
|
1121
|
+
.oaci-result .oaci-strip__score { grid-column: 2; grid-row: 1; }
|
|
1122
|
+
.oaci-result .oaci-strip__go { grid-column: 3; grid-row: 1; }
|
|
1123
|
+
.oaci-result .oaci-strip__track { grid-column: 1 / -1; grid-row: 2; }
|
|
1124
|
+
.oaci-result .oaci-strip__band { grid-column: 1 / -1; grid-row: 3; text-align: left; }
|
|
1125
|
+
/* Two reference labels on one line collide once the panel is narrow enough,
|
|
1126
|
+
so they stack: typical AI on the upper line, typical human on the lower. */
|
|
1127
|
+
.oaci-result .oaci-measure__scale { margin: 38px 16px 46px; }
|
|
1128
|
+
.oaci-result .oaci-measure__mark .oaci-measure__full { display: none; }
|
|
1129
|
+
.oaci-result .oaci-measure__mark .oaci-measure__brief { display: block; }
|
|
1130
|
+
.oaci-result .oaci-measure__mark--this .oaci-measure__full { display: block; }
|
|
1131
|
+
.oaci-result .oaci-measure__mark--machine small { top: -38px; }
|
|
1132
|
+
.oaci-result .oaci-measure__reading { margin-top: 26px !important; }
|
|
1133
|
+
.oaci-result .oaci-dive__title { flex: 1 1 100%; }
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
@container oaci (min-width: 640px) {
|
|
1137
|
+
.oaci-result .oaci-meaning { grid-template-columns: 1fr 1fr; }
|
|
1138
|
+
.oaci-result .oaci-run dl { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
@container oaci (min-width: 860px) {
|
|
1142
|
+
.oaci-result .oaci-axes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
1143
|
+
.oaci-result .oaci-run dl { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Viewport fallback where container queries are unavailable. A narrow viewport
|
|
1147
|
+
always implies a narrow component, so this cannot fire wrongly. */
|
|
1148
|
+
@supports not (container-type: inline-size) {
|
|
1149
|
+
@media (max-width: 560px) {
|
|
1150
|
+
.oaci-result > * { --oaci-text-2xl: 25px; --oaci-text-xl: 19px; }
|
|
1151
|
+
.oaci-result .oaci-mast__meta { margin-left: 0; text-align: left; }
|
|
1152
|
+
.oaci-result .oaci-strip__bar { grid-template-columns: minmax(0, 1fr) auto 14px; grid-template-rows: auto auto auto; row-gap: 7px; }
|
|
1153
|
+
.oaci-result .oaci-strip__name { grid-column: 1; grid-row: 1; white-space: normal; }
|
|
1154
|
+
.oaci-result .oaci-strip__score { grid-column: 2; grid-row: 1; }
|
|
1155
|
+
.oaci-result .oaci-strip__go { grid-column: 3; grid-row: 1; }
|
|
1156
|
+
.oaci-result .oaci-strip__track { grid-column: 1 / -1; grid-row: 2; }
|
|
1157
|
+
.oaci-result .oaci-strip__band { grid-column: 1 / -1; grid-row: 3; text-align: left; }
|
|
1158
|
+
.oaci-result .oaci-measure__scale { margin: 38px 16px 46px; }
|
|
1159
|
+
.oaci-result .oaci-measure__mark .oaci-measure__full { display: none; }
|
|
1160
|
+
.oaci-result .oaci-measure__mark .oaci-measure__brief { display: block; }
|
|
1161
|
+
.oaci-result .oaci-measure__mark--this .oaci-measure__full { display: block; }
|
|
1162
|
+
.oaci-result .oaci-measure__mark--machine small { top: -38px; }
|
|
1163
|
+
}
|
|
1164
|
+
@media (min-width: 900px) {
|
|
1165
|
+
.oaci-result .oaci-axes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
1166
|
+
.oaci-result .oaci-meaning { grid-template-columns: 1fr 1fr; }
|
|
1167
|
+
.oaci-result .oaci-run dl { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* ------------------------------------------------------------- motion ---- */
|
|
1172
|
+
|
|
1173
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1174
|
+
.oaci-result *,
|
|
1175
|
+
.oaci-result *::before,
|
|
1176
|
+
.oaci-result *::after {
|
|
1177
|
+
transition-duration: 0.001ms !important;
|
|
1178
|
+
animation-duration: 0.001ms !important;
|
|
1179
|
+
animation-iteration-count: 1 !important;
|
|
1180
|
+
scroll-behavior: auto !important;
|
|
1181
|
+
}
|
|
1182
|
+
.oaci-result .oaci-strip__bar:hover { transform: none; }
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/* ------------------------------------------------------ forced colours ---- */
|
|
1186
|
+
|
|
1187
|
+
@media (forced-colors: active) {
|
|
1188
|
+
.oaci-result .oaci-panel,
|
|
1189
|
+
.oaci-result .oaci-dive,
|
|
1190
|
+
.oaci-result .oaci-axis,
|
|
1191
|
+
.oaci-result .oaci-check,
|
|
1192
|
+
.oaci-result .oaci-certainty,
|
|
1193
|
+
.oaci-result .oaci-strip__bar { border: 1px solid CanvasText; }
|
|
1194
|
+
.oaci-result .oaci-chip,
|
|
1195
|
+
.oaci-result .oaci-status,
|
|
1196
|
+
.oaci-result .oaci-advice__try { border: 1px solid CanvasText; forced-color-adjust: none; }
|
|
1197
|
+
.oaci-result .oaci-dial__seg { opacity: 1; }
|
|
1198
|
+
.oaci-result .oaci-share,
|
|
1199
|
+
.oaci-result .oaci-share__action,
|
|
1200
|
+
.oaci-result .oaci-share__close { border: 1px solid CanvasText; }
|
|
1201
|
+
.oaci-result.oaci-share__scrim { background: Canvas; }
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/* -------------------------------------------------------------- print ---- */
|
|
1205
|
+
|
|
1206
|
+
@media print {
|
|
1207
|
+
.oaci-result {
|
|
1208
|
+
--oaci-paper: #ffffff;
|
|
1209
|
+
--oaci-panel: #ffffff;
|
|
1210
|
+
background: #ffffff;
|
|
1211
|
+
container-type: normal;
|
|
1212
|
+
}
|
|
1213
|
+
.oaci-result *,
|
|
1214
|
+
.oaci-result *::before,
|
|
1215
|
+
.oaci-result *::after { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
1216
|
+
/* Action controls are screen furniture. They never print. Neither does the share dialog. */
|
|
1217
|
+
.oaci-result [data-oaci-noprint] { display: none !important; }
|
|
1218
|
+
.oaci-result.oaci-share__scrim { display: none !important; }
|
|
1219
|
+
/* A collapsed deep dive still belongs in the printed evidence. */
|
|
1220
|
+
.oaci-result .oaci-dive[hidden] { display: block !important; }
|
|
1221
|
+
.oaci-result .oaci-certainty { border: 0; }
|
|
1222
|
+
.oaci-result .oaci-certainty > summary { list-style: none; }
|
|
1223
|
+
.oaci-result .oaci-quote { max-height: none; overflow: visible; }
|
|
1224
|
+
.oaci-result .oaci-dial,
|
|
1225
|
+
.oaci-result .oaci-dive,
|
|
1226
|
+
.oaci-result .oaci-meaning,
|
|
1227
|
+
.oaci-result .oaci-strip__bar,
|
|
1228
|
+
.oaci-result .oaci-axis,
|
|
1229
|
+
.oaci-result .oaci-check,
|
|
1230
|
+
.oaci-result .oaci-quote,
|
|
1231
|
+
.oaci-result .oaci-measure,
|
|
1232
|
+
.oaci-result .oaci-run { break-inside: avoid; }
|
|
1233
|
+
.oaci-result .oaci-mast { border-bottom: 3px solid #fb700a; }
|
|
1234
|
+
.oaci-result__body { gap: 14px; padding: 0; }
|
|
1235
|
+
}
|
|
1236
|
+
`;
|
|
1237
|
+
|
|
1238
|
+
// src/build-report-html.ts
|
|
1239
|
+
var e = (value) => escapeResultHtml(value);
|
|
1240
|
+
var PAGE_CSS = `
|
|
1241
|
+
:root{color-scheme:light dark}
|
|
1242
|
+
body{margin:0;background:var(--oaci-paper,#f2ede6)}
|
|
1243
|
+
.oacit-page{max-width:1100px;margin:0 auto;padding:0 0 56px}
|
|
1244
|
+
.oacit-page .oaci-panel{margin:22px clamp(16px,3vw,30px)}
|
|
1245
|
+
.oacit-lede{margin:22px 0 0!important;padding:0 clamp(16px,3vw,30px)!important;font-size:var(--oaci-text-md);color:var(--oaci-ink-soft)}
|
|
1246
|
+
.oacit-scan-note{border-left:4px solid var(--oaci-orange)}
|
|
1247
|
+
.oacit-page .oaci-panel>p{margin:0 0 12px!important}
|
|
1248
|
+
.oacit-page .oaci-panel>p:last-child{margin-bottom:0!important}
|
|
1249
|
+
.oacit-page h1{margin:0!important;font:800 var(--oaci-text-2xl)/1.1 var(--oaci-font-display);letter-spacing:-.02em}
|
|
1250
|
+
.oacit-page h2{margin:0 0 12px!important;font:700 var(--oaci-text-xl)/1.2 var(--oaci-font-display);letter-spacing:-.01em}
|
|
1251
|
+
.oacit-page h3{margin:22px 0 8px!important;font:700 var(--oaci-text-lg)/1.25 var(--oaci-font-display)}
|
|
1252
|
+
.oacit-table{width:100%;border-collapse:collapse;font-size:var(--oaci-text-sm)}
|
|
1253
|
+
.oacit-table caption{margin-bottom:10px;text-align:left;color:var(--oaci-note);font-size:var(--oaci-text-sm)}
|
|
1254
|
+
.oacit-table th,.oacit-table td{padding:9px 12px;border-bottom:1px solid var(--oaci-line);text-align:left;vertical-align:top}
|
|
1255
|
+
.oacit-table thead th{border-bottom:2px solid var(--oaci-line-strong);font-size:var(--oaci-text-xs);letter-spacing:.07em;text-transform:uppercase;color:var(--oaci-note)}
|
|
1256
|
+
.oacit-table tbody tr:last-child th,.oacit-table tbody tr:last-child td{border-bottom:0}
|
|
1257
|
+
.oacit-table code{font-family:var(--oaci-font-mono);font-size:var(--oaci-text-xs);overflow-wrap:anywhere}
|
|
1258
|
+
.oacit-table td b{font-weight:700;font-variant-numeric:tabular-nums}
|
|
1259
|
+
.oacit-status{display:inline-block;padding:2px 9px;border-radius:999px;background:var(--oaci-ok-bg);color:var(--oaci-ok);font-size:var(--oaci-text-xs);font-weight:800}
|
|
1260
|
+
.oacit-status[data-status=attention],.oacit-status[data-status=inconclusive]{background:var(--oaci-watch-bg);color:var(--oaci-watch)}
|
|
1261
|
+
.oacit-status[data-status=fail],.oacit-status[data-status=error]{background:var(--oaci-bad-bg);color:var(--oaci-bad)}
|
|
1262
|
+
.oacit-status[data-status=unsupported],.oacit-status[data-status=not_configured],.oacit-status[data-status=not_run]{background:var(--oaci-inset);color:var(--oaci-note)}
|
|
1263
|
+
.oacit-record{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px 22px;margin:0;font-size:var(--oaci-text-sm)}
|
|
1264
|
+
.oacit-record dt{color:var(--oaci-note);font-size:var(--oaci-text-xs);letter-spacing:.06em;text-transform:uppercase}
|
|
1265
|
+
.oacit-record dd{margin:2px 0 0!important;font-family:var(--oaci-font-mono);overflow-wrap:anywhere}
|
|
1266
|
+
@media print{
|
|
1267
|
+
body{background:#fff}
|
|
1268
|
+
.oaci-mast{border-bottom-width:2px}
|
|
1269
|
+
.oacit-page .oaci-panel{margin:14px 0;box-shadow:none;break-inside:avoid}
|
|
1270
|
+
.oacit-lede{margin:14px 0!important;padding:0!important}
|
|
1271
|
+
}
|
|
1272
|
+
`;
|
|
1273
|
+
var statusChip = (status) => `<span class="oacit-status" data-status="${e(status)}">${e(status.replaceAll("_", " "))}</span>`;
|
|
1274
|
+
var counts = (title, values) => {
|
|
1275
|
+
const entries = Object.entries(values).filter(([, value]) => value > 0);
|
|
1276
|
+
if (!entries.length) return `<p class="oaci-note">${e(title)}: none.</p>`;
|
|
1277
|
+
return `<p class="oaci-note">${e(title)}: ${entries.map(([key, value]) => `${e(key)} \xD7${e(value)}`).join(", ")}.</p>`;
|
|
1278
|
+
};
|
|
1279
|
+
function renderBuildReportHtml(report) {
|
|
1280
|
+
const routes = report.routes.map((route) => `
|
|
1281
|
+
<tr>
|
|
1282
|
+
<th scope="row"><code>${e(route.route_id)}</code></th>
|
|
1283
|
+
<td><code>${e(route.source_hash)}</code></td>
|
|
1284
|
+
<td><b>${e(route.word_count)}</b></td>
|
|
1285
|
+
<td>${route.methods.map((method) => `<div><code>${e(method.id)}</code> \xB7 <code>${e(method.version)}</code> ${statusChip(method.status)}</div>`).join("")}</td>
|
|
1286
|
+
<td>${counts("Protected", route.protected_counts)}${counts("Writing patterns", route.pattern_counts)}${route.truncated ? '<p class="oaci-note">Only the first part of this route was scanned; the character limit was reached.</p>' : ""}</td>
|
|
1287
|
+
</tr>`).join("");
|
|
1288
|
+
return `<!doctype html>
|
|
1289
|
+
<html lang="en-GB">
|
|
1290
|
+
<head>
|
|
1291
|
+
<meta charset="utf-8">
|
|
1292
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1293
|
+
<title>${e(PRODUCT_NAME)} \u2014 build scan</title>
|
|
1294
|
+
<style>${CHECKER_UI_CSS}${PAGE_CSS}</style>
|
|
1295
|
+
</head>
|
|
1296
|
+
<body>
|
|
1297
|
+
<main class="oaci-result oacit-page">
|
|
1298
|
+
<header class="oaci-mast">
|
|
1299
|
+
<span class="oaci-mast__logo"><img src="${PRODUCT_LOGO_DATA_URI}" alt="" width="44" height="44"></span>
|
|
1300
|
+
<div>
|
|
1301
|
+
<p class="oaci-kicker">Deterministic build scan</p>
|
|
1302
|
+
<h1>${e(PRODUCT_NAME)}</h1>
|
|
1303
|
+
<p class="oaci-note">${e(PRODUCT_TAGLINE)} \xB7 ${e(report.routes.length)} route${report.routes.length === 1 ? "" : "s"} scanned</p>
|
|
1304
|
+
</div>
|
|
1305
|
+
</header>
|
|
1306
|
+
|
|
1307
|
+
<p class="oacit-lede">This page is the unattended scan an Astro build runs over its own generated
|
|
1308
|
+
HTML. It is not the checker.</p>
|
|
1309
|
+
|
|
1310
|
+
<section class="oaci-panel oacit-scan-note">
|
|
1311
|
+
<h2>What this scan is, and is not</h2>
|
|
1312
|
+
<p>No trained model ran, so the AI-pattern reading is <strong>not assessed</strong>. Nothing was
|
|
1313
|
+
sent anywhere, no page text is written to this report, and a build never fails on a finding.
|
|
1314
|
+
What you get here is the deterministic evidence: an opaque identifier for each route, the hash of
|
|
1315
|
+
the text that was read, the word count, and every named check with its version and outcome.</p>
|
|
1316
|
+
<p class="oaci-note">For the complete reading \u2014 the model's five-band level, section scores,
|
|
1317
|
+
passages, evidence and a printable report \u2014 open the Opace AI Content Checker & Detector panel in the Astro dev
|
|
1318
|
+
toolbar while running <code>astro dev</code>. That is the full checker; this is build support.</p>
|
|
1319
|
+
</section>
|
|
1320
|
+
|
|
1321
|
+
<section class="oaci-panel">
|
|
1322
|
+
<h2>Routes</h2>
|
|
1323
|
+
<div class="oaci-scroll" tabindex="0" role="region" aria-label="Scanned routes">
|
|
1324
|
+
<table class="oacit-table">
|
|
1325
|
+
<caption>Route identifiers are hashes of the output path. The path itself is never written.</caption>
|
|
1326
|
+
<thead><tr><th scope="col">Opaque route</th><th scope="col">Content hash</th><th scope="col">Words</th><th scope="col">Named checks</th><th scope="col">Counts</th></tr></thead>
|
|
1327
|
+
<tbody>${routes || '<tr><td colspan="5">No generated route matched the include patterns.</td></tr>'}</tbody>
|
|
1328
|
+
</table>
|
|
1329
|
+
</div>
|
|
1330
|
+
</section>
|
|
1331
|
+
|
|
1332
|
+
<section class="oaci-panel">
|
|
1333
|
+
<h2>What this cannot tell you</h2>
|
|
1334
|
+
<ul class="oaci-limits">${report.limitations.map((item) => `<li>${e(item)}</li>`).join("")}</ul>
|
|
1335
|
+
</section>
|
|
1336
|
+
|
|
1337
|
+
<section class="oaci-panel">
|
|
1338
|
+
<h2>Run record</h2>
|
|
1339
|
+
<dl class="oacit-record">
|
|
1340
|
+
<div><dt>Mode</dt><dd>${e(report.mode)}</dd></div>
|
|
1341
|
+
<div><dt>Package</dt><dd>${e(report.package_version)}</dd></div>
|
|
1342
|
+
<div><dt>Contract</dt><dd>${e(report.contract_version)}</dd></div>
|
|
1343
|
+
<div><dt>Schema</dt><dd>${e(report.schema_version)}</dd></div>
|
|
1344
|
+
<div><dt>Route</dt><dd>${e(report.privacy_route)}</dd></div>
|
|
1345
|
+
<div><dt>Carries page text</dt><dd>${report.contains_content ? "yes" : "no"}</dd></div>
|
|
1346
|
+
<div><dt>Stamped</dt><dd>${e(report.generated_at)}</dd></div>
|
|
1347
|
+
<div><dt>AI-pattern reading</dt><dd>not_assessed</dd></div>
|
|
1348
|
+
</dl>
|
|
1349
|
+
</section>
|
|
1350
|
+
</main>
|
|
1351
|
+
</body>
|
|
1352
|
+
</html>
|
|
1353
|
+
`;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// src/report.ts
|
|
1357
|
+
var STABLE_TIME = "2000-01-01T00:00:00.000Z";
|
|
1358
|
+
var BODY = /<body\b[^>]*>([\s\S]*?)<\/body\s*>/iu;
|
|
1359
|
+
var TOKEN = /<!--[\s\S]*?-->|<[^>]*>|&(?:#x?[\da-f]+|[a-z][\da-z]+);/giu;
|
|
1360
|
+
var BLOCKS = /* @__PURE__ */ new Set(["address", "article", "aside", "blockquote", "div", "dl", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "li", "main", "nav", "ol", "p", "pre", "section", "table", "tr", "ul"]);
|
|
1361
|
+
var EXCLUDED = /* @__PURE__ */ new Set(["script", "style", "template", "noscript"]);
|
|
1362
|
+
var VOID = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
1363
|
+
function visibleHtml(html, maxCharacters = 5e4) {
|
|
1364
|
+
const normalised = html.replace(/\r\n?/gu, "\n");
|
|
1365
|
+
const body = BODY.exec(normalised)?.[1] ?? normalised;
|
|
1366
|
+
let text = "";
|
|
1367
|
+
let cursor = 0;
|
|
1368
|
+
const stack = [];
|
|
1369
|
+
const isHidden = () => stack.at(-1)?.hidden ?? false;
|
|
1370
|
+
const separator = () => {
|
|
1371
|
+
if (text && !text.endsWith("\n")) text += "\n";
|
|
1372
|
+
};
|
|
1373
|
+
for (const match of body.matchAll(TOKEN)) {
|
|
1374
|
+
const index = match.index ?? 0;
|
|
1375
|
+
if (!isHidden()) text += decodeHTML(body.slice(cursor, index));
|
|
1376
|
+
const raw = match[0];
|
|
1377
|
+
if (raw.startsWith("&")) {
|
|
1378
|
+
if (!isHidden()) text += decodeHTML(raw);
|
|
1379
|
+
} else if (!raw.startsWith("<!--")) {
|
|
1380
|
+
const parsed = /^<\s*(\/?)\s*([a-z][\w:-]*)/iu.exec(raw);
|
|
1381
|
+
const closing = parsed?.[1] === "/";
|
|
1382
|
+
const tag = parsed?.[2]?.toLowerCase();
|
|
1383
|
+
if (tag && closing) {
|
|
1384
|
+
let position = stack.length - 1;
|
|
1385
|
+
while (position >= 0 && stack[position]?.tag !== tag) position -= 1;
|
|
1386
|
+
if (position >= 0) {
|
|
1387
|
+
const frame = stack[position];
|
|
1388
|
+
stack.splice(position);
|
|
1389
|
+
if (!frame.hidden && frame.block) separator();
|
|
1390
|
+
}
|
|
1391
|
+
} else if (tag) {
|
|
1392
|
+
const hiddenAttribute = /\shidden(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?(?=[\s/>])/iu.test(raw);
|
|
1393
|
+
const aria = /\saria-hidden\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/iu.exec(raw);
|
|
1394
|
+
const ariaHidden = (aria?.[1] ?? aria?.[2] ?? aria?.[3]) === "true";
|
|
1395
|
+
const hidden = isHidden() || EXCLUDED.has(tag) || hiddenAttribute || ariaHidden;
|
|
1396
|
+
const block = BLOCKS.has(tag);
|
|
1397
|
+
if (!hidden && (tag === "br" || block)) separator();
|
|
1398
|
+
if (!VOID.has(tag) && !/\/\s*>$/u.test(raw)) stack.push({ tag, hidden, block });
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
cursor = index + raw.length;
|
|
1402
|
+
}
|
|
1403
|
+
if (!isHidden()) text += decodeHTML(body.slice(cursor));
|
|
1404
|
+
text = text.replace(/\n+$/gu, "");
|
|
1405
|
+
if (text.length <= maxCharacters) return text;
|
|
1406
|
+
let end = maxCharacters;
|
|
1407
|
+
if (end > 0 && /[\uD800-\uDBFF]/u.test(text[end - 1] ?? "")) end -= 1;
|
|
1408
|
+
return text.slice(0, end);
|
|
1409
|
+
}
|
|
1410
|
+
async function filesBelow(root) {
|
|
1411
|
+
const found = [];
|
|
1412
|
+
async function visit(dir) {
|
|
1413
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
1414
|
+
const path = join(dir, entry.name);
|
|
1415
|
+
if (entry.isDirectory()) await visit(path);
|
|
1416
|
+
else if (entry.isFile() && entry.name.endsWith(".html")) found.push(path);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
await visit(root);
|
|
1420
|
+
return found.sort();
|
|
1421
|
+
}
|
|
1422
|
+
var BUILD_SCAN_AXES = {
|
|
1423
|
+
ai_pattern: {
|
|
1424
|
+
assessment_status: "not_assessed",
|
|
1425
|
+
reason: "A build runs no trained model. Scoring a whole site without anyone asking would break the consent boundary, so the build scan never attempts an AI-pattern reading.",
|
|
1426
|
+
limitations: [
|
|
1427
|
+
"The character and writing findings below cannot stand in for a model reading.",
|
|
1428
|
+
"Open the Opace AI Content Checker & Detector panel in the Astro dev toolbar for the complete reading."
|
|
1429
|
+
]
|
|
1430
|
+
},
|
|
1431
|
+
text_integrity: { method_status: "per_route", reason: "Deterministic character checks ran for each route. See routes[].methods." },
|
|
1432
|
+
editorial: { method_status: "per_route", reason: "The named writing rules ran for each route. See routes[].methods and routes[].pattern_counts." }
|
|
1433
|
+
};
|
|
1434
|
+
function patternRegex(pattern) {
|
|
1435
|
+
const anyDepth = pattern.startsWith("**/");
|
|
1436
|
+
const source = anyDepth ? pattern.slice(3) : pattern;
|
|
1437
|
+
let expression = "";
|
|
1438
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1439
|
+
const character = source[index];
|
|
1440
|
+
if (character === "*" && source[index + 1] === "*") {
|
|
1441
|
+
expression += ".*";
|
|
1442
|
+
index += 1;
|
|
1443
|
+
} else if (character === "*") expression += "[^/]*";
|
|
1444
|
+
else expression += character.replace(/[|\\{}()[\]^$+?.]/gu, "\\$&");
|
|
1445
|
+
}
|
|
1446
|
+
return new RegExp(`^${anyDepth ? "(?:.*/)?" : ""}${expression}$`, "u");
|
|
1447
|
+
}
|
|
1448
|
+
function shouldInclude(path, options) {
|
|
1449
|
+
const normalised = path.replaceAll("\\", "/").replace(/^\.\//u, "");
|
|
1450
|
+
return options.include.some((pattern) => patternRegex(pattern).test(normalised)) && !options.exclude.some((pattern) => patternRegex(pattern).test(normalised));
|
|
1451
|
+
}
|
|
1452
|
+
async function analyseHtml(html, opaqueRoute, maxCharacters = 5e4) {
|
|
1453
|
+
const completeText = visibleHtml(html, Number.MAX_SAFE_INTEGER);
|
|
1454
|
+
const content = visibleHtml(html, maxCharacters);
|
|
1455
|
+
const routeId = `route_${createHash("sha256").update(opaqueRoute).digest("hex").slice(0, 16)}`;
|
|
1456
|
+
const result = await inspect({ schema_version: "1.0", contract_version: "1.0.0", request_id: `req_${routeId.slice(6)}`, created_at: STABLE_TIME, source: { content, content_type: "plain_text", language: "en-GB" }, checks: ["unicode.invisible", "style.patterns", "watermark.anthropic"], privacy: { allowed_routes: ["browser"], save_receipt: false, retain_content: false } }, { now: () => STABLE_TIME, analysisId: () => `analysis_${routeId.slice(6)}` });
|
|
1457
|
+
const protectedCounts = {};
|
|
1458
|
+
const patternCounts = {};
|
|
1459
|
+
for (const item of result.protected_spans) protectedCounts[item.kind] = (protectedCounts[item.kind] ?? 0) + 1;
|
|
1460
|
+
for (const item of result.pattern_findings) patternCounts[item.rule_id] = (patternCounts[item.rule_id] ?? 0) + 1;
|
|
1461
|
+
const { pass, attention, fail, inconclusive, unsupported, not_configured, not_run, error } = result.summary;
|
|
1462
|
+
return { route_id: routeId, source_hash: prefixedSha256(content), word_count: result.source.word_count, summary: { pass, attention, fail, inconclusive, unsupported, not_configured, not_run, error }, methods: result.methods.map(({ id, version, status, limitations }) => ({ id, version, status, limitations })), protected_counts: protectedCounts, pattern_counts: patternCounts, truncated: completeText.length > content.length };
|
|
1463
|
+
}
|
|
1464
|
+
async function writeBuildReport(outputUrl, options) {
|
|
1465
|
+
if (outputUrl.protocol !== "file:") throw new Error("Only file output is supported.");
|
|
1466
|
+
const output = await realpath(fileURLToPath(outputUrl));
|
|
1467
|
+
const reportDir = resolve(output, options.reportDirectory);
|
|
1468
|
+
if (reportDir !== output && !reportDir.startsWith(`${output}${sep}`)) throw new Error("Report path escaped the Astro build output.");
|
|
1469
|
+
let cursor = output;
|
|
1470
|
+
for (const segment of relative(output, reportDir).split(sep).filter(Boolean)) {
|
|
1471
|
+
cursor = join(cursor, segment);
|
|
1472
|
+
try {
|
|
1473
|
+
if ((await lstat(cursor)).isSymbolicLink()) throw new Error("Report path contains a symbolic link and was refused.");
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
if (error.code === "ENOENT") break;
|
|
1476
|
+
throw error;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const reports = [];
|
|
1480
|
+
for (const file of await filesBelow(output)) {
|
|
1481
|
+
if (file.includes(`${join(output, options.reportDirectory)}`)) continue;
|
|
1482
|
+
const route = relative(output, file);
|
|
1483
|
+
if (!shouldInclude(route, options)) continue;
|
|
1484
|
+
reports.push(await analyseHtml(await readFile(file, "utf8"), route, options.maxCharacters));
|
|
1485
|
+
}
|
|
1486
|
+
const report = { schema_version: "1.0", contract_version: "1.0.0", package_version: "0.3.1", profile: "build_scan", mode: "report_only", privacy_route: "browser", contains_content: false, generated_at: STABLE_TIME, axes: BUILD_SCAN_AXES, routes: reports, limitations: ["These checks cannot show who wrote anything.", "Anthropic's own watermark verifier is not something we can call, so it stays unsupported.", "No provider, model or local service was contacted during the build."] };
|
|
1487
|
+
const json = `${JSON.stringify(report, null, 2)}
|
|
1488
|
+
`;
|
|
1489
|
+
await mkdir(reportDir, { recursive: true });
|
|
1490
|
+
const jsonPath = join(reportDir, "report.json");
|
|
1491
|
+
const htmlPath = join(reportDir, "index.html");
|
|
1492
|
+
const html = renderBuildReportHtml(report);
|
|
1493
|
+
await writeFile(jsonPath, json, { encoding: "utf8", flag: "w" });
|
|
1494
|
+
await writeFile(htmlPath, html, { encoding: "utf8", flag: "w" });
|
|
1495
|
+
return { json: pathToFileURL(jsonPath).href, html: pathToFileURL(htmlPath).href, hash: `sha256:${createHash("sha256").update(json).digest("hex")}` };
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
export {
|
|
1499
|
+
visibleHtml,
|
|
1500
|
+
shouldInclude,
|
|
1501
|
+
analyseHtml,
|
|
1502
|
+
writeBuildReport
|
|
1503
|
+
};
|