failproofai 0.0.11-beta.8 → 0.0.11

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 (239) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +6 -6
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  19. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  21. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  27. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  30. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route/app-paths-manifest.json +3 -0
  32. package/.next/standalone/.next/server/app/api/audit/invite/route/build-manifest.json +9 -0
  33. package/.next/standalone/.next/server/app/api/audit/invite/route/server-reference-manifest.json +4 -0
  34. package/.next/standalone/.next/server/app/api/audit/invite/route.js +7 -0
  35. package/.next/standalone/.next/server/app/api/audit/invite/route.js.map +5 -0
  36. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -0
  37. package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +3 -0
  38. package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
  43. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
  45. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
  47. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
  49. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  52. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  53. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  54. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/index.html +1 -1
  57. package/.next/standalone/.next/server/app/index.rsc +15 -15
  58. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  60. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  61. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  62. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  63. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  64. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  65. package/.next/standalone/.next/server/app/page.js +2 -2
  66. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  69. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  70. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  71. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  74. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
  76. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  79. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  80. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  81. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -3
  82. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  85. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  86. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  87. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  89. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  90. package/.next/standalone/.next/server/chunks/[externals]__1_g_b3t._.js +3 -0
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0dwpg-h._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lnenda._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13i_sva._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_audit_invite_route_actions_0-2n5sy.js +3 -0
  96. package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/node_modules_0ttxbz7._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/node_modules_1epycqa._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/node_modules_1wpdcgo._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +3 -3
  102. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1d4gx_t._.js → [root-of-the-server]__00uwqi6._.js} +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +4 -0
  107. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1cd25c7._.js → [root-of-the-server]__0e4-6d8._.js} +3 -3
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +4 -0
  109. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-scthx._.js → [root-of-the-server]__0f62vu9._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +4 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l13qf2._.js → [root-of-the-server]__0k65l27._.js} +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__15i0juc._.js → [root-of-the-server]__0kjb_s4._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +4 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +4 -0
  115. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0989_dx._.js → [root-of-the-server]__1e-x7j4._.js} +2 -2
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +4 -0
  119. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/_0il3fl1._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +49 -21
  122. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  124. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0ihmbv4.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  127. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  128. package/.next/standalone/.next/server/pages/404.html +1 -1
  129. package/.next/standalone/.next/server/pages/500.html +1 -1
  130. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  132. package/.next/standalone/.next/static/chunks/{0d49wc5zca0u1.js → 02fywjt0by40a.js} +1 -1
  133. package/.next/standalone/.next/static/chunks/0f7d7hnbh4djs.js +1 -0
  134. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +1 -0
  135. package/.next/standalone/.next/static/chunks/0xdx2ehtbdoeg.js +1 -0
  136. package/.next/standalone/.next/static/chunks/{1nd1e30h8s_mc.js → 1-a5rvq67k7ed.js} +1 -1
  137. package/.next/standalone/.next/static/chunks/{1m2yj97j7f_km.js → 15csyj1_rf0-w.js} +1 -1
  138. package/.next/standalone/.next/static/chunks/1o0xa47736gi9.css +2 -0
  139. package/.next/standalone/.next/static/chunks/24cv31x607n7k.js +1 -0
  140. package/.next/standalone/.next/static/chunks/{3w8d8k_dca5rp.js → 2h0dkzyy0vocp.js} +1 -1
  141. package/.next/standalone/.next/static/chunks/2n_s8v1ae38_a.js +69 -0
  142. package/.next/standalone/.next/static/chunks/2y-jmvrjxz60x.js +6 -0
  143. package/.next/standalone/.next/static/chunks/{24z-bgbisv379.js → 3eik_d9qrvoft.js} +1 -1
  144. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +1 -0
  145. package/.next/standalone/.next/static/chunks/{0j969hb6nujdf.js → 3v61675vr6jav.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{turbopack-00qy7zfa7m--m.js → turbopack-3lrm4f20fz89b.js} +1 -1
  148. package/.next/standalone/app/api/audit/invite/route.ts +192 -0
  149. package/.next/standalone/app/api/audit/run/route.ts +35 -0
  150. package/.next/standalone/app/api/auth/login-request/route.ts +2 -2
  151. package/.next/standalone/app/api/auth/login-verify/route.ts +10 -2
  152. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +39 -63
  153. package/.next/standalone/app/audit/_components/audit-poster.tsx +326 -0
  154. package/.next/standalone/app/audit/_components/auth-dialog.tsx +23 -49
  155. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +336 -0
  156. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +187 -0
  157. package/.next/standalone/app/audit/_components/invite-dialog.tsx +230 -0
  158. package/.next/standalone/app/audit/_components/quirks-section.tsx +75 -0
  159. package/.next/standalone/app/audit/_components/share-templates.ts +63 -32
  160. package/.next/standalone/app/audit/_components/sigil.tsx +9 -66
  161. package/.next/standalone/app/audit/_components/strengths-section.tsx +20 -32
  162. package/.next/standalone/app/audit/audit-styles.css +778 -1786
  163. package/.next/standalone/app/components/sessions-list.tsx +77 -80
  164. package/.next/standalone/app/globals.css +241 -34
  165. package/.next/standalone/app/layout.tsx +1 -10
  166. package/.next/standalone/app/policies/hooks-client.tsx +45 -28
  167. package/.next/standalone/app/project/[name]/page.tsx +23 -79
  168. package/.next/standalone/app/projects/page.tsx +14 -23
  169. package/.next/standalone/assets/audit/poster-styles.css +1 -1
  170. package/.next/standalone/assets/audit/styles.css +11 -11
  171. package/.next/standalone/components/navbar.tsx +2 -37
  172. package/.next/standalone/components/reach-developers.tsx +10 -25
  173. package/.next/standalone/lib/auth/api-server-client.ts +28 -0
  174. package/.next/standalone/lib/client-telemetry.ts +4 -0
  175. package/.next/standalone/node_modules/@next/env/package.json +2 -2
  176. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  177. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  178. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  179. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  180. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  181. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  182. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  183. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  184. package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  185. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  186. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  187. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  188. package/.next/standalone/node_modules/next/package.json +15 -15
  189. package/.next/standalone/package.json +19 -14
  190. package/.next/standalone/server.js +1 -1
  191. package/README.md +2 -2
  192. package/bin/failproofai.mjs +24 -5
  193. package/dist/cli.mjs +2328 -381
  194. package/lib/auth/api-server-client.ts +28 -0
  195. package/lib/client-telemetry.ts +4 -0
  196. package/package.json +19 -14
  197. package/scripts/launch.ts +30 -4
  198. package/scripts/postinstall.mjs +10 -1
  199. package/scripts/skew-log-filter.ts +46 -0
  200. package/scripts/validate-mdx.ts +139 -0
  201. package/src/audit/cli.ts +330 -0
  202. package/src/audit/open-browser.ts +69 -0
  203. package/src/audit/social-proof.ts +34 -0
  204. package/src/auth/cli.ts +16 -13
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07tgnzi._.js +0 -3
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oeun7z._.js +0 -3
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12pit4m._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13ra2jq._.js +0 -3
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1b9z5-i._.js +0 -3
  210. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ixjiy8._.js +0 -3
  211. package/.next/standalone/.next/server/chunks/_1-1804f._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00jkjmt._.js +0 -4
  213. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013du6r._.js +0 -4
  214. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e85wxv._.js +0 -4
  215. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0gfxvb1._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hlrq6y._.js +0 -4
  217. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ihxdo5._.js +0 -4
  218. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1vvfde2._.js +0 -4
  219. package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1k58rb_.js +0 -3
  220. package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1n-0xws.js +0 -3
  221. package/.next/standalone/.next/static/chunks/09ueq8s1as8xs.css +0 -2
  222. package/.next/standalone/.next/static/chunks/0qassxjx1ef04.js +0 -1
  223. package/.next/standalone/.next/static/chunks/0qxb5czqxe-vu.js +0 -1
  224. package/.next/standalone/.next/static/chunks/1dh06515j265n.js +0 -41
  225. package/.next/standalone/.next/static/chunks/29gs4efgi3hme.js +0 -6
  226. package/.next/standalone/.next/static/chunks/2mni177pnjx6u.js +0 -1
  227. package/.next/standalone/.next/static/chunks/2so39wg7mjbi7.js +0 -1
  228. package/.next/standalone/.next/static/chunks/3gti1qdk5epqn.js +0 -1
  229. package/.next/standalone/.next/static/chunks/3wycox197ouus.css +0 -1
  230. package/.next/standalone/app/audit/_components/findings-section.tsx +0 -135
  231. package/.next/standalone/app/audit/_components/identity-section.tsx +0 -126
  232. package/.next/standalone/app/audit/_components/policies-section.tsx +0 -194
  233. package/.next/standalone/app/audit/_components/return-section.tsx +0 -416
  234. package/.next/standalone/app/audit/_components/score-section.tsx +0 -179
  235. package/.next/standalone/app/audit/_components/share-dock.tsx +0 -265
  236. package/.next/standalone/app/audit/_components/show-off-cta.tsx +0 -135
  237. /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_buildManifest.js +0 -0
  238. /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_clientMiddlewareManifest.js +0 -0
  239. /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_ssgManifest.js +0 -0
@@ -0,0 +1,326 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Section 01 — AUDIT POSTER. The single-screen shareable above-the-fold
5
+ * artifact. Replaces the old IdentitySection + ScoreSection + ShareDock
6
+ * triumvirate.
7
+ *
8
+ * Layout (inside the PNG capture region):
9
+ *
10
+ * ┌─────────────────────────────────────────────────────────────┐
11
+ * │ ▣ failproof_ai · audit № 01 of 08 · audited <date> │
12
+ * ├─────────────────────────────────────────────────────────────┤
13
+ * │ │
14
+ * │ <score>/100 the optimist ▓░▓░▓░▓░ │
15
+ * │ <rank> pace · conviction · forgetful ░▓░▓░▓░▓ │
16
+ * │ // only N% of agents are this archetype │
17
+ * │ │
18
+ * ├─────────────────────────────────────────────────────────────┤
19
+ * │ audit yours → failproof.ai │
20
+ * └─────────────────────────────────────────────────────────────┘
21
+ *
22
+ * Outside the capture region: three share buttons + scroll hint.
23
+ */
24
+ import React, { forwardRef, useMemo, useState } from "react";
25
+ import { pickArchetypeVariant, type ArchetypeKey } from "@/src/audit/archetypes";
26
+ import { type Grade } from "@/src/audit/scoring";
27
+ import { getArchetypeRarityPct } from "@/src/audit/social-proof";
28
+ import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
29
+ import { toast } from "@/app/components/toast";
30
+ import { usePostHog } from "@/contexts/PostHogContext";
31
+ import { Sigil } from "./sigil";
32
+ import { X_TEMPLATES, LI_TEMPLATES, pickTemplate, type ShareCtx } from "./share-templates";
33
+
34
+ const X_INTENT = (text: string) =>
35
+ `https://x.com/intent/tweet?text=${encodeURIComponent(text)}`;
36
+ // LinkedIn deprecated the share-offsite `summary` / `title` params, so they no
37
+ // longer pre-fill the post composer (it only scrapes OG tags from the URL). The
38
+ // feed route with `shareActive=true` + `text=` opens "start a post" with our
39
+ // text pre-filled — and the share templates already embed the befailproof.ai
40
+ // link inside that text.
41
+ const LI_INTENT = (text: string) =>
42
+ `https://www.linkedin.com/feed/?shareActive=true&text=${encodeURIComponent(text)}`;
43
+
44
+ interface Props {
45
+ archetypeKey: ArchetypeKey;
46
+ /** Stable seed for variant selection (project name is the natural fit). */
47
+ seed: string;
48
+ score: number;
49
+ grade: Grade;
50
+ /** Count of unenabled prescribed policies — passed to the share-text
51
+ * templates, not rendered on the poster itself. */
52
+ missing: number;
53
+ /** Audit timestamp (ISO string from cache). Rendered as ISO date in the
54
+ * top-right meta. UTC to keep the poster timezone-stable across shares. */
55
+ auditedAt: string;
56
+ }
57
+
58
+ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoster(
59
+ { archetypeKey, seed, score, grade, missing, auditedAt }: Props,
60
+ posterRef,
61
+ ) {
62
+ const archetype = useMemo(
63
+ () => pickArchetypeVariant(archetypeKey, seed),
64
+ [archetypeKey, seed],
65
+ );
66
+ const rarityPct = getArchetypeRarityPct(archetypeKey);
67
+ const indexLabel = String(archetype.index).padStart(2, "0");
68
+ const auditedDate = useMemo(() => formatAuditedDate(auditedAt), [auditedAt]);
69
+
70
+ const { capture } = usePostHog();
71
+ const [busy, setBusy] = useState<null | "x" | "linkedin" | "download">(null);
72
+
73
+ const captureCardBlob = async (): Promise<Blob | null> => {
74
+ const node = (posterRef as React.MutableRefObject<HTMLDivElement | null>)?.current;
75
+ if (!node) return null;
76
+ // Capture via html-to-image instead of html2canvas. The former
77
+ // serializes the DOM into an SVG <foreignObject> and rasterizes
78
+ // it through the browser's native rendering engine — so dashed
79
+ // borders, the SVG logo, gradients, and font metrics render
80
+ // exactly as they do on screen. html2canvas reimplements CSS
81
+ // in JS and was producing broken dashes and a stray pink square
82
+ // on the logo's mask.
83
+ if (typeof document !== "undefined" && document.fonts?.ready) await document.fonts.ready;
84
+ await new Promise<void>((r) => requestAnimationFrame(() => r()));
85
+ // Clone the poster into an off-screen container with a fixed width
86
+ // so html-to-image captures a self-contained subtree. The live
87
+ // .poster sits inside a flex column (.poster-section's flex: 1)
88
+ // and uses margin: 0 auto for centering — capturing it directly
89
+ // inherits that parent context, which shifts content within an
90
+ // oversized canvas. The clone has no such context.
91
+ const liveRect = node.getBoundingClientRect();
92
+ const captureWidth = Math.round(liveRect.width);
93
+ const captureHeight = Math.round(liveRect.height);
94
+
95
+ const wrapper = document.createElement("div");
96
+ wrapper.style.cssText = [
97
+ "position: fixed",
98
+ "left: -10000px",
99
+ "top: 0",
100
+ `width: ${captureWidth}px`,
101
+ `height: ${captureHeight}px`,
102
+ "padding: 0",
103
+ "margin: 0",
104
+ "background: var(--bg)",
105
+ "z-index: -1",
106
+ "pointer-events: none",
107
+ ].join(";");
108
+
109
+ const clone = node.cloneNode(true) as HTMLElement;
110
+ clone.style.cssText += [
111
+ "",
112
+ `width: ${captureWidth}px`,
113
+ `height: ${captureHeight}px`,
114
+ "max-width: none",
115
+ "min-width: 0",
116
+ "flex: 0 0 auto",
117
+ "margin: 0",
118
+ ].join(";");
119
+
120
+ wrapper.appendChild(clone);
121
+ document.body.appendChild(wrapper);
122
+
123
+ try {
124
+ const { toBlob } = await import("html-to-image");
125
+ return await toBlob(clone, {
126
+ backgroundColor: "#0e0e11",
127
+ pixelRatio: 2,
128
+ cacheBust: true,
129
+ width: captureWidth,
130
+ height: captureHeight,
131
+ });
132
+ } finally {
133
+ wrapper.remove();
134
+ }
135
+ };
136
+
137
+ const filenameFor = (channel: "x" | "linkedin" | "download") =>
138
+ `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`;
139
+
140
+ const handleShare = async (channel: "x" | "linkedin" | "download") => {
141
+ if (busy) return;
142
+ setBusy(channel);
143
+ capture("audit_card_share_clicked", {
144
+ channel,
145
+ source: "poster",
146
+ score,
147
+ grade,
148
+ missing_policies: missing,
149
+ });
150
+ try {
151
+ const blob = await captureCardBlob().catch(() => null);
152
+ if (!blob) {
153
+ capture("audit_card_capture_completed", {
154
+ trigger: channel === "download" ? "download" : `share_${channel}`,
155
+ status: "error",
156
+ image_method: "failed",
157
+ source: "poster",
158
+ });
159
+ toast(shareCardToastMessage("failed"));
160
+ return;
161
+ }
162
+
163
+ if (channel === "download") {
164
+ const ok = downloadCard(blob, filenameFor(channel));
165
+ const method = ok ? "download" : "failed";
166
+ capture("audit_card_capture_completed", {
167
+ trigger: "download",
168
+ status: ok ? "success" : "error",
169
+ image_method: method,
170
+ source: "poster",
171
+ });
172
+ toast(shareCardToastMessage(method));
173
+ return;
174
+ }
175
+
176
+ const shareCtx: ShareCtx = {
177
+ score,
178
+ arch: archetype.name.toLowerCase(),
179
+ grade,
180
+ missing,
181
+ };
182
+ const shareText = channel === "x"
183
+ ? pickTemplate(X_TEMPLATES, seed, shareCtx)
184
+ : pickTemplate(LI_TEMPLATES, seed, shareCtx);
185
+ const native = await shareCardNative(blob, filenameFor(channel), shareText);
186
+ if (native) {
187
+ capture("audit_card_capture_completed", {
188
+ trigger: `share_${channel}`,
189
+ status: "success",
190
+ image_method: "native",
191
+ source: "poster",
192
+ });
193
+ toast(shareCardToastMessage("native"));
194
+ return;
195
+ }
196
+
197
+ const fallbackMethod = await copyOrDownloadCard(blob, filenameFor(channel));
198
+ capture("audit_card_capture_completed", {
199
+ trigger: `share_${channel}`,
200
+ status: fallbackMethod === "failed" ? "error" : "success",
201
+ image_method: fallbackMethod,
202
+ source: "poster",
203
+ });
204
+ toast(shareCardToastMessage(fallbackMethod));
205
+ const intent = channel === "x" ? X_INTENT(shareText) : LI_INTENT(shareText);
206
+ globalThis.open(intent, "_blank", "noopener,noreferrer");
207
+ } finally {
208
+ setBusy(null);
209
+ }
210
+ };
211
+
212
+ return (
213
+ <section className="poster-section" data-screen-label="01 Poster">
214
+ <div className="poster" ref={posterRef}>
215
+ <header className="poster-head">
216
+ <span className="poster-wordmark">
217
+ <img
218
+ src="/logo.svg"
219
+ alt="failproof_ai"
220
+ className="poster-logo"
221
+ />
222
+ <span className="poster-sep">·</span> audit
223
+ </span>
224
+ <span className="poster-meta">
225
+ <span className="poster-ix">№ {indexLabel}</span>
226
+ <span className="of"> of 08</span>
227
+ <span className="poster-sep"> · </span>
228
+ audited {auditedDate}
229
+ </span>
230
+ </header>
231
+
232
+ <div className="poster-body">
233
+ {/* Sigil — visual anchor at the top of the centered stack */}
234
+ <div className="poster-sigil">
235
+ <Sigil archetypeKey={archetypeKey} />
236
+ </div>
237
+
238
+ {/* Persona block — name + keywords + rarity, centered */}
239
+ <div className="poster-persona">
240
+ <h1 className="persona-name">{archetype.name}</h1>
241
+ <div className="persona-keywords">
242
+ {archetype.keywords.map((k, i) => (
243
+ <React.Fragment key={k}>
244
+ <span className={`kw kw-${i}`}>{k}</span>
245
+ {i < archetype.keywords.length - 1 && (
246
+ <span className="kw-sep">·</span>
247
+ )}
248
+ </React.Fragment>
249
+ ))}
250
+ </div>
251
+ {typeof rarityPct === "number" && (
252
+ <div className="persona-rarity">
253
+ <span className="lbl">{"// only"}</span>{" "}
254
+ <span className="pct">{rarityPct}%</span>{" "}
255
+ <span className="lbl">of agents are this archetype</span>
256
+ </div>
257
+ )}
258
+ </div>
259
+
260
+ {/* Score block — heroic number, centered in the card */}
261
+ <div className="poster-score">
262
+ <span className="score-n">{score}</span>
263
+ <span className="score-of">/100</span>
264
+ </div>
265
+ </div>
266
+
267
+ <footer className="poster-foot">
268
+ <span className="poster-brand">befailproof.ai</span>
269
+ <span className="poster-cta">
270
+ audit yours <span className="arrow">→</span>{" "}
271
+ <span className="cta-cmd">npx -y failproofai audit</span>
272
+ </span>
273
+ </footer>
274
+ </div>
275
+
276
+ <div className="poster-share-row">
277
+ <button
278
+ type="button"
279
+ className="poster-share-btn"
280
+ onClick={() => handleShare("x")}
281
+ disabled={busy !== null}
282
+ >
283
+ <span className="mark" aria-hidden="true">𝕏</span>
284
+ {busy === "x" ? "rendering…" : "post your archetype"}
285
+ </button>
286
+ <button
287
+ type="button"
288
+ className="poster-share-btn"
289
+ onClick={() => handleShare("linkedin")}
290
+ disabled={busy !== null}
291
+ >
292
+ <span className="mark" aria-hidden="true">in</span>
293
+ {busy === "linkedin" ? "rendering…" : "share on linkedin"}
294
+ </button>
295
+ <button
296
+ type="button"
297
+ className="poster-share-btn"
298
+ onClick={() => handleShare("download")}
299
+ disabled={busy !== null}
300
+ >
301
+ <span className="mark" aria-hidden="true">↓</span>
302
+ {busy === "download" ? "rendering…" : "download poster"}
303
+ </button>
304
+ </div>
305
+
306
+ <div className="poster-scroll-hint" aria-hidden="true">
307
+ scroll for full report <span className="arrow">↓</span>
308
+ </div>
309
+ </section>
310
+ );
311
+ });
312
+
313
+ /** UTC ISO date (YYYY-MM-DD) so the poster's date stays timezone-stable
314
+ * across the geographies it gets shared into. */
315
+ function formatAuditedDate(iso: string): string {
316
+ try {
317
+ const d = new Date(iso);
318
+ if (Number.isNaN(d.getTime())) return iso;
319
+ const y = d.getUTCFullYear();
320
+ const m = String(d.getUTCMonth() + 1).padStart(2, "0");
321
+ const day = String(d.getUTCDate()).padStart(2, "0");
322
+ return `${y}-${m}-${day}`;
323
+ } catch {
324
+ return iso;
325
+ }
326
+ }
@@ -2,14 +2,15 @@
2
2
 
