@certrev/cert-block 1.2.1 → 2.0.0

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/README.md +135 -8
  3. package/dist/builder/card-contract.d.ts +143 -0
  4. package/dist/builder/card-contract.d.ts.map +1 -0
  5. package/dist/builder/card-contract.js +262 -0
  6. package/dist/builder/card-contract.js.map +1 -0
  7. package/dist/builder/card-report.d.ts +37 -0
  8. package/dist/builder/card-report.d.ts.map +1 -0
  9. package/dist/builder/card-report.js +43 -0
  10. package/dist/builder/card-report.js.map +1 -0
  11. package/dist/builder/cert-review-card.d.ts +7 -5
  12. package/dist/builder/cert-review-card.d.ts.map +1 -1
  13. package/dist/builder/cert-review-card.js +227 -18
  14. package/dist/builder/cert-review-card.js.map +1 -1
  15. package/dist/builder/contract-card.d.ts +53 -0
  16. package/dist/builder/contract-card.d.ts.map +1 -0
  17. package/dist/builder/contract-card.js +141 -0
  18. package/dist/builder/contract-card.js.map +1 -0
  19. package/dist/builder/delivered-face.d.ts +83 -0
  20. package/dist/builder/delivered-face.d.ts.map +1 -0
  21. package/dist/builder/delivered-face.js +135 -0
  22. package/dist/builder/delivered-face.js.map +1 -0
  23. package/dist/builder/delivery-runtime.d.ts +15 -1
  24. package/dist/builder/delivery-runtime.d.ts.map +1 -1
  25. package/dist/builder/delivery-runtime.js +26 -1
  26. package/dist/builder/delivery-runtime.js.map +1 -1
  27. package/dist/builder/index.d.ts +7 -1
  28. package/dist/builder/index.d.ts.map +1 -1
  29. package/dist/builder/index.js +8 -1
  30. package/dist/builder/index.js.map +1 -1
  31. package/dist/builder/ssr-seed.d.ts +31 -1
  32. package/dist/builder/ssr-seed.d.ts.map +1 -1
  33. package/dist/builder/ssr-seed.js +38 -4
  34. package/dist/builder/ssr-seed.js.map +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/reader/reader-css.generated.d.ts +1 -1
  40. package/dist/reader/reader-css.generated.d.ts.map +1 -1
  41. package/dist/reader/reader-css.generated.js +1 -1
  42. package/dist/reader/reader-css.generated.js.map +1 -1
  43. package/dist/reader/render.d.ts.map +1 -1
  44. package/dist/reader/render.js +71 -34
  45. package/dist/reader/render.js.map +1 -1
  46. package/dist/reader/tokens.d.ts +2 -0
  47. package/dist/reader/tokens.d.ts.map +1 -1
  48. package/dist/reader/tokens.js +28 -6
  49. package/dist/reader/tokens.js.map +1 -1
  50. package/dist/verify/get-verified-delivery.d.ts +56 -0
  51. package/dist/verify/get-verified-delivery.d.ts.map +1 -1
  52. package/dist/verify/get-verified-delivery.js +49 -4
  53. package/dist/verify/get-verified-delivery.js.map +1 -1
  54. package/dist/verify/key-set.d.ts +67 -0
  55. package/dist/verify/key-set.d.ts.map +1 -0
  56. package/dist/verify/key-set.js +190 -0
  57. package/dist/verify/key-set.js.map +1 -0
  58. package/package.json +115 -112
  59. package/src/builder/card-contract.ts +314 -0
  60. package/src/builder/card-report.ts +61 -0
  61. package/src/builder/cert-review-card.tsx +332 -26
  62. package/src/builder/contract-card.tsx +159 -0
  63. package/src/builder/delivered-face.ts +156 -0
  64. package/src/builder/delivery-runtime.ts +32 -1
  65. package/src/builder/index.tsx +16 -1
  66. package/src/builder/ssr-seed.ts +56 -4
  67. package/src/index.ts +12 -0
  68. package/src/reader/reader-css.generated.ts +1 -1
  69. package/src/reader/reader.css +297 -2
  70. package/src/reader/render.ts +87 -38
  71. package/src/reader/tokens.ts +29 -6
  72. package/src/verify/get-verified-delivery.ts +85 -4
  73. package/src/verify/key-set.ts +225 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * The delivered face gate: when the Builder card may paint the server's markup (1.3.0)
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * The Delivery API may serve a server-rendered face (`face: { html, css }`) beside the signed artifact. That sibling
7
+ * is UNSIGNED. What binds it is the signed `payload.content.faceDigest` (cert-contract 0.6.0): the lowercase hex sha256
8
+ * of the markup's and the stylesheet's UTF-8 bytes, for one saved design version. The card paints the delivered face
9
+ * only when ALL of these hold:
10
+ *
11
+ * - the verdict is `render` (the caller has already checked that);
12
+ * - the card is not pinned (a design pin keeps 1.2.1's pinned face; the caller checks that);
13
+ * - the block shows the whole card: `part` is not `'header'` or `'memo'`, so a split banner never paints the face
14
+ * twice (`showsWholeCard`);
15
+ * - the verified content carries a digest it understands (`v: 1`, two 64-char lowercase hex digests, a numeric
16
+ * `defVersion`);
17
+ * - a well-formed face was served;
18
+ * - a def was served and its `v` IS the digest's `defVersion` (markup rendered for another design version is not
19
+ * this design);
20
+ * - sha256(html) and sha256(css) equal the signed values.
21
+ *
22
+ * In every other case the card paints exactly what 1.2.1 paints. Markup whose bytes do not hash to the signed digest
23
+ * is never painted, and a WebCrypto failure counts as a mismatch, never as a pass.
24
+ *
25
+ * Hashing uses WebCrypto (`crypto.subtle.digest`), the global the kernel verifies with, so it runs wherever the card
26
+ * already verifies (Node SSR, edge runtimes, a browser in a secure context). It is react-free; the card owns the
27
+ * suspension around the async digest.
28
+ */
29
+
30
+ import type { CertContent, CertFaceDigest } from '../contract/kernel.js'
31
+ import type { DeliveredFace } from '../verify/get-verified-delivery.js'
32
+ import type { CertChromeRenderDef } from './cert-chrome-data.js'
33
+
34
+ const SHA256_HEX = /^[0-9a-f]{64}$/
35
+
36
+ /**
37
+ * Whether a block shows the whole card, by `renderCertBlock`'s own rule: only `'header'` and `'memo'` split the
38
+ * banner; absent, `'full'`, null or anything unrecognised is the full card. A split block keeps its own part even
39
+ * when a delivered face matches, because the delivered face is a whole design.
40
+ */
41
+ export function showsWholeCard(part: unknown): boolean {
42
+ return part !== 'header' && part !== 'memo'
43
+ }
44
+
45
+ /** The signed digest, accepted only in the shape cert-contract 0.6.0 defines; anything else is null. */
46
+ export function acceptedFaceDigest(content: CertContent | null | undefined): CertFaceDigest | null {
47
+ const digest: unknown = (content as { faceDigest?: unknown } | null | undefined)?.faceDigest
48
+ if (typeof digest !== 'object' || digest === null || Array.isArray(digest)) return null
49
+ const { v, defVersion, html, css } = digest as Record<string, unknown>
50
+ if (v !== 1 || typeof defVersion !== 'number' || !Number.isFinite(defVersion)) return null
51
+ if (typeof html !== 'string' || !SHA256_HEX.test(html)) return null
52
+ if (typeof css !== 'string' || !SHA256_HEX.test(css)) return null
53
+ return { v: 1, defVersion, html, css }
54
+ }
55
+
56
+ /**
57
+ * The synchronous half of the gate: the digest to hash the face against, or null when the delivered face cannot
58
+ * paint whatever its bytes are (no digest, no face, no def, or a def of another version). The card hashes only when
59
+ * this is non-null, so a delivery without a face never suspends a second time.
60
+ */
61
+ export function deliveredFaceDigest(
62
+ content: CertContent | null | undefined,
63
+ renderDef: CertChromeRenderDef | null,
64
+ face: DeliveredFace | null | undefined,
65
+ ): CertFaceDigest | null {
66
+ if (!face) return null
67
+ const digest = acceptedFaceDigest(content)
68
+ if (!digest || !renderDef || renderDef.v !== digest.defVersion) return null
69
+ return digest
70
+ }
71
+
72
+ /** Lowercase hex sha256 of a string's UTF-8 bytes, via WebCrypto. */
73
+ export async function sha256HexUtf8(text: string): Promise<string> {
74
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text)))
75
+ let hex = ''
76
+ for (const byte of digest) hex += byte.toString(16).padStart(2, '0')
77
+ return hex
78
+ }
79
+
80
+ /** The two digests of a delivered face. */
81
+ export interface DeliveredFaceHashes {
82
+ readonly html: string
83
+ readonly css: string
84
+ }
85
+
86
+ /** Hash both halves. Never rejects: a WebCrypto failure (none in an insecure context) is null, which never matches. */
87
+ export async function hashDeliveredFace(face: DeliveredFace): Promise<DeliveredFaceHashes | null> {
88
+ try {
89
+ const [html, css] = await Promise.all([sha256HexUtf8(face.html), sha256HexUtf8(face.css)])
90
+ return { html, css }
91
+ } catch {
92
+ return null
93
+ }
94
+ }
95
+
96
+ /** The asynchronous half's verdict: both halves hash to the signed values. */
97
+ export function deliveredFaceMatches(hashes: DeliveredFaceHashes | null, digest: CertFaceDigest): boolean {
98
+ return hashes !== null && hashes.html === digest.html && hashes.css === digest.css
99
+ }
100
+
101
+ /** The markup the card paints for a matching face: the stylesheet first, then the face. */
102
+ export function deliveredFaceHtml(face: DeliveredFace): string {
103
+ return `<style>${face.css}</style>${face.html}`
104
+ }
105
+
106
+ /**
107
+ * Make room in a memo of pending-then-settled work: drop the oldest SETTLED entry once the memo is full, never a pending
108
+ * one. A render suspends on a pending entry and, when React retries it, must find that entry again; evicting it would make
109
+ * every retry start (and suspend on) a new one, so a page with more documents than the memo holds would never finish
110
+ * (break-it B1). With nothing settled to drop, the memo grows past its bound until entries settle.
111
+ */
112
+ export function evictOldestSettled<V extends { settled?: unknown }>(memo: Map<string, V>, max: number): void {
113
+ if (memo.size < max) return
114
+ for (const [key, entry] of memo) {
115
+ if (entry.settled !== undefined) {
116
+ memo.delete(key)
117
+ return
118
+ }
119
+ }
120
+ }
121
+
122
+ /** One face's hashing: the pending digest, and its answer once settled. */
123
+ export interface FaceHashMemo {
124
+ readonly pending: Promise<DeliveredFaceHashes | null>
125
+ settled?: { readonly hashes: DeliveredFaceHashes | null }
126
+ }
127
+
128
+ /** How many faces' hashes are kept (insertion order; the oldest goes first). A page or a brand has one or a few. */
129
+ const FACE_HASH_MEMO_MAX = 64
130
+ const faceHashMemo = new Map<string, FaceHashMemo>()
131
+
132
+ /**
133
+ * The memoized hashing of one face, keyed by its CONTENT (both strings), not by the object: the browser parses a seed
134
+ * from the page afresh on each render, and a server's cached read and a seed of the same face share one entry. A
135
+ * failure settles to null and stays settled (a context with no WebCrypto does not gain one).
136
+ */
137
+ export function faceHashesFor(face: DeliveredFace): FaceHashMemo {
138
+ const key = `${face.html.length}:${face.html}${face.css}`
139
+ const known = faceHashMemo.get(key)
140
+ if (known) return known
141
+ const fresh: FaceHashMemo = {
142
+ pending: hashDeliveredFace(face).then((hashes) => {
143
+ fresh.settled = { hashes }
144
+ return hashes
145
+ }),
146
+ }
147
+ // 2.0.0: never a pending entry (break-it B1: more faces than the memo holds made every retry suspend again).
148
+ evictOldestSettled(faceHashMemo, FACE_HASH_MEMO_MAX)
149
+ faceHashMemo.set(key, fresh)
150
+ return fresh
151
+ }
152
+
153
+ /** Test seam: forget every face's hashes (a new process). */
154
+ export function forgetFaceHashesForTests(): void {
155
+ faceHashMemo.clear()
156
+ }
@@ -13,11 +13,20 @@
13
13
  * read on a brand's server then misses at most once per placement, and a warm one never waits.
