@certrev/cert-block 0.3.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +48 -1
  2. package/dist/builder/cert-chrome-data.d.ts +129 -0
  3. package/dist/builder/cert-chrome-data.d.ts.map +1 -0
  4. package/dist/builder/cert-chrome-data.js +59 -0
  5. package/dist/builder/cert-chrome-data.js.map +1 -0
  6. package/dist/builder/cert-review-card.d.ts +68 -0
  7. package/dist/builder/cert-review-card.d.ts.map +1 -0
  8. package/dist/builder/cert-review-card.js +129 -0
  9. package/dist/builder/cert-review-card.js.map +1 -0
  10. package/dist/builder/index.d.ts +2 -0
  11. package/dist/builder/index.d.ts.map +1 -1
  12. package/dist/builder/index.js +3 -0
  13. package/dist/builder/index.js.map +1 -1
  14. package/dist/components/preview-block.d.ts +43 -0
  15. package/dist/components/preview-block.d.ts.map +1 -0
  16. package/dist/components/preview-block.js +53 -0
  17. package/dist/components/preview-block.js.map +1 -0
  18. package/dist/components/render-cert-block.d.ts +50 -7
  19. package/dist/components/render-cert-block.d.ts.map +1 -1
  20. package/dist/components/render-cert-block.js +304 -80
  21. package/dist/components/render-cert-block.js.map +1 -1
  22. package/dist/components/render-def.d.ts +27 -0
  23. package/dist/components/render-def.d.ts.map +1 -1
  24. package/dist/components/render-def.js +38 -0
  25. package/dist/components/render-def.js.map +1 -1
  26. package/dist/index.d.ts +3 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/modal/avatar-resize.d.ts +60 -0
  31. package/dist/modal/avatar-resize.d.ts.map +1 -0
  32. package/dist/modal/avatar-resize.js +91 -0
  33. package/dist/modal/avatar-resize.js.map +1 -0
  34. package/dist/modal/cert-modal-view.d.ts +74 -0
  35. package/dist/modal/cert-modal-view.d.ts.map +1 -0
  36. package/dist/modal/cert-modal-view.js +160 -0
  37. package/dist/modal/cert-modal-view.js.map +1 -0
  38. package/dist/modal/certrev-cert-modal.d.ts +72 -0
  39. package/dist/modal/certrev-cert-modal.d.ts.map +1 -0
  40. package/dist/modal/certrev-cert-modal.js +270 -0
  41. package/dist/modal/certrev-cert-modal.js.map +1 -0
  42. package/dist/modal/certrev-cert-modal.styles.d.ts +35 -0
  43. package/dist/modal/certrev-cert-modal.styles.d.ts.map +1 -0
  44. package/dist/modal/certrev-cert-modal.styles.js +488 -0
  45. package/dist/modal/certrev-cert-modal.styles.js.map +1 -0
  46. package/dist/modal/certrev-cert.entry.d.ts +19 -0
  47. package/dist/modal/certrev-cert.entry.d.ts.map +1 -0
  48. package/dist/modal/certrev-cert.entry.js +39 -0
  49. package/dist/modal/certrev-cert.entry.js.map +1 -0
  50. package/dist/modal/certrev-cert.js +557 -0
  51. package/dist/modal/display-strings.d.ts +22 -0
  52. package/dist/modal/display-strings.d.ts.map +1 -0
  53. package/dist/modal/display-strings.js +35 -0
  54. package/dist/modal/display-strings.js.map +1 -0
  55. package/dist/modal/escape.d.ts +13 -0
  56. package/dist/modal/escape.d.ts.map +1 -0
  57. package/dist/modal/escape.js +20 -0
  58. package/dist/modal/escape.js.map +1 -0
  59. package/dist/modal/font-face.d.ts +4 -0
  60. package/dist/modal/font-face.d.ts.map +1 -0
  61. package/dist/modal/font-face.js +137 -0
  62. package/dist/modal/font-face.js.map +1 -0
  63. package/dist/modal/ftc-disclosure.d.ts +12 -0
  64. package/dist/modal/ftc-disclosure.d.ts.map +1 -0
  65. package/dist/modal/ftc-disclosure.js +12 -0
  66. package/dist/modal/ftc-disclosure.js.map +1 -0
  67. package/dist/modal/ftc-guard.d.ts +50 -0
  68. package/dist/modal/ftc-guard.d.ts.map +1 -0
  69. package/dist/modal/ftc-guard.js +117 -0
  70. package/dist/modal/ftc-guard.js.map +1 -0
  71. package/dist/modal/index.d.ts +20 -0
  72. package/dist/modal/index.d.ts.map +1 -0
  73. package/dist/modal/index.js +25 -0
  74. package/dist/modal/index.js.map +1 -0
  75. package/dist/modal/interactions.d.ts +53 -0
  76. package/dist/modal/interactions.d.ts.map +1 -0
  77. package/dist/modal/interactions.js +147 -0
  78. package/dist/modal/interactions.js.map +1 -0
  79. package/dist/modal/logo-paths.d.ts +15 -0
  80. package/dist/modal/logo-paths.d.ts.map +1 -0
  81. package/dist/modal/logo-paths.js +24 -0
  82. package/dist/modal/logo-paths.js.map +1 -0
  83. package/dist/modal/placement.d.ts +95 -0
  84. package/dist/modal/placement.d.ts.map +1 -0
  85. package/dist/modal/placement.js +302 -0
  86. package/dist/modal/placement.js.map +1 -0
  87. package/dist/modal/revalidate.d.ts +48 -0
  88. package/dist/modal/revalidate.d.ts.map +1 -0
  89. package/dist/modal/revalidate.js +65 -0
  90. package/dist/modal/revalidate.js.map +1 -0
  91. package/dist/modal/verify-client.d.ts +24 -0
  92. package/dist/modal/verify-client.d.ts.map +1 -0
  93. package/dist/modal/verify-client.js +38 -0
  94. package/dist/modal/verify-client.js.map +1 -0
  95. package/package.json +14 -4
  96. package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +10 -6
  97. package/src/__tests__/preview-block.test.ts +84 -0
  98. package/src/__tests__/render-cert-block.test.ts +380 -5
  99. package/src/builder/__tests__/builder.test.tsx +71 -0
  100. package/src/builder/cert-chrome-data.ts +159 -0
  101. package/src/builder/cert-review-card.tsx +177 -0
  102. package/src/builder/index.tsx +18 -0
  103. package/src/components/preview-block.ts +74 -0
  104. package/src/components/render-cert-block.ts +387 -102
  105. package/src/components/render-def.ts +55 -0
  106. package/src/index.ts +9 -0
  107. package/src/modal/avatar-resize.ts +99 -0
  108. package/src/modal/cert-modal-view.test.ts +175 -0
  109. package/src/modal/cert-modal-view.ts +210 -0
  110. package/src/modal/certrev-cert-modal.styles.ts +494 -0
  111. package/src/modal/certrev-cert-modal.test.ts +125 -0
  112. package/src/modal/certrev-cert-modal.ts +273 -0
  113. package/src/modal/certrev-cert.entry.ts +44 -0
  114. package/src/modal/display-strings.ts +38 -0
  115. package/src/modal/escape.ts +20 -0
  116. package/src/modal/font-face.ts +138 -0
  117. package/src/modal/ftc-disclosure.ts +13 -0
  118. package/src/modal/ftc-guard.test.ts +153 -0
  119. package/src/modal/ftc-guard.ts +119 -0
  120. package/src/modal/index.ts +34 -0
  121. package/src/modal/interactions.test.ts +250 -0
  122. package/src/modal/interactions.ts +166 -0
  123. package/src/modal/logo-paths.ts +23 -0
  124. package/src/modal/placement.test.ts +398 -0
  125. package/src/modal/placement.ts +331 -0
  126. package/src/modal/revalidate.test.ts +111 -0
  127. package/src/modal/revalidate.ts +81 -0
  128. package/src/modal/verify-client.test.ts +164 -0
  129. package/src/modal/verify-client.ts +42 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Cert-card INTERACTIONS for the native Shopify cert experience.
