six-degrees 0.4.11 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) 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 +9 -9
  4. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +12 -12
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  14. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +11 -11
  15. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  17. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  21. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +1 -1
  25. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/company-scores/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  36. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  38. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  40. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
  44. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  46. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
  48. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/settings/route.js +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 +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +1 -1
  54. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/social/route.js +3 -3
  60. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  62. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  65. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  67. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/import.html +1 -1
  72. package/.next/standalone/.next/server/app/import.rsc +14 -14
  73. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +14 -14
  74. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  75. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +13 -13
  76. package/.next/standalone/.next/server/app/index.html +1 -1
  77. package/.next/standalone/.next/server/app/index.rsc +14 -14
  78. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  79. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  80. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  81. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/launch.html +2 -2
  84. package/.next/standalone/.next/server/app/launch.rsc +14 -14
  85. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +14 -14
  86. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  87. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +13 -13
  88. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  90. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/paths.html +1 -1
  93. package/.next/standalone/.next/server/app/paths.rsc +14 -14
  94. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +14 -14
  95. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  96. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +13 -13
  97. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/profile.html +1 -1
  100. package/.next/standalone/.next/server/app/profile.rsc +14 -14
  101. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +14 -14
  102. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  103. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +13 -13
  104. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/queue.html +1 -1
  107. package/.next/standalone/.next/server/app/queue.rsc +14 -14
  108. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +14 -14
  109. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  110. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +13 -13
  111. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  113. package/.next/standalone/.next/server/app/scores.html +1 -1
  114. package/.next/standalone/.next/server/app/scores.rsc +14 -14
  115. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +14 -14
  116. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
  117. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +13 -13
  118. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  119. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/app/settings.html +1 -1
  121. package/.next/standalone/.next/server/app/settings.rsc +14 -14
  122. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  123. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  124. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +13 -13
  125. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  126. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  127. package/.next/standalone/.next/server/app/setup.html +1 -1
  128. package/.next/standalone/.next/server/app/setup.rsc +14 -14
  129. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +14 -14
  130. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  131. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +13 -13
  132. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/app/social.html +1 -1
  135. package/.next/standalone/.next/server/app/social.rsc +14 -14
  136. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +14 -14
  137. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +1 -1
  138. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +13 -13
  139. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__13dx4sd._.js} +2 -2
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
  143. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  145. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +8 -0
  148. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
  149. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
  150. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
  151. package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
  152. package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
  153. package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
  154. package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
  155. package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
  156. package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
  157. package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
  158. package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
  159. package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
  160. package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
  161. package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
  162. package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
  163. package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
  164. package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
  165. package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
  166. package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
  168. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
  169. package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
  170. package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
  171. package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
  172. package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
  173. package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
  174. package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
  175. package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
  176. package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
  177. package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
  178. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
  179. package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
  180. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
  181. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  182. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +6 -5
  184. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +19 -3
  186. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -2
  187. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
  190. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  191. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  192. package/.next/standalone/.next/server/pages/404.html +1 -1
  193. package/.next/standalone/.next/server/pages/500.html +1 -1
  194. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  195. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  196. package/.next/standalone/.next/static/chunks/03bziijw5srol.js +1 -0
  197. package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  198. package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  199. package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  200. package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  201. package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  202. package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  203. package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
  204. package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  205. package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
  206. package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  207. package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  208. package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
  209. package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  210. package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  211. package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  212. package/.next/standalone/CHANGELOG.md +187 -0
  213. package/.next/standalone/app/api/ingest/route.js +40 -1
  214. package/.next/standalone/app/api/scraper/route.js +7 -1
  215. package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
  216. package/.next/standalone/app/components/AppTabs.js +59 -0
  217. package/.next/standalone/app/components/ChainView.js +303 -89
  218. package/.next/standalone/app/components/CirclePanel.js +162 -0
  219. package/.next/standalone/app/components/CompanyScores.js +2 -2
  220. package/.next/standalone/app/components/FieldStep.js +4 -4
  221. package/.next/standalone/app/components/FilterPanel.js +121 -183
  222. package/.next/standalone/app/components/ForceGraph.js +41 -3
  223. package/.next/standalone/app/components/GalaxyLab.js +26 -31
  224. package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
  225. package/.next/standalone/app/components/MapControls.js +154 -0
  226. package/.next/standalone/app/components/NoteDetail.js +118 -0
  227. package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
  228. package/.next/standalone/app/components/PeopleMap.js +308 -0
  229. package/.next/standalone/app/components/ScanRadar.js +129 -0
  230. package/.next/standalone/app/components/ScanStatusBar.js +71 -27
  231. package/.next/standalone/app/components/SeparationView.js +626 -118
  232. package/.next/standalone/app/components/Sidebar.js +28 -2
  233. package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
  234. package/.next/standalone/app/components/settings/SectorSection.js +2 -2
  235. package/.next/standalone/app/components/settings/TierSection.js +1 -1
  236. package/.next/standalone/app/components/settings/TitleSection.js +149 -0
  237. package/.next/standalone/app/components/views.js +15 -14
  238. package/.next/standalone/app/page.js +89 -156
  239. package/.next/standalone/app/paths/page.js +39 -27
  240. package/.next/standalone/app/profile/page.js +33 -5
  241. package/.next/standalone/app/queue/page.js +56 -34
  242. package/.next/standalone/app/scores/page.js +16 -66
  243. package/.next/standalone/app/settings/page.js +27 -13
  244. package/.next/standalone/app/setup/page.js +298 -205
  245. package/.next/standalone/app/social/SocialHub.js +374 -0
  246. package/.next/standalone/app/social/page.js +10 -362
  247. package/.next/standalone/lib/chain-layout.js +105 -35
  248. package/.next/standalone/lib/degrees.js +0 -6
  249. package/.next/standalone/lib/dot-rings.js +25 -0
  250. package/.next/standalone/lib/galaxy-lab.js +65 -6
  251. package/.next/standalone/lib/ingest.js +2 -0
  252. package/.next/standalone/lib/island.js +40 -0
  253. package/.next/standalone/lib/linkedin-limits.js +6 -2
  254. package/.next/standalone/lib/map-heat.js +20 -0
  255. package/.next/standalone/lib/network-health.js +1 -1
  256. package/.next/standalone/lib/notifications.js +121 -0
  257. package/.next/standalone/lib/people-map.js +82 -0
  258. package/.next/standalone/lib/rarity.js +45 -3
  259. package/.next/standalone/lib/reach.js +7 -6
  260. package/.next/standalone/lib/rpc.js +15 -4
  261. package/.next/standalone/lib/scan-pace.js +46 -0
  262. package/.next/standalone/lib/scoring.js +39 -8
  263. package/.next/standalone/lib/scraper-client.js +19 -6
  264. package/.next/standalone/lib/sector-focus.js +4 -4
  265. package/.next/standalone/lib/separation.js +137 -10
  266. package/.next/standalone/lib/settings-effects.js +20 -11
  267. package/.next/standalone/lib/settings.js +3 -0
  268. package/.next/standalone/lib/tier-grid.js +99 -0
  269. package/.next/standalone/lib/title-ranking.js +83 -0
  270. package/.next/standalone/package-lock.json +2 -2
  271. package/.next/standalone/package.json +1 -1
  272. package/.next/standalone/scripts/scrape.py +51 -13
  273. package/.next/static/chunks/03bziijw5srol.js +1 -0
  274. package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  275. package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  276. package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  277. package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  278. package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  279. package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  280. package/.next/static/chunks/1i51yylxdzht1.js +1 -0
  281. package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  282. package/.next/static/chunks/2rqtpccruavf2.js +1 -0
  283. package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  284. package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  285. package/.next/static/chunks/355ryio1b7sm1.js +1 -0
  286. package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  287. package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  288. package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  289. package/CHANGELOG.md +187 -0
  290. package/lib/chain-layout.js +105 -35
  291. package/lib/degrees.js +0 -6
  292. package/lib/dot-rings.js +25 -0
  293. package/lib/galaxy-lab.js +65 -6
  294. package/lib/ingest.js +2 -0
  295. package/lib/island.js +40 -0
  296. package/lib/linkedin-limits.js +6 -2
  297. package/lib/map-heat.js +20 -0
  298. package/lib/network-health.js +1 -1
  299. package/lib/notifications.js +121 -0
  300. package/lib/people-map.js +82 -0
  301. package/lib/rarity.js +45 -3
  302. package/lib/reach.js +7 -6
  303. package/lib/rpc.js +15 -4
  304. package/lib/scan-pace.js +46 -0
  305. package/lib/scoring.js +39 -8
  306. package/lib/scraper-client.js +19 -6
  307. package/lib/sector-focus.js +4 -4
  308. package/lib/separation.js +137 -10
  309. package/lib/settings-effects.js +20 -11
  310. package/lib/settings.js +3 -0
  311. package/lib/tier-grid.js +99 -0
  312. package/lib/title-ranking.js +83 -0
  313. package/package.json +1 -1
  314. package/scripts/scrape.py +51 -13
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
  318. package/.next/standalone/.next/server/chunks/lib_rpc_05gt5ya.js +0 -3
  319. package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
  320. package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
  321. package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
  322. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
  323. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
  324. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
  325. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
  326. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
  327. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
  328. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
  329. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
  330. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
  331. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/_0-85h6g._.js +0 -3
  333. package/.next/standalone/.next/server/chunks/ssr/_08cfj2b._.js +0 -3
  334. package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
  335. package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
  336. package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
  337. package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
  338. package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
  339. package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
  340. package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
  341. package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
  342. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
  343. package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  344. package/.next/standalone/.next/static/chunks/15sjx07xhpn6p.js +0 -1
  345. package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
  346. package/.next/standalone/.next/static/chunks/1a74dky22dxd6.js +0 -1
  347. package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
  348. package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  349. package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
  350. package/.next/standalone/.next/static/chunks/297e1z34yi--z.js +0 -1
  351. package/.next/standalone/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
  352. package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  353. package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  354. package/.next/standalone/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
  355. package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  356. package/.next/standalone/app/components/OrbitGraph.js +0 -737
  357. package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  358. package/.next/static/chunks/15sjx07xhpn6p.js +0 -1
  359. package/.next/static/chunks/160wjb73ddwqd.js +0 -6
  360. package/.next/static/chunks/1a74dky22dxd6.js +0 -1
  361. package/.next/static/chunks/1c_n0973t07l0.js +0 -2
  362. package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  363. package/.next/static/chunks/236f0jtp68j_h.js +0 -1
  364. package/.next/static/chunks/297e1z34yi--z.js +0 -1
  365. package/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
  366. package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  367. package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  368. package/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
  369. package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  370. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  371. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  372. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
  373. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  374. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  375. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