14
14
  * A revocation still lands on the first render after its refresh does. The tests' runtime gets
15
15
  * the same cache shape, so they exercise what production runs.
16
+ *
17
+ * 2.0.0: the trust root is a key SET (`verify/key-set.ts`). `resolveKid` is still the baked set (today
18
+ * `cert-issuer-1`), and the card verifies with `resolveKidFor(baseUrl)`: a baked kid resolves as before, any other kid
19
+ * makes one read of the delivery origin's published key set, accepted only when a baked kid signs it. A rotation then
20
+ * needs no reinstall. The card's (2.0) contract-1 document digests are memoized with the delivered faces' hashes.
16
21
  */
17
22
 
18
23
  import type { ResolvePublicKeyByKid } from '../contract/kernel.js'
19
24
  import { TtlCache } from '../verify/cache.js'
20
25
  import type { CachedDelivery } from '../verify/get-verified-delivery.js'
26
+ import { forgetKeySetsForTests, keySetResolver } from '../verify/key-set.js'
27
+ import { forgetCardDigestsForTests } from './card-contract.js'
28
+ import { forgetCardReportsForTests } from './card-report.js'
29
+ import { forgetFaceHashesForTests } from './delivered-face.js'
21
30
  import { CERT_ISSUER_KID, CERT_ISSUER_PUBLIC_KEY_PEM } from './issuer-key.js'
22
31
  import { forgetPageSeedsForTests } from './ssr-seed.js'
23
32
 
@@ -27,7 +36,13 @@ export const CARD_DELIVERY_TIMEOUT_MS = 8_000
27
36
  export const CARD_DELIVERY_STALE_MS = 300_000
28
37
 