3
+ *
4
+ * Progressive enhancement, delegated from the document (one listener, survives the placement
5
+ * reposition). Two behaviours (POR-9827 chevron model):
6
+ *
7
+ * • Accordion bios — a contributor row carrying `[data-certrev-acc]` toggles its `.open` class
8
+ * (revealing the row's `.acc-bio` in place; the name caret rotates via CSS) and mirrors the
9
+ * state on `aria-expanded`. A click on a link inside the row (e.g. the "Compensated expert"
10
+ * cue) is NOT a toggle. Keyboard: Enter/Space on the role=button row.
11
+ * • In-page modals: a `[data-certrev-modal-open]` control opens the unified
12
+ * `<certrev-cert-modal>` element (POR-10102) via its `.open('cert'|'expert')` method. Bare /
13
+ * `"cert"` opens the certificate dialog; `"expert"` opens the reviewer dialog. With no JS / no
14
+ * element / no envelope a link control falls back to its href (the certificate or the CertREV
15
+ * profile page). The element owns its OWN close plumbing (× / Close / backdrop / Esc) inside its
16
+ * shadow root — a shadow click is retargeted to the host, so a document delegate can't see it.
17
+ *
18
+ * No crypto, no network — pure DOM. Idempotent install (guarded so a double-load can't double-bind).
19
+ */
20
+ import { CERT_MODAL_TAG } from './certrev-cert-modal.js';
21
+ export const ACC_ATTR = 'data-certrev-acc';
22
+ export const MODAL_OPEN_ATTR = 'data-certrev-modal-open';
23
+ export const INITIALS_ATTR = 'data-certrev-initials';
24
+ export const CARD_SELECTOR = '.certrev-card';
25
+ /** Avatar base classes whose `--initials` variant is the SSR no-photo fallback (kept in lockstep
26
+ * with certrev-badge.liquid / certrev-memo.liquid). A broken <img> is swapped to `{base} {base}--initials`. */
27
+ const AVATAR_BASE_CLASSES = ['avatar', 'certrev-memo__avatar'];
28
+ /** Documents that already have the delegated handlers, so a double script-load can't double-bind. */
29
+ const installedDocs = new WeakSet();
30
+ /** Toggle a contributor row's accordion bio open/closed (the `.open` class drives the CSS reveal +
31
+ * caret rotation) and mirror the state on aria-expanded. */
32
+ export function toggleAccordion(row) {
33
+ const open = row.classList.toggle('open');
34
+ row.setAttribute('aria-expanded', open ? 'true' : 'false');
35
+ }
36
+ /** Resolve a `data-certrev-modal-open` value to the dialog it opens. `"expert"` opens the reviewer
37
+ * modal; anything else (the bare attribute, or `"cert"`) opens the certificate modal. */
38
+ export function modalTargetKind(value) {
39
+ return value === 'expert' ? 'expert' : 'cert';
40
+ }
41
+ /** Open a cert dialog via the `<certrev-cert-modal>` element. Returns false (so the caller lets an
42
+ * href fall back) when the element is absent or declines (no envelope + no fetch source). */
43
+ export function openModal(doc, kind) {
44
+ const el = doc.querySelector(CERT_MODAL_TAG);
45
+ if (el && typeof el.open === 'function')
46
+ return el.open(kind);
47
+ return false;
48
+ }
49
+ /** Open the page's certificate modal (backward-compatible wrapper over openModal). */
50
+ export function openCertModal(doc) {
51
+ return openModal(doc, 'cert');
52
+ }
53
+ function asElement(target) {
54
+ return target && target.closest ? target : null;
55
+ }
56
+ /**
57
+ * Replace a broken avatar `<img>` with its initials placeholder — the SAME markup the Liquid emits
58
+ * when no photo URL is present. The SSR can't know a present URL will 404 (the canary's expert photo
59
+ * does), so without this a broken-image icon shows instead of the clean initials circle.
60
+ */
61
+ export function swapBrokenAvatar(img) {
62
+ const initials = img.getAttribute(INITIALS_ATTR);
63
+ if (initials == null)
64
+ return;
65
+ const base = AVATAR_BASE_CLASSES.find((c) => img.classList.contains(c));
66
+ if (!base)
67
+ return;
68
+ const span = img.ownerDocument.createElement('span');
69
+ span.className = `${base} ${base}--initials`;
70
+ span.setAttribute('aria-hidden', 'true');
71
+ span.textContent = initials;
72
+ img.replaceWith(span);
73
+ }
74
+ /**
75
+ * Swap every avatar that has ALREADY failed to load. A 404 fires `error` during parse — often
76
+ * before this bundle runs — and `error` doesn't re-fire, so the live `error` listener alone misses
77
+ * pre-init failures (observed on the canary). Safe to call repeatedly; only acts on broken imgs.
78
+ */
79
+ export function sweepBrokenAvatars(doc) {
80
+ for (const img of Array.from(doc.querySelectorAll(`img[${INITIALS_ATTR}]`))) {
81
+ if (img.complete && img.naturalWidth === 0)
82
+ swapBrokenAvatar(img);
83
+ }
84
+ }
85
+ /** Install the delegated click + keydown handlers on the document, once. */
86
+ export function initCertInteractions(doc) {
87
+ if (installedDocs.has(doc))
88
+ return;
89
+ installedDocs.add(doc);
90
+ doc.addEventListener('click', (e) => {
91
+ const el = asElement(e.target);
92
+ if (!el)
93
+ return;
94
+ // Modal open — intercept only when the element actually shows the dialog; else let the link navigate.
95
+ // (Close is owned by the <certrev-cert-modal> element inside its shadow root.)
96
+ const opener = el.closest(`[${MODAL_OPEN_ATTR}]`);
97
+ if (opener) {
98
+ if (openModal(doc, modalTargetKind(opener.getAttribute(MODAL_OPEN_ATTR))))
99
+ e.preventDefault();
100
+ return;
101
+ }
102
+ // Accordion — but never when a link inside the row was clicked (it has its own action).
103
+ const row = el.closest(`[${ACC_ATTR}]`);
104
+ if (row && !el.closest('a, button'))
105
+ toggleAccordion(row);
106
+ });
107
+ // Broken avatar photos → initials placeholder. `error` events don't bubble, so listen in the
108
+ // CAPTURE phase. One-shot per img (replaceWith removes it, so it can't re-fire).
109
+ doc.addEventListener('error', (e) => {
110
+ const t = e.target;
111
+ if (t && t.tagName === 'IMG' && t.hasAttribute(INITIALS_ATTR))
112
+ swapBrokenAvatar(t);
113
+ }, true);
114
+ // Catch avatars that already failed before this listener attached (see sweepBrokenAvatars).
115
+ sweepBrokenAvatars(doc);
116
+ // Keyboard parity: Enter/Space activates a role=button accordion row or a modal-open row (a <div>
117
+ // fires no native click on Enter). Native <a>/<button> triggers handle their own activation
118
+ // (their click reaches the click handler above), so skip those here to avoid a double-fire.
119
+ doc.addEventListener('keydown', (e) => {
120
+ if (e.key !== 'Enter' && e.key !== ' ' && e.key !== 'Spacebar')
121
+ return;
122
+ const el = asElement(e.target);
123
+ if (!el || el.closest('a, button'))
124
+ return;
125
+ const opener = el.closest(`[${MODAL_OPEN_ATTR}]`);
126
+ if (opener) {
127
+ if (openModal(doc, modalTargetKind(opener.getAttribute(MODAL_OPEN_ATTR)))) {
128
+ e.preventDefault();
129
+ return;
130
+ }
131
+ }
132
+ const row = el.closest(`[${ACC_ATTR}]`);
133
+ if (row) {
134
+ e.preventDefault();
135
+ toggleAccordion(row);
136
+ }
137
+ });
138
+ }
139
+ /** Run interactions setup once the DOM is parsed. */
140
+ export function initCertInteractionsOnReady(doc) {
141
+ const run = () => initCertInteractions(doc);
142
+ if (doc.readyState === 'loading')
143
+ doc.addEventListener('DOMContentLoaded', run, { once: true });
144
+ else
145
+ run();
146
+ }
147
+ //# sourceMappingURL=interactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.js","sourceRoot":"","sources":["../../src/modal/interactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG,kBAAkB,CAAA;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAA;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,uBAAuB,CAAA;AACpD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAA;AAU5C;gHACgH;AAChH,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAU,CAAA;AAEvE,qGAAqG;AACrG,MAAM,aAAa,GAAG,IAAI,OAAO,EAAY,CAAA;AAE7C;6DAC6D;AAC7D,MAAM,UAAU,eAAe,CAAC,GAAY;IAC3C,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,GAAG,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAC3D,CAAC;AAED;0FAC0F;AAC1F,MAAM,UAAU,eAAe,CAAC,KAAoB;IACnD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAA;AAC9C,CAAC;AAED;8FAC8F;AAC9F,MAAM,UAAU,SAAS,CAAC,GAAa,EAAE,IAAe;IACvD,MAAM,EAAE,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAA4B,CAAA;IACvE,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,KAAK,CAAA;AACb,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,GAAa;IAC1C,OAAO,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED,SAAS,SAAS,CAAC,MAA0B;IAC5C,OAAO,MAAM,IAAK,MAAkB,CAAC,OAAO,CAAC,CAAC,CAAE,MAAkB,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;IAChD,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAM;IAC5B,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IACvE,IAAI,CAAC,IAAI;QAAE,OAAM;IACjB,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,SAAS,GAAG,GAAG,IAAI,IAAI,IAAI,YAAY,CAAA;IAC5C,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAA;IAC3B,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAa;IAC/C,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAmB,OAAO,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC;QAC/F,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,YAAY,KAAK,CAAC;YAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAClE,CAAC;AACF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,GAAa;IACjD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAM;IAClC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEtB,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACnC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE;YAAE,OAAM;QAEf,sGAAsG;QACtG,+EAA+E;QAC/E,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,eAAe,GAAG,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,SAAS,CAAC,GAAG,EAAE,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC;gBAAE,CAAC,CAAC,cAAc,EAAE,CAAA;YAC7F,OAAM;QACP,CAAC;QAED,wFAAwF;QACxF,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAA;QACvC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,eAAe,CAAC,GAAG,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,6FAA6F;IAC7F,iFAAiF;IACjF,GAAG,CAAC,gBAAgB,CACnB,OAAO,EACP,CAAC,CAAC,EAAE,EAAE;QACL,MAAM,CAAC,GAAG,CAAC,CAAC,MAAwB,CAAA;QACpC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC;YAAE,gBAAgB,CAAC,CAAqB,CAAC,CAAA;IACvG,CAAC,EACD,IAAI,CACJ,CAAA;IAED,4FAA4F;IAC5F,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAEvB,kGAAkG;IAClG,4FAA4F;IAC5F,4FAA4F;IAC5F,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QACrC,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,UAAU;YAAE,OAAM;QACtE,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAM;QAE1C,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,eAAe,GAAG,CAAC,CAAA;QACjD,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,SAAS,CAAC,GAAG,EAAE,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3E,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,OAAM;YACP,CAAC;QACF,CAAC;QAED,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAA;QACvC,IAAI,GAAG,EAAE,CAAC;YACT,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,eAAe,CAAC,GAAG,CAAC,CAAA;QACrB,CAAC;IACF,CAAC,CAAC,CAAA;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,2BAA2B,CAAC,GAAa;IACxD,MAAM,GAAG,GAAG,GAAS,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAA;IACjD,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS;QAAE,GAAG,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;;QAC1F,GAAG,EAAE,CAAA;AACX,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The CertREV 4-chevron logo mark — the canonical SVG path data (the four `d`
3
+ * attributes, drawn in a `0 0 375 375` viewBox). This is the SINGLE SOURCE for
4
+ * the mark across every cert-render surface (the standalone /embed renderers,
5
+ * the <certrev-cert-modal> web component, the Shopify Liquid snippets, the
6
+ * WordPress PHP renderer, and @certrev/cert-block).
7
+ *
8
+ * The paths were previously hand-copied into ~13 files; each of those should
9
+ * import THIS instead of re-declaring them. The codegen
10
+ * (`scripts/build-cert-design-artifacts.ts`) stamps these into
11
+ * `generated/design-tokens.json` + the WordPress `class-certrev-design-tokens.php`
12
+ * so the Liquid/PHP twins can never drift from this array.
13
+ */
14
+ export declare const LOGO_PATHS: readonly ["M67.59 221.77L138.77 292.95C145.8 299.98 145.8 311.44 138.77 318.47L123.02 334.22C116 341.25 104.54 341.25 97.5 334.22L26.32 263.04C19.29 256.01 19.29 244.55 26.32 237.52L42.07 221.77C49.1 214.74 60.56 214.74 67.59 221.77Z", "M96.79 335.08L81.12 319.42C73.8 312.1 73.8 300.23 81.12 292.92L315.88 58.16C323.2 50.85 335.06 50.85 342.38 58.16L358.04 73.83C365.36 81.14 365.36 93.01 358.04 100.33L123.29 335.08C115.97 342.4 104.1 342.4 96.79 335.08Z", "M203.5 335.14L187.83 319.48C180.52 312.16 180.52 300.29 187.83 292.98L315.88 164.93C323.2 157.62 335.06 157.62 342.38 164.93L358.04 180.6C365.36 187.92 365.36 199.78 358.04 207.1L230 335.14C222.68 342.46 210.82 342.46 203.5 335.14Z", "M310.21 335.2L294.54 319.54C287.23 312.22 287.23 300.36 294.54 293.04L315.88 271.71C323.2 264.39 335.06 264.39 342.38 271.71L358.04 287.37C365.36 294.69 365.36 306.55 358.04 313.87L336.71 335.2C329.39 342.52 317.53 342.52 310.21 335.2Z"];
15
+ //# sourceMappingURL=logo-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo-paths.d.ts","sourceRoot":"","sources":["../../src/modal/logo-paths.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,s6BAKb,CAAC"}
@@ -0,0 +1,24 @@
1
+ // ⚠ VENDORED, BYTE-FAITHFUL copy of portal `src/lib/design-guide/logo.ts` `LOGO_PATHS`
2
+ // (POR-10721 W2). Byte-identical to `../components/render-cert-block.ts`'s private copy;
3
+ // kept modal-local so the browser bundle never pulls in the SSR renderer. Unifying the
4
+ // package's two copies onto one shared leaf module is a post-publish follow-up.
5
+ /**
6
+ * The CertREV 4-chevron logo mark — the canonical SVG path data (the four `d`
7
+ * attributes, drawn in a `0 0 375 375` viewBox). This is the SINGLE SOURCE for
8
+ * the mark across every cert-render surface (the standalone /embed renderers,
9
+ * the <certrev-cert-modal> web component, the Shopify Liquid snippets, the
10
+ * WordPress PHP renderer, and @certrev/cert-block).
11
+ *
12
+ * The paths were previously hand-copied into ~13 files; each of those should
13
+ * import THIS instead of re-declaring them. The codegen
14
+ * (`scripts/build-cert-design-artifacts.ts`) stamps these into
15
+ * `generated/design-tokens.json` + the WordPress `class-certrev-design-tokens.php`
16
+ * so the Liquid/PHP twins can never drift from this array.
17
+ */
18
+ export const LOGO_PATHS = [
19
+ "M67.59 221.77L138.77 292.95C145.8 299.98 145.8 311.44 138.77 318.47L123.02 334.22C116 341.25 104.54 341.25 97.5 334.22L26.32 263.04C19.29 256.01 19.29 244.55 26.32 237.52L42.07 221.77C49.1 214.74 60.56 214.74 67.59 221.77Z",
20
+ "M96.79 335.08L81.12 319.42C73.8 312.1 73.8 300.23 81.12 292.92L315.88 58.16C323.2 50.85 335.06 50.85 342.38 58.16L358.04 73.83C365.36 81.14 365.36 93.01 358.04 100.33L123.29 335.08C115.97 342.4 104.1 342.4 96.79 335.08Z",
21
+ "M203.5 335.14L187.83 319.48C180.52 312.16 180.52 300.29 187.83 292.98L315.88 164.93C323.2 157.62 335.06 157.62 342.38 164.93L358.04 180.6C365.36 187.92 365.36 199.78 358.04 207.1L230 335.14C222.68 342.46 210.82 342.46 203.5 335.14Z",
22
+ "M310.21 335.2L294.54 319.54C287.23 312.22 287.23 300.36 294.54 293.04L315.88 271.71C323.2 264.39 335.06 264.39 342.38 271.71L358.04 287.37C365.36 294.69 365.36 306.55 358.04 313.87L336.71 335.2C329.39 342.52 317.53 342.52 310.21 335.2Z",
23
+ ];
24
+ //# sourceMappingURL=logo-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo-paths.js","sourceRoot":"","sources":["../../src/modal/logo-paths.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,yFAAyF;AACzF,uFAAuF;AACvF,gFAAgF;AAChF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACzB,gOAAgO;IAChO,6NAA6N;IAC7N,yOAAyO;IACzO,6OAA6O;CACpO,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Cert-card PLACEMENT for the single `target: body` CertREV app embed.
3
+ *
4
+ * The embed renders globally just before `</body>` — the only theme-app-extension primitive that
5
+ * reliably renders on modern themes (Horizon and others forbid app blocks inside the blog-post
6
+ * content section, so an in-content app block isn't available). On DOM-ready this module, ONCE:
7
+ *
8
+ * (a) Relocates the @graph `<script data-certrev-graph>` into `<head>`. JSON-LD is inert, so the
9
+ * move is invisible + reliable: JS-executing crawlers (Google + most AI fetchers) read it
10
+ * head-side, while non-JS fetchers still read the server-rendered copy that shipped in
11
+ * `<body>`. The AEO payload is never gambled on body-only.
12
+ * (b) Repositions the TWO cert wrappers to mirror the legacy iframe layout (iframe→native parity),
13
+ * BOTH anchored off the html-generator's `#references` list so they land in the SAME article:
14
+ * • `.certrev-card-wrap` (`data-certrev-anchor="top"`) → the TOP of the article content
15
+ * that OWNS `#references` (the contributors banner sat at the top of the legacy `body_html`).
16
+ * • `.certrev-memo-wrap` (`data-certrev-anchor="bottom"`) → the BOTTOM, just before the
17
+ * references list if present (where the legacy inline memo sat), else after the article.
18
+ * When no references exist, each falls back to a chrome-aware semantic-landmark / theme-class
19
+ * scan, with a STYLED end-of-article fallback when nothing matches.
20
+ * (c) Records the matched tier on each wrap as `data-certrev-placement` — the observability the
21
+ * pre-launch theme-matrix sweep + the POR-9578 liveness check read, so a silent misplacement
22
+ * becomes an inspectable signal rather than a surprise on a real brand's store.
23
+ *
24
+ * It runs BEFORE the badge re-verify pass and never moves a wrap twice, so the `<certrev-badge>`
25
+ * element is never detached/re-attached mid-verify (no duplicate Delivery-API calls).
26
+ */
27
+ /** Anchor tiers for the BOTTOM (memo) wrap, best→worst — landmarks first (stable across themes),
28
+ * theme classes last, above the styled body-bottom fallback. The memo is appended AFTER the matched
29
+ * anchor (or just before the references list). */
30
+ export declare const PLACEMENT_ANCHORS: ReadonlyArray<{
31
+ readonly selector: string;
32
+ readonly tier: string;
33
+ }>;
34
+ /** Anchor tiers for the TOP (contributors card) wrap. Body-CONTENT containers come FIRST so the
35
+ * `afterbegin` insert lands above the article text but below the title/hero — matching the legacy
36
+ * contributors banner's first-in-`body_html` position. The post-level landmarks (`article`/`main`)
37
+ * are the fallback (an `afterbegin` there may land above an in-element title; acceptable + flagged
38
+ * for per-theme tuning, and observable via the recorded tier). */
39
+ export declare const TOP_ANCHORS: ReadonlyArray<{
40
+ readonly selector: string;
41
+ readonly tier: string;
42
+ }>;
43
+ /** The union of TOP_ANCHORS selectors — the shapes that count as an "article content container". The
44
+ * card resolves its anchor by walking UP from `#references` (`closest`) to the nearest of these, so
45
+ * it lands in the exact article body that owns the references — never a same-classed element that
46
+ * merely precedes the article in document order (see placeWrap + SITE_CHROME_SELECTOR). */
47
+ export declare const TOP_CONTENT_SELECTOR: string;
48
+ /** Site-chrome containers a cert wrap must NEVER anchor into. The anchor selectors (`.rte` / `article`
49
+ * / `main`) are NOT article-exclusive on real merchant themes: a VISIBLE announcement bar, header,
50
+ * footer, nav, or cart drawer often carries its own `.rte` that precedes the article in document
51
+ * order — IntelliPure's announcement bar is literally `<div class="announcement__content … rte">`.
52
+ * `isRenderable` can't reject those (they're visible), so `querySelector('.rte')` returned the
53
+ * announcement bar and the contributors card was injected there instead of the article (POR-9809 —
54
+ * "contributors not showing"). Placement scopes anchors out of chrome STRUCTURALLY via this set. */
55
+ export declare const SITE_CHROME_SELECTOR = "header, [role=\"banner\"], [class*=\"announcement\"], footer, [role=\"contentinfo\"], nav, [role=\"navigation\"], cart-drawer, .cart-drawer, .drawer, .mini-cart, .modal, [role=\"dialog\"]";
56
+ /** Achieved-tier attribute the JS stamps on each wrap (CSS body-fallback styling + observability). */
57
+ export declare const PLACEMENT_ATTR = "data-certrev-placement";
58
+ /** Desired-position marker the Liquid sets on each wrap: `top` (card) | `bottom` (memo). */
59
+ export declare const ANCHOR_TARGET_ATTR = "data-certrev-anchor";
60
+ export declare const GRAPH_SELECTOR = "script[type=\"application/ld+json\"][data-certrev-graph]";
61
+ export declare const CARD_WRAP_SELECTOR = ".certrev-card-wrap";
62
+ export declare const MEMO_WRAP_SELECTOR = ".certrev-memo-wrap";
63
+ /** The floating-pill wrap (layout 'floating'). CSS owns its fixed placement; the JS only stamps it. */
64
+ export declare const PILL_WRAP_SELECTOR = ".certrev-pill-wrap";
65
+ /** Min free right gutter (px) beside the article column for the fixed rail. 320 rail + margins. */
66
+ export declare const RAIL_MIN_GUTTER = 384;
67
+ /** Gap (px) between the article column's right edge and the fixed rail. */
68
+ export declare const RAIL_GAP = 24;
69
+ /** The article's references list (emitted by html-generator into the clean body_html). The memo
70
+ * renders just before it, matching the legacy inline-memo position (memo → references). */
71
+ export declare const REFERENCES_SELECTOR = "#references";
72
+ export interface PlaceResult {
73
+ /** Whether the wrapper was moved to an article anchor (false on the body-bottom fallback). */
74
+ readonly anchored: boolean;
75
+ /** The matched anchor tier (`references` | `article` | `main` | `theme-class` | `body-fallback`). */
76
+ readonly tier: string;
77
+ }
78
+ export interface PlaceCertResult {
79
+ /** Whether the @graph script was relocated into <head> this pass. */
80
+ readonly graph: boolean;
81
+ /** Placement of the contributors card wrap (null when absent). */
82
+ readonly card: PlaceResult | null;
83
+ /** Placement of the memo wrap (null when absent). */
84
+ readonly memo: PlaceResult | null;
85
+ }
86
+ /**
87
+ * Reposition ONE wrap to its `data-certrev-anchor` target. Idempotent: a wrap already carrying
88
+ * `data-certrev-placement` is left untouched (records the achieved tier on first pass).
89
+ */
90
+ export declare function placeWrap(doc: Document, wrap: HTMLElement): PlaceResult;
91
+ /** Relocate the @graph into `<head>`, then reposition the card (top) + memo (bottom) wraps — ONCE. */
92
+ export declare function placeCert(doc: Document): PlaceCertResult;
93
+ /** Run placement once the DOM is parsed (mirrors `initBadgeRevalidation`'s readiness handling). */
94
+ export declare function initCertPlacement(doc: Document): void;
95
+ //# sourceMappingURL=placement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../../src/modal/placement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;mDAEmD;AACnD,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAOjG,CAAA;AAED;;;;mEAImE;AACnE,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAO3F,CAAA;AAED;;;4FAG4F;AAC5F,eAAO,MAAM,oBAAoB,QAAgD,CAAA;AAEjF;;;;;;qGAMqG;AACrG,eAAO,MAAM,oBAAoB,gMACmJ,CAAA;AAEpL,sGAAsG;AACtG,eAAO,MAAM,cAAc,2BAA2B,CAAA;AACtD,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,wBAAwB,CAAA;AACvD,eAAO,MAAM,cAAc,6DAA2D,CAAA;AACtF,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AACtD,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AACtD,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AACtD,mGAAmG;AACnG,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,2EAA2E;AAC3E,eAAO,MAAM,QAAQ,KAAK,CAAA;AAC1B;4FAC4F;AAC5F,eAAO,MAAM,mBAAmB,gBAAgB,CAAA;AAEhD,MAAM,WAAW,WAAW;IAC3B,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,qGAAqG;IACrG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;IACjC,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;CACjC;AAkED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,GAAG,WAAW,CAyFvE;AAED,sGAAsG;AACtG,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,eAAe,CAoBxD;AAwCD,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAOrD"}
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Cert-card PLACEMENT for the single `target: body` CertREV app embed.
3
+ *
4
+ * The embed renders globally just before `</body>` — the only theme-app-extension primitive that
5
+ * reliably renders on modern themes (Horizon and others forbid app blocks inside the blog-post
6
+ * content section, so an in-content app block isn't available). On DOM-ready this module, ONCE:
7
+ *
8
+ * (a) Relocates the @graph `<script data-certrev-graph>` into `<head>`. JSON-LD is inert, so the
9
+ * move is invisible + reliable: JS-executing crawlers (Google + most AI fetchers) read it
10
+ * head-side, while non-JS fetchers still read the server-rendered copy that shipped in
11
+ * `<body>`. The AEO payload is never gambled on body-only.
12
+ * (b) Repositions the TWO cert wrappers to mirror the legacy iframe layout (iframe→native parity),
13
+ * BOTH anchored off the html-generator's `#references` list so they land in the SAME article:
14
+ * • `.certrev-card-wrap` (`data-certrev-anchor="top"`) → the TOP of the article content
15
+ * that OWNS `#references` (the contributors banner sat at the top of the legacy `body_html`).
16
+ * • `.certrev-memo-wrap` (`data-certrev-anchor="bottom"`) → the BOTTOM, just before the
17
+ * references list if present (where the legacy inline memo sat), else after the article.
18
+ * When no references exist, each falls back to a chrome-aware semantic-landmark / theme-class
19
+ * scan, with a STYLED end-of-article fallback when nothing matches.
20
+ * (c) Records the matched tier on each wrap as `data-certrev-placement` — the observability the
21
+ * pre-launch theme-matrix sweep + the POR-9578 liveness check read, so a silent misplacement
22
+ * becomes an inspectable signal rather than a surprise on a real brand's store.
23
+ *
24
+ * It runs BEFORE the badge re-verify pass and never moves a wrap twice, so the `<certrev-badge>`
25
+ * element is never detached/re-attached mid-verify (no duplicate Delivery-API calls).
26
+ */
27
+ /** Anchor tiers for the BOTTOM (memo) wrap, best→worst — landmarks first (stable across themes),
28
+ * theme classes last, above the styled body-bottom fallback. The memo is appended AFTER the matched
29
+ * anchor (or just before the references list). */
30
+ export const PLACEMENT_ANCHORS = [
31
+ { selector: 'main article', tier: 'article' },
32
+ { selector: 'article', tier: 'article' },
33
+ { selector: 'main', tier: 'main' },
34
+ { selector: '.article-template__content', tier: 'theme-class' },
35
+ { selector: '[id^="ArticleContent"]', tier: 'theme-class' },
36
+ { selector: '.rte', tier: 'theme-class' },
37
+ ];
38
+ /** Anchor tiers for the TOP (contributors card) wrap. Body-CONTENT containers come FIRST so the
39
+ * `afterbegin` insert lands above the article text but below the title/hero — matching the legacy
40
+ * contributors banner's first-in-`body_html` position. The post-level landmarks (`article`/`main`)
41
+ * are the fallback (an `afterbegin` there may land above an in-element title; acceptable + flagged
42
+ * for per-theme tuning, and observable via the recorded tier). */
43
+ export const TOP_ANCHORS = [
44
+ { selector: '.article-template__content', tier: 'theme-class' },
45
+ { selector: '[id^="ArticleContent"]', tier: 'theme-class' },
46
+ { selector: '.rte', tier: 'theme-class' },
47
+ { selector: 'main article', tier: 'article' },
48
+ { selector: 'article', tier: 'article' },
49
+ { selector: 'main', tier: 'main' },
50
+ ];
51
+ /** The union of TOP_ANCHORS selectors — the shapes that count as an "article content container". The
52
+ * card resolves its anchor by walking UP from `#references` (`closest`) to the nearest of these, so
53
+ * it lands in the exact article body that owns the references — never a same-classed element that
54
+ * merely precedes the article in document order (see placeWrap + SITE_CHROME_SELECTOR). */
55
+ export const TOP_CONTENT_SELECTOR = TOP_ANCHORS.map((a) => a.selector).join(', ');
56
+ /** Site-chrome containers a cert wrap must NEVER anchor into. The anchor selectors (`.rte` / `article`
57
+ * / `main`) are NOT article-exclusive on real merchant themes: a VISIBLE announcement bar, header,
58
+ * footer, nav, or cart drawer often carries its own `.rte` that precedes the article in document
59
+ * order — IntelliPure's announcement bar is literally `<div class="announcement__content … rte">`.
60
+ * `isRenderable` can't reject those (they're visible), so `querySelector('.rte')` returned the
61
+ * announcement bar and the contributors card was injected there instead of the article (POR-9809 —
62
+ * "contributors not showing"). Placement scopes anchors out of chrome STRUCTURALLY via this set. */
63
+ export const SITE_CHROME_SELECTOR = 'header, [role="banner"], [class*="announcement"], footer, [role="contentinfo"], nav, [role="navigation"], cart-drawer, .cart-drawer, .drawer, .mini-cart, .modal, [role="dialog"]';
64
+ /** Achieved-tier attribute the JS stamps on each wrap (CSS body-fallback styling + observability). */
65
+ export const PLACEMENT_ATTR = 'data-certrev-placement';
66
+ /** Desired-position marker the Liquid sets on each wrap: `top` (card) | `bottom` (memo). */
67
+ export const ANCHOR_TARGET_ATTR = 'data-certrev-anchor';
68
+ export const GRAPH_SELECTOR = 'script[type="application/ld+json"][data-certrev-graph]';
69
+ export const CARD_WRAP_SELECTOR = '.certrev-card-wrap';
70
+ export const MEMO_WRAP_SELECTOR = '.certrev-memo-wrap';
71
+ /** The floating-pill wrap (layout 'floating'). CSS owns its fixed placement; the JS only stamps it. */
72
+ export const PILL_WRAP_SELECTOR = '.certrev-pill-wrap';
73
+ /** Min free right gutter (px) beside the article column for the fixed rail. 320 rail + margins. */
74
+ export const RAIL_MIN_GUTTER = 384;
75
+ /** Gap (px) between the article column's right edge and the fixed rail. */
76
+ export const RAIL_GAP = 24;
77
+ /** The article's references list (emitted by html-generator into the clean body_html). The memo
78
+ * renders just before it, matching the legacy inline-memo position (memo → references). */
79
+ export const REFERENCES_SELECTOR = '#references';
80
+ /**
81
+ * Whether `el` is RENDERABLE — not inside a `display:none` / `visibility:hidden` / `[hidden]` /
82
+ * `aria-hidden` subtree. The guard exists because anchor selectors like `.rte` / `main` / `article`
83
+ * are NOT article-exclusive on real merchant themes: a CLOSED cart-drawer / nav / search popup often
84
+ * carries its own `.rte` (e.g. an empty-cart message) that sits BEFORE the article in document order,
85
+ * so `querySelector('.rte')` returns the DRAWER's copy and the cert card gets relocated into a hidden
86
+ * panel — invisible to the reader (observed live on IntelliPure's theme: the card landed in
87
+ * `<cart-drawer hidden>`). Skipping hidden matches makes placement fall through to the visible article
88
+ * content for BOTH the top card and the bottom memo.
89
+ *
90
+ * Walks ancestors via `getComputedStyle` + the hidden/aria-hidden attributes (NOT `offsetParent`,
91
+ * which jsdom always reports null since it does no layout — that would false-reject everything in the
92
+ * unit suite). Normal elements compute `display:block` / `visibility:visible` and pass.
93
+ */
94
+ function isRenderable(el) {
95
+ for (let node = el; node; node = node.parentElement) {
96
+ if (node.hasAttribute('hidden') || node.getAttribute('aria-hidden') === 'true')
97
+ return false;
98
+ const style = getComputedStyle(node);
99
+ if (style.display === 'none' || style.visibility === 'hidden')
100
+ return false;
101
+ }
102
+ return true;
103
+ }
104
+ /**
105
+ * Whether `el` sits inside SITE CHROME (announcement bar / header / footer / nav / cart drawer /
106
+ * modal). Complements `isRenderable`: chrome anchors are usually VISIBLE, so the visibility guard
107
+ * can't reject them — but a cert wrap must never anchor there. This is the guard that keeps the
108
+ * contributors card out of IntelliPure's `.rte`-classed announcement bar (POR-9809).
109
+ */
110
+ function isInSiteChrome(el) {
111
+ return el.closest(SITE_CHROME_SELECTOR) !== null;
112
+ }
113
+ /** Observability tier for an already-resolved container: the first anchor selector it matches
114
+ * (`theme-class` | `article` | `main`), defaulting to `article`. Used to record the tier of the
115
+ * `#references`-resolved card container so the placement attribute stays in the known vocabulary. */
116
+ function anchorTier(el, anchors) {
117
+ for (const { selector, tier } of anchors)
118
+ if (el.matches(selector))
119
+ return tier;
120
+ return 'article';
121
+ }
122
+ /**
123
+ * First anchor that exists, is RENDERABLE (not in a hidden drawer/popup), is NOT site chrome (a
124
+ * visible announcement bar / header / nav — POR-9809), and is safely positionable relative to
125
+ * `wrap`. Iterates ALL matches per selector (not just the first in document order) so an unusable
126
+ * early match (a closed drawer's `.rte`, or the announcement bar's) is skipped for the article copy.
127
+ */
128
+ function firstUsableAnchor(doc, wrap, anchors) {
129
+ for (const { selector, tier } of anchors) {
130
+ for (const el of Array.from(doc.querySelectorAll(selector))) {
131
+ // Never anchor to the wrap itself or to an element that contains / is contained by it (that
132
+ // would detach the wrap into its own subtree or no-op the move), never to a HIDDEN match, and
133
+ // never into site chrome (a visible announcement bar / header / nav carrying its own `.rte`).
134
+ if (el !== wrap && !el.contains(wrap) && !wrap.contains(el) && isRenderable(el) && !isInSiteChrome(el))
135
+ return { el, tier };
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+ /**
141
+ * Reposition ONE wrap to its `data-certrev-anchor` target. Idempotent: a wrap already carrying
142
+ * `data-certrev-placement` is left untouched (records the achieved tier on first pass).
143
+ */
144
+ export function placeWrap(doc, wrap) {
145
+ const existing = wrap.getAttribute(PLACEMENT_ATTR);
146
+ if (existing)
147
+ return { anchored: existing !== 'body-fallback', tier: existing };
148
+ const anchorAttr = wrap.getAttribute(ANCHOR_TARGET_ATTR);
149
+ // RAIL (layout 'sidebar'): pin the whole rail beside the article column when there's room, else
150
+ // gracefully fall back to the banner-style in-flow TOP placement (a full-width card). The owner is
151
+ // resolved exactly like the top card (`#references` → closest content container, chrome-aware).
152
+ if (anchorAttr === 'rail') {
153
+ const refs = doc.querySelector(REFERENCES_SELECTOR);
154
+ const owner = refs?.closest(TOP_CONTENT_SELECTOR) ?? firstUsableAnchor(doc, wrap, TOP_ANCHORS)?.el ?? null;
155
+ const win = doc.defaultView;
156
+ if (owner && win && isRenderable(owner) && !isInSiteChrome(owner)) {
157
+ const rect = owner.getBoundingClientRect();
158
+ const gutter = win.innerWidth - rect.right;
159
+ if (gutter >= RAIL_MIN_GUTTER) {
160
+ // Fixed gutter rail — the working "sticky" for an injected widget (a CSS-sticky float has no
161
+ // stick room without theme layout control). The wrap stays at the embed point.
162
+ wrap.style.setProperty('--certrev-rail-left', `${Math.round(rect.right + RAIL_GAP)}px`);
163
+ wrap.setAttribute(PLACEMENT_ATTR, 'rail');
164
+ return { anchored: true, tier: 'rail' };
165
+ }
166
+ // GRACEFUL FALLBACK: no gutter → banner-style in-flow top placement (full-width card).
167
+ owner.insertAdjacentElement('afterbegin', wrap);
168
+ const fallbackTier = anchorTier(owner, TOP_ANCHORS);
169
+ wrap.setAttribute(PLACEMENT_ATTR, fallbackTier);
170
+ return { anchored: true, tier: fallbackTier };
171
+ }
172
+ wrap.setAttribute(PLACEMENT_ATTR, 'body-fallback');
173
+ return { anchored: false, tier: 'body-fallback' };
174
+ }
175
+ const target = anchorAttr === 'bottom' ? 'bottom' : 'top';
176
+ let tier = 'body-fallback';
177
+ if (target === 'top') {
178
+ // Anchor the card to the TOP of the article CONTENT that OWNS `#references` — the same reliable
179
+ // marker the memo uses at the bottom. `#references` (emitted by the html-generator, always present
180
+ // on a certified article) lives INSIDE the body-content container, so `closest(TOP_CONTENT_SELECTOR)`
181
+ // resolves the exact article `.rte` / `article` / `main` — never a same-classed site-chrome element
182
+ // that merely precedes the article in document order (IntelliPure's `.rte` announcement bar, which
183
+ // is visible so isRenderable can't reject it — POR-9809 "contributors not showing"). This also ties
184
+ // the card + memo to the SAME article region by construction.
185
+ const refs = doc.querySelector(REFERENCES_SELECTOR);
186
+ const owner = refs?.closest(TOP_CONTENT_SELECTOR) ?? null;
187
+ if (owner &&
188
+ owner !== wrap &&
189
+ !owner.contains(wrap) &&
190
+ !wrap.contains(owner) &&
191
+ isRenderable(owner) &&
192
+ !isInSiteChrome(owner)) {
193
+ // First child of the content container → above the article text, below the title/hero.
194
+ owner.insertAdjacentElement('afterbegin', wrap);
195
+ tier = anchorTier(owner, TOP_ANCHORS);
196
+ }
197
+ else {
198
+ // No references (rare) → fall back to the chrome-aware anchor scan.
199
+ const found = firstUsableAnchor(doc, wrap, TOP_ANCHORS);
200
+ if (found) {
201
+ found.el.insertAdjacentElement('afterbegin', wrap);
202
+ tier = found.tier;
203
+ }
204
+ }
205
+ }
206
+ else {
207
+ // Memo → just before the references list when one exists. The references are the reliable
208
+ // bottom-of-content marker INDEPENDENT of which landmark matched: a theme can have a small hero
209
+ // <article> BEFORE the content <article> (IntelliPure: `article banner` then `article
210
+ // page-width`), and `firstUsableAnchor` matches the hero first — so `afterend(hero)` would
211
+ // strand the memo at the TOP, above the card. Anchoring off `#references` (the html-generator's
212
+ // references list, always present on a certified article) avoids that. The old code required the
213
+ // refs to live INSIDE the matched landmark (`found.el.contains(refs)`), which failed exactly when
214
+ // the matched landmark was the hero and the refs were in the separate content article.
215
+ const refs = doc.querySelector(REFERENCES_SELECTOR);
216
+ if (refs && refs !== wrap && !wrap.contains(refs) && !refs.contains(wrap) && isRenderable(refs)) {
217
+ refs.insertAdjacentElement('beforebegin', wrap);
218
+ tier = 'references';
219
+ }
220
+ else {
221
+ const found = firstUsableAnchor(doc, wrap, PLACEMENT_ANCHORS);
222
+ if (found) {
223
+ found.el.insertAdjacentElement('afterend', wrap);
224
+ tier = found.tier;
225
+ }
226
+ }
227
+ }
228
+ wrap.setAttribute(PLACEMENT_ATTR, tier);
229
+ return { anchored: tier !== 'body-fallback', tier };
230
+ }
231
+ /** Relocate the @graph into `<head>`, then reposition the card (top) + memo (bottom) wraps — ONCE. */
232
+ export function placeCert(doc) {
233
+ // (a) @graph → <head> (idempotent: skip if already there or absent).
234
+ const graph = doc.querySelector(GRAPH_SELECTOR);
235
+ let graphMoved = false;
236
+ if (graph && graph.parentNode !== doc.head) {
237
+ doc.head.appendChild(graph);
238
+ graphMoved = true;
239
+ }
240
+ // (b)+(c) reposition each wrap to its target, once.
241
+ const cardEl = doc.querySelector(CARD_WRAP_SELECTOR);
242
+ const memoEl = doc.querySelector(MEMO_WRAP_SELECTOR);
243
+ // The floating pill (layout 'floating') is CSS-fixed — no repositioning; stamp it for observability
244
+ // only, so a live-liveness check still sees a placement attribute on the pill wrap.
245
+ doc.querySelector(PILL_WRAP_SELECTOR)?.setAttribute(PLACEMENT_ATTR, 'pill-fixed');
246
+ return {
247
+ graph: graphMoved,
248
+ card: cardEl ? placeWrap(doc, cardEl) : null,
249
+ memo: memoEl ? placeWrap(doc, memoEl) : null,
250
+ };
251
+ }
252
+ /**
253
+ * A fixed sidebar rail only fits while the article column leaves ≥`RAIL_MIN_GUTTER` of right gutter;
254
+ * a viewport shrink (resize / rotate) can erase it. Bind a debounced resize listener that re-measures
255
+ * and, on gutter loss, DEMOTES the rail to the in-flow banner-style top placement — ONE-WAY (never
256
+ * bounces back on scrollbar jitter): once demoted the listener detaches. No-op unless a fixed rail
257
+ * exists. Idempotent per pass (a rail already demoted carries no `rail` placement so it's skipped).
258
+ */
259
+ function bindRailResize(doc) {
260
+ const win = doc.defaultView;
261
+ if (!win)
262
+ return;
263
+ const wrap = doc.querySelector(`${MEMO_WRAP_SELECTOR}[${ANCHOR_TARGET_ATTR}="rail"]`);
264
+ if (!wrap || wrap.getAttribute(PLACEMENT_ATTR) !== 'rail')
265
+ return;
266
+ let timer;
267
+ const onResize = () => {
268
+ if (timer)
269
+ clearTimeout(timer);
270
+ timer = setTimeout(() => {
271
+ // Only act while still a fixed rail; once demoted we're done (one-way — no bounce-back).
272
+ if (wrap.getAttribute(PLACEMENT_ATTR) !== 'rail') {
273
+ win.removeEventListener('resize', onResize);
274
+ return;
275
+ }
276
+ const refs = doc.querySelector(REFERENCES_SELECTOR);
277
+ const owner = refs?.closest(TOP_CONTENT_SELECTOR) ?? firstUsableAnchor(doc, wrap, TOP_ANCHORS)?.el ?? null;
278
+ const gutter = owner ? win.innerWidth - owner.getBoundingClientRect().right : 0;
279
+ if (gutter < RAIL_MIN_GUTTER) {
280
+ // Demote to in-flow — clear the fixed var + attr, then re-run placement once (the rail
281
+ // branch now measures a lost gutter → the banner-style top card fallback).
282
+ wrap.style.removeProperty('--certrev-rail-left');
283
+ wrap.removeAttribute(PLACEMENT_ATTR);
284
+ placeWrap(doc, wrap);
285
+ win.removeEventListener('resize', onResize);
286
+ }
287
+ }, 150);
288
+ };
289
+ win.addEventListener('resize', onResize);
290
+ }
291
+ /** Run placement once the DOM is parsed (mirrors `initBadgeRevalidation`'s readiness handling). */
292
+ export function initCertPlacement(doc) {
293
+ const run = () => {
294
+ placeCert(doc);
295
+ bindRailResize(doc);
296
+ };
297
+ if (doc.readyState === 'loading')
298
+ doc.addEventListener('DOMContentLoaded', run, { once: true });
299
+ else
300
+ run();
301
+ }
302
+ //# sourceMappingURL=placement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.js","sourceRoot":"","sources":["../../src/modal/placement.ts"],"names":[],"mappings":"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"}