six-degrees 0.4.0-beta.1 → 0.4.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 (389) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +14 -14
  4. package/.next/standalone/.next/required-server-files.json +6 -1
  5. package/.next/standalone/.next/routes-manifest.json +12 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
  9. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +12 -12
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  20. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  22. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  24. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  26. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  30. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  47. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  54. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  59. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  61. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/favicon.ico.body +0 -0
  65. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/import.html +1 -1
  68. package/.next/standalone/.next/server/app/import.rsc +14 -14
  69. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +14 -14
  70. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  71. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
  72. package/.next/standalone/.next/server/app/index.html +1 -1
  73. package/.next/standalone/.next/server/app/index.rsc +14 -14
  74. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
  75. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  76. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  77. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/launch.html +2 -2
  80. package/.next/standalone/.next/server/app/launch.rsc +14 -14
  81. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +14 -14
  82. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  83. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
  84. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/paths.html +1 -1
  89. package/.next/standalone/.next/server/app/paths.rsc +14 -14
  90. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +14 -14
  91. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  92. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
  93. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app/profile.html +1 -1
  96. package/.next/standalone/.next/server/app/profile.rsc +14 -14
  97. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +14 -14
  98. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  99. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
  100. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/queue.html +1 -1
  103. package/.next/standalone/.next/server/app/queue.rsc +14 -14
  104. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +14 -14
  105. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  106. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
  107. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/app/scores.html +1 -1
  110. package/.next/standalone/.next/server/app/scores.rsc +14 -14
  111. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +14 -14
  112. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  113. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +12 -12
  114. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/settings.html +1 -1
  117. package/.next/standalone/.next/server/app/settings.rsc +14 -14
  118. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  119. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  120. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
  121. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/app/setup.html +1 -1
  124. package/.next/standalone/.next/server/app/setup.rsc +14 -14
  125. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +14 -14
  126. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  127. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
  128. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0rc27za._.js → [root-of-the-server]__0bat8nx._.js} +2 -2
  132. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__20tmem7._.js} +2 -2
  156. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -6
  157. package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  159. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/_1mxn_lb._.js +1 -1
  161. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +26 -8
  162. package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
  163. package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
  164. package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
  165. package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
  166. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
  167. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
  168. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
  169. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
  170. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
  171. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
  172. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
  173. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
  174. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
  175. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
  176. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00d8snt._.js +1 -1
  177. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00g61b8._.js +1 -1
  178. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08lf3gy._.js +1 -1
  179. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  180. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ho-u0w._.js +1 -1
  181. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ze473p._.js +1 -1
  182. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1cnxxc9._.js +1 -1
  184. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ll97ry._.js +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m0255u._.js +1 -1
  186. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1w9z0zv._.js +1 -1
  187. package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
  190. package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
  192. package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +1 -1
  193. package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/{_0cu6-an._.js → _1vpfyd-._.js} +2 -2
  195. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
  196. package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
  197. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
  198. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
  199. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +1 -1
  200. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  201. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
  202. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +1 -1
  203. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +6 -2
  204. package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +1 -1
  205. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
  206. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  207. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  208. package/.next/standalone/.next/server/pages/404.html +1 -1
  209. package/.next/standalone/.next/server/pages/500.html +1 -1
  210. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  211. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  212. package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  213. package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  214. package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
  215. package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  216. package/.next/standalone/.next/static/chunks/10784r7w7t5gw.js +18 -0
  217. package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  218. package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
  219. package/.next/standalone/.next/static/chunks/2p5yt1o9tnxjn.js +2 -0
  220. package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  221. package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  222. package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  223. package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  224. package/.next/standalone/.next/static/chunks/3ol1xux1-ofm4.js +6 -0
  225. package/.next/standalone/.next/static/chunks/3zs_6-nn0dapb.js +1 -0
  226. package/.next/standalone/.next/static/media/favicon.0zdzw5kurn8m-.ico +0 -0
  227. package/.next/standalone/CHANGELOG.md +211 -0
  228. package/.next/standalone/CONTRIBUTING.md +15 -4
  229. package/.next/standalone/README.md +21 -15
  230. package/.next/standalone/SECURITY.md +10 -0
  231. package/.next/standalone/app/api/admin-delete/route.js +3 -0
  232. package/.next/standalone/app/api/ingest/route.js +19 -1
  233. package/.next/standalone/app/api/scraper/route.js +79 -11
  234. package/.next/standalone/app/api/update-images/route.js +41 -7
  235. package/.next/standalone/app/avatars/[file]/route.js +6 -1
  236. package/.next/standalone/app/components/Avatar.js +5 -2
  237. package/.next/standalone/app/components/ChainView.js +713 -469
  238. package/.next/standalone/app/components/EmptyState.js +1 -1
  239. package/.next/standalone/app/components/FieldStep.js +94 -0
  240. package/.next/standalone/app/components/FilterPanel.js +37 -1
  241. package/.next/standalone/app/components/ForceGraph.js +228 -774
  242. package/.next/standalone/app/components/GridView.js +9 -7
  243. package/.next/standalone/app/components/LinkedInLimits.js +40 -8
  244. package/.next/standalone/app/components/ListView.js +6 -4
  245. package/.next/standalone/app/components/MappingProgress.js +5 -6
  246. package/.next/standalone/app/components/OnboardingGate.js +1 -1
  247. package/.next/standalone/app/components/OrbitGraph.js +3 -1
  248. package/.next/standalone/app/components/OutlinkQuest.js +5 -4
  249. package/.next/standalone/app/components/RingsView.js +9 -8
  250. package/.next/standalone/app/components/Sidebar.js +209 -112
  251. package/.next/standalone/app/components/TheirCircle.js +18 -11
  252. package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
  253. package/.next/standalone/app/components/settings/SectorSection.js +17 -191
  254. package/.next/standalone/app/components/settings/TierSection.js +7 -12
  255. package/.next/standalone/app/components/views.js +6 -4
  256. package/.next/standalone/app/favicon.ico +0 -0
  257. package/.next/standalone/app/globals.css +7 -0
  258. package/.next/standalone/app/launch/page.js +2 -2
  259. package/.next/standalone/app/page.js +153 -31
  260. package/.next/standalone/app/paths/page.js +11 -3
  261. package/.next/standalone/app/queue/page.js +18 -10
  262. package/.next/standalone/app/setup/page.js +423 -260
  263. package/.next/standalone/db/schema.js +15 -1
  264. package/.next/standalone/eslint.config.mjs +3 -3
  265. package/.next/standalone/install.sh +3 -3
  266. package/.next/standalone/lib/brokerage.js +94 -0
  267. package/.next/standalone/lib/chain-layout.js +228 -0
  268. package/.next/standalone/lib/circle.js +23 -4
  269. package/.next/standalone/lib/csv.js +4 -6
  270. package/.next/standalone/lib/data-export.js +2 -1
  271. package/.next/standalone/lib/data-folder.js +4 -2
  272. package/.next/standalone/lib/degrees.js +30 -0
  273. package/.next/standalone/lib/galaxy.js +12 -0
  274. package/.next/standalone/lib/ingest.js +18 -11
  275. package/.next/standalone/lib/linkedin-limits.js +35 -8
  276. package/.next/standalone/lib/photos.js +47 -0
  277. package/.next/standalone/lib/quest.js +8 -3
  278. package/.next/standalone/lib/reach.js +113 -0
  279. package/.next/standalone/lib/scan-state.js +1 -0
  280. package/.next/standalone/lib/scanner-setup.js +20 -1
  281. package/.next/standalone/lib/scoring.js +769 -18
  282. package/.next/standalone/lib/scraper-client.js +27 -0
  283. package/.next/standalone/lib/search-risk.js +38 -0
  284. package/.next/standalone/lib/sector-focus.js +13 -0
  285. package/.next/standalone/lib/separation.js +4 -2
  286. package/.next/standalone/lib/settings-client.js +21 -0
  287. package/.next/standalone/lib/settings.js +3 -1
  288. package/.next/standalone/lib/ties.js +23 -0
  289. package/.next/standalone/next.config.mjs +25 -3
  290. package/.next/standalone/package-lock.json +2 -2
  291. package/.next/standalone/package.json +1 -1
  292. package/.next/standalone/scripts/build-app.mjs +11 -2
  293. package/.next/standalone/scripts/check-node.mjs +1 -1
  294. package/.next/standalone/scripts/image_store.py +77 -21
  295. package/.next/standalone/scripts/scrape.py +271 -29
  296. package/.next/standalone/server.js +1 -1
  297. package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  298. package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  299. package/.next/static/chunks/0l1568qff0efj.js +1 -0
  300. package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  301. package/.next/static/chunks/10784r7w7t5gw.js +18 -0
  302. package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  303. package/.next/static/chunks/28bu26ausqzmn.js +1 -0
  304. package/.next/static/chunks/2p5yt1o9tnxjn.js +2 -0
  305. package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  306. package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  307. package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  308. package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  309. package/.next/static/chunks/3ol1xux1-ofm4.js +6 -0
  310. package/.next/static/chunks/3zs_6-nn0dapb.js +1 -0
  311. package/.next/static/media/favicon.0zdzw5kurn8m-.ico +0 -0
  312. package/CHANGELOG.md +211 -0
  313. package/README.md +21 -15
  314. package/SECURITY.md +10 -0
  315. package/db/schema.js +15 -1
  316. package/lib/brokerage.js +94 -0
  317. package/lib/chain-layout.js +228 -0
  318. package/lib/circle.js +23 -4
  319. package/lib/csv.js +4 -6
  320. package/lib/data-export.js +2 -1
  321. package/lib/data-folder.js +4 -2
  322. package/lib/degrees.js +30 -0
  323. package/lib/galaxy.js +12 -0
  324. package/lib/ingest.js +18 -11
  325. package/lib/linkedin-limits.js +35 -8
  326. package/lib/photos.js +47 -0
  327. package/lib/quest.js +8 -3
  328. package/lib/reach.js +113 -0
  329. package/lib/scan-state.js +1 -0
  330. package/lib/scanner-setup.js +20 -1
  331. package/lib/scoring.js +769 -18
  332. package/lib/scraper-client.js +27 -0
  333. package/lib/search-risk.js +38 -0
  334. package/lib/sector-focus.js +13 -0
  335. package/lib/separation.js +4 -2
  336. package/lib/settings-client.js +21 -0
  337. package/lib/settings.js +3 -1
  338. package/lib/ties.js +23 -0
  339. package/package.json +1 -1
  340. package/scripts/check-node.mjs +1 -1
  341. package/scripts/image_store.py +77 -21
  342. package/scripts/scrape.py +271 -29
  343. package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
  344. package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
  345. package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
  346. package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
  347. package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
  348. package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
  349. package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
  350. package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
  351. package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
  352. package/.next/standalone/.next/server/chunks/ssr/_0223v9s._.js +0 -3
  353. package/.next/standalone/.next/server/chunks/ssr/_0bcf13g._.js +0 -3
  354. package/.next/standalone/.next/server/chunks/ssr/_0wxbq78._.js +0 -3
  355. package/.next/standalone/.next/server/chunks/ssr/_0z88zcl._.js +0 -3
  356. package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
  357. package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
  358. package/.next/standalone/.next/static/chunks/0_yurn4q91vq8.js +0 -16
  359. package/.next/standalone/.next/static/chunks/0d_mh-06srh2k.js +0 -1
  360. package/.next/standalone/.next/static/chunks/0x3oadfikvb1c.js +0 -1
  361. package/.next/standalone/.next/static/chunks/1d5hl1io798n2.js +0 -1
  362. package/.next/standalone/.next/static/chunks/1qt67cvj9sjwi.js +0 -1
  363. package/.next/standalone/.next/static/chunks/1roznk7hkjes1.js +0 -44
  364. package/.next/standalone/.next/static/chunks/2_bf2n4gstad9.js +0 -33
  365. package/.next/standalone/.next/static/chunks/36gbwek52x_w-.js +0 -2
  366. package/.next/standalone/.next/static/chunks/3a55suosthntc.js +0 -1
  367. package/.next/standalone/.next/static/chunks/3fq1zq-6cfwbc.js +0 -1
  368. package/.next/standalone/.next/static/chunks/3nbnio971zlji.js +0 -2
  369. package/.next/standalone/.next/static/chunks/3s6awp3qq9s7w.js +0 -1
  370. package/.next/standalone/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  371. package/.next/static/chunks/0_yurn4q91vq8.js +0 -16
  372. package/.next/static/chunks/0d_mh-06srh2k.js +0 -1
  373. package/.next/static/chunks/0x3oadfikvb1c.js +0 -1
  374. package/.next/static/chunks/1d5hl1io798n2.js +0 -1
  375. package/.next/static/chunks/1qt67cvj9sjwi.js +0 -1
  376. package/.next/static/chunks/1roznk7hkjes1.js +0 -44
  377. package/.next/static/chunks/2_bf2n4gstad9.js +0 -33
  378. package/.next/static/chunks/36gbwek52x_w-.js +0 -2
  379. package/.next/static/chunks/3a55suosthntc.js +0 -1
  380. package/.next/static/chunks/3fq1zq-6cfwbc.js +0 -1
  381. package/.next/static/chunks/3nbnio971zlji.js +0 -2
  382. package/.next/static/chunks/3s6awp3qq9s7w.js +0 -1
  383. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  384. /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_buildManifest.js +0 -0
  385. /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_clientMiddlewareManifest.js +0 -0
  386. /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_ssgManifest.js +0 -0
  387. /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_buildManifest.js +0 -0
  388. /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_clientMiddlewareManifest.js +0 -0
  389. /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → LlDgA9g8ToBkDLlo9Qtov}/_ssgManifest.js +0 -0