29
38
  export interface CardDeliveryRuntime {
39
+ /** The baked kids: the package's own trust root (and the only keys that may sign a published key set). */
30
40
  readonly resolveKid: ResolvePublicKeyByKid
41
+ /**
42
+ * 2.0.0: what the card verifies a read from `baseUrl` with: the baked kids, then that origin's signed published set.
43
+ * Absent: derived from `resolveKid`, `fetchImpl` and `timeoutMs` (`keySetResolver`).
44
+ */
45
+ readonly resolveKidFor?: (baseUrl: string) => ResolvePublicKeyByKid
31
46
  readonly fetchImpl: () => typeof fetch
32
47
  readonly cache: TtlCache<CachedDelivery>
33
48
  readonly now: () => number
@@ -65,12 +80,28 @@ export function cardDeliveryRuntime(): CardDeliveryRuntime {
65
80
  return runtime
66
81
  }
67
82
 
83
+ /** 2.0.0: the resolver for a read from `baseUrl`: the runtime's own, else the baked kids plus the origin's key set. */
84
+ export function cardResolveKidFor(rt: CardDeliveryRuntime, baseUrl: string): ResolvePublicKeyByKid {
85
+ if (rt.resolveKidFor) return rt.resolveKidFor(baseUrl)
86
+ return keySetResolver({
87
+ baseUrl,
88
+ bakedResolveKid: rt.resolveKid,
89
+ fetchImpl: rt.fetchImpl(),
90
+ ...(rt.timeoutMs !== undefined ? { timeoutMs: rt.timeoutMs } : {}),
91
+ })
92
+ }
93
+
68
94
  /**
69
95
  * Test seam: replace parts of the runtime (a fresh cache unless one is given) and forget the page's envelope seeds
70
- * (a fresh runtime is a fresh page). Returns a restore.
96
+ * (a fresh runtime is a fresh page), (1.3.0) every delivered face's memoized hashes and (2.0.0) every card
97
+ * document's digest and every published key set. Returns a restore.
71
98
  */
72
99
  export function setCardDeliveryRuntimeForTests(overrides: Partial<CardDeliveryRuntime>): () => void {
73
100
  forgetPageSeedsForTests()
101
+ forgetFaceHashesForTests()
102
+ forgetCardDigestsForTests()
103
+ forgetKeySetsForTests()
104
+ forgetCardReportsForTests()
74
105
  const previous = runtime
75
106
  const now = overrides.now ?? previous.now
76
107
  runtime = {
@@ -27,7 +27,10 @@
27
27
  * for the entry it renders inside (`builderContext.content.id`) from the CertREV Delivery API on
28
28
  * `baseUrl` (an https certrev.com origin, nothing else), verifies it against the baked
29
29
  * `cert-issuer-1` key, re-judges revocation and expiry at every render, and paints the brand's
30
- * render def served beside it. A revoked, expired, unverifiable or unreachable cert, a marker that
30
+ * render def served beside it. 1.3.0: when the Delivery API also serves a server-rendered face
31
+ * (`face: { html, css }`, unsigned) and the verified envelope signs that face's sha256 digests for the
32
+ * def version served (`content.faceDigest`), an unpinned whole-card block paints that face instead;
33
+ * markup that does not hash to the signed digest is never painted. A revoked, expired, unverifiable or unreachable cert, a marker that
31
34
  * is not exactly that shape, or a missing entry id renders nothing. A block WITHOUT the marker
32
35
  * (every entry exported before 1.1.0) renders from its options exactly as 1.0.3 did, so the
33
36
  * paragraph above still describes it: an editor who deletes the marker gets the options face back,
@@ -152,6 +155,17 @@ export const certRevAnchorComponent: CertRevBuilderRegistration = {
152
155
  // 1.2.0-canary.5: the design pin, for CertREV's own canary replica (`<Content context={{ certrevDesignPin }}>`).
153
156
  export { type DesignPin, designPinFromUrl, parseDesignPin } from '../verify/get-verified-delivery.js'
154
157
 
158
+ // 2.0.0: the contract-1 card (CertREV's server-rendered card document), its fallback report and the Builder flow rule.
159
+ export {
160
+ acceptedCardBinding,
161
+ type CardBinding,
162
+ type CardDocumentV1,
163
+ type CardPartName,
164
+ cardDocumentDigest,
165
+ cardPartsFor,
166
+ parseCardDocument,
167
+ } from './card-contract.js'
168
+ export { CARD_FALLBACK_EVENT, type CardFallbackDetail, type CardFallbackReason } from './card-report.js'
155
169
  // ── The Builder.io PUSH kit (CertReviewCard) + the single-sourced wire type ──
156
170
  export {
157
171
  BUILDER_CERT_CHROME_KEYS,
@@ -173,6 +187,7 @@ export {
173
187
  PLACEMENT_INPUTS,
174
188
  WIRE_INPUTS,
175
189
  } from './cert-review-card.js'
190
+ export { BUILDER_CARD_FLOW_CSS } from './contract-card.js'
176
191
  export {
177
192
  CERT_FACE_FIELDS,
178
193
  credentialLabel,
@@ -48,13 +48,37 @@
48
48
  * server withheld. A record holds no payload, so `parseSeed` never takes one for a seed. A `'none'` record sits on a
49
49
  * `hidden` element with nothing in it.
50
50
  *
51
+ * A DELIVERED FACE RIDES THE SEED (1.3.0), and only when the server painted it: the seed then ends with
52
+ * `face: { html, css }`, the server-rendered markup and stylesheet the Delivery API served beside the envelope, whose
53
+ * hashes the signed `payload.content.faceDigest` binds. A browser repaints that face from the seed only after hashing
54
+ * it against the seed payload's digest itself (`cert-review-card.tsx`); a seed with no `face`, a malformed one, or one
55
+ * whose hashes fail paints what 1.2.1 paints for it. A seed with no face is byte for byte the 1.2.1 seed. The face is
56
+ * on the page twice (painted, and in the seed), which is its page-weight cost.
57
+ *
58
+ * A CONTRACT-1 CARD RIDES THE SEED (2.0.0), and only when the server printed it: the seed then ends with `card`, the
59
+ * card document exactly as the Delivery API served it, whose RFC 8785 digest the signed `payload.content.faceDigest`
60
+ * binds (`contract: 1`, `doc`). The browser prints that card from the seed only after it has checked it against the
61
+ * seed payload's binding itself (`cert-review-card.tsx`), so a client re-render (React's recovery from a hydration
62
+ * error, a streamed record, a later mount of the same placement) prints the server's card with no read. A seed with no
63
+ * card is the 1.3.1 seed byte for byte. The card is on the page twice (printed, and in the seed), as 1.3.0's face is.
64
+ *
65
+ * A FALLBACK RECORD MAY NAME ITS REASON (2.0.0): where a contract-1 placement prints nothing, its `'none'` record ends
66
+ * with `reason` (`card_missing`, `card_verify_failed`, `card_unsupported_contract`, `card_pin_mismatch`), the same
67
+ * reason the card reports. Every other record is the 1.3.1 record byte for byte, and 1.3.1 ignores the key.
68
+ *
51
69
  * React-free and browser-only in effect: with no `document` (a server) `pageSeedFor` finds nothing, so a server never
52
70
  * reads or keeps a seed and a multi-tenant render process holds nothing per page.
53
71
  */
54
72
 
55
73
  import { HIDEABLE_FIELDS } from '../components/render-def.js'
56
74
  import type { CertContent, CertPayload, CertSignature } from '../contract/kernel.js'
57
- import { type DesignPin, parseDesignPin, parseRenderDef } from '../verify/get-verified-delivery.js'
75
+ import {
76
+ type DeliveredFace,
77
+ type DesignPin,
78
+ parseDeliveredFace,
79
+ parseDesignPin,
80
+ parseRenderDef,
81
+ } from '../verify/get-verified-delivery.js'
58
82
  import type { CertChromeRenderDef } from './cert-chrome-data.js'
59
83
 
60
84
  export const SEED_ATTRIBUTE = 'data-certrev-envelope'
@@ -72,6 +96,16 @@ export interface DeliverySeed {
72
96
  */
73
97
  readonly signature?: CertSignature
74
98
  readonly renderDef: CertChromeRenderDef | null
99
+ /**
100
+ * 1.3.0: the delivered face the server painted (the file header); absent whenever it painted anything else. On a
101
+ * seed read from the page it is UNVERIFIED until hashed against `payload.content.faceDigest`.
102
+ */
103
+ readonly face?: DeliveredFace
104
+ /**
105
+ * 2.0.0: the contract-1 card document the server printed (the file header), exactly as served; absent whenever it
106
+ * printed anything else. On a seed read from the page it is UNVERIFIED until checked against the payload's binding.
107
+ */
108
+ readonly card?: Readonly<Record<string, unknown>>
75
109
  }
76
110
 
77
111
  /** A seed as read from the page: the parsed seed and the attribute's exact text (re-rendered verbatim on hydration). */
@@ -89,6 +123,8 @@ export interface DeliveryFallback {
89
123
  readonly externalId: string
90
124
  readonly design?: DesignPin
91
125
  readonly face: FallbackFace
126
+ /** 2.0.0: why a contract-1 placement printed nothing; absent on every other record. */
127
+ readonly reason?: string
92
128
  }
93
129
 
94
130
  /** A fallback record as read from the page, with the attribute's exact text. */
@@ -183,6 +219,10 @@ export function serializeSeed(seed: DeliverySeed): string {
183
219
  payload: seed.payload,
184
220
  ...(seed.signature ? { signature: seed.signature } : {}),
185
221
  renderDef: seed.renderDef,
222
+ // 1.3.0: last, and only with a delivered face, so a seed without one is the 1.2.1 bytes.
223
+ ...(seed.face ? { face: { html: seed.face.html, css: seed.face.css } } : {}),
224
+ // 2.0.0: last, and only with a printed contract-1 card, so a seed without one is the 1.3.1 bytes.
225
+ ...(seed.card ? { card: seed.card } : {}),
186
226
  })
187
227
  }
188
228
 
@@ -193,6 +233,7 @@ export function serializeFallback(fallback: DeliveryFallback): string {
193
233
  externalId: fallback.externalId,
194
234
  ...serializedPin(fallback.design),
195
235
  face: fallback.face,
236
+ ...(fallback.reason === undefined ? {} : { reason: fallback.reason }),
196
237
  })
197
238
  }
198
239
 
@@ -214,7 +255,7 @@ export function parseSeed(raw: string): DeliverySeed | null {
214
255
  return null
215
256
  }
216
257
  if (!isRecord(value) || value.v !== 1) return null
217
- const { baseUrl, externalId, design, payload, signature, renderDef } = value
258
+ const { baseUrl, externalId, design, payload, signature, renderDef, face, card } = value
218
259
  if (typeof baseUrl !== 'string' || typeof externalId !== 'string') return null
219
260
  if (!isRecord(payload) || !isRecord(payload.subject) || !isRecord(payload.lifecycle) || !isRecord(payload.content)) {
220
261
  return null
@@ -222,6 +263,8 @@ export function parseSeed(raw: string): DeliverySeed | null {
222
263
  // A pin the seed names is judged by the same grammar the card's own pin is; one that fails it is not a seed.
223
264
  const pin = parsedPin(design)
224
265
  if (pin === null) return null
266
+ // 1.3.0: a face that is not two strings is no face; the seed is still a seed (and paints as 1.2.1 paints it).
267
+ const delivered = parseDeliveredFace(face)
225
268
  return {
226
269
  baseUrl,
227
270
  externalId,
@@ -229,6 +272,9 @@ export function parseSeed(raw: string): DeliverySeed | null {
229
272
  payload: payload as unknown as CertPayload,
230
273
  ...(isRecord(signature) ? { signature: signature as unknown as CertSignature } : {}),
231
274
  renderDef: parseRenderDef(renderDef),
275
+ ...(delivered ? { face: delivered } : {}),
276
+ // 2.0.0: a card that is not a JSON object is no card (the seed paints as its binding says without one).
277
+ ...(isRecord(card) ? { card } : {}),
232
278
  }
233
279
  }
234
280
 
@@ -241,12 +287,18 @@ export function parseFallback(raw: string): DeliveryFallback | null {
241
287
  return null
242
288
  }
243
289
  if (!isRecord(value) || value.v !== 1 || 'payload' in value) return null
244
- const { baseUrl, externalId, design, face } = value
290
+ const { baseUrl, externalId, design, face, reason } = value
245
291
  if (typeof baseUrl !== 'string' || typeof externalId !== 'string') return null
246
292
  if (face !== 'options' && face !== 'none') return null
247
293
  const pin = parsedPin(design)
248
294
  if (pin === null) return null
249
- return { baseUrl, externalId, ...(pin ? { design: pin } : {}), face }
295
+ return {
296
+ baseUrl,
297
+ externalId,
298
+ ...(pin ? { design: pin } : {}),
299
+ face,
300
+ ...(typeof reason === 'string' ? { reason } : {}),
301
+ }
250
302
  }
251
303
 
252
304
  /** One placement's key: the origin, the entry and the pin (the read's own key, so a pin never shares a seed). */
package/src/index.ts CHANGED
@@ -106,6 +106,7 @@ export {
106
106
  type CertCredential,
107
107
  type CertDeliveryEnvelope,
108
108
  type CertDisplayConfig,
109
+ type CertFaceDigest,
109
110
  type CertLifecycle,
110
111
  type CertPayload,
111
112
  type CertSignature,
@@ -135,6 +136,7 @@ export {
135
136
  export { TtlCache, type TtlCacheOptions } from './verify/cache.js'
136
137
  export {
137
138
  type CachedDelivery,
139
+ type DeliveredFace,
138
140
  type DesignPin,
139
141
  deliveryCacheKey,
140
142
  deliveryUrl,
@@ -142,6 +144,7 @@ export {
142
144
  type GetVerifiedDeliveryOptions,
143
145
  getVerifiedDelivery,
144
146
  invalidateDelivery,
147
+ parseDeliveredFace,
145
148
  parseDesignPin,
146
149
  parseRenderDef,
147
150
  peekVerifiedDelivery,
@@ -157,6 +160,15 @@ export {
157
160
  invalidateVerdict,
158
161
  sharedVerdictCache,
159
162
  } from './verify/get-verified-envelope.js'
163
+ // 2.0.0: the issuer key SET (baked kids, then the origin's published set signed by a baked kid).
164
+ export {
165
+ type AcceptedKeySet,
166
+ KEY_SET_PATH,
167
+ type KeySetResolverOptions,
168
+ keySetResolver,
169
+ type PublishedKeySet,
170
+ verifyKeySet,
171
+ } from './verify/key-set.js'
160
172
  export {
161
173
  type FetchingKidResolverOptions,
162
174
  fetchingKidResolver,
@@ -2,4 +2,4 @@
2
2
  // biome-ignore-all lint: generated file
3
3
  // GENERATED by scripts/refresh-reader.mjs from src/reader/reader.css (the portal reader stylesheet, byte for
4
4
  // byte). Do not edit: run the script. reader-css.test.ts asserts this string equals the .css file.
5
- export const READER_CSS = "/* CERTREV-READER-GEN:BEGIN */\n/* GENERATED by scripts/build-cert-reader-css.mjs from src/lib/cert-render/reader/site-snapshot/ (certrev.com,\n * component Reader, \"What your reader sees\"). DO NOT EDIT: run `pnpm build:cert-reader-css`. The face is driven by\n * four variables the Liquid root sets inline: --cr-ba accent, --cr-ba-fg accent foreground, --cr-br corner, --cr-bf\n * font. */\n.certrev-reader {\n\tcolor: #141414 !important;\n\tfont-family: var(--cr-bf) !important;\n\tfont-size: 16px !important;\n\tline-height: 1.65 !important;\n\t-webkit-font-smoothing: antialiased !important;\n\ttext-rendering: optimizelegibility !important;\n\tfont-style: normal !important;\n\tfont-weight: 400 !important;\n\tfont-stretch: 101% !important;\n\tfont-variant: normal !important;\n\tfont-feature-settings: normal !important;\n\tfont-variation-settings: normal !important;\n\tfont-kerning: auto !important;\n\tfont-optical-sizing: auto !important;\n\tfont-size-adjust: none !important;\n\tletter-spacing: normal !important;\n\tword-spacing: normal !important;\n\ttext-transform: none !important;\n\ttext-align: start !important;\n\ttext-indent: 0 !important;\n\ttext-shadow: none !important;\n\twhite-space: normal !important;\n\tword-break: normal !important;\n\toverflow-wrap: normal !important;\n\thyphens: manual !important;\n}\n.certrev-reader,\n.certrev-reader *,\n.certrev-reader *:before,\n.certrev-reader *:after {\n\tbox-sizing: border-box;\n}\n.certrev-reader :is(div, section, aside, p, blockquote, span, a, b, strong, em, i, time, small) {\n\tcolor: inherit !important;\n\tfont-family: inherit !important;\n\tfont-size: inherit !important;\n\tfont-style: inherit !important;\n\tfont-weight: inherit !important;\n\tfont-stretch: inherit !important;\n\tletter-spacing: inherit !important;\n\tline-height: inherit !important;\n\ttext-transform: inherit !important;\n\tword-spacing: inherit !important;\n\ttext-align: inherit !important;\n\ttext-indent: inherit !important;\n}\n.certrev-reader :is(b, strong) {\n\tfont-weight: bolder !important;\n}\n.certrev-reader :is(em, i) {\n\tfont-style: italic !important;\n}\n.certrev-reader p {\n\tfont-family:\n\t\t\"DM Sans\", \"DM Sans Fallback\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif !important;\n}\n.certrev-reader .certrev-r-card {\n\tz-index: 1;\n\tborder: 1px solid #14141421;\n\tborder-radius: calc(var(--cr-br) * 0.7);\n\tbackground: #fff;\n\tflex-shrink: 0;\n\tmargin: 12px 0 0;\n\tposition: relative;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-card.pad {\n\tpadding: 15px 18px;\n\toverflow: visible;\n}\n.certrev-reader .certrev-r-card.pad .certrev-r-bys {\n\tgap: 14px;\n}\n.certrev-reader .certrev-r-cap {\n\twhite-space: nowrap;\n\tbackground: var(--cr-ba);\n\tcolor: var(--cr-ba-fg) !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\talign-items: center;\n\tgap: 11px;\n\tpadding: 14px 20px;\n\tfont-size: 10.5px !important;\n\tfont-weight: 700 !important;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-cap.center {\n\tletter-spacing: 0.16em !important;\n\tjustify-content: center;\n\tpadding: 12px;\n}\n.certrev-reader .certrev-r-cap img {\n\tflex-shrink: 0;\n\twidth: 18px;\n\theight: 18px;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-cap-body {\n\tpadding: 15px 18px 13px;\n}\n.certrev-reader .certrev-r-bys {\n\tgrid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);\n\tgap: 20px;\n\tdisplay: grid;\n}\n.certrev-reader .certrev-r-by {\n\talign-items: flex-start;\n\tgap: 12px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-by > div {\n\tmin-width: 0;\n}\n.certrev-reader .certrev-r-by b {\n\tfont-size: 14.5px !important;\n\tfont-weight: 700 !important;\n\tline-height: 1.3 !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-by em {\n\tcolor: #14141495 !important;\n\tmargin-top: 4px;\n\tfont-size: 12.5px !important;\n\tfont-style: normal !important;\n\tline-height: 1.45 !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-role {\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-top: 9px;\n\tfont-size: 9px !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-role.on {\n\tcolor: #141414 !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader .certrev-r-face {\n\tbackground: #14141412;\n\twidth: 42px;\n\theight: 42px;\n\tcolor: #141414 !important;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tdisplay: block;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-face.sm {\n\twidth: 36px;\n\theight: 36px;\n}\n.certrev-reader .certrev-r-face.lg {\n\twidth: 46px;\n\theight: 46px;\n}\n.certrev-reader .certrev-r-face svg {\n\tfill: currentColor;\n\topacity: 0.38;\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-rule {\n\tbackground: #14141421;\n\theight: 1px;\n\tmargin: 15px 0 12px;\n}\n.certrev-reader .certrev-r-foot {\n\tjustify-content: space-between;\n\talign-items: center;\n\tgap: 16px;\n\tfont-size: 12.5px !important;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-foot span {\n\twhite-space: nowrap;\n}\n.certrev-reader .certrev-r-foot em {\n\tfont-style: normal !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-right: 9px;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-foot span + span {\n\tcolor: #14141495 !important;\n}\n.certrev-reader .certrev-r-kick {\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.16em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-bottom: 14px;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-memo {\n\tgap: 15px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-memo > div {\n\tflex: 1;\n\tmin-width: 0;\n}\n.certrev-reader .certrev-r-memo-name {\n\tcolor: #14141495 !important;\n\tmargin-bottom: 11px;\n\tfont-size: 15px !important;\n}\n.certrev-reader .certrev-r-memo-name b {\n\tcolor: #141414 !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader blockquote {\n\tborder-left: 3px solid var(--cr-ba);\n\tcolor: #141414b8 !important;\n\ttext-wrap: pretty;\n\tmargin: 0;\n\tpadding-left: 16px;\n\tfont-size: 13.5px !important;\n\tfont-style: italic !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-links {\n\tgap: 22px;\n\tmargin-top: 14px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-links a {\n\tcolor: #141414 !important;\n\twhite-space: nowrap;\n\tfont-size: 13.5px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: none;\n}\n.certrev-reader .certrev-r-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-vfoot {\n\talign-items: baseline;\n\tgap: 16px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-vfoot .certrev-r-scope {\n\tflex: 1;\n\tmin-width: 0;\n\tmargin-top: 0;\n}\n.certrev-reader .certrev-r-verified {\n\tcolor: #141414b8 !important;\n\tflex-shrink: 0;\n\talign-items: center;\n\tgap: 9px;\n\tfont-size: 12.5px !important;\n\tdisplay: inline-flex;\n}\n.certrev-reader .certrev-r-verified img {\n\tflex-shrink: 0;\n\twidth: 17px;\n\theight: 17px;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-verified b {\n\tfont-family:\n\t\t\"Plus Jakarta Sans\", \"Plus Jakarta Sans Fallback\", -apple-system, system-ui, Helvetica, Arial, sans-serif !important;\n\tfont-weight: 800 !important;\n}\n.certrev-reader .certrev-r-scope {\n\tcolor: #14141495 !important;\n\ttext-wrap: pretty;\n\tmargin-top: 9px;\n\tfont-size: 12.5px !important;\n\tline-height: 1.5 !important;\n}\n.certrev-reader .certrev-r-scope em {\n\tfont-style: normal !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.1em !important;\n\ttext-transform: uppercase !important;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-side {\n\tz-index: 1;\n\tfloat: right;\n\tborder: 1px solid #14141421;\n\tborder-radius: var(--cr-br);\n\twidth: 268px;\n\tbox-shadow: 0 4px 18px #14141412;\n\tbackground: #fff;\n\tmargin: 2px 0 18px 28px;\n\tposition: relative;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-side-body {\n\tpadding: 18px;\n}\n.certrev-reader .certrev-r-side-body .certrev-r-by b {\n\tfont-size: 14px !important;\n}\n.certrev-reader .certrev-r-side-body p {\n\tcolor: #141414b8 !important;\n\ttext-wrap: pretty;\n\tmargin: 15px 0 0;\n\tfont-size: 13px !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-side-body blockquote {\n\tmargin-top: 15px;\n\tpadding-left: 15px;\n\tfont-size: 13.5px !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-side-body .certrev-r-scope {\n\tmargin-top: 15px;\n\tfont-size: 11.5px !important;\n}\n.certrev-reader .certrev-r-side-links {\n\tborder-top: 1px solid #14141421;\n\tgrid-template-columns: 1fr 1fr;\n\tdisplay: grid;\n}\n.certrev-reader .certrev-r-side-links a {\n\ttext-align: center !important;\n\tcolor: #141414 !important;\n\tpadding: 13px;\n\tfont-size: 13px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: none;\n}\n.certrev-reader .certrev-r-side-links a:first-child {\n\tborder-right: 1px solid #14141421;\n}\n.certrev-reader .certrev-r-side-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-pill {\n\tz-index: 3;\n\tbackground: var(--cr-ba);\n\tcolor: var(--cr-ba-fg) !important;\n\tborder-radius: 999px;\n\talign-items: center;\n\tgap: 13px;\n\tpadding: 12px 21px;\n\tdisplay: inline-flex;\n\tposition: absolute;\n\tbottom: 26px;\n\tright: 26px;\n\tbox-shadow: 0 10px 26px #14141429;\n}\n.certrev-reader .certrev-r-pill span {\n\twhite-space: nowrap;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tfont-size: 10.5px !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader .certrev-r-pill a {\n\tcolor: inherit !important;\n\ttext-underline-offset: 3px;\n\tfont-size: 13.5px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: underline;\n}\n.certrev-reader .certrev-r-pill i {\n\topacity: 0.42;\n\tfont-style: normal !important;\n}\n@media (max-width: 820px) {\n\t.certrev-reader .certrev-r-side {\n\t\tfloat: none;\n\t\twidth: auto;\n\t\tmargin: 20px 0;\n\t}\n\t.certrev-reader .certrev-r-bys {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 18px;\n\t}\n\t.certrev-reader .certrev-r-cap {\n\t\tletter-spacing: 0.1em !important;\n\t\tpadding: 12px 16px;\n\t\tfont-size: 9.5px !important;\n\t}\n\t.certrev-reader .certrev-r-cap-body,\n\t.certrev-reader .certrev-r-card.pad {\n\t\tpadding: 16px 18px;\n\t}\n\t.certrev-reader .certrev-r-pill {\n\t\tgap: 10px;\n\t\tpadding: 10px 16px;\n\t\tbottom: 20px;\n\t\tright: 20px;\n\t}\n}\n@media (max-width: 560px) {\n\t.certrev-reader .certrev-r-links {\n\t\tflex-direction: column;\n\t\tgap: 9px;\n\t}\n\t.certrev-reader .certrev-r-foot {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 6px;\n\t}\n\t.certrev-reader .certrev-r-foot span {\n\t\twhite-space: normal;\n\t}\n\t.certrev-reader .certrev-r-vfoot {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 10px;\n\t}\n\t.certrev-reader .certrev-r-vfoot .certrev-r-scope {\n\t\twidth: 100%;\n\t}\n\t.certrev-reader .certrev-r-cap {\n\t\twhite-space: normal;\n\t\tletter-spacing: 0.08em !important;\n\t\tpadding: 11px 14px;\n\t\tfont-size: 9px !important;\n\t}\n\t.certrev-reader .certrev-r-cap-body,\n\t.certrev-reader .certrev-r-card.pad {\n\t\tpadding: 14px 15px;\n\t}\n\t.certrev-reader .certrev-r-foot {\n\t\tflex-wrap: wrap;\n\t\tgap: 8px 14px;\n\t}\n\t.certrev-reader .certrev-r-links {\n\t\tflex-wrap: wrap;\n\t\tgap: 16px;\n\t}\n\t.certrev-reader .certrev-r-vfoot {\n\t\tflex-wrap: wrap;\n\t\tgap: 10px;\n\t}\n\t.certrev-reader .certrev-r-face.lg {\n\t\twidth: 38px;\n\t\theight: 38px;\n\t}\n\t.certrev-reader .certrev-r-side {\n\t\tmargin: 16px 0;\n\t}\n\t.certrev-reader .certrev-r-side-links a {\n\t\tpadding: 12px 8px;\n\t}\n\t.certrev-reader .certrev-r-pill {\n\t\ttext-align: center !important;\n\t\tflex-wrap: wrap;\n\t\tjustify-content: center;\n\t\tgap: 6px 12px;\n\t\tpadding: 11px 14px;\n\t\tbottom: 14px;\n\t\tleft: 14px;\n\t\tright: 14px;\n\t}\n\t.certrev-reader .certrev-r-pill span {\n\t\twhite-space: normal;\n\t}\n}\n/* Theme-bleed armor (hand-written, not from the snapshot; see build-cert-reader-css.mjs). */\n.certrev-reader img {\n\tmargin: 0 !important;\n\tmax-width: 100% !important;\n\tfloat: none !important;\n\tborder-radius: 0 !important;\n}\n.certrev-reader a {\n\tbackground-image: none !important;\n}\n.certrev-reader .certrev-r-links a,\n.certrev-reader .certrev-r-side-links a {\n\ttext-decoration: none !important;\n}\n.certrev-reader .certrev-r-links a:not(:hover),\n.certrev-reader .certrev-r-side-links a:not(:hover) {\n\tcolor: #141414 !important;\n}\n.certrev-reader .certrev-r-links a:hover,\n.certrev-reader .certrev-r-side-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-pill a {\n\tcolor: inherit !important;\n\ttext-decoration: underline !important;\n}\n.certrev-reader .certrev-r-face img {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n.certrev-reader .certrev-r-bys.solo {\n\tgrid-template-columns: minmax(0, 1fr);\n}\n.certrev-reader .certrev-r-pill {\n\tposition: fixed;\n\tmargin-bottom: env(safe-area-inset-bottom, 0px);\n}\n.certrev-memo-wrap[data-certrev-placement=\"rail\"] .certrev-reader .certrev-r-side {\n\tfloat: none;\n\twidth: auto;\n\tmargin: 0;\n}\n.certrev-memo-wrap[data-certrev-placement=\"body-fallback\"] .certrev-reader .certrev-r-side,\n.certrev-sidebar-wrap:not([data-certrev-placement]) .certrev-reader .certrev-r-side {\n\tfloat: none;\n\tmargin-right: auto;\n\tmargin-left: auto;\n}\n.certrev-reader .certrev-r-side-links.single {\n\tgrid-template-columns: minmax(0, 1fr);\n}\n.certrev-reader .certrev-r-side-links.single a:first-child {\n\tborder-right: 0;\n}\n:is(.certrev-card-wrap, .certrev-memo-wrap):has(.certrev-reader) {\n\tmargin-block: 0 !important;\n\tpadding: 0 !important;\n}\n/* Reader fonts (generated from site-snapshot/site-fonts.css.txt: certrev.com's own faces and metric-matched\n * fallbacks, the same declarations but for the file, the committed copy on assets.certrev.com, and the stretch, which\n * keeps them to reader text; see build-cert-reader-css.mjs). */\n@font-face {\n\tfont-family: \"DM Sans Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 94.9%;\n\tdescent-override: 29.66%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 104.53%;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Times New Roman);\n\tascent-override: 97.25%;\n\tdescent-override: 22.56%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 111.26%;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 75.79%;\n\tdescent-override: 22.29%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 134.59%;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 98.88%;\n\tdescent-override: 21.15%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 104.98%;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans\";\n\tfont-style: normal;\n\tfont-weight: 800;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jakarta-800-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans\";\n\tfont-style: normal;\n\tfont-weight: 800;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jakarta-800-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* CERTREV-READER-GEN:END */\n"
5
+ export const READER_CSS = "/* CERTREV-READER-GEN:BEGIN */\n/* GENERATED by scripts/build-cert-reader-css.mjs from src/lib/cert-render/reader/site-snapshot/ (certrev.com,\n * component Reader, \"What your reader sees\"). DO NOT EDIT: run `pnpm build:cert-reader-css`. The face is driven by\n * four variables the Liquid root sets inline: --cr-ba accent, --cr-ba-fg accent foreground, --cr-br corner, --cr-bf\n * font; a toned face (class certrev-r-toned) adds --cr-sf surface and --cr-ink ink. */\n.certrev-reader {\n\tcolor: #141414 !important;\n\tfont-family: var(--cr-bf) !important;\n\tfont-size: 16px !important;\n\tline-height: 1.65 !important;\n\t-webkit-font-smoothing: antialiased !important;\n\ttext-rendering: optimizelegibility !important;\n\tfont-style: normal !important;\n\tfont-weight: 400 !important;\n\tfont-stretch: 101% !important;\n\tfont-variant: normal !important;\n\tfont-feature-settings: normal !important;\n\tfont-variation-settings: normal !important;\n\tfont-kerning: auto !important;\n\tfont-optical-sizing: auto !important;\n\tfont-size-adjust: none !important;\n\tletter-spacing: normal !important;\n\tword-spacing: normal !important;\n\ttext-transform: none !important;\n\ttext-align: start !important;\n\ttext-indent: 0 !important;\n\ttext-shadow: none !important;\n\twhite-space: normal !important;\n\tword-break: normal !important;\n\toverflow-wrap: normal !important;\n\thyphens: manual !important;\n}\n.certrev-reader,\n.certrev-reader *,\n.certrev-reader *:before,\n.certrev-reader *:after {\n\tbox-sizing: border-box;\n}\n.certrev-reader :is(div, section, aside, details, summary, p, blockquote, span, a, b, strong, em, i, time, small) {\n\tcolor: inherit !important;\n\tfont-family: inherit !important;\n\tfont-size: inherit !important;\n\tfont-style: inherit !important;\n\tfont-weight: inherit !important;\n\tfont-stretch: inherit !important;\n\tletter-spacing: inherit !important;\n\tline-height: inherit !important;\n\ttext-transform: inherit !important;\n\tword-spacing: inherit !important;\n\ttext-align: inherit !important;\n\ttext-indent: inherit !important;\n}\n.certrev-reader :is(b, strong) {\n\tfont-weight: bolder !important;\n}\n.certrev-reader :is(em, i) {\n\tfont-style: italic !important;\n}\n.certrev-reader p {\n\tfont-family:\n\t\t\"DM Sans\", \"DM Sans Fallback\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif !important;\n}\n.certrev-reader .certrev-r-card {\n\tz-index: 1;\n\tborder: 1px solid #14141421;\n\tborder-radius: calc(var(--cr-br) * 0.7);\n\tbackground: #fff;\n\tflex-shrink: 0;\n\tmargin: 12px 0 0;\n\tposition: relative;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-card.pad {\n\tpadding: 15px 18px;\n\toverflow: visible;\n}\n.certrev-reader .certrev-r-card.pad .certrev-r-bys {\n\tgap: 14px;\n}\n.certrev-reader .certrev-r-cap {\n\twhite-space: nowrap;\n\tbackground: var(--cr-ba);\n\tcolor: var(--cr-ba-fg) !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\talign-items: center;\n\tgap: 11px;\n\tpadding: 14px 20px;\n\tfont-size: 10.5px !important;\n\tfont-weight: 700 !important;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-cap.center {\n\tletter-spacing: 0.16em !important;\n\tjustify-content: center;\n\tpadding: 12px;\n}\n.certrev-reader .certrev-r-cap img {\n\tflex-shrink: 0;\n\twidth: 18px;\n\theight: 18px;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-cap-body {\n\tpadding: 15px 18px 13px;\n}\n.certrev-reader .certrev-r-bys {\n\tgrid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);\n\tgap: 20px;\n\tdisplay: grid;\n}\n.certrev-reader .certrev-r-by {\n\talign-items: flex-start;\n\tgap: 12px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-by > div {\n\tmin-width: 0;\n}\n.certrev-reader .certrev-r-by b {\n\tfont-size: 14.5px !important;\n\tfont-weight: 700 !important;\n\tline-height: 1.3 !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-by em {\n\tcolor: #14141495 !important;\n\tmargin-top: 4px;\n\tfont-size: 12.5px !important;\n\tfont-style: normal !important;\n\tline-height: 1.45 !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-role {\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-top: 9px;\n\tfont-size: 9px !important;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-role.on {\n\tcolor: #141414 !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader .certrev-r-face {\n\tbackground: #14141412;\n\twidth: 42px;\n\theight: 42px;\n\tcolor: #141414 !important;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tdisplay: block;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-face.sm {\n\twidth: 36px;\n\theight: 36px;\n}\n.certrev-reader .certrev-r-face.lg {\n\twidth: 46px;\n\theight: 46px;\n}\n.certrev-reader .certrev-r-face svg {\n\tfill: currentColor;\n\topacity: 0.38;\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-rule {\n\tbackground: #14141421;\n\theight: 1px;\n\tmargin: 15px 0 12px;\n}\n.certrev-reader .certrev-r-foot {\n\tjustify-content: space-between;\n\talign-items: center;\n\tgap: 16px;\n\tfont-size: 12.5px !important;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-foot span {\n\twhite-space: nowrap;\n}\n.certrev-reader .certrev-r-foot em {\n\tfont-style: normal !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-right: 9px;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-foot span + span {\n\tcolor: #14141495 !important;\n}\n.certrev-reader .certrev-r-kick {\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.16em !important;\n\ttext-transform: uppercase !important;\n\tcolor: #14141495 !important;\n\tmargin-bottom: 14px;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-memo {\n\tgap: 15px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-memo > div {\n\tflex: 1;\n\tmin-width: 0;\n}\n.certrev-reader .certrev-r-memo-name {\n\tcolor: #14141495 !important;\n\tmargin-bottom: 11px;\n\tfont-size: 15px !important;\n}\n.certrev-reader .certrev-r-memo-name b {\n\tcolor: #141414 !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader blockquote {\n\tborder-left: 3px solid var(--cr-ba);\n\tcolor: #141414b8 !important;\n\ttext-wrap: pretty;\n\tmargin: 0;\n\tpadding-left: 16px;\n\tfont-size: 13.5px !important;\n\tfont-style: italic !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-links {\n\tgap: 22px;\n\tmargin-top: 14px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-links a {\n\tcolor: #141414 !important;\n\twhite-space: nowrap;\n\tfont-size: 13.5px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: none;\n}\n.certrev-reader .certrev-r-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-vfoot {\n\talign-items: baseline;\n\tgap: 16px;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-vfoot .certrev-r-scope {\n\tflex: 1;\n\tmin-width: 0;\n\tmargin-top: 0;\n}\n.certrev-reader .certrev-r-verified {\n\tcolor: #141414b8 !important;\n\tflex-shrink: 0;\n\talign-items: center;\n\tgap: 9px;\n\tfont-size: 12.5px !important;\n\tdisplay: inline-flex;\n}\n.certrev-reader .certrev-r-verified img {\n\tflex-shrink: 0;\n\twidth: 17px;\n\theight: 17px;\n\tdisplay: block;\n}\n.certrev-reader .certrev-r-verified b {\n\tfont-family:\n\t\t\"Plus Jakarta Sans\", \"Plus Jakarta Sans Fallback\", -apple-system, system-ui, Helvetica, Arial, sans-serif !important;\n\tfont-weight: 800 !important;\n}\n.certrev-reader .certrev-r-scope {\n\tcolor: #14141495 !important;\n\ttext-wrap: pretty;\n\tmargin-top: 9px;\n\tfont-size: 12.5px !important;\n\tline-height: 1.5 !important;\n}\n.certrev-reader .certrev-r-scope em {\n\tfont-style: normal !important;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.1em !important;\n\ttext-transform: uppercase !important;\n\tfont-size: 9.5px !important;\n}\n.certrev-reader .certrev-r-side {\n\tz-index: 1;\n\tfloat: right;\n\tborder: 1px solid #14141421;\n\tborder-radius: var(--cr-br);\n\twidth: 268px;\n\tbox-shadow: 0 4px 18px #14141412;\n\tbackground: #fff;\n\tmargin: 2px 0 18px 28px;\n\tposition: relative;\n\toverflow: hidden;\n}\n.certrev-reader .certrev-r-side-body {\n\tpadding: 18px;\n}\n.certrev-reader .certrev-r-side-body .certrev-r-by b {\n\tfont-size: 14px !important;\n}\n.certrev-reader .certrev-r-side-body p {\n\tcolor: #141414b8 !important;\n\ttext-wrap: pretty;\n\tmargin: 15px 0 0;\n\tfont-size: 13px !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-side-body blockquote {\n\tmargin-top: 15px;\n\tpadding-left: 15px;\n\tfont-size: 13.5px !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-side-body .certrev-r-scope {\n\tmargin-top: 15px;\n\tfont-size: 11.5px !important;\n}\n.certrev-reader .certrev-r-side-links {\n\tborder-top: 1px solid #14141421;\n\tgrid-template-columns: 1fr 1fr;\n\tdisplay: grid;\n}\n.certrev-reader .certrev-r-side-links a {\n\ttext-align: center !important;\n\tcolor: #141414 !important;\n\tpadding: 13px;\n\tfont-size: 13px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: none;\n}\n.certrev-reader .certrev-r-side-links a:first-child {\n\tborder-right: 1px solid #14141421;\n}\n.certrev-reader .certrev-r-side-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-pill {\n\tz-index: 3;\n\tbackground: var(--cr-ba);\n\tcolor: var(--cr-ba-fg) !important;\n\tborder-radius: 999px;\n\talign-items: center;\n\tgap: 13px;\n\tpadding: 12px 21px;\n\tdisplay: inline-flex;\n\tposition: absolute;\n\tbottom: 26px;\n\tright: 26px;\n\tbox-shadow: 0 10px 26px #14141429;\n}\n.certrev-reader .certrev-r-pill span {\n\twhite-space: nowrap;\n\tfont-family: \"JetBrains Mono\", \"JetBrains Mono Fallback\", ui-monospace, SFMono-Regular, Menlo, monospace !important;\n\tletter-spacing: 0.14em !important;\n\ttext-transform: uppercase !important;\n\tfont-size: 10.5px !important;\n\tfont-weight: 700 !important;\n}\n.certrev-reader .certrev-r-pill a {\n\tcolor: inherit !important;\n\ttext-underline-offset: 3px;\n\tfont-size: 13.5px !important;\n\tfont-weight: 700 !important;\n\ttext-decoration: underline;\n}\n.certrev-reader .certrev-r-pill i {\n\topacity: 0.42;\n\tfont-style: normal !important;\n}\n@media (max-width: 820px) {\n\t.certrev-reader .certrev-r-side {\n\t\tfloat: none;\n\t\twidth: auto;\n\t\tmargin: 20px 0;\n\t}\n\t.certrev-reader .certrev-r-bys {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 18px;\n\t}\n\t.certrev-reader .certrev-r-cap {\n\t\tletter-spacing: 0.1em !important;\n\t\tpadding: 12px 16px;\n\t\tfont-size: 9.5px !important;\n\t}\n\t.certrev-reader .certrev-r-cap-body,\n\t.certrev-reader .certrev-r-card.pad {\n\t\tpadding: 16px 18px;\n\t}\n\t.certrev-reader .certrev-r-pill {\n\t\tgap: 10px;\n\t\tpadding: 10px 16px;\n\t\tbottom: 20px;\n\t\tright: 20px;\n\t}\n}\n@media (max-width: 560px) {\n\t.certrev-reader .certrev-r-links {\n\t\tflex-direction: column;\n\t\tgap: 9px;\n\t}\n\t.certrev-reader .certrev-r-foot {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 6px;\n\t}\n\t.certrev-reader .certrev-r-foot span {\n\t\twhite-space: normal;\n\t}\n\t.certrev-reader .certrev-r-vfoot {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 10px;\n\t}\n\t.certrev-reader .certrev-r-vfoot .certrev-r-scope {\n\t\twidth: 100%;\n\t}\n\t.certrev-reader .certrev-r-cap {\n\t\twhite-space: normal;\n\t\tletter-spacing: 0.08em !important;\n\t\tpadding: 11px 14px;\n\t\tfont-size: 9px !important;\n\t}\n\t.certrev-reader .certrev-r-cap-body,\n\t.certrev-reader .certrev-r-card.pad {\n\t\tpadding: 14px 15px;\n\t}\n\t.certrev-reader .certrev-r-foot {\n\t\tflex-wrap: wrap;\n\t\tgap: 8px 14px;\n\t}\n\t.certrev-reader .certrev-r-links {\n\t\tflex-wrap: wrap;\n\t\tgap: 16px;\n\t}\n\t.certrev-reader .certrev-r-vfoot {\n\t\tflex-wrap: wrap;\n\t\tgap: 10px;\n\t}\n\t.certrev-reader .certrev-r-face.lg {\n\t\twidth: 38px;\n\t\theight: 38px;\n\t}\n\t.certrev-reader .certrev-r-side {\n\t\tmargin: 16px 0;\n\t}\n\t.certrev-reader .certrev-r-side-links a {\n\t\tpadding: 12px 8px;\n\t}\n\t.certrev-reader .certrev-r-pill {\n\t\ttext-align: center !important;\n\t\tflex-wrap: wrap;\n\t\tjustify-content: center;\n\t\tgap: 6px 12px;\n\t\tpadding: 11px 14px;\n\t\tbottom: 14px;\n\t\tleft: 14px;\n\t\tright: 14px;\n\t}\n\t.certrev-reader .certrev-r-pill span {\n\t\twhite-space: normal;\n\t}\n}\n/* Theme-bleed armor (hand-written, not from the snapshot; see build-cert-reader-css.mjs). */\n.certrev-reader img {\n\tmargin: 0 !important;\n\tmax-width: 100% !important;\n\tfloat: none !important;\n\tborder-radius: 0 !important;\n}\n.certrev-reader a {\n\tbackground-image: none !important;\n}\n.certrev-reader .certrev-r-links a,\n.certrev-reader .certrev-r-side-links a {\n\ttext-decoration: none !important;\n}\n.certrev-reader .certrev-r-links a:not(:hover),\n.certrev-reader .certrev-r-side-links a:not(:hover) {\n\tcolor: #141414 !important;\n}\n.certrev-reader .certrev-r-links a:hover,\n.certrev-reader .certrev-r-side-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader .certrev-r-pill a {\n\tcolor: inherit !important;\n\ttext-decoration: underline !important;\n}\n.certrev-reader .certrev-r-face img {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n.certrev-reader .certrev-r-bys.solo {\n\tgrid-template-columns: minmax(0, 1fr);\n}\n/* Card wrap (owner request 2026-10-12): a date never splits and the credential line breaks only between its parts. Selectors the snapshot does not carry. */\n.certrev-reader .certrev-r-by > div b,\n.certrev-reader .certrev-r-by > div em {\n\toverflow-wrap: break-word;\n}\n.certrev-reader .certrev-r-by > div em > span:first-child {\n\tmax-width: 100%;\n\tdisplay: inline-block;\n}\n.certrev-reader .certrev-r-by .certrev-r-nw {\n\twhite-space: nowrap;\n}\n/* Balanced card (owner request 2026-10-12, \"split evenly between the author and expert ... balanced symmetry\"): the\n two bylines are equal 50/50 columns (the snapshot's 0.82fr/1.18fr tilted the card to the expert), carry the same\n 42px face, type scale and rhythm, and their role labels (\"Written by\" / \"Reviewed by\") sit on one shared baseline\n at the foot of each column in one style (the snapshot inked only \"Reviewed by\"). Under 821px the columns stack\n with the snapshot's single 18px gap between them, so both entries get identical spacing. */\n@media (min-width: 821px) {\n\t.certrev-reader .certrev-r-bys {\n\t\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\t\tgap: 24px;\n\t}\n}\n.certrev-reader .certrev-r-bys .certrev-r-by > div {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-self: stretch;\n}\n.certrev-reader .certrev-r-bys .certrev-r-role {\n\tcolor: #141414 !important;\n\tfont-weight: 700 !important;\n\tmargin-top: auto;\n\tpadding-top: 9px;\n}\n/* The certified date and the compensation cue sit on one row under both columns, on a shared baseline, left-justified\n like the rest of the card (Owen 2026-10-17, on a live client's pictures: \"where it says certified is weirdly center aligned\n instead of left justified\"; the 2026-10-12 balanced card had centred it). */\n.certrev-reader .certrev-r-card .certrev-r-foot {\n\tjustify-content: flex-start;\n\talign-items: baseline;\n\tgap: 6px 28px;\n\ttext-align: left;\n\tflex-wrap: wrap;\n\tdisplay: flex;\n}\n.certrev-reader .certrev-r-card .certrev-r-foot span {\n\twhite-space: nowrap;\n}\n/* The hairline above the footer is an empty div: a theme that hides every empty div (a live client theme does, and a scoped one such as .rte div:empty would out-specify a plain rule, so it is important and carries the card and the element)\n removes it, and the footer then sits tight under the role labels. */\n.certrev-reader .certrev-r-card div.certrev-r-rule {\n\tdisplay: block !important;\n}\n.certrev-reader .certrev-r-card .certrev-r-foot span:empty {\n\tdisplay: none;\n}\n.certrev-reader .certrev-r-card .certrev-r-bys + .certrev-r-foot {\n\tborder-top: 1px solid #14141421;\n\tmargin-top: 15px;\n\tpadding-top: 12px;\n}\n@media (max-width: 560px) {\n\t.certrev-reader .certrev-r-card .certrev-r-foot {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 6px;\n\t}\n}\n/* Memo (owner request 2026-10-13, a live client's iPhone capture): a theme rule such as .rte blockquote centres the quote, pads 80px above it,\n adds block margins and paints a large mask-image quote glyph (::before); none of that is the face's. The quote is\n pinned left with no block padding or margin and no pseudo-element glyph (important: the theme's selector can be as\n specific as ours). On a phone (560px and under) the avatar and name sit on one row and the quote spans the full card\n width beneath them (the inner div becomes display: contents so the name, quote and links are grid items of the\n memo); the vertical rule is dropped, the two links share one row, and every item starts at the card's own edge, the\n same edge as \"Verified by CertREV\" and the disclaimer. One row gap sets the vertical rhythm. No JS, no clamp. */\n.certrev-reader .certrev-r-memo blockquote {\n\ttext-align: left !important;\n\tmargin-block: 0 !important;\n\tpadding-block: 0 !important;\n}\n.certrev-reader .certrev-r-memo blockquote::before,\n.certrev-reader .certrev-r-memo blockquote::after {\n\tcontent: none !important;\n\tdisplay: none !important;\n}\n@media (max-width: 560px) {\n\t.certrev-reader .certrev-r-memo {\n\t\tgrid-template-columns: auto minmax(0, 1fr);\n\t\talign-items: center;\n\t\tgap: 12px;\n\t\tdisplay: grid;\n\t}\n\t.certrev-reader .certrev-r-memo > div {\n\t\tdisplay: contents;\n\t}\n\t.certrev-reader .certrev-r-memo .certrev-r-memo-name {\n\t\tmargin: 0;\n\t}\n\t.certrev-reader .certrev-r-memo blockquote {\n\t\tborder-left: 0 !important;\n\t\tgrid-column: 1 / -1;\n\t\tpadding-left: 0 !important;\n\t}\n\t.certrev-reader .certrev-r-memo .certrev-r-links {\n\t\tflex-flow: row wrap;\n\t\tgrid-column: 1 / -1;\n\t\tgap: 6px 20px;\n\t\tmargin-top: 0;\n\t}\n}\n.certrev-reader .certrev-r-pill {\n\tposition: fixed;\n\tmargin-bottom: env(safe-area-inset-bottom, 0px);\n}\n.certrev-memo-wrap[data-certrev-placement=\"rail\"] .certrev-reader .certrev-r-side {\n\tfloat: none;\n\twidth: auto;\n\tmargin: 0;\n}\n.certrev-memo-wrap[data-certrev-placement=\"body-fallback\"] .certrev-reader .certrev-r-side,\n.certrev-sidebar-wrap:not([data-certrev-placement]) .certrev-reader .certrev-r-side {\n\tfloat: none;\n\tmargin-right: auto;\n\tmargin-left: auto;\n}\n.certrev-reader .certrev-r-side-links.single {\n\tgrid-template-columns: minmax(0, 1fr);\n}\n.certrev-reader .certrev-r-side-links.single a:first-child {\n\tborder-right: 0;\n}\n/* Collapsible on phones (owner decision 3d0838a4): wider than 640px the card's own bio and memo, at 640px and under the\n same pair in a closed details. */\n.certrev-reader .certrev-r-more {\n\tdisplay: none !important;\n}\n@media (max-width: 640px) {\n\t.certrev-reader .certrev-r-side-body > p,\n\t.certrev-reader .certrev-r-side-body > blockquote {\n\t\tdisplay: none !important;\n\t}\n\t.certrev-reader .certrev-r-more {\n\t\tdisplay: block !important;\n\t\tmargin: 0 !important;\n\t\tpadding: 0 !important;\n\t\tborder: 0 !important;\n\t\tborder-radius: 0 !important;\n\t\tbackground: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: auto !important;\n\t\tmax-height: none !important;\n\t\toverflow: visible !important;\n\t\topacity: 1 !important;\n\t\tvisibility: visible !important;\n\t\ttransform: none !important;\n\t}\n}\n.certrev-reader .certrev-r-more > summary {\n\tdisplay: flex !important;\n\talign-items: center;\n\tgap: 8px;\n\tmin-height: 44px;\n\tmargin: 3px 0 -10px !important;\n\tpadding: 12px 0 !important;\n\tborder: 0 !important;\n\tbackground: none !important;\n\tlist-style: none !important;\n\tcursor: pointer;\n\tcolor: #141414 !important;\n\tfont-size: 13px !important;\n\tfont-weight: 700 !important;\n\tline-height: 1.55 !important;\n}\n.certrev-reader .certrev-r-more > summary::-webkit-details-marker {\n\tdisplay: none;\n}\n.certrev-reader .certrev-r-more > summary::before {\n\tcontent: none !important;\n\tdisplay: none !important;\n}\n.certrev-reader .certrev-r-more > summary::after {\n\tcontent: \"\" !important;\n\tdisplay: block !important;\n\tposition: static !important;\n\tfloat: none !important;\n\tflex: none;\n\twidth: 6px !important;\n\theight: 6px !important;\n\tmargin: -3px 0 0 !important;\n\tborder: solid currentColor !important;\n\tborder-width: 0 2px 2px 0 !important;\n\tbackground: none !important;\n\ttransform: rotate(45deg) !important;\n}\n.certrev-reader .certrev-r-more[open] > summary::after {\n\tmargin-top: 3px !important;\n\ttransform: rotate(-135deg) !important;\n}\n.certrev-reader .certrev-r-more > summary:focus-visible {\n\toutline: 2px solid currentColor !important;\n\toutline-offset: 2px;\n}\n.certrev-reader .certrev-r-more::details-content {\n\ttransition: none !important;\n\tanimation: none !important;\n}\n.certrev-reader .certrev-r-more[open]::details-content {\n\tdisplay: block !important;\n\tcontent-visibility: visible !important;\n\tblock-size: auto !important;\n\theight: auto !important;\n\tmax-height: none !important;\n\toverflow: visible !important;\n\topacity: 1 !important;\n\tvisibility: visible !important;\n\ttransform: none !important;\n}\n:is(.certrev-card-wrap, .certrev-memo-wrap):has(.certrev-reader) {\n\tmargin-block: 0 !important;\n\tpadding: 0 !important;\n}\n/* Surface and ink (generated from the rules above, nothing hand-copied: a TONED face, whose root carries\n * .certrev-r-toned, reads the white panel as --cr-sf and the #141414 ink as --cr-ink; an untoned face matches none of\n * these rules; see build-cert-reader-css.mjs). */\n.certrev-reader.certrev-r-toned {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-card {\n\tborder: 1px solid color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n\tbackground: var(--cr-sf, #fff);\n}\n.certrev-reader.certrev-r-toned .certrev-r-by em {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-role {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-role.on {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-face {\n\tbackground: color-mix(in srgb, var(--cr-ink, #141414) 7.06%, transparent);\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-rule {\n\tbackground: color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n}\n.certrev-reader.certrev-r-toned .certrev-r-foot em {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-foot span + span {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-kick {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-memo-name {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-memo-name b {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned blockquote {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 72.16%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-links a {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-verified {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 72.16%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-scope {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 58.43%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-side {\n\tborder: 1px solid color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n\tbox-shadow: 0 4px 18px color-mix(in srgb, var(--cr-ink, #141414) 7.06%, transparent);\n\tbackground: var(--cr-sf, #fff);\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-body p {\n\tcolor: color-mix(in srgb, var(--cr-ink, #141414) 72.16%, transparent) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-links {\n\tborder-top: 1px solid color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-links a {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-links a:first-child {\n\tborder-right: 1px solid color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n}\n.certrev-reader.certrev-r-toned .certrev-r-pill {\n\tbox-shadow: 0 10px 26px color-mix(in srgb, var(--cr-ink, #141414) 16.08%, transparent);\n}\n.certrev-reader.certrev-r-toned .certrev-r-links a:not(:hover),\n.certrev-reader.certrev-r-toned .certrev-r-side-links a:not(:hover) {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-bys .certrev-r-role {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-card .certrev-r-bys + .certrev-r-foot {\n\tborder-top: 1px solid color-mix(in srgb, var(--cr-ink, #141414) 12.94%, transparent);\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-links.single a:first-child {\n\tborder-right: 0;\n}\n.certrev-reader.certrev-r-toned .certrev-r-more > summary {\n\tcolor: var(--cr-ink, #141414) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-side-links a:hover {\n\tcolor: var(--cr-ba) !important;\n}\n.certrev-reader.certrev-r-toned .certrev-r-more > summary:focus-visible {\n\toutline: 2px solid currentColor !important;\n\toutline-offset: 2px;\n}\n/* Reader fonts (generated from site-snapshot/site-fonts.css.txt: certrev.com's own faces and metric-matched\n * fallbacks, the same declarations but for the file, the committed copy on assets.certrev.com, and the stretch, which\n * keeps them to reader text; see build-cert-reader-css.mjs). */\n@font-face {\n\tfont-family: \"DM Sans Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 94.9%;\n\tdescent-override: 29.66%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 104.53%;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Times New Roman);\n\tascent-override: 97.25%;\n\tdescent-override: 22.56%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 111.26%;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 75.79%;\n\tdescent-override: 22.29%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 134.59%;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans Fallback\";\n\tfont-stretch: 101%;\n\tsrc: local(Arial);\n\tascent-override: 98.88%;\n\tdescent-override: 21.15%;\n\tline-gap-override: 0.0%;\n\tsize-adjust: 104.98%;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"DM Sans\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-dm-sans-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: italic;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-italic-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 600;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Playfair Display\";\n\tfont-style: normal;\n\tfont-weight: 900;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-playfair-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 500;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"JetBrains Mono\";\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jetbrains-mono-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans\";\n\tfont-style: normal;\n\tfont-weight: 800;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jakarta-800-latin-ext.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,\n\t\tU+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n@font-face {\n\tfont-family: \"Plus Jakarta Sans\";\n\tfont-style: normal;\n\tfont-weight: 800;\n\tfont-display: swap;\n\tfont-stretch: 101%;\n\tsrc: url(\"https://assets.certrev.com/fonts/cert-reader-jakarta-800-latin.woff2\") format(\"woff2\");\n\tunicode-range:\n\t\tU+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191,\n\t\tU+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* CERTREV-READER-GEN:END */\n"