@certrev/cert-block 1.0.1 → 1.0.3

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 CHANGED
@@ -5,6 +5,64 @@ 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.3 · 2026-08-18
9
+
10
+ Patch: restores the placement a pre-existing Builder entry renders. **If you integrated via
11
+ Builder before 1.0.0, upgrade to this from any 1.0.x.**
12
+
13
+ - **1.0.0 silently repainted already-exported Builder entries from banner to sidebar.** `mode` is
14
+ a PLACEMENT input, not one of the 19 wire keys, and the exporter emits only the wire projection,
15
+ so every entry exported to date carries no `mode` key. Through 0.5.5 `CertReviewCard` read
16
+ `props.mode ?? 'banner'` and those entries rendered the banner face. 1.0.0 removed that fallback
17
+ so the renderer would own a single default, which changed what those entries paint. Consumers
18
+ install unpinned, so it reached live pages with no entry edit and no upgrade on their side.
19
+ - **The fallback is restored, as a bridge and not as a reversal.** The sidebar default stands
20
+ everywhere it was actually the point: a direct `renderCertBlock` caller omitting `mode` still
21
+ gets sidebar, and `sidebar` is still the registered `defaultValue`, so every block created in
22
+ the editor from now on carries an explicit `mode` and never reaches the fallback. The bridge
23
+ covers only entries that predate the exporter emitting `mode`, and is deleted once the exporter
24
+ writes it and existing entries are backfilled. The registered enum, `defaultValue`, helper text
25
+ and `render-cert-block.ts` are untouched by this release.
26
+ - **A third gate, of a kind neither existing one could be.** The rendered-surface sweep checks
27
+ text that IS on a surface; the string-literal rule checks strings that ARE in the source. This
28
+ regression was the absence of a value: what an entry paints when an input is missing. That is
29
+ invisible to both, and it is why 1.0.0 shipped green. `builder.test.tsx` now pins the
30
+ exported-entry shape (no `mode`, and `mode: null`) to the banner face, pins an explicit `mode`
31
+ as still winning, and pins the direct renderer as still defaulting to sidebar so the bridge can
32
+ never quietly widen into a package-wide change. Confirmed to fail with the fallback removed.
33
+ - **One editor-facing em dash**, in the design-time anchor placeholder (`builder/index.tsx`), now
34
+ a colon. It is JSX text rather than a quoted string, so the 1.0.2 literal rule could not see it
35
+ either; recorded here because it is the same blind spot, not a separate lesson.
36
+
37
+ ## 1.0.2 · 2026-08-18
38
+
39
+ Patch: copy only, in strings nobody renders. No API change, no type change, no behaviour change.
40
+
41
+ - **The 1.0.1 sweep checked the wrong layer, and 1.0.1 shipped an em dash anyway.** It reached a
42
+ reader through the timeout error in `fetchWithDeadline`, which set one between the deadline and
43
+ the URL and prints into a consumer's console with our name on it. That is visible copy by every
44
+ test this changelog has been applying, and the 33-surface rendered sweep could not see it, because
45
+ a thrown error is not a surface anything renders. Found by auditing the published tarball rather
46
+ than the render. It now uses a middle dot: the URL it appends is labelled data.
47
+ - **Two more in the same class, found by sweeping every string in shipped code rather than the one
48
+ that was reported.** `renderPreviewBlock`'s missing-`expiresAt` error takes a full stop, because
49
+ the second half is a complete sentence stating why. Nine CSS section comments inside
50
+ `CERT_MODAL_CSS` take middle dots: that template literal is injected verbatim into the modal's
51
+ shadow root, so its comments ship to every page the modal opens on and are readable in devtools,
52
+ unlike the TypeScript comments around it, which `tsc` emits but nothing displays.
53
+ - **The gate is now two tests, because one could never have been enough.** The rendered sweep and
54
+ the new string-literal sweep catch disjoint sets: the rendered one sees assembled output but only
55
+ for surfaces something renders, and the literal one sees every shipped string but cannot tell
56
+ which ones reach a human. The literal rule lives in `scripts/check-publish-hygiene.mjs`, strips
57
+ comments with a real tokenizer rather than a regex (the ~795 in comments stay out of scope, and a
58
+ regex cannot tell `//` in a URL from a comment), skips test files, and runs in CI. Confirmed to
59
+ fail on all four 1.0.1 strings and pass with them fixed.
60
+ - **A note on how the first check missed the modal bundle.** Grepping the built
61
+ `dist/modal/certrev-cert.js` for the character returned 0 on 1.0.1, and that was a false negative:
62
+ esbuild's default `--charset` is ascii, so it emits non-ASCII as a `\u` escape rather than as the
63
+ character. Ten occurrences were in there. Any future check of a minified artifact has to decode it
64
+ first; grepping the raw bytes reads as clean no matter what is in it.
65
+
8
66
  ## 1.0.1 · 2026-08-18
9
67
 
10
68
  Patch: copy only. No API change, no type change, no behaviour change beyond the five characters
package/README.md CHANGED
@@ -606,7 +606,7 @@ published at `@certrev/cert-block/fixtures`).
606
606
 