@@ -6,6 +6,193 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.0] - 2026-10-02
10
+
11
+ ### Added
12
+ - **Rank titles your way** (Settings → Scores → Titles). Set how much each kind of title counts, out
13
+ of 10, or start from a preset: Founders first, Investors, or A role I'm looking for. Add the roles
14
+ you're after ("account executive", "head of growth", "recruiter") with their own points, and
15
+ everyone whose title has those words rises, whatever their level: for outbound to one role, or
16
+ hiring for it. Saving rescores everyone and says how many people changed tier, and a card's score
17
+ says "your ranking" where it moved a title. Nothing set scores exactly as before.
18
+
19
+ ### Changed
20
+ - **The Scan page is a launch, not a manual.** It opens with what the scanner is and what it never does
21
+ (never posts or messages anyone, never sees your password, nothing leaves this Mac, stops when you
22
+ say), then the four steps on a rail: Get ready, Sign in, Who you know, Who they know. The one
23
+ you're on pulses, with one big button that does it and how long it takes; any other opens with a
24
+ tap. While it runs, "Your scanner is working" and Stop sit right under the step. How many people
25
+ this round and who first are beside the radar; the daily budget, how much of each list, the hidden
26
+ window, retrying hidden lists and paused lists are in Fine-tune, closed until you want them. The
27
+ log is one tap away, and opens by itself when a run stops badly. Scan has the same header as every
28
+ page now.
29
+ - **Paths reads like Network Circle.** Its notch is now Map and Companies: companies count for more
30
+ than industries, and an industry opens from a company's card (its industry is a link there). Whether
31
+ the map's bubbles are companies or your connections (what was the People tab) is a switch at the
32
+ top of the Filters panel. Filters use the same tiers × degrees grid as Network Circle. Colour is by
33
+ sector or by **Heat**: for companies, how strong your people there are (the average power of the
34
+ five strongest); for people, how strong their cluster is, person for person; size still says how
35
+ many. And **Physics**: switch it to Live to drag bubbles (the ones joined to them follow) and change
36
+ Spread, Pull to their group and Pull along lines. Still, as before, is the default. Old links to
37
+ the People or Industries tab still land on the map.
38
+ - **Heat: power as a thermal map.** A new Colour by in Network Circle's Physics: every dot coloured
39
+ from cold violet through red and orange to white hot by where their power score ranks among the
40
+ people showing, with a soft glow behind the hotter ones that pools where powerful people cluster,
41
+ like a thermal camera. Works with every layout, the replay and the filter grid.
42
+ - **Separation all the way right counts every mutual, scanned or not.** It aims at whoever you share
43
+ the most mutual connections with, even when most of them are in circles you haven't scanned: an
44
+ unscanned mutual is still a likely introduction, so it no longer holds someone back. The ones the
45
+ app can draw are lines as before; the rest are grey dots, each with a faint line in, with "+37 more
46
+ mutuals, in circles not scanned yet". The person glows less the less of it is scanned. The Next
47
+ names are in the same order and say their mutual counts. Narrow the tiers (S only, say) first to
48
+ aim at the strongest.
49
+ - **The right panel follows the circle you're in.** Open someone's circle in Bridge Chains and the
50
+ panel is about them: how many people are in their circle, tier chips to see just one tier, a
51
+ search inside it, the people you added from it (with Scan… for the ones ready), then everyone
52
+ else in it, strongest first. Click anyone for their card.
53
+ - **Orbit is a layout of the Galaxy now**, in Physics next to Rings and Clusters, not a separate
54
+ view: every tier held on its own orbit, S nearest you, and each connection's circle tucked in
55
+ behind them. It's still the Galaxy, so you can drag dots and move every slider on it.
56
+ - **Light up a branch on hover starts off.** It dimmed and relit thousands of dots on every hover,
57
+ which flickered; it's still in Physics if you want it, and moving from one dot to the next no
58
+ longer flashes the whole Galaxy in between.
59
+ - **Social is part of Outlink.** Outlink has the same header as every page, and its views sit in
60
+ the notch: Circles, To add, Pending, and Messages & follow-ups, which is the whole Social tab (your
61
+ LinkedIn export, the live messages sync, the CRM with its inbox, follow-ups, pipeline, sent and
62
+ received). Adding people and keeping track of them are now one place. The Social tab is gone from
63
+ the header, and old links to it land on Messages & follow-ups.
64
+ - **Notifications open.** Click one and it takes over the right panel with the detail: who it is
65
+ about, their tier and score, who you met them through, and, for an S or A, that they're a
66
+ valuable person to know; with buttons to open their card, their circle in Bridge Chains, or
67
+ them in Separation. A notification naming several people lists them all.
68
+ - **New notifications:** someone you met through a bridge added you back ("Ana added you back,
69
+ through Yuki"); a circle scan found people at top companies (a company score of 8 or more), named
70
+ in one notification; and a scan finished ("Scan done: Yuki's circle", "Check for new: done").
71
+ Accepted requests and high-value new connections now say who they are about, and older ones are
72
+ matched by the name in their title.
73
+ - **Paths has the same header as the map.** The app's tabs stay at the top, with Paths lit, and Map,
74
+ People, Industries and Companies sit in the notch under them, as the map page's views do; a tab
75
+ for Network Circle, Degrees or Separation takes you straight to it. Paths' filters moved into a
76
+ Filters panel on the left, like the map page's, with new ways to read the map: what a bubble's
77
+ size means (your people there, the S and A among them, or the ones you already know; on People,
78
+ cluster value, size or S tier) and which names show (the biggest, all, or none).
79
+ - **All the way to easy, Separation aims at whoever the most of your connections lead to**, with
80
+ every one of those lines drawn, the strongest first among equals; the "Next" names are the
81
+ runners-up by the same measure. It used to aim at the top of the list, which could be someone
82
+ with many mutual connections by LinkedIn's count but only one the app could draw.
83
+ - **The Scan page has a radar, and a speed: Slow, Medium or Fast.** Mapping the 2nd degree starts
84
+ from a round Scan button in the middle of a radar: its sweep turns while a scan runs, and the
85
+ ring round it fills with today's searches against your budget. Beside it, three speeds. Fast is
86
+ how the scanner has always run, and nothing is faster; Medium waits 45 seconds before each page
87
+ of results and 3 minutes after every 10, Slow 90 seconds and 5 minutes, and both keep profiles
88
+ further apart (90 and 120 seconds). Each says about how many searches an hour it makes and how
89
+ long the rest of today's budget would take. The daily budget stays the cap at every speed:
90
+ slower spreads it out, it doesn't shrink it. The speed is saved with the budget and the scanner
91
+ says which it is using at the start of every run; the Scan page's time estimates follow it.
92
+ - **Scores moved into Settings.** Your field, how tiers are graded and every company's score are a
93
+ Scores section in Settings (the gear), and the Scores tab is gone from the header. Old links to
94
+ the Scores tab land in the same place in Settings.
95
+ - **Profile has Insights.** An Insights button beside Profile shows network health: how much of
96
+ your 2nd degree you reach two or more ways, the effective reach of your connections, and the
97
+ connections who reach the most people no one else does. It was at the top of the Scores tab.
98
+ - **Paths → People.** A new view beside the company map, drawn the same way but with your
99
+ connections as the bubbles: each is sized by the value of the cluster behind them (S tier counts
100
+ 3, A tier 2, everyone else 1; or by cluster size, or by S tier inside), coloured by their sector
101
+ and grouped like the companies. A white centre is the share of their cluster you can only reach
102
+ through them, a gold ring means S-tier people inside, and a line joins two connections whose
103
+ clusters share people. Point at someone for their numbers and how their cluster compares to the
104
+ rest you've scanned; click to open it in Bridge Chains. Shows the connections you've scanned,
105
+ or all of them, with the ones not scanned yet as small grey dots.
106
+ - **Separation's map draws connections as pills and people as cards** on a computer (a phone keeps
107
+ the dots). Each connection shows their initials, tier and what they are to the people on the
108
+ map: the only door to someone, the best way in for some, or only another way in; the ones with
109
+ a solid line are lit. Each person is a card with rank, role, score and how many ways in. With
110
+ the slider all the way to easy, the one person becomes a large card with LinkedIn's mutual
111
+ count and how many of those are drawn. Everything still glides as the slider moves.
112
+ - **Separation's list is a ledger.** A title bar says how many are shown and that the order
113
+ follows the slider. The way in shows the connection's face with "only way in" or how many more
114
+ ways there are under it, a new column shows how rare the way in is as a bar (full and cyan for
115
+ one door, short and orange for the warmest), and the power score sits beside its bar.
116
+ - **Separation is a tab of its own**, beside Degrees in the header, instead of a view inside
117
+ Degrees. It uses the same Filters grid as Degrees. Degrees keeps Bridge Chains, Pyramid and List.
118
+ - **Separation has a slider.** One slider reorders everyone as you drag it: the rarest ways in
119
+ at one end (strong people only one of your connections knows), the easiest at the other
120
+ (strong people you share the most mutual connections with), and each person's own score alone
121
+ in the middle, as the list has always been. A line under it says in words what it is
122
+ surfacing. It only reorders: scores, tiers and ranks stay as they are, and it claims no odds.
123
+ Double-click the slider, or tap 50, to go back to the middle. It replaces the Power / Ways in
124
+ switch. The tier chips and the rarity chips ("Only show") sit in the same card.
125
+ - **Separation's map follows the slider, and glides.** From the rare end to the middle it fans out
126
+ to ten people, each with the one or two doors that lead to them. Towards the easy end it
127
+ closes in on fewer people, and at the end on one: the person you're aiming at, with rings
128
+ round them and every connection of yours who leads to them drawn in, the top-scored one solid.
129
+ As the slider moves, people, connections and lines slide to their new places and newcomers
130
+ fade in (still, with Reduce Motion on). Pick anyone in the list, or one of the "Next" names
131
+ under the map, to aim at them instead. Beside each name it says how many mutual connections
132
+ lead to them: "only way in", "3 ways in", or, where LinkedIn's own count is higher than the
133
+ connections the app can draw, "37 mutuals · 6 mapped".
134
+ - **Path, in Separation.** A Path button beside the tier chips narrows everyone to one sector or
135
+ one company. The picker opens under the button with the sectors (inferred, as in Paths), the
136
+ biggest companies and a box to type in, and goes away once you've picked; the button then
137
+ shows your pick with a × to clear it.
138
+ - **Separation's heading no longer sits under the notch.**
139
+ - **One filter for tiers and degrees, the same in Network Circle and in Degrees.** The Filters
140
+ panel shows the five tiers as bigger rows, each with six dots, one for each degree. Tap a tier
141
+ to hide or show it, as before. Tap a dot to show or hide that degree for that tier alone:
142
+ S tier at 2nd degree without everyone else's, say. Tap a number at the top to switch a degree
143
+ for every tier at once. A dot with nobody behind it is faint and can't be tapped, each row
144
+ says how many people it is showing, and the last people on screen can't be switched off.
145
+ Network Circle starts with your own connections; Degrees starts with everything. In Degrees
146
+ the rows used to pick one bridge tier at a time; now 1st degree is your bridges and 2nd is
147
+ the people in their circles, each by their own tier.
148
+ - **The Galaxy's physics is the main thing in Network Circle's Filters panel.** The sliders,
149
+ layouts, colours, find and replay sit straight in the panel under the tiers, always on, with
150
+ no switch to turn on first and no "experimental" box. With no slider moved the Galaxy is laid
151
+ out as before; pointing at a dot now lights up its branch unless you turn that off.
152
+ - **Pyramid and List left Network Circle.** Its views are the Galaxy and Orbit. Pyramid and List
153
+ stay in Degrees.
154
+ - **An opened circle shows the chains that lead on from it.** Anyone you added from that circle
155
+ and then scanned keeps their dot in the circle, and the cluster their scan formed sits outside
156
+ it, joined to that dot by a line, so you can see where each path came from. Inside a cluster,
157
+ anyone you added and scanned in turn has their own dot marked and a line on to their cluster,
158
+ for as far as your scans reach. Every dot in a cluster is joined to its middle by a faint line.
159
+ Each cluster says whose circle it is, how many are in it and which degree that is. Point at
160
+ one and the way back to the middle lights up; click and it opens with the whole trail. The
161
+ people you added sit together on the side the chains lead off to, their names taking turns
162
+ above and below so they don't overlap, and the view zooms to fit when it opens.
163
+ - **Bridge Chains shows the bridges you connected with yourself.** Someone you met through a
164
+ bridge's circle is a link in that bridge's chain, not a bridge of their own: they're inside its
165
+ circle.
166
+ - **A bridge's ring now counts the people you added through it.** The ring sits tight on the
167
+ dot, with one bar for each person: orange once they've been scanned and have a cluster of
168
+ their own, green while they're ready for a scan. A small green number at the top right says
169
+ how many are ready, in place of the "ready" label. How much of a bridge's own list is scanned
170
+ is in its tooltip.
171
+ - **A crowded Bridge Chains is easier to read.** When your bridges need more than one ring, the
172
+ rings sit further apart, each with its own guide line, and the counts under each name wait
173
+ until you point at the bridge.
174
+ - **Orbit moved to Network Circle.** It shows the size of every circle at once, which is a
175
+ picture of the network, not a way to follow a chain. The corner switch in Degrees is gone.
176
+ - **Views are in the notch, under the tabs.** Galaxy, Orbit, Pyramid and List in Network Circle,
177
+ and Bridge Chains, Separation, Pyramid and List in Degrees, switch from the notch that hangs
178
+ under the top tabs, instead of a list at the top of the Filters panel. A scan's progress
179
+ shows in the same notch, beside them. The Filters panel now only filters.
180
+
181
+ ### Fixed
182
+ - The scanner's log, shown on the Scan page, says "scanning" throughout; a few lines still said the
183
+ old word.
184
+ - The "Scanning…" dot in the right panel pulses again; the animation it named was never defined.
185
+ - **Separation's map lines now meet every box.** Each connection sits level with the people it
186
+ leads to, so a connection with one person is on that person's row and the line between them runs
187
+ straight; lines start and end exactly on the pill and the card, with a small dot where they plug in.
188
+ - **"Hide the Chrome window while scanning" did nothing for scans started on the Scan page.**
189
+ Its own buttons (Scan, Check for new, Auto scan and the rest) sent their request without the
190
+ setting, so Chrome opened anyway; only scans started from a person's card or Resume were hidden.
191
+ Every scan start now goes through one place that adds it.
192
+ - **"Only way in" could show beside someone with two ways in drawn.** Rarity trusted the mutual
193
+ count a scan saved even when the app had since found them through more of your connections
194
+ than that. It now takes whichever is larger.
195
+
9
196
  ## [0.4.11] - 2026-09-29
