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,230 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Invite dialog — modal that takes a comma/newline-separated list of friend
5
+ * emails and POSTs them to /api/audit/invite. The api-server composes the
6
+ * actual invite email (Cc'ing the sender) using the same email infrastructure
7
+ * that backs the OTP flow.
8
+ *
9
+ * Anonymous users get bounced through the AuthDialog by the caller; this
10
+ * component assumes the session is already established.
11
+ */
12
+
13
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
+ import { usePostHog } from "@/contexts/PostHogContext";
15
+ import { toast } from "@/app/components/toast";
16
+
17
+ interface Props {
18
+ open: boolean;
19
+ onClose: () => void;
20
+ /** Source tag for PostHog so we can split "invited from come-back-better"
21
+ * from any future entry points. */
22
+ source: string;
23
+ /** Called when the proxy returns 401 (session expired between probe and
24
+ * submit). The parent should re-open the AuthDialog so the user can
25
+ * re-authenticate; without this, a 401 dead-ends with an inline error. */
26
+ onUnauthorized?: () => void;
27
+ /** Sender's audit score (0–100), forwarded to the api-server so the invite
28
+ * body can show "mine came out at N/100". Omitted → score-free copy. */
29
+ score?: number;
30
+ }
31
+
32
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
33
+ const MAX_RECIPIENTS = 10;
34
+
35
+ function parseEmails(input: string): { valid: string[]; invalid: string[] } {
36
+ const tokens = input
37
+ .split(/[\s,;]+/)
38
+ .map((t) => t.trim().toLowerCase())
39
+ .filter(Boolean);
40
+ const valid: string[] = [];
41
+ const invalid: string[] = [];
42
+ const seen = new Set<string>();
43
+ for (const t of tokens) {
44
+ if (seen.has(t)) continue;
45
+ seen.add(t);
46
+ if (EMAIL_RE.test(t)) valid.push(t);
47
+ else invalid.push(t);
48
+ }
49
+ return { valid, invalid };
50
+ }
51
+
52
+ export function InviteDialog({ open, onClose, source, onUnauthorized, score }: Props): React.ReactElement | null {
53
+ const { capture } = usePostHog();
54
+ const [value, setValue] = useState("");
55
+ const [busy, setBusy] = useState(false);
56
+ const [error, setError] = useState<string | null>(null);
57
+ const inputRef = useRef<HTMLTextAreaElement | null>(null);
58
+
59
+ useEffect(() => {
60
+ if (open) {
61
+ setValue("");
62
+ setBusy(false);
63
+ setError(null);
64
+ capture("audit_invite_dialog_opened", { source });
65
+ }
66
+ }, [capture, open, source]);
67
+
68
+ useEffect(() => {
69
+ if (!open) return;
70
+ const t = setTimeout(() => inputRef.current?.focus(), 50);
71
+ return () => clearTimeout(t);
72
+ }, [open]);
73
+
74
+ useEffect(() => {
75
+ if (!open) return;
76
+ const onKey = (e: KeyboardEvent): void => {
77
+ if (e.key === "Escape" && !busy) onClose();
78
+ };
79
+ window.addEventListener("keydown", onKey);
80
+ return () => window.removeEventListener("keydown", onKey);
81
+ }, [open, busy, onClose]);
82
+
83
+ const { valid, invalid } = useMemo(() => parseEmails(value), [value]);
84
+
85
+ const submit = useCallback(
86
+ async (e: React.FormEvent<HTMLFormElement>) => {
87
+ e.preventDefault();
88
+ if (busy) return;
89
+ if (valid.length === 0) {
90
+ setError("add at least one valid email address.");
91
+ return;
92
+ }
93
+ if (valid.length > MAX_RECIPIENTS) {
94
+ setError(`up to ${MAX_RECIPIENTS} emails at a time. send the rest in a follow-up.`);
95
+ return;
96
+ }
97
+ setBusy(true);
98
+ setError(null);
99
+ capture("audit_invite_submitted", {
100
+ source,
101
+ recipient_count: valid.length,
102
+ had_invalid: invalid.length > 0,
103
+ });
104
+ try {
105
+ const res = await fetch("/api/audit/invite", {
106
+ method: "POST",
107
+ headers: { "content-type": "application/json" },
108
+ body: JSON.stringify({ to: valid, score }),
109
+ });
110
+ const body = (await res.json().catch(() => ({}))) as {
111
+ sent?: string[];
112
+ failed?: string[];
113
+ code?: string;
114
+ message?: string;
115
+ };
116
+ if (res.status === 401) {
117
+ // Session expired between probe and submit — route back through auth.
118
+ // Without this, repeated submits would dead-end with the same 401.
119
+ if (onUnauthorized) {
120
+ onClose();
121
+ onUnauthorized();
122
+ } else {
123
+ setError("session expired. please sign in again.");
124
+ }
125
+ return;
126
+ }
127
+ if (!res.ok) {
128
+ const msg = body.message ?? "couldn't send invites.";
129
+ setError(msg);
130
+ return;
131
+ }
132
+ const sent = body.sent?.length ?? 0;
133
+ const failed = body.failed?.length ?? 0;
134
+ toast(
135
+ failed > 0
136
+ ? `📨 sent ${sent}, ${failed} bounced — copy the bounce and try again.`
137
+ : `📨 sent ${sent} ${sent === 1 ? "invite" : "invites"}. thanks for spreading the word.`,
138
+ );
139
+ onClose();
140
+ } catch (err) {
141
+ const message = err instanceof Error ? err.message : String(err);
142
+ setError(`network error: ${message}`);
143
+ } finally {
144
+ setBusy(false);
145
+ }
146
+ },
147
+ [busy, valid, invalid, capture, source, onClose, onUnauthorized, score],
148
+ );
149
+
150
+ if (!open) return null;
151
+
152
+ return (
153
+ <div
154
+ className="auth-dialog-backdrop"
155
+ role="dialog"
156
+ aria-modal="true"
157
+ aria-labelledby="invite-dialog-title"
158
+ onClick={(e) => {
159
+ if (!busy && e.target === e.currentTarget) onClose();
160
+ }}
161
+ >
162
+ <div className="auth-dialog">
163
+ <button
164
+ type="button"
165
+ className="auth-close"
166
+ onClick={onClose}
167
+ disabled={busy}
168
+ aria-label="close"
169
+ >
170
+ ×
171
+ </button>
172
+
173
+ <h2 id="invite-dialog-title" className="auth-headline">
174
+ invite friends to audit theirs
175
+ </h2>
176
+ <p className="auth-sub">
177
+ paste emails separated by commas, spaces, or newlines. you&apos;ll be Cc&apos;d on every invite so they know it&apos;s from you.
178
+ </p>
179
+
180
+ <form onSubmit={submit} className="auth-form">
181
+ <textarea
182
+ ref={inputRef}
183
+ name="emails"
184
+ placeholder="alice@x.com, bob@y.com&#10;carol@z.com"
185
+ disabled={busy}
186
+ className="auth-input invite-textarea"
187
+ value={value}
188
+ onChange={(e) => setValue(e.target.value)}
189
+ rows={4}
190
+ required
191
+ />
192
+ <div className="invite-summary">
193
+ {valid.length > 0 && (
194
+ <span className="invite-valid">{valid.length} valid</span>
195
+ )}
196
+ {invalid.length > 0 && (
197
+ <span className="invite-invalid">
198
+ {invalid.length} invalid: {invalid.slice(0, 3).join(", ")}
199
+ {invalid.length > 3 ? `, +${invalid.length - 3} more` : ""}
200
+ </span>
201
+ )}
202
+ {valid.length > MAX_RECIPIENTS && (
203
+ <span className="invite-invalid">
204
+ only {MAX_RECIPIENTS} per send — first {MAX_RECIPIENTS} will go.
205
+ </span>
206
+ )}
207
+ </div>
208
+ {error && <div className="auth-error">{error}</div>}
209
+ <div className="auth-actions">
210
+ <button
211
+ type="submit"
212
+ className="auth-btn primary"
213
+ disabled={busy || valid.length === 0}
214
+ >
215
+ {busy ? "sending…" : `send ${valid.length || ""} invite${valid.length === 1 ? "" : "s"}`.trim()}
216
+ </button>
217
+ <button
218
+ type="button"
219
+ className="auth-btn"
220
+ onClick={onClose}
221
+ disabled={busy}
222
+ >
223
+ cancel
224
+ </button>
225
+ </div>
226
+ </form>
227
+ </div>
228
+ </div>
229
+ );
230
+ }
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Section 03 — QUIRKS. "what to improve."
5
+ *
6
+ * 4-column table: when · what slipped (+ policy that would've caught it) ·
7
+ * severity pill · recurrence. Each row corresponds to a triggered
8
+ * detector. No per-finding card chrome, no 4-quad body, no corner
9
+ * crosshairs — evidence and fix live in section 04 (How to improve).
10
+ */
11
+ import React from "react";
12
+ import type { FindingCard } from "@/src/audit/findings";
13
+
14
+ interface Props {
15
+ findings: FindingCard[];
16
+ }
17
+
18
+ type Severity = "low" | "medium" | "high";
19
+
20
+ /** Heuristic severity from occurrence count — until the audit pipeline
21
+ * carries a real severity field. Tuned so a single occurrence reads as
22
+ * low and >5 reads as high. */
23
+ function severityFromCount(count: number): Severity {
24
+ if (count >= 6) return "high";
25
+ if (count >= 2) return "medium";
26
+ return "low";
27
+ }
28
+
29
+ function recurrenceLabel(count: number): string {
30
+ if (count <= 1) return "new";
31
+ if (count < 10) return `${count}× seen`;
32
+ return "recurring";
33
+ }
34
+
35
+ export function QuirksSection({ findings }: Props) {
36
+ if (findings.length === 0) return null;
37
+
38
+ return (
39
+ <section className="audit-sec" data-screen-label="03 Quirks">
40
+ <div className="audit-sec-head">
41
+ <span className="audit-sec-eyebrow">
42
+ <span className="ix">03</span>{"// quirks"}
43
+ </span>
44
+ <span className="audit-sec-meta">{findings.length} slipped through</span>
45
+ </div>
46
+ <h2 className="audit-sec-title">what to improve</h2>
47
+
48
+ <div className="quirks-table">
49
+ <div className="quirks-thead">
50
+ <span>when</span>
51
+ <span>what slipped</span>
52
+ <span>severity</span>
53
+ <span>seen</span>
54
+ </div>
55
+ {findings.map((f) => {
56
+ const sev = severityFromCount(f.count);
57
+ return (
58
+ <div key={f.sourceSlug} className="quirks-row">
59
+ <span className="q-when">{f.lastSeen}</span>
60
+ <span className="q-what">
61
+ <span className="q-title">{f.title}</span>
62
+ <span className="q-policy">
63
+ would&apos;ve been caught by:{" "}
64
+ <code>{f.policy}</code>
65
+ </span>
66
+ </span>
67
+ <span className={`q-pill q-pill-${sev}`}>{sev}</span>
68
+ <span className="q-recur">{recurrenceLabel(f.count)}</span>
69
+ </div>
70
+ );
71
+ })}
72
+ </div>
73
+ </section>
74
+ );
75
+ }
@@ -1,16 +1,19 @@
1
1
  /**
2
2
  * Social-share copy for the audit identity card.
3
3
  *
4
- * Five quirky, emoji-forward templates for X and five measured, professional
5
- * templates for LinkedIn. `pickTemplate` selects one deterministically from a
6
- * seed (the behaviour fingerprint), so a given audit run always renders the
7
- * same post while different runs / personas vary. Pure — no React, no DOM — so
8
- * it's unit-testable and shared by the client `IdentitySection` component.
4
+ * Ten templates for X and ten for LinkedIn. `pickTemplate` selects one
5
+ * deterministically from a seed (the behaviour fingerprint), so a given audit
6
+ * run always renders the same post while different runs / personas vary then
7
+ * appends a clipboard paste hint. Pure — no React, no DOM — so it's
8
+ * unit-testable and shared by the client poster component.
9
+ *
10
+ * Each template references the score and/or archetype and ends on the
11
+ * install-free CTA + handle. We intentionally never include a URL: a bare link
12
+ * would make X/LinkedIn render a link-preview card and swallow the copied
13
+ * image. The npx command is the call to action instead.
9
14
  */
10
15
  import type { Grade } from "@/src/audit/scoring";
11
16
 
12
- const SITE_URL = "https://befailproof.ai";
13
-
14
17
  export interface ShareCtx {
15
18
  score: number;
16
19
  /** Lowercased archetype name, e.g. "the cowboy". */
@@ -20,34 +23,60 @@ export interface ShareCtx {
20
23
  missing: number;
21
24
  }
22
25
 
23
- const pol = (n: number) => (n === 1 ? "policy" : "policies");
26
+ /** Call-to-action + handle per channel. No URL by design (see file header). */
27
+ const X_CTA = "npx -y failproofai audit · @failproofai";
28
+ const LI_CTA = "npx -y failproofai audit · @Failproof AI";
29
+
30
+ /** Appended to every picked share so the user knows the audit-card PNG is on
31
+ * their clipboard and just needs pasting into the post. */
32
+ const PASTE_LINE = "[ audit card copied to your clipboard — paste it into the post ]";
24
33
 
25
- /** Quirky, emoji-forward — for X. */
34
+ /** Short, curiosity-forward — for X / Twitter. Ends on the npx CTA + @failproofai. */
26
35
  export const X_TEMPLATES: ((c: ShareCtx) => string)[] = [
27
- ({ score, arch, grade, missing }) =>
28
- `my AI coding agent just got profiled and it's "${arch}" 🤠\n\n${score}/100 · ${grade} tier${missing > 0 ? ` · ${missing} ${pol(missing)} between me and glory` : ` · every guardrail live`}\n\nwhat's yours? → ${SITE_URL}`,
29
- ({ score, arch, grade }) =>
30
- `turns out my coding agent has a whole personality and it's ${arch} 💀\n\nscored ${score}/100 (${grade} tier). the audit does not miss.\n\nrun yours in 30s → ${SITE_URL}`,
31
- ({ score, arch, grade }) =>
32
- `${score}/100. ${grade} tier. archetype: ${arch} 👀\n\nfailproofai reverse-engineered my agent's entire vibe from its own session logs.\n\n${SITE_URL}`,
33
- ({ score, arch, grade, missing }) =>
34
- `plot twist: my AI agent is ${arch} 🎭\n\n${score}/100 · ${grade} tier${missing > 0 ? ` · ${missing} ${pol(missing)} away from behaving` : ` · spotless, somehow`}\n\nbefailproof.ai`,
35
- ({ score, arch, grade }) =>
36
- `i let failproofai audit my coding agent and it called me ${arch} ${grade === "S" || grade === "A" ? "😎" : "😬"}\n\n${score}/100, ${grade} tier. brutally accurate. no notes.\n\n${SITE_URL}`,
36
+ ({ score, arch }) =>
37
+ `my coding agent has a personality and it's ${arch}. did not see that coming.\n\nran a failproof audit, scored ${score}/100, all local. find out what yours is → ${X_CTA}`,
38
+ ({ score, arch }) =>
39
+ `spent 30 seconds auditing my coding agent. learned more about it than i did all month.\n\nit's ${arch}. scored ${score}/100. → ${X_CTA}`,
40
+ ({ score }) =>
41
+ `my agent scored ${score}/100. think yours can beat it?\n\n30 sec audit, runs locally, gives you a personality too ${X_CTA}`,
42
+ ({ arch }) =>
43
+ `turns out my coding agent is ${arch} and honestly it explains everything.\n\n→ ${X_CTA}`,
44
+ ({ score, arch }) =>
45
+ `ran a failproof audit on my agent. it reads how it actually behaves when things break, not just the happy path.\n\npersonality: ${arch}. score: ${score}/100. all local ${X_CTA}`,
46
+ ({ score, arch }) =>
47
+ `got to know my coding agent today. it's ${arch} and scored ${score}/100. wow.\n\naudit yours in 30s, nothing leaves your machine → ${X_CTA}`,
48
+ ({ score, arch }) =>
49
+ `your coding agent has a personality. you just haven't met it.\n\ni met mine today: ${arch}, ${score}/100. → ${X_CTA}`,
50
+ ({ score, arch }) =>
51
+ `everyone talks about what their agent can build. nobody talks about how it acts when it breaks.\n\nmine's ${arch}, scored ${score}/100, all local → ${X_CTA}`,
52
+ ({ score, arch }) =>
53
+ `every agent builder should run this once.\n\n30 sec, local, gives your agent a personality and a quality score. mine: ${arch}, ${score}/100.\n\n${X_CTA}`,
54
+ ({ score, arch }) =>
55
+ `i'll go first: ${arch}, ${score}/100.\n\nwhat's your coding agent? 30 sec audit, no signup → ${X_CTA}`,
37
56
  ];
38
57
 
39
- /** Measured, professional — for LinkedIn. */
58
+ /** Longer, reflective — for LinkedIn. Ends on the npx CTA + @Failproof AI. */
40
59
  export const LI_TEMPLATES: ((c: ShareCtx) => string)[] = [
41
- ({ score, arch, grade, missing }) =>
42
- `I ran a failproofai audit on our AI coding agents.\n\nResult: ${score}/100 ${grade} tier, behavioural archetype "${arch}". ${missing > 0 ? `${missing} prescribed ${pol(missing)} would close the remaining gaps.` : `Every prescribed policy is already live.`}\n\nUnderstanding how agents actually behave across real sessions is the first step to securing them. Free and open-source: ${SITE_URL}`,
43
- ({ score, arch, grade, missing }) =>
44
- `Security posture check on our coding-agent stack, via failproofai.\n\nScore: ${score}/100 (${grade} tier). Behavioural profile: "${arch}". ${missing > 0 ? `${missing} ${pol(missing)} flagged as real, addressable attack surface.` : `No open policy gaps.`}\n\nThirty seconds to run your own assessment: ${SITE_URL}`,
45
- ({ score, arch, grade }) =>
46
- `Most teams can't answer a simple question: how do our AI agents actually behave when no one is watching?\n\nfailproofai audited ours and profiled it as "${arch}" ${score}/100, ${grade} tier. Concrete, evidence-backed, and mapped to the exact policies that close each gap.\n\n${SITE_URL}`,
47
- ({ score, arch, grade, missing }) =>
48
- `Agent security isn't a vibe it's measurable.\n\nWe scored ${score}/100 (${grade} tier) on the failproofai audit, archetype "${arch}". ${missing > 0 ? `${missing} prescribed ${pol(missing)} remain to harden the stack.` : `Full coverage on every prescribed policy.`}\n\nWorth 30 seconds for any team shipping with AI agents: ${SITE_URL}`,
49
- ({ score, arch, grade }) =>
50
- `Ran our coding agents through a failproofai behavioural audit.\n\n${score}/100, ${grade} tier archetype "${arch}". The report maps every risky pattern to the policy that prevents it, so remediation becomes a checklist rather than a guess.\n\nOpen-source, free to run: ${SITE_URL}`,
60
+ ({ score, arch }) =>
61
+ `I've spent more hours with my coding agent this month than with most people I know. Today I realized I couldn't tell you a single thing about how it actually behaves.\n\nSo I audited it. Turns out it's ${arch}, and it scored ${score}/100.\n\nThe audit reads its real history, including how it handles failures, not just the clean runs. 30 seconds, runs entirely locally.\n\nMeet yours: ${LI_CTA}`,
62
+ ({ score, arch }) =>
63
+ `Took a personality test today. It wasn't for me — it was for my AI coding agent.\n\nThe result: ${arch}, with a quality score of ${score}/100.\n\nIt works by reading the agent's actual run history, so the personality comes from how it really behaves, not a vibe. Took 30 seconds and stayed local.\n\nTry it on yours: ${LI_CTA}`,
64
+ ({ score, arch }) =>
65
+ `Everyone asks what their AI agent can build. I just found out mine has a temper.\n\nAfter watching how it reacts to a failed command, the audit called it ${arch}. It also scored its quality at ${score}/100.\n\nOddly useful to see it written down. 30 seconds, all local: ${LI_CTA}`,
66
+ ({ score, arch }) =>
67
+ `My agent does this very specific thing every time a command fails. Today I learned there's basically a name for it.\n\nRan a quick audit and it came back ${arch}, scored ${score}/100. The whole read is based on how the agent handles things going wrong, which is where its real character shows.\n\n30 seconds, nothing leaves your machine: ${LI_CTA}`,
68
+ ({ score, arch }) =>
69
+ `My AI coding agent scored ${score}/100 today. I didn't know you could even measure that.\n\nThe same audit also handed it a personality: ${arch}.\n\nIt reads the agent's real history and tells you where it's solid and where it slips. Took 30 seconds and ran fully locally: ${LI_CTA}`,
70
+ ({ score, arch }) =>
71
+ `I'm usually the first to scroll past "audit your X" tools. This one took 30 seconds and actually told me something.\n\nMy coding agent is ${arch}, and it scored ${score}/100.\n\nIt reads the agent's real run history rather than asking me anything, and none of it leaves the machine: ${LI_CTA}`,
72
+ ({ score, arch }) =>
73
+ `What is your coding agent's personality? I realized today that I had no answer.\n\nSo I checked. Mine is ${arch}, scored ${score}/100, with a clear read on where it shines and where it slips.\n\nIf you work with agents, run it and drop yours in the comments. 30 seconds, fully local: ${LI_CTA}`,
74
+ ({ score, arch }) =>
75
+ `I trusted my coding agent a little less after this morning, and quite a bit more by the afternoon.\n\nI ran an audit on it. It showed me exactly where it's reliable and where it isn't, gave it a personality (${arch}), and scored it ${score}/100.\n\nKnowing the gaps is what made me trust it more. 30 seconds, all local: ${LI_CTA}`,
76
+ ({ score, arch }) =>
77
+ `I almost skipped this because I assumed it would quietly ship my code off somewhere. It doesn't. Everything runs locally.\n\n30 seconds later I had my agent's personality (${arch}) and a quality score (${score}/100).\n\nIf privacy is usually what stops you from trying these, this one is different: ${LI_CTA}`,
78
+ ({ score, arch }) =>
79
+ `Found out my AI coding agent has a personality type. Found out it's ${arch}. Found out that explains a lot.\n\nIt scored ${score}/100 too. The audit reads how the agent handles failures, not just the runs where everything goes fine.\n\n30 seconds, runs locally: ${LI_CTA}`,
51
80
  ];
52
81
 
53
82
  /** djb2 hash — stable per seed so the template choice is deterministic. */
@@ -57,11 +86,13 @@ function hashStr(s: string): number {
57
86
  return h >>> 0;
58
87
  }
59
88
 
60
- /** Deterministically pick + render one template for the given seed. */
89
+ /** Deterministically pick + render one template for the given seed, then append
90
+ * the clipboard paste hint so the user knows to paste the copied audit card. */
61
91
  export function pickTemplate(
62
92
  templates: ((c: ShareCtx) => string)[],
63
93
  seed: string,
64
94
  ctx: ShareCtx,
65
95
  ): string {
66
- return templates[hashStr(seed) % templates.length](ctx);
96
+ const body = templates[hashStr(seed) % templates.length](ctx);
97
+ return `${body}\n\n${PASTE_LINE}`;
67
98
  }
@@ -1,39 +1,22 @@
1
1
  "use client";
2
2
 
3
3
  /**
4
- * Pixel sigil — renders an 8x8 grid from the SIGILS table.
4
+ * Pixel sigil — 8×8 grid for the archetype. Bare implementation: no plate,
5
+ * no crosshairs, no reveal animation. The poster owns whatever framing it
6
+ * wants around the sigil.
5
7
  *
6
- * Each archetype has an 8x8 character grid where:
7
- * . = empty cell o = ink (foreground)
8
- * p = pink accent g = green accent d = dim
9
- *
10
- * Two rendering modes:
11
- *
12
- * • Default (used on the audit hero, identity-section): wraps the grid in
13
- * a brutalist "instrument plate" — register crosshairs at the four
14
- * corners, a header strip with the archetype index + an 8×8 coordinate
15
- * label, the pixel grid mounted on a dashed inner frame with 20px cells,
16
- * a footer strip naming the archetype, and a stacked pink + dim hard-
17
- * offset shadow for depth. Cells fade in along a diagonal wave on
18
- * mount (`--cx` / `--cy` custom properties), guarded by
19
- * `prefers-reduced-motion`.
20
- *
21
- * • hideLabel (used in the ShowOff CTA + html2canvas capture): a bare
22
- * `.sigil` grid with no plate or labels, so the showoff card can scale
23
- * the sigil down independently and html2canvas doesn't have to capture
24
- * the new plate chrome.
8
+ * Cell letters: `.` empty · `o` ink · `p` pink accent · `g` green accent ·
9
+ * `d` dim.
25
10
  */
26
11
  import React from "react";
27
- import { ARCHETYPES, SIGILS, type ArchetypeKey } from "@/src/audit/archetypes";
12
+ import { SIGILS, type ArchetypeKey } from "@/src/audit/archetypes";
28
13
 
29
14
  interface Props {
30
15
  archetypeKey: ArchetypeKey;
31
- hideLabel?: boolean;
32
16
  }
33
17
 
34
- export function Sigil({ archetypeKey, hideLabel }: Props) {
18
+ export function Sigil({ archetypeKey }: Props) {
35
19
  const grid = SIGILS[archetypeKey] ?? SIGILS.optimist;
36
- const archetype = ARCHETYPES[archetypeKey];
37
20
  const cells: React.ReactElement[] = [];
38
21
 
39
22
  for (let y = 0; y < 8; y++) {
@@ -45,49 +28,9 @@ export function Sigil({ archetypeKey, hideLabel }: Props) {
45
28
  else if (c === "p") cls += " p";
46
29
  else if (c === "g") cls += " g";
47
30
  else if (c === "d") cls += " d";
48
- cells.push(
49
- <div
50
- key={`${y}-${x}`}
51
- className={cls}
52
- style={{
53
- ["--cx" as string]: x,
54
- ["--cy" as string]: y,
55
- } as React.CSSProperties}
56
- />,
57
- );
31
+ cells.push(<div key={`${y}-${x}`} className={cls} />);
58
32
  }
59
33
  }
60
34
 
61
- if (hideLabel) {
62
- return (
63
- <div className="sigil-wrap" data-bare="true">
64
- <div className="sigil">{cells}</div>
65
- </div>
66
- );
67
- }
68
-
69
- const indexLabel = String(archetype.index).padStart(2, "0");
70
-
71
- return (
72
- <div className="sigil-wrap">
73
- <div className="sigil-plate">
74
- <span className="sigil-mark tl" aria-hidden="true" />
75
- <span className="sigil-mark tr" aria-hidden="true" />
76
- <span className="sigil-mark bl" aria-hidden="true" />
77
- <span className="sigil-mark br" aria-hidden="true" />
78
-
79
- <div className="sigil-strip sigil-strip--top">
80
- <span className="sigil-ix">№ {indexLabel}</span>
81
- <span className="sigil-coord">8×8</span>
82
- </div>
83
-
84
- <div className="sigil">{cells}</div>
85
-
86
- <div className="sigil-strip sigil-strip--bot">
87
- <span className="sigil-strip-key">sigil</span>
88
- <span className="sigil-strip-val">{archetypeKey}</span>
89
- </div>
90
- </div>
91
- </div>
92
- );
35
+ return <div className="sigil">{cells}</div>;
93
36
  }
@@ -1,57 +1,45 @@
1
1
  "use client";
2
2
 
3
3
  /**
4
- * Section 02 — STRENGTHS. "your agent does this right." A leaderboard
5
- * of green-checked behaviors derived from the AuditResult (see
6
- * `src/audit/strengths.ts`).
4
+ * Section 02 — STRENGTHS. "what it's great at." Calm row list:
5
+ * glyph · headline + sub · right-aligned metric. No card chrome,
6
+ * no hover backgrounds, no checkmark backdrop.
7
7
  */
8
8
  import React from "react";
9
9
  import type { Strength } from "@/src/audit/strengths";
10
10
 
11
11
  interface Props {
12
12
  strengths: Strength[];
13
- totalDetectorsTriggered: number;
14
- totalDetectorsAvailable: number;
15
13
  }
16
14
 
17
- export function StrengthsSection({
18
- strengths, totalDetectorsTriggered, totalDetectorsAvailable,
19
- }: Props) {
15
+ export function StrengthsSection({ strengths }: Props) {
20
16
  if (strengths.length === 0) return null;
21
17
 
22
18
  return (
23
- <section className="section" data-screen-label="02 Strengths">
24
- <div className="section-mast">
25
- <div className="section-label">
26
- <span className="glyph">━━</span> strengths
27
- {" "}<span style={{ color: "var(--dim)" }}>·</span>{" "}
28
- what your agent has figured out
29
- </div>
30
- <div className="section-meta">
31
- <span className="g">●</span>{" "}
32
- {totalDetectorsAvailable - totalDetectorsTriggered} of {totalDetectorsAvailable} clean
33
- </div>
19
+ <section className="audit-sec" data-screen-label="02 Strengths">
20
+ <div className="audit-sec-head">
21
+ <span className="audit-sec-eyebrow">
22
+ <span className="ix">02</span>{"// strengths"}
23
+ </span>
24
+ <span className="audit-sec-meta">{strengths.length} standouts</span>
34
25
  </div>
35
- <h2 className="section-h">your agent does this right.</h2>
26
+ <h2 className="audit-sec-title">what it&apos;s great at</h2>
36
27
 
37
- <div className="strengths-grid">
28
+ <div className="strength-list">
38
29
  {strengths.map((s, i) => (
39
30
  <div key={i} className="strength-row">
40
- <div className="strength-check">✓</div>
41
- <div className="strength-body">
42
- <div className="strength-headline">{s.headline}</div>
43
- <div className="strength-detail">{s.detail}</div>
44
- </div>
45
- <div className="strength-metric">
31
+ <span className="strength-check" aria-hidden="true">✓</span>
32
+ <span className="strength-body">
33
+ <span className="strength-headline">{s.headline}</span>
34
+ <span className="strength-detail">{s.detail}</span>
35
+ </span>
36
+ <span className="strength-metric">
46
37
  {s.metric}
47
- <span className="unit">{s.unit}</span>
48
- </div>
38
+ {s.unit && <span className="unit">{s.unit}</span>}
39
+ </span>
49
40
  </div>
50
41
  ))}
51
42
  </div>
52
- <div className="strengths-footer">
53
- — these are your agent&apos;s defaults. keep them.
54
- </div>
55
43
  </section>
56
44
  );
57
45
  }