607
607
  ## Version
608
608
 
609
- `1.0.1`: see [CHANGELOG.md](./CHANGELOG.md) for the release history and the semver contract
609
+ `1.0.3`: 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.
@@ -28,8 +28,12 @@ export declare const CERT_COMPONENT_NAME = "CertREV Cert";
28
28
  export interface CertReviewCardProps extends Partial<BuilderCertChromeData> {
29
29
  /**
30
30
  * Placement face — the ENTRY/editor chooses this, NOT the exporter (not a wire-type field).
31
- * Absent ⇒ whatever `renderCertBlock` defaults to (`sidebar`); this component names no
32
- * fallback of its own, so there is exactly ONE place the default is written down.
31
+ *
32
+ * Absent ⇒ `banner`, via the compatibility bridge in the render body below. That is NOT the
33
+ * package default: a direct `renderCertBlock` caller omitting `mode` gets `sidebar`, and
34
+ * `sidebar` is the registered input's `defaultValue`, so blocks created in the editor carry
35
+ * an explicit `mode`. The bridge covers only entries exported before the exporter emitted
36
+ * `mode` at all, and is deleted once those are backfilled. See the render body for why.
33
37
  */
34
38
  mode?: CertBlockMode | null;
35
39
  /**
@@ -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,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"}
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;;;;;;;;OAQG;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,sCAoFxD;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"}
@@ -81,12 +81,28 @@ export function CertReviewCard(props) {
81
81
  }
82
82
  : undefined;
83
83
  const html = renderCertBlock({
84
- // NO fallback here, deliberately: `renderCertBlock` owns the ONE placement default
85
- // (absent or unrecognised ⇒ sidebar). This line used to say `?? 'banner'`, and it was
86
- // one of THREE layers each naming a default — here, the registered input's helper text,
87
- // and the wire type's own doc comment — while the renderer, the only place that actually
88
- // paints a face, named none. Three copies of a default is three chances to disagree.
89
- mode: props.mode ?? undefined,
84
+ // COMPATIBILITY BRIDGE, NOT THE DESIGN. Delete this fallback once the exporter emits an
85
+ // explicit `mode` and existing entries have been backfilled; until then it must stay.
86
+ //
87
+ // `renderCertBlock` owns the ONE placement default (absent or unrecognised ⇒ sidebar) and
88
+ // that decision STANDS: it is what a direct caller gets, and `sidebar` is the registered
89
+ // input's `defaultValue`, so every block created in the editor from now on carries an
90
+ // explicit `mode` and never reaches this line.
91
+ //
92
+ // The problem is the entries that already exist. `mode` is a PLACEMENT input, not one of
93
+ // the 19 wire keys (`BUILDER_CERT_CHROME_KEYS`), and the exporter emits only the wire
94
+ // projection (`options: { ...certData }`), so every entry exported to date has NO `mode`
95
+ // key at all. Through 0.5.5 this line read `?? 'banner'` and those entries rendered the
96
+ // banner face. Removing it in 1.0.0 did not give them the renderer's default in the
97
+ // abstract; it silently repainted live pages from banner to sidebar, with no entry edit
98
+ // and no version pin to protect them, because their install line is unpinned.
99
+ //
100
+ // So this is a bridge for pre-existing entries, NOT a reversal of the sidebar default.
101
+ // The three-copies-of-a-default problem that motivated removing it is still real and is
102
+ // still fixed: the helper text and the wire type's doc comment no longer name a default,
103
+ // and this line names one only for input the renderer can no longer distinguish from a
104
+ // deliberate choice. The exporter writing `mode` is what retires it.
105
+ mode: props.mode ?? 'banner',
90
106
  theme,
91
107
  // First-class banner memo split — renderCertBlock composes the requested
92
108
  // part directly; no string-splitting. Ignored outside banner mode, and an unrecognised
@@ -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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"}
@@ -67,7 +67,7 @@ export function CertRevEditorPlaceholder() {
67
67
  font: '13px system-ui',
68
68
  color: '#6b7280',
69
69
  background: '#fff5fa',
70
- }, children: ["CertREV Review \u2014 ", _jsx("b", { children: "resolves at publish" }), " from issuer verification. The expert badge appears here once this page\u2019s article is certified, and only while the credential is valid."] }));
70
+ }, children: ["CertREV Review: ", _jsx("b", { children: "resolves at publish" }), " from issuer verification. The expert badge appears here once this page\u2019s article is certified, and only while the credential is valid."] }));
71
71
  }
72
72
  /**
73
73
  * The Builder block. ZERO inputs — a layout anchor only. Renders the current page's VERIFIED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,uCAEgB,8CAA0B,oJAEtC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAIxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,iCAEe,8CAA0B,oJAErC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAIxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
@@ -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 — a preview must be time-bounded.');
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,+EAA+E,CAAC,CAAA;IACjG,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"}
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 \u2014 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 \u2014 \"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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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";
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 — reviewing-expert row */
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 — "Certifies the article" */
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 — meta row */
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 — signature + stamp footer */
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 — identity row */
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 — credential chips */
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 — bio */
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 — trust line (the disclosure footer below now owns the spacing to the actions) */
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 — material-connection cue (compensated only) + scope line
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 \u2014 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=`
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 \u2014 reviewing-expert row */
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 \u2014 "Certifies the article" */
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 \u2014 meta row */
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 \u2014 signature + stamp footer */
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 \u2014 identity row */
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 \u2014 credential chips */
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 \u2014 bio */
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 \u2014 trust line (the disclosure footer below now owns the spacing to the actions) */
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 \u2014 material-connection cue (compensated only) + scope line
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,CAmBnB"}
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
- reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline — ${url}`));
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.1",
3
+ "version": "1.0.3",
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",
@@ -33,8 +33,12 @@ export const CERT_COMPONENT_NAME = 'CertREV Cert'
33
33
  export interface CertReviewCardProps extends Partial<BuilderCertChromeData> {
34
34
  /**
35
35
  * Placement face — the ENTRY/editor chooses this, NOT the exporter (not a wire-type field).
36
- * Absent ⇒ whatever `renderCertBlock` defaults to (`sidebar`); this component names no
37
- * fallback of its own, so there is exactly ONE place the default is written down.
36
+ *
37
+ * Absent ⇒ `banner`, via the compatibility bridge in the render body below. That is NOT the
38
+ * package default: a direct `renderCertBlock` caller omitting `mode` gets `sidebar`, and
39
+ * `sidebar` is the registered input's `defaultValue`, so blocks created in the editor carry
40
+ * an explicit `mode`. The bridge covers only entries exported before the exporter emitted
41
+ * `mode` at all, and is deleted once those are backfilled. See the render body for why.
38
42
  */
39
43
  mode?: CertBlockMode | null
40
44
  /**
@@ -106,12 +110,28 @@ export function CertReviewCard(props: CertReviewCardProps) {
106
110
  : undefined
107
111
 
108
112
  const html = renderCertBlock({
109
- // NO fallback here, deliberately: `renderCertBlock` owns the ONE placement default
110
- // (absent or unrecognised ⇒ sidebar). This line used to say `?? 'banner'`, and it was
111
- // one of THREE layers each naming a default — here, the registered input's helper text,
112
- // and the wire type's own doc comment — while the renderer, the only place that actually
113
- // paints a face, named none. Three copies of a default is three chances to disagree.
114
- mode: props.mode ?? undefined,
113
+ // COMPATIBILITY BRIDGE, NOT THE DESIGN. Delete this fallback once the exporter emits an
114
+ // explicit `mode` and existing entries have been backfilled; until then it must stay.
115
+ //
116
+ // `renderCertBlock` owns the ONE placement default (absent or unrecognised ⇒ sidebar) and
117
+ // that decision STANDS: it is what a direct caller gets, and `sidebar` is the registered
118
+ // input's `defaultValue`, so every block created in the editor from now on carries an
119
+ // explicit `mode` and never reaches this line.
120
+ //
121
+ // The problem is the entries that already exist. `mode` is a PLACEMENT input, not one of
122
+ // the 19 wire keys (`BUILDER_CERT_CHROME_KEYS`), and the exporter emits only the wire
123
+ // projection (`options: { ...certData }`), so every entry exported to date has NO `mode`
124
+ // key at all. Through 0.5.5 this line read `?? 'banner'` and those entries rendered the
125
+ // banner face. Removing it in 1.0.0 did not give them the renderer's default in the
126
+ // abstract; it silently repainted live pages from banner to sidebar, with no entry edit
127
+ // and no version pin to protect them, because their install line is unpinned.
128
+ //
129
+ // So this is a bridge for pre-existing entries, NOT a reversal of the sidebar default.
130
+ // The three-copies-of-a-default problem that motivated removing it is still real and is
131
+ // still fixed: the helper text and the wire type's doc comment no longer name a default,
132
+ // and this line names one only for input the renderer can no longer distinguish from a
133
+ // deliberate choice. The exporter writing `mode` is what retires it.
134
+ mode: props.mode ?? 'banner',
115
135
  theme,
116
136
  // First-class banner memo split — renderCertBlock composes the requested
117
137
  // part directly; no string-splitting. Ignored outside banner mode, and an unrecognised
@@ -95,7 +95,7 @@ export function CertRevEditorPlaceholder() {
95
95
  background: '#fff5fa',
96
96
  }}
97
97
  >
98
- CertREV Review — <b>resolves at publish</b> from issuer verification. The expert badge appears here once this
98
+ CertREV Review: <b>resolves at publish</b> from issuer verification. The expert badge appears here once this
99
99
  page’s article is certified, and only while the credential is valid.
100
100
  </div>
101
101
  )
@@ -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 — a preview must be time-bounded.')
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 — reviewing-expert row */
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 — "Certifies the article" */
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 — meta row */
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 — signature + stamp footer */
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 — identity row */
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 — credential chips */
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 — bio */
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 — trust line (the disclosure footer below now owns the spacing to the actions) */
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 — material-connection cue (compensated only) + scope line
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
- reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline — ${url}`))
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