10
197
 
11
198
  ### Added
@@ -2,6 +2,7 @@ import { db as supabase } from '../../../lib/db';
2
2
  import { uniqueByProfile, splitAlreadyConnected, toIsoDate, refreshNotifications, mutualCountOf } from '../../../lib/ingest';
3
3
  import { tiePairs } from '../../../lib/ties';
4
4
  import { promoteToFirstDegree } from '../../../lib/promote';
5
+ import { addedBackNotification, topCompanyNotification } from '../../../lib/notifications';
5
6
  import { localPhoto } from '../../../lib/photos';
6
7
 
7
8
  function parseHeadline(h) {
@@ -113,6 +114,8 @@ export async function POST(request) {
113
114
  const newRecords = records.filter(r => !existingUrls.has(r.profile_url));
114
115
  const existingRecords = records.filter(r => existingUrls.has(r.profile_url));
115
116
  let promoted = 0;
117
+ // Who was promoted, and through whom, for an "added you back" notification once they're rescored.
118
+ const addedBack = [];
116
119
 
117
120
  // Someone you met through a bridge and have now actually connected with.
118
121
  //
@@ -144,7 +147,7 @@ export async function POST(request) {
144
147
  ...(rec.profile_image_url ? { profile_image_url: rec.profile_image_url } : {}),
145
148
  },
146
149
  });