@@ -1,5 +1,7 @@
1
1
  'use client';
2
2
 
3
+ import { localPhoto } from '../../lib/photos';
4
+
3
5
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
4
6
  const TIERS = ['S', 'A', 'B', 'C', 'D'];
5
7
 
@@ -38,8 +40,8 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
38
40
  onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.03)'}
39
41
  >
40
42
  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
41
- {cl.bridge.profile_image_url ? (
42
- <img src={cl.bridge.profile_image_url} alt="" style={{
43
+ {localPhoto(cl.bridge.profile_image_url) ? (
44
+ <img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{
43
45
  width: 40, height: 40, borderRadius: '50%', objectFit: 'cover',
44
46
  border: `2px solid ${TIER_COLORS[cl.bridge.tier]}`,
45
47
  }} onError={e => { e.target.style.display = 'none'; }} />
@@ -67,13 +69,13 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
67
69
  {cl.members.slice(0, 8).map((m, i) => (
68
70
  <div key={m.id} style={{
69
71
  width: 22, height: 22, borderRadius: '50%', fontSize: 8, fontWeight: 700,
70
- background: m.profile_image_url ? 'transparent' : (TIER_COLORS[m.tier] || '#333'),
72
+ background: localPhoto(m.profile_image_url) ? 'transparent' : (TIER_COLORS[m.tier] || '#333'),
71
73
  color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
72
74
  border: '2px solid #0a0a1a', marginLeft: i > 0 ? -6 : 0, position: 'relative', zIndex: 8 - i,
73
75
  overflow: 'hidden',
74
76
  }}>
75
- {m.profile_image_url ? (
76
- <img src={m.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
77
+ {localPhoto(m.profile_image_url) ? (
78
+ <img src={localPhoto(m.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
77
79
  ) : m.name?.charAt(0)}
78
80
  </div>
79
81
  ))}
@@ -131,8 +133,8 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
131
133
  onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.03)'}
132
134
  >
133
135
  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
134
- {c.profile_image_url ? (
135
- <img src={c.profile_image_url} alt="" style={{
136
+ {localPhoto(c.profile_image_url) ? (
137
+ <img src={localPhoto(c.profile_image_url)} alt="" style={{
136
138
  width: 32, height: 32, borderRadius: '50%', objectFit: 'cover',
137
139
  border: `2px solid ${TIER_COLORS[tier]}`,
138
140
  }} onError={e => { e.target.style.display = 'none'; }} />
@@ -1,16 +1,19 @@
1
1
  'use client';
2
2
 
3
- // The Scan page's view of what LinkedIn allows: the search budget, the cooldown
4
- // lock, and everyone whose list was only partly read, with a way back into each.
5
- // TRAPS §35. The numbers come from /api/scraper (lib/linkedin-limits.js,
6
- // lib/paused.js), which reads the same files the scanner writes.
3
+ // The Scan page's view of what LinkedIn allows: the search budget and the cap
4
+ // on profile views, the cooldown lock, and everyone whose list was only partly
5
+ // read, with a way back into each. TRAPS §16, §35. The numbers come from
6
+ // /api/scraper (lib/linkedin-limits.js, lib/paused.js), which reads the same
7
+ // files the scanner writes.
7
8
 
8
9
  import { useState } from 'react';
10
+ import { limitQuestion, limitNote, SAFE_LIMITS } from '../../lib/search-risk';
9
11
 
10
12
  const LINE = '1px solid rgba(255,255,255,0.1)';
11
13
  const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
12
14
  const DAILY = [25, 50, 100, 200, 500];
13
15
  const MONTHLY = [100, 250, 500, 1000, 0];
16
+ const PROFILES = [10, 25, 50, 100];
14
17
 
15
18
  const when = (ms) => new Date(ms).toLocaleString(undefined, { weekday: 'short', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
16
19
  const day = (ms) => new Date(ms).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
@@ -55,6 +58,15 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
55
58
  if (!li) return null;
56
59
  const { limits } = li;
57
60
  const today = li.unreadable ? '?' : li.searchesToday;
61
+ const views = li.unreadable ? '?' : li.profilesToday;
62
+ const set = (change) => onSetLimits({ ...limits, ...change });
63
+ // A budget past what LinkedIn has put up with is asked about first (lib/search-risk.js).
64
+ const ask = (change) => {
65
+ const q = limitQuestion(limits, { ...limits, ...change });
66
+ if (q && !window.confirm(q)) return;
67
+ set(change);
68
+ };
69
+ const note = limitNote(limits);
58
70
  return (
59
71
  <div style={{ padding: '12px 14px', borderRadius: 8, border: LINE, background: 'rgba(255,255,255,0.03)', fontSize: 12.5, color: '#b8c4c4' }}>
60
72
  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 14 }}>
@@ -66,22 +78,42 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
66
78
  <div><b style={{ color: '#fff' }}>{li.searchesMonth}</b> of {limits.monthly || 'no limit'} this month · resets {day(li.monthResets)}</div>
67
79
  <Bar used={li.searchesMonth} cap={limits.monthly} />
68
80
  </div>
81
+ <div>
82
+ <div><b style={{ color: '#fff' }}>{views}</b> of {limits.profiles} profile views today</div>
83
+ <Bar used={li.unreadable ? limits.profiles : li.profilesToday} cap={limits.profiles} />
84
+ </div>
69
85
  </div>
70
86
  <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginTop: 10 }}>
71
87
  <span>Budget:</span>
72
- <select value={limits.daily} disabled={disabled} onChange={(e) => onSetLimits({ daily: Number(e.target.value), monthly: limits.monthly })} style={sel}>
88
+ <select value={limits.daily} disabled={disabled} onChange={(e) => ask({ daily: Number(e.target.value) })} style={sel}>
73
89
  {DAILY.map((n) => <option key={n} value={n}>{n} a day</option>)}
74
90
  </select>
75
- <select value={limits.monthly} disabled={disabled} onChange={(e) => onSetLimits({ daily: limits.daily, monthly: Number(e.target.value) })} style={sel}>
91
+ <select value={limits.monthly} disabled={disabled} onChange={(e) => ask({ monthly: Number(e.target.value) })} style={sel}>
76
92
  {MONTHLY.map((n) => <option key={n} value={n}>{n ? `${n} a month` : 'no monthly cap (Premium)'}</option>)}
77
93
  </select>
78
- <span style={{ color: '#778' }}>{li.profilesToday} profile views today</span>
94
+ <select value={limits.profiles} disabled={disabled} onChange={(e) => set({ profiles: Number(e.target.value) })} style={sel}>
95
+ {PROFILES.map((n) => <option key={n} value={n}>{n} profile views a day</option>)}
96
+ </select>
79
97
  </div>
80
98
  <div style={{ marginTop: 8, color: '#778', lineHeight: 1.6 }}>
81
99
  Every page of someone&rsquo;s connections is one search. LinkedIn limits a free account&rsquo;s people
82
100
  searches by the month (it doesn&rsquo;t say how many; reports put it around 250–350), resetting on the 1st.
83
101
  When a budget is used, a scan saves what it read and stops; the next one carries on from the same page.
84
- {limits.daily > 100 && <b style={{ color: '#FFD700' }}> {limits.daily} a day can use up a free account&rsquo;s month in a day or two.</b>}
102
+ </div>
103
+ {note && (
104
+ <div style={{ marginTop: 8, display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap', color: '#FFD700', lineHeight: 1.6 }}>
105
+ <b style={{ flex: 1, minWidth: 200 }}>{note}</b>
106
+ <button
107
+ onClick={() => set(SAFE_LIMITS)}
108
+ disabled={disabled}
109
+ style={{ ...sel, cursor: disabled ? 'not-allowed' : 'pointer', color: '#FFD700', borderColor: 'rgba(255,215,0,0.4)' }}
110
+ >Back to {SAFE_LIMITS.daily} a day, {SAFE_LIMITS.monthly} a month</button>
111
+ </div>
112
+ )}
113
+ <div style={{ marginTop: 6, color: '#778', lineHeight: 1.6 }}>
114
+ Each circle scan opens the person&rsquo;s profile once, which is one profile view. Profile views are what
115
+ LinkedIn restricted an account for, after about 20 in an hour. The scanner opens at most one a minute, and
116
+ once today&rsquo;s are used, a scan stops before the next profile and tries that person next time.
85
117
  </div>
86
118
  </div>
87
119
  );
@@ -1,5 +1,7 @@
1
1
  'use client';
2
2
 
3
+ import { localPhoto } from '../../lib/photos';
4
+
3
5
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
4
6
 
5
7
  export default function ListView({ connections, degree2 = [], onSelect, mode }) {
@@ -47,8 +49,8 @@ export default function ListView({ connections, degree2 = [], onSelect, mode })
47
49
  >
48
50
  <span style={{ fontSize: 12, fontWeight: 700, color: '#444' }}>{i + 1}</span>
49
51
  <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
50
- {cl.bridge.profile_image_url ? (
51
- <img src={cl.bridge.profile_image_url} alt="" style={{
52
+ {localPhoto(cl.bridge.profile_image_url) ? (
53
+ <img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{
52
54
  width: 28, height: 28, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
53
55
  border: `2px solid ${TIER_COLORS[cl.bridge.tier]}`,
54
56
  }} onError={e => { e.target.style.display = 'none'; }} />
@@ -113,8 +115,8 @@ export default function ListView({ connections, degree2 = [], onSelect, mode })
113
115
  onMouseLeave={e => e.currentTarget.style.background = i < 3 ? `${TIER_COLORS[c.tier]}08` : 'transparent'}
114
116
  >
115
117
  <span style={{ fontSize: 12, fontWeight: 700, color: i < 3 ? TIER_COLORS[c.tier] : '#444' }}>{i + 1}</span>
116
- {c.profile_image_url ? (
117
- <img src={c.profile_image_url} alt="" style={{
118
+ {localPhoto(c.profile_image_url) ? (
119
+ <img src={localPhoto(c.profile_image_url)} alt="" style={{
118
120
  width: 28, height: 28, borderRadius: '50%', objectFit: 'cover',
119
121
  border: `2px solid ${TIER_COLORS[c.tier]}`,
120
122
  }} onError={e => { e.target.style.display = 'none'; }} />
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { TIER_COLORS_CLASSIC as TIER_COLORS } from '../../lib/tiers';
4
+ import { reachIndex, circleState } from '../../lib/reach';
4
5
 
5
6
  // How much of your 1st degree has had its circle opened.
6
7
  //
@@ -19,13 +20,11 @@ const CARD = {
19
20
  };
20
21
 
21
22
  export function computeMapping(degree1 = [], degree2 = [], skips = []) {
22
- const mappedIds = new Set(degree2.map((c) => c.source_connection_id).filter(Boolean));
23
- const hiddenUrls = new Set(skips.map((s) => s.profileUrl).filter(Boolean));
24
-
23
+ // One rule for whose circle is open, hidden or still to do: lib/reach.js.
24
+ const reach = reachIndex(degree1, degree2, { skips });
25
25
  const state = (c) => {
26
- if (mappedIds.has(c.id)) return 'mapped';
27
- if (c.profile_url && hiddenUrls.has(c.profile_url)) return 'hidden';
28
- return 'todo';
26
+ const s = circleState(c, reach);
27
+ return s === 'scanned' ? 'mapped' : s;
29
28
  };
30
29
 
31
30
  const counts = { mapped: 0, hidden: 0, todo: 0 };
@@ -24,7 +24,7 @@ export default function OnboardingGate({ children }) {
24
24
  ) : (
25
25
  <div style={{ maxWidth: 420 }}>
26
26
  <div style={{ fontSize: 18, fontWeight: 700, marginBottom: 8 }}>
27
- Can’t reach 6 Degrees’ local server
27
+ Can’t reach Six Degrees’ local server
28
28
  </div>
29
29
  <div style={{ color: '#888', fontSize: 14, lineHeight: 1.6 }}>
30
30
  The page loaded but the app behind it is not answering. If you started it
@@ -32,6 +32,7 @@ import {
32
32
  zoomTransform,
33
33
  } from 'd3';
34
34
  import { TIER_COLORS, TIER_ORDER, TIER_RING_RADIUS, initialsFor } from '../../lib/tiers';
35
+ import { localPhoto } from '../../lib/photos';
35
36
 
36
37
  const GOLDEN_ANGLE = 2.399963;
37
38
  const LABEL_ZOOM_THRESHOLD = 1.1;
@@ -374,7 +375,8 @@ const OrbitGraph = forwardRef(function OrbitGraph(
374
375
  .attr('font-size', d.r * 0.8).attr('font-weight', 700)
375
376
  .attr('fill', '#0b0b16').style('pointer-events', 'none');
376
377
 
377
- const imageUrl = isUser ? userImage : (conn?.profile_image_url ?? null);
378
+ // Only a photo saved on this computer (lib/photos.js).
379
+ const imageUrl = localPhoto(isUser ? userImage : conn?.profile_image_url);
378
380
  if (imageUrl) {
379
381
  const clipId = `og-clip-${safeId(d.id)}`;
380
382
  defs.append('clipPath').attr('id', clipId).append('circle').attr('r', d.r);
@@ -11,6 +11,7 @@ import { useMemo, useState } from 'react';
11
11
  import Link from 'next/link';
12
12
  import { buildQuest, XP_SEND, STAGE_SIZE } from '../../lib/quest';
13
13
  import { initialsFor } from '../../lib/tiers';
14
+ import { localPhoto } from '../../lib/photos';
14
15
 
15
16
  const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
16
17
  const LINE = '1px solid rgba(255,255,255,0.1)';
@@ -24,8 +25,8 @@ function Face({ person, size = 36, ring }) {
24
25
  display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: size * 0.34, fontWeight: 800,
25
26
  border: `2px solid ${ring || color}`,
26
27
  }}>{initialsFor(person?.name || '?')}</div>
27
- {person?.profile_image_url && (
28
- <img src={person.profile_image_url} alt="" onError={(e) => { e.currentTarget.style.display = 'none'; }}
28
+ {localPhoto(person?.profile_image_url) && (
29
+ <img src={localPhoto(person.profile_image_url)} alt="" onError={(e) => { e.currentTarget.style.display = 'none'; }}
29
30
  style={{ position: 'absolute', inset: 0, width: size, height: size, borderRadius: '50%', objectFit: 'cover', border: `2px solid ${ring || color}` }} />
30
31
  )}
31
32
  </div>
@@ -47,8 +48,8 @@ function Ring({ progress, size = 86, color = '#FF6B35', children }) {
47
48
  );
48
49
  }
49
50
 
50
- export default function OutlinkQuest({ recs, sentIds, added, mappedIds, onSend, onUndo }) {
51
- const quest = useMemo(() => buildQuest({ recs, sentIds, added, mappedIds }), [recs, sentIds, added, mappedIds]);
51
+ export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, onSend, onUndo }) {
52
+ const quest = useMemo(() => buildQuest({ recs, sentIds, added, mappedIds, reach }), [recs, sentIds, added, mappedIds, reach]);
52
53
  const [open, setOpen] = useState(null); // bridge id of the expanded cluster
53
54
  const [toast, setToast] = useState(null);
54
55
  const { level } = quest;
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState } from 'react';
4
+ import { localPhoto } from '../../lib/photos';
4
5
 
5
6
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
6
7
  const TIERS = ['S', 'A', 'B', 'C', 'D'];
@@ -58,8 +59,8 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
58
59
  boxShadow: hoveredId === cl.bridge.id ? `0 0 20px ${TIER_COLORS[cl.bridge.tier]}60` : `0 0 10px ${TIER_COLORS[cl.bridge.tier]}20`,
59
60
  transition: 'box-shadow 0.2s',
60
61
  }}>
61
- {cl.bridge.profile_image_url ? (
62
- <img src={cl.bridge.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
62
+ {localPhoto(cl.bridge.profile_image_url) ? (
63
+ <img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
63
64
  ) : (
64
65
  <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
65
66
  background: TIER_COLORS[cl.bridge.tier], color: cl.bridge.tier === 'S' ? '#000' : '#fff', fontSize: 20, fontWeight: 800 }}>
@@ -95,13 +96,13 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
95
96
  <div style={{
96
97
  width: size, height: size, borderRadius: '50%',
97
98
  border: `2px solid ${TIER_COLORS[tier]}`,
98
- overflow: 'hidden', background: p.profile_image_url ? '#111' : TIER_COLORS[tier],
99
+ overflow: 'hidden', background: localPhoto(p.profile_image_url) ? '#111' : TIER_COLORS[tier],
99
100
  transition: 'transform 0.15s, box-shadow 0.15s',
100
101
  transform: isHov ? 'scale(1.3)' : 'scale(1)',
101
102
  boxShadow: isHov ? `0 0 12px ${TIER_COLORS[tier]}60` : 'none',
102
103
  }}>
103
- {p.profile_image_url ? (
104
- <img src={p.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}
104
+ {localPhoto(p.profile_image_url) ? (
105
+ <img src={localPhoto(p.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}
105
106
  onError={e => { e.target.style.display = 'none'; e.target.parentElement.style.background = TIER_COLORS[tier]; }} />
106
107
  ) : (
107
108
  <span style={{ display: 'flex', width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center',
@@ -202,7 +203,7 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
202
203
  width: size, height: size, borderRadius: '50%',
203
204
  border: `2px solid ${TIER_COLORS[tier]}`,
204
205
  overflow: 'hidden',
205
- background: c.profile_image_url ? '#111' : TIER_COLORS[tier],
206
+ background: localPhoto(c.profile_image_url) ? '#111' : TIER_COLORS[tier],
206
207
  display: 'flex', alignItems: 'center', justifyContent: 'center',
207
208
  transition: 'transform 0.15s, box-shadow 0.15s',
208
209
  transform: isHovered ? 'scale(1.3)' : 'scale(1)',
@@ -210,8 +211,8 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
210
211
  zIndex: isHovered ? 10 : 1,
211
212
  position: 'relative',
212
213
  }}>
213
- {c.profile_image_url ? (
214
- <img src={c.profile_image_url} alt="" style={{
214
+ {localPhoto(c.profile_image_url) ? (
215
+ <img src={localPhoto(c.profile_image_url)} alt="" style={{
215
216
  width: '100%', height: '100%', objectFit: 'cover',
216
217
  }} onError={e => {
217
218
  e.target.style.display = 'none';