3
3
  /**
4
4
  * Auth dialog — modal overlay shown when an unauthenticated user clicks
5
- * "[ set a reminder ]". Two-step flow:
5
+ * a cadence button to set a reminder. Two-step flow:
6
6
  *
7
7
  * 1. Email entry → POST /api/auth/login-request
8
8
  * 2. OTP entry → POST /api/auth/login-verify
9
9
  *
10
- * Styled to match the rest of the /audit page: pixel brackets, sharp pink
11
- * accent, terminal-style frame. The dialog never sees the refresh token —
12
- * the dashboard's API route writes it to ~/.failproofai/auth.json.
10
+ * Calm chrome to match the rest of /audit: 1px borders, plain
11
+ * lowercase copy, no corner crosshair frame. The dialog never sees
12
+ * the refresh token — the dashboard's API route writes it to
13
+ * ~/.failproofai/auth.json.
13
14
  */
14
15
 
15
16
  import React, { useCallback, useEffect, useRef, useState } from "react";
@@ -23,15 +24,15 @@ export interface AuthedUser {
23
24
 
24
25
  interface Props {
25
26
  open: boolean;
26
- /** Copy shown above the title, e.g. "oops you are unknown." */
27
+ /** Optional title. Defaults to "where to route the reminder?". */
27
28
  headline?: string;
28
- /** Copy under the title explaining why we need auth right now. */
29
- reason?: string;
29
+ /** Optional subtitle shown under the title on the email step. Defaults to
30
+ * "we'll send a one-time code to confirm." */
31
+ subhead?: string;
30
32
  onClose: () => void;
31
33
  /** Fired after successful verify. Caller decides what to do next. */
32
34
  onAuthed: (user: AuthedUser) => void;
33
- /** Telemetry tag identifying which CTA opened the dialog. Defaults to
34
- * "unknown" so existing call sites continue to compile. */
35
+ /** Telemetry tag identifying which CTA opened the dialog. */
35
36
  source?: string;
36
37
  }
37
38
 
@@ -52,8 +53,8 @@ function describeFetchError(err: unknown): string {
52
53
 
53
54
  export function AuthDialog({
54
55
  open,
55
- headline = "oops you are unknown.",
56
- reason = "verify yourself to continue.",
56
+ headline = "where to route the reminder?",
57
+ subhead = "we'll send a one-time code to confirm.",
57
58
  onClose,
58
59
  onAuthed,
59
60
  source = "unknown",
@@ -65,8 +66,6 @@ export function AuthDialog({
65
66
  const emailInputRef = useRef<HTMLInputElement | null>(null);
66
67
  const codeInputRef = useRef<HTMLInputElement | null>(null);
67
68
 
68
- // Reset internal state every time the dialog opens. Also fire the
69
- // funnel-opened event so we can measure dismissal vs verification rates.
70
69
  useEffect(() => {
71
70
  if (open) {
72
71
  setStep({ kind: "email", error: null });
@@ -76,9 +75,6 @@ export function AuthDialog({
76
75
  }
77
76
  }, [capture, open, source]);
78
77
 
79
- // Fire dismissed when the dialog closes WITHOUT a successful verify.
80
- // We piggyback on `open` flipping false instead of intercepting every
81
- // close path so resend / step transitions don't double-count.
82
78
  const wasOpenRef = useRef(false);
83
79
  useEffect(() => {
84
80
  if (open) {
@@ -94,7 +90,6 @@ export function AuthDialog({
94
90
  wasOpenRef.current = false;
95
91
  }, [capture, open, source, step.kind]);
96
92
 
97
- // Autofocus the right input as the step changes.
98
93
  useEffect(() => {
99
94
  if (!open) return;
100
95
  const t = setTimeout(() => {
@@ -104,7 +99,6 @@ export function AuthDialog({
104
99
  return () => clearTimeout(t);
105
100
  }, [open, step.kind]);
106
101
 
107
- // ESC to close.
108
102
  useEffect(() => {
109
103
  if (!open) return;
110
104
  const onKey = (e: KeyboardEvent): void => {
@@ -114,7 +108,6 @@ export function AuthDialog({
114
108
  return () => window.removeEventListener("keydown", onKey);
115
109
  }, [open, busy, onClose]);
116
110
 
117
- // Resend countdown ticker.
118
111
  const resendActive = step.kind === "code" && step.resendIn > 0;
119
112
  useEffect(() => {
120
113
  if (!resendActive) return;
@@ -129,9 +122,6 @@ export function AuthDialog({
129
122
  const requestCode = useCallback(
130
123
  async (email: string, opts: { isResend?: boolean } = {}): Promise<void> => {
131
124
  const { isResend = false } = opts;
132
- // Show resend errors inline on the OTP step — the previously sent
133
- // code is still usable. Only the first-send error path bounces back
134
- // to the email step.
135
125
  const setError = (msg: string) => {
136
126
  if (isResend) {
137
127
  setStep((s) => (s.kind === "code" ? { ...s, error: msg } : s));
@@ -268,11 +258,6 @@ export function AuthDialog({
268
258
  }}
269
259
  >
270
260
  <div className="auth-dialog">
271
- <span className="corner tl">┌</span>
272
- <span className="corner tr">┐</span>
273
- <span className="corner bl">└</span>
274
- <span className="corner br">┘</span>
275
-
276
261
  <button
277
262
  type="button"
278
263
  className="auth-close"
@@ -280,21 +265,17 @@ export function AuthDialog({
280
265
  disabled={busy}
281
266
  aria-label="close"
282
267
  >
283
- [ x ]
268
+ ×
284
269
  </button>
285
270
 
286
- <div className="auth-label">━━ identity check</div>
287
271
  <h2 id="auth-dialog-title" className="auth-headline">
288
272
  {headline}
289
273
  </h2>
290
274
 
291
275
  {step.kind === "email" && (
292
276
  <>
293
- <p className="auth-sub">{reason}</p>
277
+ <p className="auth-sub">{subhead}</p>
294
278
  <form onSubmit={onEmailSubmit} className="auth-form">
295
- <label className="auth-field-label" htmlFor="auth-dialog-email">
296
- email
297
- </label>
298
279
  <input
299
280
  ref={emailInputRef}
300
281
  id="auth-dialog-email"
@@ -311,7 +292,7 @@ export function AuthDialog({
311
292
  {step.error && <div className="auth-error">{step.error}</div>}
312
293
  <div className="auth-actions">
313
294
  <button type="submit" className="auth-btn primary" disabled={busy}>
314
- {busy ? "[ sending… ]" : "[ send code ]"}
295
+ {busy ? "sending…" : "send code"}
315
296
  </button>
316
297
  <button
317
298
  type="button"
@@ -319,7 +300,7 @@ export function AuthDialog({
319
300
  onClick={onClose}
320
301
  disabled={busy}
321
302
  >
322
- [ cancel ]
303
+ cancel
323
304
  </button>
324
305
  </div>
325
306
  </form>
@@ -329,14 +310,10 @@ export function AuthDialog({
329
310
  {step.kind === "code" && (
330
311
  <>
331
312
  <p className="auth-sub">
332
- we sent a code to <span className="auth-email">{step.email}</span>.
333
- <br />
334
- check your inbox — it expires in {Math.ceil(step.expiresIn / 60)} min.
313
+ code sent to <span className="auth-email">{step.email}</span>.
314
+ expires in {Math.ceil(step.expiresIn / 60)} min.
335
315
  </p>
336
316
  <form onSubmit={onCodeSubmit} className="auth-form">
337
- <label className="auth-field-label" htmlFor="auth-dialog-code">
338
- one-time code
339
- </label>
340
317
  <input
341
318
  ref={codeInputRef}
342
319
  id="auth-dialog-code"
@@ -354,7 +331,7 @@ export function AuthDialog({
354
331
  {step.error && <div className="auth-error">{step.error}</div>}
355
332
  <div className="auth-actions">
356
333
  <button type="submit" className="auth-btn primary" disabled={busy}>
357
- {busy ? "[ verifying… ]" : "[ verify ]"}
334
+ {busy ? "verifying…" : "verify"}
358
335
  </button>
359
336
  <button
360
337
  type="button"
@@ -363,8 +340,8 @@ export function AuthDialog({
363
340
  disabled={busy || step.resendIn > 0}
364
341
  >
365
342
  {step.resendIn > 0
366
- ? `[ resend in ${step.resendIn}s ]`
367
- : "[ resend code ]"}
343
+ ? `resend in ${step.resendIn}s`
344
+ : "resend code"}
368
345
  </button>
369
346
  </div>
370
347
  <button
@@ -382,15 +359,12 @@ export function AuthDialog({
382
359
  {step.kind === "done" && (
383
360
  <>
384
361
  <p className="auth-sub">
385
- <span className="auth-ok">✓</span> you are{" "}
362
+ <span className="auth-ok">✓</span> signed in as{" "}
386
363
  <span className="auth-email">{step.user.email}</span>.
387
364
  </p>
388
- <p className="auth-sub" style={{ marginTop: 8 }}>
389
- session saved locally.
390
- </p>
391
365
  <div className="auth-actions">
392
366
  <button type="button" className="auth-btn primary" onClick={onClose}>
393
- [ continue ]
367
+ continue
394
368
  </button>
395
369
  </div>
396
370
  </>