147
- if (res.promoted) promoted += 1;
150
+ if (res.promoted) { promoted += 1; addedBack.push(res); }
148
151
  }
149
152
  }
150
153
 
@@ -240,6 +243,41 @@ export async function POST(request) {
240
243
  } catch (e) { /* notifications are optional */ }
241
244
  }
242
245
 
246
+ // A circle scan that found people at top companies: one notification naming them (lib/notifications.js).
247
+ if (degree === 2 && bridgeId && newRecords.length) {
248
+ try {
249
+ const found = [];
250
+ const urls = newRecords.map((r) => r.profile_url).filter(Boolean);
251
+ for (let i = 0; i < urls.length; i += 100) {
252
+ let q = supabase.from('linkedin_connections').select('id, name, company, company_prestige_score, power_score, tier')
253
+ .in('profile_url', urls.slice(i, i + 100)).eq('source_connection_id', bridgeId);
254
+ if (userId) q = q.eq('user_id', userId);
255
+ const { data } = await q;
256
+ found.push(...(data || []));
257
+ }
258
+ const { data: via } = await supabase.from('linkedin_connections').select('id, name').eq('id', bridgeId).single();
259
+ const note = topCompanyNotification({ people: found, via, userId });
260
+ if (note) await supabase.from('notifications').insert([note]);
261
+ } catch (e) { /* notifications are optional */ }
262
+ }
263
+
264
+ // Someone you met through a bridge who is now a connection: "added you back, through …".
265
+ if (addedBack.length) {
266
+ try {
267
+ const rows = [];
268
+ for (const res of addedBack.slice(0, 10)) {
269
+ const { data: person } = await supabase.from('linkedin_connections')
270
+ .select('id, name, tier, power_score, headline, unlocked_from_bridge_id').eq('id', res.id).single();
271
+ if (!person) continue;
272
+ const viaId = res.originId ?? person.unlocked_from_bridge_id;
273
+ const via = viaId != null ? { id: viaId, name: res.originName || null } : null;
274
+ const note = addedBackNotification({ person, via, userId });
275
+ if (note) rows.push(note);
276
+ }
277
+ if (rows.length) await supabase.from('notifications').insert(rows);
278
+ } catch (e) { /* notifications are optional */ }
279
+ }
280
+
243
281
  // Auto-detect accepted pending requests: if a pending person just showed up as d1
