@certrev/cert-block 1.0.0 → 1.0.2
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 +56 -1
- package/README.md +1 -1
- package/dist/builder/cert-review-card.d.ts.map +1 -1
- package/dist/builder/cert-review-card.js +5 -3
- package/dist/builder/cert-review-card.js.map +1 -1
- package/dist/components/ExpertBio.d.ts.map +1 -1
- package/dist/components/ExpertBio.js +1 -1
- package/dist/components/ExpertBio.js.map +1 -1
- package/dist/components/preview-block.js +1 -1
- package/dist/components/preview-block.js.map +1 -1
- package/dist/modal/certrev-cert-modal.styles.d.ts +1 -1
- package/dist/modal/certrev-cert-modal.styles.js +9 -9
- package/dist/modal/certrev-cert.js +10 -10
- package/dist/verify/fetch-with-deadline.d.ts.map +1 -1
- package/dist/verify/fetch-with-deadline.js +3 -1
- package/dist/verify/fetch-with-deadline.js.map +1 -1
- package/package.json +1 -1
- package/src/builder/cert-review-card.tsx +5 -3
- package/src/components/ExpertBio.tsx +3 -1
- package/src/components/preview-block.ts +1 -1
- package/src/modal/certrev-cert-modal.styles.ts +9 -9
- package/src/verify/fetch-with-deadline.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,61 @@ change the rendered cert output and never break a compiling integration** (regis
|
|
|
5
5
|
exports, types). Anything that would alter what a visitor sees, or require integration changes,
|
|
6
6
|
is announced loudly here first.
|
|
7
7
|
|
|
8
|
+
## 1.0.2 · 2026-08-18
|
|
9
|
+
|
|
10
|
+
Patch: copy only, in strings nobody renders. No API change, no type change, no behaviour change.
|
|
11
|
+
|
|
12
|
+
- **The 1.0.1 sweep checked the wrong layer, and 1.0.1 shipped an em dash anyway.** It reached a
|
|
13
|
+
reader through the timeout error in `fetchWithDeadline`, which set one between the deadline and
|
|
14
|
+
the URL and prints into a consumer's console with our name on it. That is visible copy by every
|
|
15
|
+
test this changelog has been applying, and the 33-surface rendered sweep could not see it, because
|
|
16
|
+
a thrown error is not a surface anything renders. Found by auditing the published tarball rather
|
|
17
|
+
than the render. It now uses a middle dot: the URL it appends is labelled data.
|
|
18
|
+
- **Two more in the same class, found by sweeping every string in shipped code rather than the one
|
|
19
|
+
that was reported.** `renderPreviewBlock`'s missing-`expiresAt` error takes a full stop, because
|
|
20
|
+
the second half is a complete sentence stating why. Nine CSS section comments inside
|
|
21
|
+
`CERT_MODAL_CSS` take middle dots: that template literal is injected verbatim into the modal's
|
|
22
|
+
shadow root, so its comments ship to every page the modal opens on and are readable in devtools,
|
|
23
|
+
unlike the TypeScript comments around it, which `tsc` emits but nothing displays.
|
|
24
|
+
- **The gate is now two tests, because one could never have been enough.** The rendered sweep and
|
|
25
|
+
the new string-literal sweep catch disjoint sets: the rendered one sees assembled output but only
|
|
26
|
+
for surfaces something renders, and the literal one sees every shipped string but cannot tell
|
|
27
|
+
which ones reach a human. The literal rule lives in `scripts/check-publish-hygiene.mjs`, strips
|
|
28
|
+
comments with a real tokenizer rather than a regex (the ~795 in comments stay out of scope, and a
|
|
29
|
+
regex cannot tell `//` in a URL from a comment), skips test files, and runs in CI. Confirmed to
|
|
30
|
+
fail on all four 1.0.1 strings and pass with them fixed.
|
|
31
|
+
- **A note on how the first check missed the modal bundle.** Grepping the built
|
|
32
|
+
`dist/modal/certrev-cert.js` for the character returned 0 on 1.0.1, and that was a false negative:
|
|
33
|
+
esbuild's default `--charset` is ascii, so it emits non-ASCII as a `\u` escape rather than as the
|
|
34
|
+
character. Ten occurrences were in there. Any future check of a minified artifact has to decode it
|
|
35
|
+
first; grepping the raw bytes reads as clean no matter what is in it.
|
|
36
|
+
|
|
37
|
+
## 1.0.1 · 2026-08-18
|
|
38
|
+
|
|
39
|
+
Patch: copy only. No API change, no type change, no behaviour change beyond the five characters
|
|
40
|
+
named below.
|
|
41
|
+
|
|
42
|
+
- **Five em dashes were rendering to readers in 1.0.0.** Two of them on the customer's own article:
|
|
43
|
+
the expert credential list set one between each abbreviation and its expansion (`MD`, then
|
|
44
|
+
`Doctor of Medicine`). That is labelled data, so it now uses a middle dot, matching the preview
|
|
45
|
+
caption and the modal. The other three sat in the
|
|
46
|
+
Builder editor's helper text, where an editor reads them: one list qualifier takes a full stop
|
|
47
|
+
(the middle dots already separate the three values, so a fourth would read as a fourth option),
|
|
48
|
+
one pair of independent clauses takes a comma before `and`, and one consequence clause takes a
|
|
49
|
+
colon.
|
|
50
|
+
- **Why 1.0.0 shipped with them.** Every guard in place checked PROSE or SOURCE. The prose guard
|
|
51
|
+
covers the README and this changelog; a source grep cannot tell a string literal that renders
|
|
52
|
+
from a comment that does not, and this package has ~795 em dashes in comments that are
|
|
53
|
+
deliberately in scope for neither. So the one thing nobody checked was the output. They were
|
|
54
|
+
found by rendering the package and grepping what came out, during the mirror leak sweep, after
|
|
55
|
+
1.0.0 was already immutable on npm.
|
|
56
|
+
- **`rendered-voice.test.tsx` closes it as a gate**, not as something to remember: it renders all
|
|
57
|
+
33 surfaces (every `mode` × `part`, both badge styles, the pro-bono and empty-memo shapes, the
|
|
58
|
+
preview, the JSON-LD, both modal dialogs, all five React components, and the Builder
|
|
59
|
+
registration) and asserts zero em dashes, plus an en dash only between digits. It carries a
|
|
60
|
+
coverage floor, because an empty render is indistinguishable from a clean one. Confirmed to fail
|
|
61
|
+
on the 1.0.0 strings, naming each surface and quoting the sentence.
|
|
62
|
+
|
|
8
63
|
## 1.0.0 · 2026-08-17
|
|
9
64
|
|
|
10
65
|
**A major, and the semver contract at the top of this file is why.** It says patch and minor
|
|
@@ -141,7 +196,7 @@ surfaces the kill switch *should* cover is an open design question, documented n
|
|
|
141
196
|
`description` is rewritten to 236 characters, because npm caps the packument description at 255
|
|
142
197
|
and was serving this one cut off mid-word at "…and a".
|
|
143
198
|
|
|
144
|
-
|
|
199
|
+
541 tests, up from 249.
|
|
145
200
|
|
|
146
201
|
## 0.5.5 · 2026-07-31
|
|
147
202
|
|
package/README.md
CHANGED
|
@@ -606,7 +606,7 @@ published at `@certrev/cert-block/fixtures`).
|
|
|
606
606
|
|
|
607
607
|
## Version
|
|
608
608
|
|
|
609
|
-
`1.0.
|
|
609
|
+
`1.0.2`: see [CHANGELOG.md](./CHANGELOG.md) for the release history and the semver contract
|
|
610
610
|
(patch/minor never change the rendered cert output or break a compiling integration). Publishes
|
|
611
611
|
**publicly** to npm as `@certrev/cert-block` (`publishConfig.access: public`) via GitHub Actions
|
|
612
612
|
trusted publishing (OIDC); the internal `@certrev` GitHub-Packages channel mirrors it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cert-review-card.d.ts","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,oCAAoC,CAAA;AAExF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC1E;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;CACxC;AAsCD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAoExD;AAMD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACpE,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC3B;AA0CD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,YAAY,EAO5C,CAAA;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,YAAY,
|
|
1
|
+
{"version":3,"file":"cert-review-card.d.ts","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,oCAAoC,CAAA;AAExF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC1E;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;CACxC;AAsCD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAoExD;AAMD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACpE,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC3B;AA0CD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,YAAY,EAO5C,CAAA;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,YAAY,EA2BnD,CAAA;AAED,gGAAgG;AAChG,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CACxC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,4BAIlC,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC;IACjD,SAAS,EAAE,OAAO,cAAc,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,YAAY,EAAE,CAAA;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,iCAMlC,CAAA"}
|
|
@@ -150,8 +150,8 @@ const WIRE_INPUT_TYPE = {
|
|
|
150
150
|
* So the fix is truthful labelling, not removal.
|
|
151
151
|
*/
|
|
152
152
|
const WIRE_INPUT_HELPER_TEXT = {
|
|
153
|
-
compensationCue: 'System-populated by CertREV. Only its PRESENCE is read (absent = a pro-bono reviewer, so no cue renders); the cue itself is a locked constant, so editing this text changes nothing on the page
|
|
154
|
-
scopeLine: 'System-populated by CertREV. The FTC scope line renders byte-verbatim from a locked constant
|
|
153
|
+
compensationCue: 'System-populated by CertREV. Only its PRESENCE is read (absent = a pro-bono reviewer, so no cue renders); the cue itself is a locked constant, so editing this text changes nothing on the page, and clearing it does not remove the disclosure.',
|
|
154
|
+
scopeLine: 'System-populated by CertREV. The FTC scope line renders byte-verbatim from a locked constant: editing this text changes nothing on the page.',
|
|
155
155
|
};
|
|
156
156
|
/**
|
|
157
157
|
* The wire-type inputs — DERIVED from `BUILDER_CERT_CHROME_KEYS`, so the 19-field
|
|
@@ -191,7 +191,9 @@ export const PLACEMENT_INPUTS = [
|
|
|
191
191
|
name: 'part',
|
|
192
192
|
type: 'string',
|
|
193
193
|
friendlyName: 'Banner part',
|
|
194
|
-
|
|
194
|
+
// A full stop, not a middle dot: the dots already separate the three values, so a fourth
|
|
195
|
+
// would read as a fourth option rather than as a qualifier on the list.
|
|
196
|
+
helperText: 'full (default) · header · memo. Banner placement only.',
|
|
195
197
|
defaultValue: 'full',
|
|
196
198
|
enum: [
|
|
197
199
|
{ label: 'Full (header + memo)', value: 'full' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cert-review-card.js","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cert-review-card.js","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpertBio.d.ts","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAKxD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1C;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+
|
|
1
|
+
{"version":3,"file":"ExpertBio.d.ts","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAKxD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1C;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA4D9C"}
|
|
@@ -20,6 +20,6 @@ export function ExpertBio(props) {
|
|
|
20
20
|
return (_jsxs("aside", { className: rootClass, style: rootStyle, "aria-label": `About the reviewing expert, ${expert.displayName}`, children: [photoUrl ? (_jsx("img", { className: `${ROOT_CLASS}__photo`, src: photoUrl,
|
|
21
21
|
// Decorative: the name is announced by this aside's aria-label AND rendered as text
|
|
22
22
|
// immediately below, so alt text repeats it a third time. Matches the modal's avatar.
|
|
23
|
-
alt: "", width: 64, height: 64, loading: "lazy", decoding: "async" })) : null, _jsxs("div", { className: `${ROOT_CLASS}__body`, children: [profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__name-link`, href: profileUrl, rel: CERTREV_LINK_REL, children: nameNode })) : (nameNode), expert.credentials.length > 0 ? (_jsx("ul", { className: `${ROOT_CLASS}__credential-list`, children: expert.credentials.map((c) => (_jsxs("li", { className: `${ROOT_CLASS}__credential`, children: [_jsx("abbr", { title: c.fullName, children: c.abbreviation }), _jsxs("span", { className: `${ROOT_CLASS}__credential-full`, children: [" \
|
|
23
|
+
alt: "", width: 64, height: 64, loading: "lazy", decoding: "async" })) : null, _jsxs("div", { className: `${ROOT_CLASS}__body`, children: [profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__name-link`, href: profileUrl, rel: CERTREV_LINK_REL, children: nameNode })) : (nameNode), expert.credentials.length > 0 ? (_jsx("ul", { className: `${ROOT_CLASS}__credential-list`, children: expert.credentials.map((c) => (_jsxs("li", { className: `${ROOT_CLASS}__credential`, children: [_jsx("abbr", { title: c.fullName, children: c.abbreviation }), _jsxs("span", { className: `${ROOT_CLASS}__credential-full`, children: [" \u00B7 ", c.fullName] })] }, `${c.abbreviation}:${c.fullName}`))) })) : null, _jsx("p", { className: `${ROOT_CLASS}__role`, children: "Verified expert reviewer" })] })] }));
|
|
24
24
|
}
|
|
25
25
|
//# sourceMappingURL=ExpertBio.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpertBio.js","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":";AASA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAU3C,MAAM,UAAU,GAAG,oBAAoB,CAAA;AAEvC,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;IACzB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAA;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAC1E,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,IAAI,IAAI,CAAA;IAC1C,MAAM,SAAS,GAAG,GAAG,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IAChF,MAAM,SAAS,GAA2B,EAAE,kBAAkB,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;IAErF,uFAAuF;IACvF,2FAA2F;IAC3F,yFAAyF;IACzF,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1F,MAAM,QAAQ,GAAG,CAChB,MAAC,OAAO,IAAC,SAAS,EAAE,GAAG,UAAU,QAAQ,aACvC,MAAM,CAAC,WAAW,EAClB,UAAU,CAAC,CAAC,CAAC,gBAAM,SAAS,EAAE,GAAG,UAAU,eAAe,mBAAK,UAAU,IAAQ,CAAC,CAAC,CAAC,IAAI,IAChF,CACV,CAAA;IAED,OAAO,CACN,iBAAO,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,gBAAc,+BAA+B,MAAM,CAAC,WAAW,EAAE,aAC5G,QAAQ,CAAC,CAAC,CAAC,CACX,cACC,SAAS,EAAE,GAAG,UAAU,SAAS,EACjC,GAAG,EAAE,QAAQ;gBACb,oFAAoF;gBACpF,sFAAsF;gBACtF,GAAG,EAAC,EAAE,EACN,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,EAAE,EACV,OAAO,EAAC,MAAM,EACd,QAAQ,EAAC,OAAO,GACf,CACF,CAAC,CAAC,CAAC,IAAI,EACR,eAAK,SAAS,EAAE,GAAG,UAAU,QAAQ,aACnC,UAAU,CAAC,CAAC,CAAC,CACb,YAAG,SAAS,EAAE,GAAG,UAAU,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,gBAAgB,YAC/E,QAAQ,GACN,CACJ,CAAC,CAAC,CAAC,CACH,QAAQ,CACR,EACA,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAChC,aAAI,SAAS,EAAE,GAAG,UAAU,mBAAmB,YAC7C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC9B,cAA4C,SAAS,EAAE,GAAG,UAAU,cAAc,aACjF,eAAM,KAAK,EAAE,CAAC,CAAC,QAAQ,YAAG,CAAC,CAAC,YAAY,GAAQ,
|
|
1
|
+
{"version":3,"file":"ExpertBio.js","sourceRoot":"","sources":["../../src/components/ExpertBio.tsx"],"names":[],"mappings":"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"}
|
|
@@ -32,7 +32,7 @@ export function previewExpiry(html) {
|
|
|
32
32
|
export function renderPreviewBlock(input, options) {
|
|
33
33
|
const expires = options.expiresAt.trim();
|
|
34
34
|
if (!expires) {
|
|
35
|
-
throw new Error('renderPreviewBlock: `expiresAt` is required
|
|
35
|
+
throw new Error('renderPreviewBlock: `expiresAt` is required. A preview must be time-bounded.');
|
|
36
36
|
}
|
|
37
37
|
// The bound must be REAL — an unparseable expiry is a toothless bound (downstream can't refuse a
|
|
38
38
|
// stale preview it can't date). Reject anything Date can't parse, so `previewExpiry()` consumers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-block.js","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAA6B,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkBnF,MAAM,aAAa,GAAG,wCAAwC,CAAA;AAE9D,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC9D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,OAA4B;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC
|
|
1
|
+
{"version":3,"file":"preview-block.js","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAA6B,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkBnF,MAAM,aAAa,GAAG,wCAAwC,CAAA;AAE9D,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC9D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,OAA4B;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IAChG,CAAC;IACD,iGAAiG;IACjG,iGAAiG;IACjG,iCAAiC;IACjC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,oEAAoE,OAAO,KAAK,CAAC,CAAA;IAClG,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAA;IAC7C,MAAM,KAAK,GAAG,SAAS;QACtB,CAAC,CAAC,wLAAwL,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC,QAAQ;QAChP,CAAC,CAAC,EAAE,CAAA;IACL,OAAO,CACN,sFAAsF,UAAU,CAAC,OAAO,CAAC,8BAA8B;QACvI,+DAA+D,eAAe,CAAC,OAAO,CAAC,IAAI;QAC3F,KAAK;QACL,eAAe,CAAC,KAAK,CAAC;QACtB,QAAQ,CACR,CAAA;AACF,CAAC"}
|
|
@@ -31,5 +31,5 @@ export declare function verifiedStamp(idSuffix?: string): string;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const CERT_MODAL_FONT_FAMILIES: readonly ["DM Sans", "Playfair Display", "JetBrains Mono", "Plus Jakarta Sans", "Allura"];
|
|
33
33
|
/** The full scoped stylesheet, adopted via a constructable `CSSStyleSheet`. */
|
|
34
|
-
export declare const CERT_MODAL_CSS = "\n:host {\n\t--cr-navy: #0a1b3f;\n\t--cr-lime: #d4e157;\n\t--cr-white: #ffffff;\n\t--cr-navy-06: rgba(10, 27, 63, 0.06);\n\t--cr-navy-10: rgba(10, 27, 63, 0.1);\n\t--cr-navy-25: rgba(10, 27, 63, 0.25);\n\t--cr-navy-40: rgba(10, 27, 63, 0.4);\n\t--cr-navy-45: rgba(10, 27, 63, 0.45);\n\t--cr-navy-50: rgba(10, 27, 63, 0.5);\n\t--cr-navy-55: rgba(10, 27, 63, 0.55);\n\t--cr-navy-60: rgba(10, 27, 63, 0.6);\n\t--cr-navy-85: rgba(10, 27, 63, 0.85);\n\t--cr-white-60: rgba(255, 255, 255, 0.6);\n\t--cr-white-85: rgba(255, 255, 255, 0.85);\n\t--cr-cta-hover: #0c2149;\n\t--cr-ghost-hover: rgba(10, 27, 63, 0.12);\n\t--cr-shadow: 0 30px 60px -18px rgba(10, 27, 63, 0.5);\n\t--cr-backdrop: rgba(10, 27, 63, 0.45);\n\t--cr-sans: 'DM Sans', system-ui, -apple-system, sans-serif;\n\t--cr-serif: 'Playfair Display', Georgia, serif;\n\t--cr-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;\n\t--cr-wordmark: 'Plus Jakarta Sans', system-ui, sans-serif;\n\t--cr-script: 'Allura', cursive;\n}\n\n.crm-dialog {\n\tborder: none;\n\tborder-radius: 14px;\n\tpadding: 0;\n\twidth: 92%;\n\tmax-height: 92vh;\n\toverflow: auto;\n\tbox-shadow: var(--cr-shadow);\n\tbackground: var(--cr-white);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-sans);\n\t/* fixed, NOT relative: a :modal dialog (showModal) renders in the TOP LAYER; a\n\t * relative dialog anchors to its scrolled document position, so once the page is scrolled the\n\t * panel renders off-screen while the ::backdrop still covers the viewport (measured top -1972 at\n\t * scrollY 2537). fixed is viewport-anchored (the UA inset:0 + margin:auto centers it) and still\n\t * provides the containing block for the absolutely-positioned .crm-close (x stays in-corner). */\n\tposition: fixed;\n\t/* defensive resets against a hostile brand theme reaching the top-layer dialog */\n\tbox-sizing: border-box;\n\ttext-align: left;\n\tline-height: 1.4;\n}\n.crm-dialog *,\n.crm-dialog *::before,\n.crm-dialog *::after {\n\tbox-sizing: border-box;\n}\n.crm-dialog::backdrop {\n\tbackground: var(--cr-backdrop);\n}\n.crm-dialog--cert {\n\tmax-width: 484px;\n}\n.crm-dialog--expert {\n\tmax-width: 436px;\n}\n\n/* Close (\u00D7) */\n.crm-close {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 15px;\n\tz-index: 2;\n\tbackground: none;\n\tborder: none;\n\tcursor: pointer;\n\tpadding: 2px;\n\tcolor: var(--cr-white-60);\n\tfont-size: 1.35rem;\n\tline-height: 1;\n\tfont-family: var(--cr-sans);\n\ttransition: color 0.15s;\n}\n.crm-close:hover {\n\tcolor: var(--cr-white);\n}\n\n/* Header bar */\n.crm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tpadding: 13px 22px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n}\n.crm-head__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-white-85);\n}\n.crm-head__wordmark {\n\tmargin-left: auto;\n\tmargin-right: 22px;\n\tfont-family: var(--cr-wordmark);\n\tfont-weight: 800;\n\tfont-size: 13px;\n\tletter-spacing: 0.03em;\n\tcolor: var(--cr-white);\n}\n\n/* Body */\n.crm-body--cert {\n\tpadding: 24px 28px 26px;\n}\n.crm-body--expert {\n\tpadding: 22px 26px 24px;\n}\n\n/* Avatar (shared, lime ring) */\n.crm-avatar {\n\tposition: relative;\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\tfont-weight: 700;\n\tobject-fit: cover;\n\tbox-shadow: 0 0 0 2px var(--cr-white), 0 0 0 3.5px var(--cr-lime);\n}\n.crm-avatar--56 {\n\twidth: 56px;\n\theight: 56px;\n\tfont-size: 17px;\n}\n.crm-avatar--64 {\n\twidth: 64px;\n\theight: 64px;\n\tfont-size: 1.35rem;\n\tletter-spacing: 0.02em;\n}\n\n/* Certificate \
|
|
34
|
+
export declare const CERT_MODAL_CSS = "\n:host {\n\t--cr-navy: #0a1b3f;\n\t--cr-lime: #d4e157;\n\t--cr-white: #ffffff;\n\t--cr-navy-06: rgba(10, 27, 63, 0.06);\n\t--cr-navy-10: rgba(10, 27, 63, 0.1);\n\t--cr-navy-25: rgba(10, 27, 63, 0.25);\n\t--cr-navy-40: rgba(10, 27, 63, 0.4);\n\t--cr-navy-45: rgba(10, 27, 63, 0.45);\n\t--cr-navy-50: rgba(10, 27, 63, 0.5);\n\t--cr-navy-55: rgba(10, 27, 63, 0.55);\n\t--cr-navy-60: rgba(10, 27, 63, 0.6);\n\t--cr-navy-85: rgba(10, 27, 63, 0.85);\n\t--cr-white-60: rgba(255, 255, 255, 0.6);\n\t--cr-white-85: rgba(255, 255, 255, 0.85);\n\t--cr-cta-hover: #0c2149;\n\t--cr-ghost-hover: rgba(10, 27, 63, 0.12);\n\t--cr-shadow: 0 30px 60px -18px rgba(10, 27, 63, 0.5);\n\t--cr-backdrop: rgba(10, 27, 63, 0.45);\n\t--cr-sans: 'DM Sans', system-ui, -apple-system, sans-serif;\n\t--cr-serif: 'Playfair Display', Georgia, serif;\n\t--cr-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;\n\t--cr-wordmark: 'Plus Jakarta Sans', system-ui, sans-serif;\n\t--cr-script: 'Allura', cursive;\n}\n\n.crm-dialog {\n\tborder: none;\n\tborder-radius: 14px;\n\tpadding: 0;\n\twidth: 92%;\n\tmax-height: 92vh;\n\toverflow: auto;\n\tbox-shadow: var(--cr-shadow);\n\tbackground: var(--cr-white);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-sans);\n\t/* fixed, NOT relative: a :modal dialog (showModal) renders in the TOP LAYER; a\n\t * relative dialog anchors to its scrolled document position, so once the page is scrolled the\n\t * panel renders off-screen while the ::backdrop still covers the viewport (measured top -1972 at\n\t * scrollY 2537). fixed is viewport-anchored (the UA inset:0 + margin:auto centers it) and still\n\t * provides the containing block for the absolutely-positioned .crm-close (x stays in-corner). */\n\tposition: fixed;\n\t/* defensive resets against a hostile brand theme reaching the top-layer dialog */\n\tbox-sizing: border-box;\n\ttext-align: left;\n\tline-height: 1.4;\n}\n.crm-dialog *,\n.crm-dialog *::before,\n.crm-dialog *::after {\n\tbox-sizing: border-box;\n}\n.crm-dialog::backdrop {\n\tbackground: var(--cr-backdrop);\n}\n.crm-dialog--cert {\n\tmax-width: 484px;\n}\n.crm-dialog--expert {\n\tmax-width: 436px;\n}\n\n/* Close (\u00D7) */\n.crm-close {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 15px;\n\tz-index: 2;\n\tbackground: none;\n\tborder: none;\n\tcursor: pointer;\n\tpadding: 2px;\n\tcolor: var(--cr-white-60);\n\tfont-size: 1.35rem;\n\tline-height: 1;\n\tfont-family: var(--cr-sans);\n\ttransition: color 0.15s;\n}\n.crm-close:hover {\n\tcolor: var(--cr-white);\n}\n\n/* Header bar */\n.crm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tpadding: 13px 22px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n}\n.crm-head__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-white-85);\n}\n.crm-head__wordmark {\n\tmargin-left: auto;\n\tmargin-right: 22px;\n\tfont-family: var(--cr-wordmark);\n\tfont-weight: 800;\n\tfont-size: 13px;\n\tletter-spacing: 0.03em;\n\tcolor: var(--cr-white);\n}\n\n/* Body */\n.crm-body--cert {\n\tpadding: 24px 28px 26px;\n}\n.crm-body--expert {\n\tpadding: 22px 26px 24px;\n}\n\n/* Avatar (shared, lime ring) */\n.crm-avatar {\n\tposition: relative;\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\tfont-weight: 700;\n\tobject-fit: cover;\n\tbox-shadow: 0 0 0 2px var(--cr-white), 0 0 0 3.5px var(--cr-lime);\n}\n.crm-avatar--56 {\n\twidth: 56px;\n\theight: 56px;\n\tfont-size: 17px;\n}\n.crm-avatar--64 {\n\twidth: 64px;\n\theight: 64px;\n\tfont-size: 1.35rem;\n\tletter-spacing: 0.02em;\n}\n\n/* Certificate \u00B7 reviewing-expert row */\n.crm-expert-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n}\n.crm-expert-row__info {\n\tflex: 1;\n\tmin-width: 0;\n}\n.crm-expert-row__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.14em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-45);\n}\n.crm-expert-row__name {\n\tfont-size: 15px;\n\tfont-weight: 700;\n\tcolor: var(--cr-navy);\n\tmargin-top: 2px;\n}\n.crm-expert-row__creds {\n\tfont-size: 12px;\n\tcolor: var(--cr-navy-60);\n\tmargin-top: 1px;\n}\n\n/* Certificate \u00B7 \"Certifies the article\" */\n.crm-certifies {\n\tmargin: 18px 0 0;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n}\n.crm-certifies__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 9px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-40);\n}\n.crm-certifies__title {\n\tfont-family: var(--cr-serif);\n\tfont-weight: 700;\n\tfont-size: 18px;\n\tline-height: 1.25;\n\tcolor: var(--cr-navy);\n\tmargin-top: 5px;\n\ttext-wrap: balance;\n}\n\n/* Certificate \u00B7 meta row */\n.crm-meta {\n\tdisplay: flex;\n\tgap: 10px;\n\tmargin-top: 16px;\n}\n.crm-meta__col {\n\tflex: 1;\n}\n.crm-meta__label {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8px;\n\tletter-spacing: 0.12em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-40);\n}\n.crm-meta__value {\n\tfont-size: 12.5px;\n\tfont-weight: 600;\n\tcolor: var(--cr-navy);\n\tmargin-top: 3px;\n}\n.crm-meta__value--mono {\n\tfont-family: var(--cr-mono);\n\tfont-size: 12px;\n}\n.crm-meta__status {\n\tmargin-top: 3px;\n}\n\n/* Certified chip (meta status) */\n.crm-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 2px 7px;\n\tborder-radius: 4px;\n\tbackground: var(--cr-lime);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n}\n\n/* Certificate \u00B7 signature + stamp footer */\n.crm-sig {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tjustify-content: space-between;\n\tmargin-top: 20px;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n\tgap: 12px;\n}\n.crm-sig__name {\n\tfont-family: var(--cr-script);\n\tfont-size: 30px;\n\tline-height: 0.9;\n\tcolor: var(--cr-navy);\n}\n.crm-sig__rule {\n\twidth: 146px;\n\tmax-width: 100%;\n\theight: 1px;\n\tbackground: var(--cr-navy-25);\n\tmargin: 4px 0 6px;\n}\n.crm-sig__url {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8px;\n\tfont-weight: 500;\n\tletter-spacing: 0.12em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-50);\n}\n.crm-stamp {\n\ttransform: rotate(-8deg);\n\tflex-shrink: 0;\n}\n.crm-stamp svg {\n\tdisplay: block;\n}\n\n/* Actions (shared) */\n.crm-actions {\n\tdisplay: flex;\n\tgap: 10px;\n\tmargin-top: 20px;\n}\n.crm-cta {\n\tflex: 1;\n\tdisplay: inline-block;\n\ttext-align: center;\n\tpadding: 11px 16px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\ttext-decoration: none;\n\tborder-radius: 8px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tfont-family: var(--cr-sans);\n\tborder: none;\n\tcursor: pointer;\n\ttransition: background 0.15s;\n}\n.crm-cta:hover {\n\tbackground: var(--cr-cta-hover);\n}\n.crm-ghost {\n\tpadding: 11px 18px;\n\tbackground: var(--cr-navy-06);\n\tcolor: var(--cr-navy);\n\tborder: none;\n\tborder-radius: 8px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tfont-family: var(--cr-sans);\n\tcursor: pointer;\n\ttransition: background 0.15s;\n}\n.crm-ghost:hover {\n\tbackground: var(--cr-ghost-hover);\n}\n\n/* Reviewer \u00B7 identity row */\n.crm-id {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n}\n.crm-id__info {\n\tmin-width: 0;\n}\n.crm-id__name {\n\tfont-size: 16px;\n\tfont-weight: 700;\n\tline-height: 1.25;\n\tcolor: var(--cr-navy);\n}\n.crm-id__subtitle {\n\tfont-size: 12.5px;\n\tline-height: 1.4;\n\tcolor: var(--cr-navy-60);\n\tmargin-top: 2px;\n}\n\n/* Reviewer \u00B7 credential chips */\n.crm-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 7px;\n\tmargin-top: 16px;\n}\n.crm-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tpadding: 4px 10px;\n\tborder-radius: 999px;\n\tbackground: var(--cr-navy-06);\n\tcolor: var(--cr-navy);\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n/* Reviewer \u00B7 bio */\n.crm-bio {\n\tmargin: 16px 0 0;\n\tfont-size: 0.9rem;\n\tline-height: 1.55;\n\tcolor: var(--cr-navy-85);\n}\n\n/* Reviewer \u00B7 trust line (the disclosure footer below now owns the spacing to the actions) */\n.crm-trust {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tflex-wrap: wrap;\n\tmargin: 16px 0 0;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n}\n.crm-trust__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tpadding: 3px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--cr-lime);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 700;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n}\n.crm-trust__text {\n\tfont-family: var(--cr-mono);\n\tfont-size: 9px;\n\tletter-spacing: 0.05em;\n\tcolor: var(--cr-navy-50);\n}\n\n/* FTC disclosure footer \u00B7 material-connection cue (compensated only) + scope line\n * (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */\n.crm-disclosure {\n\tmargin-top: 14px;\n\tpadding-top: 14px;\n\tborder-top: 1px solid var(--cr-navy-10);\n\tfont-family: var(--cr-sans);\n\tfont-size: 11.5px;\n\tline-height: 1.5;\n\tcolor: var(--cr-navy-50);\n}\n.crm-disclosure__cue {\n\tfont-family: var(--cr-mono);\n\tfont-size: 10px;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy);\n}\n\n/* Narrow storefronts / phones */\n@media (max-width: 440px) {\n\t.crm-body--cert {\n\t\tpadding: 20px 18px 22px;\n\t}\n\t.crm-body--expert {\n\t\tpadding: 18px 18px 20px;\n\t}\n}\n";
|
|
35
35
|
//# sourceMappingURL=certrev-cert-modal.styles.d.ts.map
|
|
@@ -196,7 +196,7 @@ export const CERT_MODAL_CSS = `
|
|
|
196
196
|
letter-spacing: 0.02em;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/* Certificate
|
|
199
|
+
/* Certificate · reviewing-expert row */
|
|
200
200
|
.crm-expert-row {
|
|
201
201
|
display: flex;
|
|
202
202
|
align-items: center;
|
|
@@ -226,7 +226,7 @@ export const CERT_MODAL_CSS = `
|
|
|
226
226
|
margin-top: 1px;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/* Certificate
|
|
229
|
+
/* Certificate · "Certifies the article" */
|
|
230
230
|
.crm-certifies {
|
|
231
231
|
margin: 18px 0 0;
|
|
232
232
|
padding-top: 16px;
|
|
@@ -250,7 +250,7 @@ export const CERT_MODAL_CSS = `
|
|
|
250
250
|
text-wrap: balance;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
/* Certificate
|
|
253
|
+
/* Certificate · meta row */
|
|
254
254
|
.crm-meta {
|
|
255
255
|
display: flex;
|
|
256
256
|
gap: 10px;
|
|
@@ -296,7 +296,7 @@ export const CERT_MODAL_CSS = `
|
|
|
296
296
|
text-transform: uppercase;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
/* Certificate
|
|
299
|
+
/* Certificate · signature + stamp footer */
|
|
300
300
|
.crm-sig {
|
|
301
301
|
display: flex;
|
|
302
302
|
align-items: flex-end;
|
|
@@ -376,7 +376,7 @@ export const CERT_MODAL_CSS = `
|
|
|
376
376
|
background: var(--cr-ghost-hover);
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
/* Reviewer
|
|
379
|
+
/* Reviewer · identity row */
|
|
380
380
|
.crm-id {
|
|
381
381
|
display: flex;
|
|
382
382
|
align-items: center;
|
|
@@ -398,7 +398,7 @@ export const CERT_MODAL_CSS = `
|
|
|
398
398
|
margin-top: 2px;
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
/* Reviewer
|
|
401
|
+
/* Reviewer · credential chips */
|
|
402
402
|
.crm-chips {
|
|
403
403
|
display: flex;
|
|
404
404
|
flex-wrap: wrap;
|
|
@@ -416,7 +416,7 @@ export const CERT_MODAL_CSS = `
|
|
|
416
416
|
font-weight: 600;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
/* Reviewer
|
|
419
|
+
/* Reviewer · bio */
|
|
420
420
|
.crm-bio {
|
|
421
421
|
margin: 16px 0 0;
|
|
422
422
|
font-size: 0.9rem;
|
|
@@ -424,7 +424,7 @@ export const CERT_MODAL_CSS = `
|
|
|
424
424
|
color: var(--cr-navy-85);
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
/* Reviewer
|
|
427
|
+
/* Reviewer · trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
428
428
|
.crm-trust {
|
|
429
429
|
display: flex;
|
|
430
430
|
align-items: center;
|
|
@@ -455,7 +455,7 @@ export const CERT_MODAL_CSS = `
|
|
|
455
455
|
color: var(--cr-navy-50);
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
-
/* FTC disclosure footer
|
|
458
|
+
/* FTC disclosure footer · material-connection cue (compensated only) + scope line
|
|
459
459
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
460
460
|
.crm-disclosure {
|
|
461
461
|
margin-top: 14px;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* biome-ignore-all format: generated cert-badge bundle, see @certrev/cert-block src/modal/ */ /* biome-ignore-all lint: generated cert-badge bundle, see @certrev/cert-block src/modal/ */
|
|
2
|
-
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$e=(()=>{let e=new Int8Array(128).fill(-1);for(let t=0;t<H.length;t++)e[H.charCodeAt(t)]=t;return e[45]=62,e[95]=63,e})();function g(e){let t=[];for(let a=0;a<e.length;a++){let c=e.charCodeAt(a),d=c<128?$e[c]:-1;d>=0&&t.push(d)}let r=Math.floor(t.length*6/8),n=new Uint8Array(r),o=0,i=0,s=0;for(let a=0;a<t.length;a++)o=o<<6|t[a],i+=6,i>=8&&(i-=8,n[s++]=o>>i&255);return n}function C(e){return g(e)}function p(e,t=new Set){if(typeof e=="number"&&isNaN(e))throw new Error("NaN is not allowed");if(typeof e=="number"&&!isFinite(e))throw new Error("Infinity is not allowed");if(e===null||typeof e!="object")return JSON.stringify(e);if(typeof e.toJSON=="function"){if(t.has(e))throw new Error("Circular reference detected");t.add(e);let n=p(e.toJSON(),t);return t.delete(e),n}if(t.has(e))throw new Error("Circular reference detected");t.add(e);let r;if(Array.isArray(e))r=`[${e.map(o=>p(o===void 0||typeof o=="symbol"?null:o,t)).join(",")}]`;else{let n=[];for(let o of Object.keys(e).sort())e[o]===void 0||typeof e[o]=="symbol"||n.push(`${p(o)}:${p(e[o],t)}`);r=`{${n.join(",")}}`}return t.delete(e),r}var Oe=typeof p=="function"?p:p.default;function K(e){let t=Oe(e);if(typeof t!="string")throw new Error("canonicalize: value is not JSON-serializable");return t}function T(e){return new TextEncoder().encode(K(e))}function b(e,t="$"){if(typeof e=="number"){if(!Number.isInteger(e)||!Number.isSafeInteger(e))throw new Error(`canonicalPayloadBytes: number at ${t} (${e}) is not a safe integer
|
|
2
|
+
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$e=(()=>{let e=new Int8Array(128).fill(-1);for(let t=0;t<H.length;t++)e[H.charCodeAt(t)]=t;return e[45]=62,e[95]=63,e})();function g(e){let t=[];for(let a=0;a<e.length;a++){let c=e.charCodeAt(a),d=c<128?$e[c]:-1;d>=0&&t.push(d)}let r=Math.floor(t.length*6/8),n=new Uint8Array(r),o=0,i=0,s=0;for(let a=0;a<t.length;a++)o=o<<6|t[a],i+=6,i>=8&&(i-=8,n[s++]=o>>i&255);return n}function C(e){return g(e)}function p(e,t=new Set){if(typeof e=="number"&&isNaN(e))throw new Error("NaN is not allowed");if(typeof e=="number"&&!isFinite(e))throw new Error("Infinity is not allowed");if(e===null||typeof e!="object")return JSON.stringify(e);if(typeof e.toJSON=="function"){if(t.has(e))throw new Error("Circular reference detected");t.add(e);let n=p(e.toJSON(),t);return t.delete(e),n}if(t.has(e))throw new Error("Circular reference detected");t.add(e);let r;if(Array.isArray(e))r=`[${e.map(o=>p(o===void 0||typeof o=="symbol"?null:o,t)).join(",")}]`;else{let n=[];for(let o of Object.keys(e).sort())e[o]===void 0||typeof e[o]=="symbol"||n.push(`${p(o)}:${p(e[o],t)}`);r=`{${n.join(",")}}`}return t.delete(e),r}var Oe=typeof p=="function"?p:p.default;function K(e){let t=Oe(e);if(typeof t!="string")throw new Error("canonicalize: value is not JSON-serializable");return t}function T(e){return new TextEncoder().encode(K(e))}function b(e,t="$"){if(typeof e=="number"){if(!Number.isInteger(e)||!Number.isSafeInteger(e))throw new Error(`canonicalPayloadBytes: number at ${t} (${e}) is not a safe integer. Signed-payload numbers must be safe integers so PHP/Go/Java verifiers reproduce identical canonical bytes`);return}if(Array.isArray(e)){e.forEach((r,n)=>b(r,`${t}[${n}]`));return}if(e&&typeof e=="object")for(let[r,n]of Object.entries(e))b(n,`${t}.${r}`)}function R(e){return b(e),T(e)}function k(e){return b(e),T(e)}var E="ed25519";var m={name:"Ed25519"};function Pe(e){return e.replace(/-----BEGIN [^-]+-----/g,"").replace(/-----END [^-]+-----/g,"").replace(/\s+/g,"")}async function q(e){switch(e.format){case"pem":return crypto.subtle.importKey("spki",g(Pe(e.pem)),m,!1,["verify"]);case"spki-base64":return crypto.subtle.importKey("spki",g(e.base64),m,!1,["verify"]);case"spki-der":return crypto.subtle.importKey("spki",e.bytes,m,!1,["verify"]);case"raw":{if(e.bytes.length!==32)throw new Error(`ed25519 raw public key must be 32 bytes, got ${e.bytes.length}`);return crypto.subtle.importKey("raw",e.bytes,m,!1,["verify"])}}}function ze(e){if("format"in e)return!1;let t=e;return typeof t.type=="string"&&typeof t.algorithm=="object"&&Array.isArray(t.usages)}async function G(e){return ze(e)?e:q(e)}async function j(e,t,r){let n=R(e),o=C(t);try{return await crypto.subtle.verify(m,r,o,n)}catch{return!1}}async function Ve(e,t){let{payload:r,signature:n}=e??{};if(!r||!n||r.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(n.alg!==E)return{ok:!1,reason:"unsupported_alg"};let o=await t(n.kid);if(!o)return{ok:!1,reason:"unknown_key"};let i;try{i=await G(o)}catch{return{ok:!1,reason:"unknown_key"}}return await j(r,n.sig,i)?{ok:!0,key:i}:{ok:!1,reason:"invalid_signature"}}function J(e,t){let r=t.now??new Date;if(e.subject.platform!==t.platform)return{decision:"suppress",reason:"platform_mismatch"};if(e.subject.externalId!==t.externalId)return{decision:"suppress",reason:"subject_mismatch"};if(e.lifecycle.revokedAt!==null)return{decision:"suppress",reason:"revoked"};if(r.getTime()>=Date.parse(e.lifecycle.expiresAt))return{decision:"suppress",reason:"expired"};let n=e.subject.contentDigest,o=t.liveContentHash;return n!==null&&o!=null&&n!==o?{decision:"suppress",reason:"content_drift"}:{decision:"render",payload:e}}async function W(e,t,r){let n;try{n=await Ve(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?J(e.payload,r):{decision:"suppress",reason:n.reason}}function v(e){return e?.kind==="tombstone"}async function He(e,t){let{signature:r}=e??{};if(!e?.subject||!r||e.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(r.alg!==E)return{ok:!1,reason:"unsupported_alg"};let n=await t(r.kid);if(!n)return{ok:!1,reason:"unknown_key"};let o;try{o=await G(n)}catch{return{ok:!1,reason:"unknown_key"}}let{signature:i,...s}=e,a=C(r.sig),c=!1;try{c=await crypto.subtle.verify(m,o,a,k(s))}catch{c=!1}return c?{ok:!0,key:o}:{ok:!1,reason:"invalid_signature"}}async function Z(e,t,r){let n;try{n=await He(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?e.subject.platform!==r.platform?{decision:"suppress",reason:"platform_mismatch"}:e.subject.externalId!==r.externalId?{decision:"suppress",reason:"subject_mismatch"}:{decision:"suppress",reason:"revoked"}:{decision:"suppress",reason:n.reason}}async function I(e,t,r){return v(e)?Z(e,t,r):W(e,t,r)}var Y=`
|
|
3
3
|
@font-face {
|
|
4
4
|
font-family: "DM Sans";
|
|
5
5
|
font-style: normal;
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
letter-spacing: 0.02em;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
/* Certificate \
|
|
267
|
+
/* Certificate \xB7 reviewing-expert row */
|
|
268
268
|
.crm-expert-row {
|
|
269
269
|
display: flex;
|
|
270
270
|
align-items: center;
|
|
@@ -294,7 +294,7 @@
|
|
|
294
294
|
margin-top: 1px;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
/* Certificate \
|
|
297
|
+
/* Certificate \xB7 "Certifies the article" */
|
|
298
298
|
.crm-certifies {
|
|
299
299
|
margin: 18px 0 0;
|
|
300
300
|
padding-top: 16px;
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
text-wrap: balance;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
/* Certificate \
|
|
321
|
+
/* Certificate \xB7 meta row */
|
|
322
322
|
.crm-meta {
|
|
323
323
|
display: flex;
|
|
324
324
|
gap: 10px;
|
|
@@ -364,7 +364,7 @@
|
|
|
364
364
|
text-transform: uppercase;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
/* Certificate \
|
|
367
|
+
/* Certificate \xB7 signature + stamp footer */
|
|
368
368
|
.crm-sig {
|
|
369
369
|
display: flex;
|
|
370
370
|
align-items: flex-end;
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
background: var(--cr-ghost-hover);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
/* Reviewer \
|
|
447
|
+
/* Reviewer \xB7 identity row */
|
|
448
448
|
.crm-id {
|
|
449
449
|
display: flex;
|
|
450
450
|
align-items: center;
|
|
@@ -466,7 +466,7 @@
|
|
|
466
466
|
margin-top: 2px;
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
/* Reviewer \
|
|
469
|
+
/* Reviewer \xB7 credential chips */
|
|
470
470
|
.crm-chips {
|
|
471
471
|
display: flex;
|
|
472
472
|
flex-wrap: wrap;
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
font-weight: 600;
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
/* Reviewer \
|
|
487
|
+
/* Reviewer \xB7 bio */
|
|
488
488
|
.crm-bio {
|
|
489
489
|
margin: 16px 0 0;
|
|
490
490
|
font-size: 0.9rem;
|
|
@@ -492,7 +492,7 @@
|
|
|
492
492
|
color: var(--cr-navy-85);
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
-
/* Reviewer \
|
|
495
|
+
/* Reviewer \xB7 trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
496
496
|
.crm-trust {
|
|
497
497
|
display: flex;
|
|
498
498
|
align-items: center;
|
|
@@ -523,7 +523,7 @@
|
|
|
523
523
|
color: var(--cr-navy-50);
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
-
/* FTC disclosure footer \
|
|
526
|
+
/* FTC disclosure footer \xB7 material-connection cue (compensated only) + scope line
|
|
527
527
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
528
528
|
.crm-disclosure {
|
|
529
529
|
margin-top: 14px;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-with-deadline.d.ts","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"fetch-with-deadline.d.ts","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,QAAQ,CAAC,CAqBnB"}
|
|
@@ -39,7 +39,9 @@ export async function fetchWithDeadline(fetchImpl, url, init, timeoutMs = DEFAUL
|
|
|
39
39
|
const deadline = new Promise((_, reject) => {
|
|
40
40
|
timer = setTimeout(() => {
|
|
41
41
|
controller.abort();
|
|
42
|
-
|
|
42
|
+
// Middle dot, not an em dash: this prints into a consumer's console with our name on it,
|
|
43
|
+
// so it is visible copy, and the URL it appends is labelled data.
|
|
44
|
+
reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline · ${url}`));
|
|
43
45
|
}, timeoutMs);
|
|
44
46
|
});
|
|
45
47
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-with-deadline.js","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAuB,EACvB,GAAW,EACX,IAAiB,EACjB,YAAoB,wBAAwB;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,IAAI,KAAgD,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,UAAU,CAAC,KAAK,EAAE,CAAA;YAClB,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC,EAAE,SAAS,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,uFAAuF;QACvF,0EAA0E;QAC1E,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9F,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"fetch-with-deadline.js","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAuB,EACvB,GAAW,EACX,IAAiB,EACjB,YAAoB,wBAAwB;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,IAAI,KAAgD,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,UAAU,CAAC,KAAK,EAAE,CAAA;YAClB,yFAAyF;YACzF,kEAAkE;YAClE,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC,EAAE,SAAS,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,uFAAuF;QACvF,0EAA0E;QAC1E,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9F,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@certrev/cert-block",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Headless render edge for the CertREV CertDeliveryEnvelope: SSR-safe React cert components, a framework-agnostic <certrev-badge> Web Component, a schema.org JSON-LD projector, and a fail-closed verify layer over the shared VerdictKernel.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -218,9 +218,9 @@ const WIRE_INPUT_TYPE: Record<(typeof BUILDER_CERT_CHROME_KEYS)[number], Builder
|
|
|
218
218
|
*/
|
|
219
219
|
const WIRE_INPUT_HELPER_TEXT: Partial<Record<(typeof BUILDER_CERT_CHROME_KEYS)[number], string>> = {
|
|
220
220
|
compensationCue:
|
|
221
|
-
'System-populated by CertREV. Only its PRESENCE is read (absent = a pro-bono reviewer, so no cue renders); the cue itself is a locked constant, so editing this text changes nothing on the page
|
|
221
|
+
'System-populated by CertREV. Only its PRESENCE is read (absent = a pro-bono reviewer, so no cue renders); the cue itself is a locked constant, so editing this text changes nothing on the page, and clearing it does not remove the disclosure.',
|
|
222
222
|
scopeLine:
|
|
223
|
-
'System-populated by CertREV. The FTC scope line renders byte-verbatim from a locked constant
|
|
223
|
+
'System-populated by CertREV. The FTC scope line renders byte-verbatim from a locked constant: editing this text changes nothing on the page.',
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
@@ -262,7 +262,9 @@ export const PLACEMENT_INPUTS: readonly BuilderInput[] = [
|
|
|
262
262
|
name: 'part',
|
|
263
263
|
type: 'string',
|
|
264
264
|
friendlyName: 'Banner part',
|
|
265
|
-
|
|
265
|
+
// A full stop, not a middle dot: the dots already separate the three values, so a fourth
|
|
266
|
+
// would read as a fourth option rather than as a qualifier on the list.
|
|
267
|
+
helperText: 'full (default) · header · memo. Banner placement only.',
|
|
266
268
|
defaultValue: 'full',
|
|
267
269
|
enum: [
|
|
268
270
|
{ label: 'Full (header + memo)', value: 'full' },
|
|
@@ -70,7 +70,9 @@ export function ExpertBio(props: ExpertBioProps) {
|
|
|
70
70
|
{expert.credentials.map((c) => (
|
|
71
71
|
<li key={`${c.abbreviation}:${c.fullName}`} className={`${ROOT_CLASS}__credential`}>
|
|
72
72
|
<abbr title={c.fullName}>{c.abbreviation}</abbr>
|
|
73
|
-
|
|
73
|
+
{/* Middle dot, not an em dash: this is labelled data (abbreviation, then what it
|
|
74
|
+
expands to), and it renders on the customer's own article. */}
|
|
75
|
+
<span className={`${ROOT_CLASS}__credential-full`}> · {c.fullName}</span>
|
|
74
76
|
</li>
|
|
75
77
|
))}
|
|
76
78
|
</ul>
|
|
@@ -52,7 +52,7 @@ export function previewExpiry(html: string): string | null {
|
|
|
52
52
|
export function renderPreviewBlock(input: RenderCertBlockInput, options: PreviewBlockOptions): string {
|
|
53
53
|
const expires = options.expiresAt.trim()
|
|
54
54
|
if (!expires) {
|
|
55
|
-
throw new Error('renderPreviewBlock: `expiresAt` is required
|
|
55
|
+
throw new Error('renderPreviewBlock: `expiresAt` is required. A preview must be time-bounded.')
|
|
56
56
|
}
|
|
57
57
|
// The bound must be REAL — an unparseable expiry is a toothless bound (downstream can't refuse a
|
|
58
58
|
// stale preview it can't date). Reject anything Date can't parse, so `previewExpiry()` consumers
|
|
@@ -203,7 +203,7 @@ export const CERT_MODAL_CSS = `
|
|
|
203
203
|
letter-spacing: 0.02em;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
/* Certificate
|
|
206
|
+
/* Certificate · reviewing-expert row */
|
|
207
207
|
.crm-expert-row {
|
|
208
208
|
display: flex;
|
|
209
209
|
align-items: center;
|
|
@@ -233,7 +233,7 @@ export const CERT_MODAL_CSS = `
|
|
|
233
233
|
margin-top: 1px;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
/* Certificate
|
|
236
|
+
/* Certificate · "Certifies the article" */
|
|
237
237
|
.crm-certifies {
|
|
238
238
|
margin: 18px 0 0;
|
|
239
239
|
padding-top: 16px;
|
|
@@ -257,7 +257,7 @@ export const CERT_MODAL_CSS = `
|
|
|
257
257
|
text-wrap: balance;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
/* Certificate
|
|
260
|
+
/* Certificate · meta row */
|
|
261
261
|
.crm-meta {
|
|
262
262
|
display: flex;
|
|
263
263
|
gap: 10px;
|
|
@@ -303,7 +303,7 @@ export const CERT_MODAL_CSS = `
|
|
|
303
303
|
text-transform: uppercase;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
/* Certificate
|
|
306
|
+
/* Certificate · signature + stamp footer */
|
|
307
307
|
.crm-sig {
|
|
308
308
|
display: flex;
|
|
309
309
|
align-items: flex-end;
|
|
@@ -383,7 +383,7 @@ export const CERT_MODAL_CSS = `
|
|
|
383
383
|
background: var(--cr-ghost-hover);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
/* Reviewer
|
|
386
|
+
/* Reviewer · identity row */
|
|
387
387
|
.crm-id {
|
|
388
388
|
display: flex;
|
|
389
389
|
align-items: center;
|
|
@@ -405,7 +405,7 @@ export const CERT_MODAL_CSS = `
|
|
|
405
405
|
margin-top: 2px;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
/* Reviewer
|
|
408
|
+
/* Reviewer · credential chips */
|
|
409
409
|
.crm-chips {
|
|
410
410
|
display: flex;
|
|
411
411
|
flex-wrap: wrap;
|
|
@@ -423,7 +423,7 @@ export const CERT_MODAL_CSS = `
|
|
|
423
423
|
font-weight: 600;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
/* Reviewer
|
|
426
|
+
/* Reviewer · bio */
|
|
427
427
|
.crm-bio {
|
|
428
428
|
margin: 16px 0 0;
|
|
429
429
|
font-size: 0.9rem;
|
|
@@ -431,7 +431,7 @@ export const CERT_MODAL_CSS = `
|
|
|
431
431
|
color: var(--cr-navy-85);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
/* Reviewer
|
|
434
|
+
/* Reviewer · trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
435
435
|
.crm-trust {
|
|
436
436
|
display: flex;
|
|
437
437
|
align-items: center;
|
|
@@ -462,7 +462,7 @@ export const CERT_MODAL_CSS = `
|
|
|
462
462
|
color: var(--cr-navy-50);
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
-
/* FTC disclosure footer
|
|
465
|
+
/* FTC disclosure footer · material-connection cue (compensated only) + scope line
|
|
466
466
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
467
467
|
.crm-disclosure {
|
|
468
468
|
margin-top: 14px;
|
|
@@ -46,7 +46,9 @@ export async function fetchWithDeadline(
|
|
|
46
46
|
const deadline = new Promise<never>((_, reject) => {
|
|
47
47
|
timer = setTimeout(() => {
|
|
48
48
|
controller.abort()
|
|
49
|
-
|
|
49
|
+
// Middle dot, not an em dash: this prints into a consumer's console with our name on it,
|
|
50
|
+
// so it is visible copy, and the URL it appends is labelled data.
|
|
51
|
+
reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline · ${url}`))
|
|
50
52
|
}, timeoutMs)
|
|
51
53
|
})
|
|
52
54
|
|