244
282
  if (degree === 1 && records.length > 0) {
245
283
  try {
@@ -286,6 +324,7 @@ export async function POST(request) {
286
324
  title: `${p.name} accepted! +${XP_ACCEPT[p.tier] || 5} XP`,
287
325
  message: `${p.tier}-Tier connection — bridge their cluster to reach D3`,
288
326
  icon: '🤝',
327
+ data: { personId: p.id, viaId: p.source_connection_id ?? null, tier: p.tier ?? null },
289
328
  }))
290
329
  ).catch(() => {});
291
330
  }
@@ -7,6 +7,8 @@ import { scanProgress } from '../../../lib/scan-progress';
7
7
  import { linkedinState, writeLimits, liftCooldown } from '../../../lib/linkedin-limits';
8
8
  import { pausedList, readProgress, readUnclear } from '../../../lib/paused';
9
9
  import { getDb } from '../../../lib/db-client';
10
+ import { db as notesDb } from '../../../lib/db';
11
+ import { scanDoneNotification } from '../../../lib/notifications';
10
12
  import { registerScanState } from '../../../lib/scan-state';
11
13
  import { pendingImport } from '../../../lib/data-import';
12
14
  import { waitingPhotoCount } from '../../../lib/photos';
@@ -540,7 +542,7 @@ export async function POST(request) {
540
542
 
541
543
  // The two settings a person changes here. Neither starts anything.
542
544
  if (action === 'set-limits') {
543
- const limits = writeLimits(dataDir(), { daily: body.daily, monthly: body.monthly, profiles: body.profiles });
545
+ const limits = writeLimits(dataDir(), { daily: body.daily, monthly: body.monthly, profiles: body.profiles, pace: body.pace });
544
546
  return Response.json({ ok: true, limits });
545
547
  }
546
548
  if (action === 'lift-cooldown') {
@@ -712,6 +714,7 @@ export async function POST(request) {
712
714
  const reason = state.stderrTail.filter((l) => !/Warning|warnings\.warn\(/.test(l)).slice(-6);
713
715
  state.failure = reason.length ? reason : null;
714
716
  }
717
+ const stopped = state.stopping;
715
718
  push(state.stopping ? 'Stopped.' : code === 0 ? 'Finished.' : `Stopped (exit ${code}).`);
716
719
  state.stopping = false;
717
720
  state.running = false;
@@ -721,6 +724,9 @@ export async function POST(request) {
721
724
  state.recent = [{
722
725
  action: state.action, target: state.target, startedAt: state.startedAt, exitCode: code, failure: state.failure,
723
726
  }, ...state.recent].slice(0, 5);
727
+ // A scan that finished leaves a notification ("Scan done: …"); a stop or a failure doesn't.
728
+ const done = scanDoneNotification({ action: state.action, target: state.target, exitCode: code, stopped, log: state.log.slice(-12), userId: profileId || null });
729
+ if (done) Promise.resolve(notesDb.from('notifications').insert([done])).catch(() => {});
724
730
  // A setup's private folder (the download and what was unpacked from it).
725
731
  if (cleanup) {
726
732
  try { cleanup(); } catch { /* swept at the next setup (sweepSetupLeftovers) */ }
@@ -1,5 +1,5 @@
1
1
  import { getDb } from '../../../../lib/db-client';
2
- import { companyOverrides, readForScoring, scoringRows, sectorFocusOf, tierScaleOf } from '../../../../lib/rpc';
2
+ import { companyOverrides, readForScoring, scoringRows, sectorFocusOf, tierScaleOf, titleRankingOf } from '../../../../lib/rpc';
3
3
  import { parseSectorFocus, previewSectorFocus } from '../../../../lib/sector-focus';
4
4
 
5
5
  // Scores → Your sector, before you save: what a sector focus would change,
@@ -32,6 +32,7 @@ export async function POST(request) {
32
32
  from: sectorFocusOf(db),
33
33
  to,
34
34
  tierScale: tierScaleOf(db),
35
+ titles: titleRankingOf(db),
35
36
  }));
36
37
  } catch (err) {
37
38
  return Response.json({ error: err.message }, { status: 500 });
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ // The app's tabs, the same on every page (Blake, 2026-10-02: "for paths we
4
+ // need a notch for it so the tabs are visible and continuity is there").
5
+ // Network Circle, Degrees and Separation are views of the map page: there they
6
+ // switch in place (onMode); anywhere else they are links back to it with the
7
+ // tab named (/?mode=…). A page's own sub-tabs go in the notch under this row
8
+ // (lib/island.js setNotchTabs), which looks for #main-tabs to hang from.
9
+
10
+ import Link from 'next/link';
11
+ import { IS_DEMO } from '../../lib/demo';
12
+
13
+ const MODES = [
14
+ { key: 'network', label: 'Network Circle', short: 'Circle', on: { background: '#fff', color: '#000' }, off: { background: 'transparent', color: '#888' } },
15
+ { key: 'degrees', label: 'Degrees', on: { background: 'linear-gradient(135deg, #FFD700, #FF6B35)', color: '#000' }, off: { background: 'rgba(255,255,255,0.12)', color: '#fff' } },
16
+ { key: 'separation', label: 'Separation', on: { background: 'linear-gradient(135deg, #00E5FF, #FFD700 55%, #FF7043)', color: '#000' }, off: { background: 'rgba(255,255,255,0.12)', color: '#fff' } },
17
+ ];
18
+ const PAGES = [
19
+ { key: 'paths', href: '/paths', label: 'Paths', color: '#00ff88', on: 'linear-gradient(135deg, #00ff88, #3498DB)', demo: true },
20
+ // Social is part of Outlink now (Messages & follow-ups); /social forwards there.
21
+ { key: 'outlink', href: '/queue', label: 'Outlink', color: '#FF6B35', on: 'linear-gradient(135deg, #FF6B35, #FFD700)', own: true },
22
+ { key: 'scan', href: '/setup', label: 'Scan', color: '#666', on: 'linear-gradient(135deg, #00ff88, #1abc9c)', own: true },
23
+ ];
24
+
25
+ /**
26
+ * @param {{ active: string, isMobile?: boolean, csvMode?: boolean, onMode?: (key: string) => void,
27
+ * children?: any, after?: any }} props
28
+ * `children` sits after the map's tabs (the map page's sample or CSV chip),
29
+ * `after` at the end of the row (the Auto scan button). `own` pages (Outlink,
30
+ * Scan) need your own network, so they hide for the sample or a CSV.
31
+ */
32
+ export default function AppTabs({ active, isMobile = false, csvMode = false, onMode, children, after }) {
33
+ const pad = isMobile ? '6px 10px' : '8px 16px';
34
+ const base = { padding: pad, borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600, textDecoration: 'none' };
35
+ return (
36
+ <div id="main-tabs" style={{ display: 'flex', gap: isMobile ? 2 : 4, background: 'rgba(255,255,255,0.08)', borderRadius: 8, padding: isMobile ? 2 : 3, flexWrap: isMobile ? 'wrap' : 'nowrap' }}>
37
+ {MODES.map((m) => {
38
+ const style = { ...base, cursor: 'pointer', ...(active === m.key ? m.on : m.off) };
39
+ const label = isMobile && m.short ? m.short : m.label;
40
+ return onMode
41
+ ? <button key={m.key} type="button" onClick={() => onMode(m.key)} style={style}>{label}</button>
42
+ : <Link key={m.key} href={`/?mode=${m.key}`} style={{ ...style, display: 'flex', alignItems: 'center' }}>{label}</Link>;
43
+ })}
44
+ {children}
45
+ {PAGES.filter((p) => (IS_DEMO ? false : p.own ? !csvMode : true)).map((p) => {
46
+ const on = active === p.key;
47
+ return (
48
+ <Link key={p.key} href={p.href} title={p.title} aria-current={on ? 'page' : undefined} style={{
49
+ ...base, display: 'flex', alignItems: 'center', gap: 4,
50
+ background: on ? p.on : 'rgba(255,255,255,0.06)', color: on ? '#000' : p.color,
51
+ }}>
52
+ {p.label}{p.badge && <span style={{ fontSize: 9, opacity: 0.7, marginLeft: 2 }}>{p.badge}</span>}
53
+ </Link>
54
+ );
55
+ })}
56
+ {after}
57
+ </div>
58
+ );
59
+ }