six-degrees 0.3.0 → 0.4.0-beta.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 (299) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +34 -6
  5. package/.next/standalone/.next/routes-manifest.json +6 -0
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.rsc +10 -10
  17. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
  18. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +9 -9
  19. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +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.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +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.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
  37. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/import.html +1 -1
  52. package/.next/standalone/.next/server/app/import.rsc +12 -12
  53. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +12 -12
  54. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  55. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +11 -11
  56. package/.next/standalone/.next/server/app/index.html +1 -1
  57. package/.next/standalone/.next/server/app/index.rsc +12 -12
  58. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +11 -11
  59. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +12 -12
  60. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  61. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/launch.html +2 -2
  64. package/.next/standalone/.next/server/app/launch.rsc +12 -12
  65. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +12 -12
  66. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  67. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +11 -11
  68. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/paths.html +1 -1
  73. package/.next/standalone/.next/server/app/paths.rsc +12 -12
  74. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +12 -12
  75. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  76. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +11 -11
  77. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/profile.html +1 -1
  80. package/.next/standalone/.next/server/app/profile.rsc +12 -12
  81. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +12 -12
  82. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  83. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +11 -11
  84. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/queue.html +1 -1
  87. package/.next/standalone/.next/server/app/queue.rsc +12 -12
  88. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +12 -12
  89. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  90. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +11 -11
  91. package/.next/standalone/.next/server/app/scores/page/app-paths-manifest.json +3 -0
  92. package/.next/standalone/.next/server/app/scores/page/build-manifest.json +20 -0
  93. package/.next/standalone/.next/server/app/scores/page/next-font-manifest.json +11 -0
  94. package/.next/standalone/.next/server/app/scores/page/react-loadable-manifest.json +1 -0
  95. package/.next/standalone/.next/server/app/scores/page/server-reference-manifest.json +4 -0
  96. package/.next/standalone/.next/server/app/scores/page.js +13 -0
  97. package/.next/standalone/.next/server/app/scores/page.js.map +5 -0
  98. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -0
  99. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +3 -0
  100. package/.next/standalone/.next/server/app/scores.html +1 -0
  101. package/.next/standalone/.next/server/app/scores.meta +26 -0
  102. package/.next/standalone/.next/server/app/scores.rsc +24 -0
  103. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -0
  104. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +4 -0
  105. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +28 -0
  106. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/settings.html +1 -1
  109. package/.next/standalone/.next/server/app/settings.rsc +12 -12
  110. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +12 -12
  111. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  112. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +11 -11
  113. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/setup.html +1 -1
  116. package/.next/standalone/.next/server/app/setup.rsc +12 -12
  117. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +12 -12
  118. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  119. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +11 -11
  120. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  121. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rc27za._.js +8 -0
  122. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/_02twlca._.js +10 -0
  124. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -6
  127. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +3 -2
  130. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
  131. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0an6blh._.js → [root-of-the-server]__0zzcbqg._.js} +2 -2
  132. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1z1j3ek._.js → [root-of-the-server]__19ip1qq._.js} +2 -2
  134. package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +3 -0
  135. package/.next/standalone/.next/server/chunks/ssr/_0223v9s._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_0bcf13g._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/_0cu6-an._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/ssr/_0wxbq78._.js +3 -0
  139. package/.next/standalone/.next/server/chunks/ssr/_0z88zcl._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/_1cyxocj._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_scores_page_actions_0x-w58n.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
  146. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +4 -0
  147. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
  149. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +3 -0
  150. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  151. package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0x3i8za._.js +131 -0
  153. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_1bunoni._.js → node_modules_next_dist_1v8aef8._.js} +2 -2
  154. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  155. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  156. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  157. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  158. package/.next/standalone/.next/server/pages/404.html +1 -1
  159. package/.next/standalone/.next/server/pages/500.html +1 -1
  160. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  161. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  162. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  163. package/.next/standalone/.next/static/chunks/0_yurn4q91vq8.js +16 -0
  164. package/.next/standalone/.next/static/chunks/0d_mh-06srh2k.js +1 -0
  165. package/.next/standalone/.next/static/chunks/0x3oadfikvb1c.js +1 -0
  166. package/.next/standalone/.next/static/chunks/1d5hl1io798n2.js +1 -0
  167. package/.next/standalone/.next/static/chunks/1qt67cvj9sjwi.js +1 -0
  168. package/.next/{static/chunks/1chp3nx45meg5.js → standalone/.next/static/chunks/1roznk7hkjes1.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/2_bf2n4gstad9.js +33 -0
  170. package/.next/standalone/.next/static/chunks/36gbwek52x_w-.js +2 -0
  171. package/.next/standalone/.next/static/chunks/3a55suosthntc.js +1 -0
  172. package/.next/standalone/.next/static/chunks/3fq1zq-6cfwbc.js +1 -0
  173. package/.next/standalone/.next/static/chunks/3nbnio971zlji.js +2 -0
  174. package/.next/standalone/.next/static/chunks/3s6awp3qq9s7w.js +1 -0
  175. package/.next/standalone/CHANGELOG.md +134 -0
  176. package/.next/standalone/README.md +30 -15
  177. package/.next/standalone/app/api/company-scores/legacy/route.js +1 -1
  178. package/.next/standalone/app/api/company-scores/route.js +1 -1
  179. package/.next/standalone/app/api/ingest/route.js +17 -1
  180. package/.next/standalone/app/api/outreach/route.js +107 -44
  181. package/.next/standalone/app/api/scraper/route.js +69 -8
  182. package/.next/standalone/app/api/settings/sector-preview/route.js +3 -2
  183. package/.next/standalone/app/api/settings/sector-suggestions/route.js +1 -1
  184. package/.next/standalone/app/components/CompanyScores.js +16 -10
  185. package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
  186. package/.next/standalone/app/components/OrbitGraph.js +30 -0
  187. package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
  188. package/.next/standalone/app/components/SeparationView.js +96 -14
  189. package/.next/standalone/app/components/Sidebar.js +258 -239
  190. package/.next/standalone/app/components/TheirCircle.js +293 -0
  191. package/.next/standalone/app/components/settings/SectorSection.js +5 -5
  192. package/.next/standalone/app/components/settings/TierSection.js +104 -0
  193. package/.next/standalone/app/components/useRequests.js +12 -0
  194. package/.next/standalone/app/components/useScanner.js +13 -0
  195. package/.next/standalone/app/components/views.js +1 -3
  196. package/.next/standalone/app/launch/page.js +4 -1
  197. package/.next/standalone/app/page.js +55 -51
  198. package/.next/standalone/app/paths/page.js +17 -12
  199. package/.next/standalone/app/profile/page.js +3 -3
  200. package/.next/standalone/app/queue/page.js +38 -50
  201. package/.next/standalone/app/scores/page.js +72 -0
  202. package/.next/standalone/app/settings/page.js +15 -3
  203. package/.next/standalone/db/schema.js +1 -0
  204. package/.next/standalone/lib/circle.js +169 -0
  205. package/.next/standalone/lib/companies.js +1 -1
  206. package/.next/standalone/lib/csv.js +17 -4
  207. package/.next/standalone/lib/db-client.js +1 -0
  208. package/.next/standalone/lib/ingest.js +12 -0
  209. package/.next/standalone/lib/legacy-offer.js +7 -6
  210. package/.next/standalone/lib/legacy-scores.js +2 -2
  211. package/.next/standalone/lib/rarity.js +80 -0
  212. package/.next/standalone/lib/requests-client.js +113 -0
  213. package/.next/standalone/lib/rpc.js +26 -15
  214. package/.next/standalone/lib/score-guess.js +60 -0
  215. package/.next/standalone/lib/scoring.js +1002 -61
  216. package/.next/standalone/lib/scraper-client.js +208 -17
  217. package/.next/standalone/lib/sector-directory.js +1 -1
  218. package/.next/standalone/lib/sector-focus.js +4 -3
  219. package/.next/standalone/lib/settings-effects.js +19 -1
  220. package/.next/standalone/lib/settings.js +4 -1
  221. package/.next/standalone/package-lock.json +2 -2
  222. package/.next/standalone/package.json +2 -2
  223. package/.next/standalone/public/demo-data.json +1 -1
  224. package/.next/standalone/scripts/gen-synthetic.mjs +1 -1
  225. package/.next/standalone/scripts/scrape.py +62 -0
  226. package/.next/static/chunks/0_yurn4q91vq8.js +16 -0
  227. package/.next/static/chunks/0d_mh-06srh2k.js +1 -0
  228. package/.next/static/chunks/0x3oadfikvb1c.js +1 -0
  229. package/.next/static/chunks/1d5hl1io798n2.js +1 -0
  230. package/.next/static/chunks/1qt67cvj9sjwi.js +1 -0
  231. package/.next/{standalone/.next/static/chunks/1chp3nx45meg5.js → static/chunks/1roznk7hkjes1.js} +1 -1
  232. package/.next/static/chunks/2_bf2n4gstad9.js +33 -0
  233. package/.next/static/chunks/36gbwek52x_w-.js +2 -0
  234. package/.next/static/chunks/3a55suosthntc.js +1 -0
  235. package/.next/static/chunks/3fq1zq-6cfwbc.js +1 -0
  236. package/.next/static/chunks/3nbnio971zlji.js +2 -0
  237. package/.next/static/chunks/3s6awp3qq9s7w.js +1 -0
  238. package/CHANGELOG.md +134 -0
  239. package/README.md +30 -15
  240. package/db/schema.js +1 -0
  241. package/lib/circle.js +169 -0
  242. package/lib/companies.js +1 -1
  243. package/lib/csv.js +17 -4
  244. package/lib/db-client.js +1 -0
  245. package/lib/ingest.js +12 -0
  246. package/lib/legacy-offer.js +7 -6
  247. package/lib/legacy-scores.js +2 -2
  248. package/lib/rarity.js +80 -0
  249. package/lib/requests-client.js +113 -0
  250. package/lib/rpc.js +26 -15
  251. package/lib/score-guess.js +60 -0
  252. package/lib/scoring.js +1002 -61
  253. package/lib/scraper-client.js +208 -17
  254. package/lib/sector-directory.js +1 -1
  255. package/lib/sector-focus.js +4 -3
  256. package/lib/settings-effects.js +19 -1
  257. package/lib/settings.js +4 -1
  258. package/package.json +2 -2
  259. package/public/demo-data.json +1 -1
  260. package/scripts/scrape.py +62 -0
  261. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
  262. package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +0 -3
  263. package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +0 -3
  264. package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +0 -3
  265. package/.next/standalone/.next/server/chunks/ssr/_0942v8n._.js +0 -3
  266. package/.next/standalone/.next/server/chunks/ssr/_0hl_t7r._.js +0 -3
  267. package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +0 -132
  268. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +0 -3
  269. package/.next/standalone/.next/server/chunks/ssr/node_modules_16vay6l._.js +0 -3
  270. package/.next/standalone/.next/server/chunks/ssr/node_modules_1kciwn4._.js +0 -3
  271. package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  272. package/.next/standalone/.next/static/chunks/08k1crid-05dt.js +0 -1
  273. package/.next/standalone/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  274. package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +0 -2
  275. package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +0 -1
  276. package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  277. package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +0 -1
  278. package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  279. package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +0 -1
  280. package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  281. package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  282. package/.next/standalone/app/components/BridgeRing.js +0 -753
  283. package/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  284. package/.next/static/chunks/08k1crid-05dt.js +0 -1
  285. package/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  286. package/.next/static/chunks/0q-_khndfxubv.js +0 -2
  287. package/.next/static/chunks/1c79veqedvubt.js +0 -1
  288. package/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  289. package/.next/static/chunks/1uadqylrod5_v.js +0 -1
  290. package/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  291. package/.next/static/chunks/289ktj97di6zg.js +0 -1
  292. package/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  293. package/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  294. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_buildManifest.js +0 -0
  295. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_clientMiddlewareManifest.js +0 -0
  296. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_ssgManifest.js +0 -0
  297. /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_buildManifest.js +0 -0
  298. /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_clientMiddlewareManifest.js +0 -0
  299. /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_ssgManifest.js +0 -0
@@ -1,7 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
4
- import { scraperStatus, startScrape, notReadyMessage } from '../lib/scraper-client';
4
+ import { scraperStatus, beginScrape, notReadyMessage, busyReason } from '../lib/scraper-client';
5
+ import useScanner from './components/useScanner';
6
+ import useRequests from './components/useRequests';
7
+ import { requestCount } from '../lib/requests-client';
5
8
  import { loadNetwork } from '../lib/network';
6
9
  import { resolveView } from './components/views';
7
10
  import Sidebar from './components/Sidebar';
@@ -59,9 +62,10 @@ function HomeInner() {
59
62
  const [visualMode, setVisualMode] = useState('galaxy');
60
63
  const [notifications, setNotifications] = useState([]);
61
64
  const [showNotifs, setShowNotifs] = useState(false);
62
- const [pending, setPending] = useState([]);
63
65
  const [csvMode, setCsvMode] = useState(false);
64
66
  const [csvSource, setCsvSource] = useState('csv');
67
+ // Everyone with a request out, shared with every view (lib/requests-client.js).
68
+ const pendingCount = requestCount(useRequests());
65
69
 
66
70
  useEffect(() => {
67
71
  if (!userId) return;
@@ -93,8 +97,6 @@ function HomeInner() {
93
97
  if (!IS_DEMO && !hasCsvNetwork()) {
94
98
  // Fetch notifications
95
99
  fetch(`/api/notifications?userId=${userId}`).then(r => r.json()).then(d => setNotifications(d.notifications || [])).catch(() => {});
96
- // Fetch pending outreach
97
- fetch(`/api/outreach?userId=${userId}`).then(r => r.json()).then(d => setPending(d.pending || [])).catch(() => {});
98
100
  }
99
101
  }, [userId]);
100
102
 
@@ -215,6 +217,16 @@ function HomeInner() {
215
217
  >
216
218
  Paths
217
219
  </Link>}
220
+ {!IS_DEMO && !csvMode && <Link
221
+ href="/scores"
222
+ style={{
223
+ padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
224
+ background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
225
+ display: 'flex', alignItems: 'center', gap: 4,
226
+ }}
227
+ >
228
+ Scores
229
+ </Link>}
218
230
  {!IS_DEMO && !csvMode && <Link
219
231
  href="/queue"
220
232
  style={{
@@ -316,28 +328,7 @@ function HomeInner() {
316
328
  )}
317
329
  </div>}
318
330
  {/* Refresh button — checks connections + notifies */}
319
- {!IS_DEMO && !csvMode && <button
320
- onClick={async () => {
321
- try {
322
- const status = await scraperStatus();
323
- const blocked = notReadyMessage(status);
324
- if (blocked) { alert(blocked); return; }
325
- if (status.running) { alert('The scanner is already busy.'); return; }
326
- await startScrape('refresh');
327
- alert('Checking for new connections — watch it on the Scan page.');
328
- } catch (e) {
329
- alert(e.message);
330
- }
331
- }}
332
- title="Refresh connections + bridges"
333
- style={{
334
- width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none', cursor: 'pointer',
335
- background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
336
- display: isMobile ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: 8,
337
- }}
338
- >
339
- ↻
340
- </button>}
331
+ {!IS_DEMO && !csvMode && <RefreshButton isMobile={isMobile} />}
341
332
  {/* Profile icon — top right */}
342
333
  <a href={IS_DEMO ? '/launch' : csvMode ? '/import' : '/profile'} style={{
343
334
  width: isMobile ? 30 : 36, height: isMobile ? 30 : 36, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
@@ -356,8 +347,8 @@ function HomeInner() {
356
347
  </a>
357
348
  </div>
358
349
 
359
- {/* Pending count badge — in header */}
360
- {isDegreesMode && pending.length > 0 && (
350
+ {/* Pending count badge — in header: everyone with a request out, once each (lib/requests-client.js) */}
351
+ {isDegreesMode && !csvMode && pendingCount > 0 && (
361
352
  <Link href="/queue" style={{
362
353
  padding: '5px 12px', borderRadius: 16, fontSize: 12, fontWeight: 600, textDecoration: 'none',
363
354
  background: 'rgba(255,107,53,0.12)', color: '#FF6B35',
@@ -365,7 +356,7 @@ function HomeInner() {
365
356
  display: 'inline-flex', alignItems: 'center', gap: 5,
366
357
  }}>
367
358
  <span style={{ fontSize: 10 }}>⏳</span>
368
- {pending.length} Pending
359
+ {pendingCount} Pending
369
360
  </Link>
370
361
  )}
371
362
  </header>
@@ -428,6 +419,10 @@ function HomeInner() {
428
419
  selectedId: selected?.id ?? null,
429
420
  tierColors: TIER_COLORS,
430
421
  focusNodeRef,
422
+ // Every row, whatever the filters: rarity counts the ways in across
423
+ // all your scanned circles, and "already connected" needs everyone.
424
+ fullDegree1: degree1,
425
+ fullDegree2: degree2,
431
426
  };
432
427
  return <View {...viewProps} />;
433
428
  })()}
@@ -440,32 +435,10 @@ function HomeInner() {
440
435
  mode={mode}
441
436
  collapsed={sidebarCollapsed}
442
437
  filter={filter}
443
- pending={pending}
444
438
  onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
445
439
  onSelect={(node) => { setSelected(node || null); }}
446
440
  onSwitchMode={(newMode) => { setMode(newMode); setFilter('all'); }}
447
441
  onFocusNode={(nodeId) => { if (focusNodeRef.current) focusNodeRef.current(nodeId); }}
448
- onMarkSent={async (person) => {
449
- if (IS_DEMO) return;
450
- const id = person.id;
451
- const url = person.profile_url;
452
- await fetch('/api/outreach', {
453
- method: 'POST',
454
- headers: { 'Content-Type': 'application/json' },
455
- body: JSON.stringify({ action: 'mark-sent', connectionId: id, profileUrl: url }),
456
- });
457
- setPending(prev => [...prev, person]);
458
- }}
459
- onUndoPending={async (person) => {
460
- if (IS_DEMO) return;
461
- const id = person.id;
462
- await fetch('/api/outreach', {
463
- method: 'POST',
464
- headers: { 'Content-Type': 'application/json' },
465
- body: JSON.stringify({ action: 'undo', connectionId: id }),
466
- });
467
- setPending(prev => prev.filter(p => p.id !== id));
468
- }}
469
442
  />
470
443
 
471
444
  {/* Galaxy experimental toggle — bottom right, Degrees mode only.
@@ -501,3 +474,34 @@ function HomeInner() {
501
474
  </div>
502
475
  );
503
476
  }
477
+
478
+ // Its own component so the scanner's answer, which changes every second or two
479
+ // while a scan runs, re-renders this button and not the whole map.
480
+ function RefreshButton({ isMobile }) {
481
+ const scan = useScanner();
482
+ const busy = busyReason(scan);
483
+ return (
484
+ <button
485
+ onClick={async () => {
486
+ try {
487
+ const blocked = notReadyMessage(await scraperStatus());
488
+ if (blocked) { alert(blocked); return; }
489
+ await beginScrape('refresh');
490
+ alert('Checking for new connections — watch it on the Scan page.');
491
+ } catch (e) {
492
+ alert(e.message);
493
+ }
494
+ }}
495
+ disabled={Boolean(busy)}
496
+ title={busy ? `${busy}. One scan at a time.` : 'Refresh connections + bridges'}
497
+ style={{
498
+ width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none',
499
+ cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.4 : 1,
500
+ background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
501
+ display: isMobile ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: 8,
502
+ }}
503
+ >
504
+ ↻
505
+ </button>
506
+ );
507
+ }
@@ -1,20 +1,20 @@
1
1
  'use client';
2
2
 
3
3
  import { Suspense, useEffect, useState } from 'react';
4
- import { useSearchParams } from 'next/navigation';
5
- import { runScrape, scraperStatus, notReadyMessage } from '../../lib/scraper-client';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
5
+ import { runScrape, scraperStatus, notReadyMessage, busyReason } from '../../lib/scraper-client';
6
6
  import { loadNetwork } from '../../lib/network';
7
7
  import { IS_DEMO } from '../../lib/demo';
8
8
  import { hasCsvNetwork, loadCsvNetwork } from '../../lib/csv';
9
9
  import OnboardingGate from '../components/OnboardingGate';
10
10
  import PathsAnalyzer from '../components/PathsAnalyzer';
11
- import CompanyScores from '../components/CompanyScores';
12
11
  import { useUser } from '../components/UserProvider';
12
+ import useScanner from '../components/useScanner';
13
13
  import Link from 'next/link';
14
14
  import { companyOf, getSeniority } from '../../lib/companies';
15
15
 
16
16
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
17
- const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies'], ['scores', 'Scores']];
17
+ const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies']];
18
18
 
19
19
  export default function PathsPage() {
20
20
  // Suspense because PathsInner reads the address's ?tab= (useSearchParams),
@@ -32,11 +32,14 @@ function PathsInner() {
32
32
  const [d2Data, setD2Data] = useState([]);
33
33
  const [d3Data, setD3Data] = useState([]);
34
34
  // Map and Industries are the analyzer; Companies is the list it grew from.
35
- // A link can open a tab (/paths?tab=scores, from Settings → Your sector).
35
+ // A link can open a tab (/paths?tab=companies). Company scores have a tab of
36
+ // their own now (app/scores): an old link to /paths?tab=scores goes there.
36
37
  // The router's search params rather than window.location: on a click from
37
38
  // another page the address bar only changes after this page has rendered.
38
39
  const asked = useSearchParams().get('tab');
39
40
  const [tab, setTab] = useState(() => (TABS.some(([k]) => k === asked) ? asked : 'map'));
41
+ const router = useRouter();
42
+ useEffect(() => { if (asked === 'scores') router.replace('/scores#companies'); }, [asked, router]);
40
43
  // Bumped when company scores change, so the network reloads with new tiers.
41
44
  const [reloadKey, setReloadKey] = useState(0);
42
45
 
@@ -92,6 +95,9 @@ function PathsInner() {
92
95
 
93
96
  const [scanning, setScanning] = useState(false);
94
97
  const [scanLog, setScanLog] = useState([]);
98
+ // Anything else the scanner runs (a profile card's scan, say) greys this out too.
99
+ const scan = useScanner();
100
+ const busy = !scanning && scan.running ? busyReason(scan) : null;
95
101
 
96
102
  function enrichPeople(people) {
97
103
  const d1Urls = new Set(d1Data.map(c => c.profile_url));
@@ -255,13 +261,14 @@ function PathsInner() {
255
261
  <span style={{ fontSize: 11, color: '#888' }}>
256
262
  {connectedCount}/{companyPeople.length} connected
257
263
  </span>
258
- <button onClick={scanFullCompany} disabled={scanning}
264
+ <button onClick={scanFullCompany} disabled={scanning || Boolean(busy)}
265
+ title={busy ? `${busy}. One scan at a time.` : undefined}
259
266
  style={{
260
- marginLeft: 'auto', padding: '5px 12px', borderRadius: 6, border: 'none', cursor: scanning ? 'not-allowed' : 'pointer',
261
- background: scanning ? '#333' : 'rgba(0,255,136,0.15)', color: scanning ? '#555' : '#00ff88',
267
+ marginLeft: 'auto', padding: '5px 12px', borderRadius: 6, border: 'none', cursor: scanning || busy ? 'not-allowed' : 'pointer',
268
+ background: scanning || busy ? '#333' : 'rgba(0,255,136,0.15)', color: scanning || busy ? '#555' : '#00ff88',
262
269
  fontSize: 11, fontWeight: 600,
263
270
  }}>
264
- {scanning ? 'Scanning...' : '+ Scan Full Company'}
271
+ {scanning ? 'Scanning...' : busy ? 'Scanner busy' : '+ Scan Full Company'}
265
272
  </button>
266
273
  <span
267
274
  title="Not yet tested against live LinkedIn. Results may be incomplete."
@@ -275,9 +282,7 @@ function PathsInner() {
275
282
  </div>
276
283
  </header>
277
284
 
278
- {!selectedCompany && tab === 'scores' ? (
279
- <CompanyScores onRescored={() => setReloadKey((k) => k + 1)} />
280
- ) : !selectedCompany && tab !== 'companies' ? (
285
+ {!selectedCompany && tab !== 'companies' ? (
281
286
  <PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data} tab={tab}
282
287
  onOpenCompany={(co) => selectCompany({ ...co, sCount: co.S, aCount: co.A })} />
283
288
  ) : (
@@ -33,7 +33,7 @@ function ProfileInner() {
33
33
  const [notifications, setNotifications] = useState([]);
34
34
  const [queueStats, setQueueStats] = useState({ total: 0, sent: 0, accepted: 0 });
35
35
  const [mapping, setMapping] = useState({ degree1: [], degree2: [], skips: [] });
36
- // "Your Sectors" is what you picked in Settings → Your sector. (users.sectors,
36
+ // "Your Sectors" is what you picked in Scores → Your sector. (users.sectors,
37
37
  // which this card used to show, is never written by anything; the Sidebar
38
38
  // and Outlink still read it as free text.) null while it loads; a load that
39
39
  // fails says so rather than showing "none picked" (TRAPS §7).
@@ -376,8 +376,8 @@ function ProfileInner() {
376
376
  }}>
377
377
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
378
378
  <h3 style={{ fontSize: 14, fontWeight: 700, margin: 0 }}>Your Sectors</h3>
379
- <Link href="/settings#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
380
- {sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick in Settings' : 'Settings'} &rarr;
379
+ <Link href="/scores#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
380
+ {sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick on Scores' : 'Scores'} &rarr;
381
381
  </Link>
382
382
  </div>
383
383
  {!sectorFocus ? (
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
3
+ import { useEffect, useMemo, useState } from 'react';
4
4
  import { loadNetwork } from '../../lib/network';
5
5
  import { IS_DEMO } from '../../lib/demo';
6
6
  import OutlinkQuest from '../components/OutlinkQuest';
@@ -8,6 +8,9 @@ import OnboardingGate from '../components/OnboardingGate';
8
8
  import { useUser } from '../components/UserProvider';
9
9
  import { rowCompanyScore, TOP_COMPANY } from '../../lib/scoring';
10
10
  import Link from 'next/link';
11
+ import useRequests from '../components/useRequests';
12
+ import { hasRequest, markRequested, undoRequest } from '../../lib/requests-client';
13
+ import { keyFor } from '../../lib/separation';
11
14
 
12
15
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
13
16
 
@@ -38,8 +41,12 @@ function QueueInner() {
38
41
  const [loading, setLoading] = useState(true);
39
42
  const [filter, setFilter] = useState('all');
40
43
  const [selected, setSelected] = useState(new Set());
41
- const [sentIds, setSentIds] = useState(new Set());
42
- const [pendingList, setPendingList] = useState([]);
44
+ // Requests out come from the one list every view shares (lib/requests-client.js),
45
+ // per person, so a request sent on the map shows here and one sent here shows
46
+ // there. What this page loads is only the rows to draw them with.
47
+ const requests = useRequests();
48
+ const [serverPending, setServerPending] = useState([]);
49
+ const [bridgeById, setBridgeById] = useState({});
43
50
  // Circles is the game (lib/quest.js); the list and Pending are still here.
44
51
  const [view, setView] = useState('quest'); // quest | recs | pending
45
52
  const [added, setAdded] = useState([]);
@@ -80,13 +87,8 @@ function QueueInner() {
80
87
  const bridgeById = {};
81
88
  d1.forEach(c => { bridgeById[c.id] = c; });
82
89
 
83
- // Pre-load sent IDs
84
- const sentSet = new Set((pendingRes.pending || []).map(p => p.id));
85
- setSentIds(sentSet);
86
- setPendingList((pendingRes.pending || []).map(p => ({
87
- ...p,
88
- bridge: bridgeById[p.source_connection_id] || null,
89
- })));
90
+ setServerPending(pendingRes.pending || []);
91
+ setBridgeById(bridgeById);
90
92
 
91
93
  const recommendations = d2
92
94
  .filter(c => (c.tier === 'S' || c.tier === 'A' || c.tier === 'B') && !d1Urls.has(c.profile_url))
@@ -104,6 +106,24 @@ function QueueInner() {
104
106
  load();
105
107
  }, [userId]);
106
108
 
109
+ // Every row of anyone with a request out (a person's copies all count), and
110
+ // the people themselves, once each, for the Pending tab.
111
+ const sentIds = useMemo(
112
+ () => new Set([...serverPending, ...recs].filter((r) => hasRequest(r, requests)).map((r) => r.id)),
113
+ [serverPending, recs, requests],
114
+ );
115
+ const pendingList = useMemo(() => {
116
+ const seen = new Set();
117
+ const out = [];
118
+ for (const r of [...serverPending, ...recs]) {
119
+ const key = keyFor(r);
120
+ if (seen.has(key) || !hasRequest(r, requests)) continue;
121
+ seen.add(key);
122
+ out.push({ ...r, bridge: bridgeById[r.source_connection_id] || r.bridge || null });
123
+ }
124
+ return out;
125
+ }, [serverPending, recs, requests, bridgeById]);
126
+
107
127
  // Demo builds: this page is excluded from the public demo
108
128
  if (IS_DEMO) {
109
129
  return (
@@ -303,22 +323,8 @@ function QueueInner() {
303
323
  sentIds={sentIds}
304
324
  added={added}
305
325
  mappedIds={mappedIds}
306
- onSend={async (r) => {
307
- try {
308
- await fetch('/api/outreach', { method: 'POST', headers: { 'Content-Type': 'application/json' },
309
- body: JSON.stringify({ action: 'mark-sent', connectionId: r.id, profileUrl: r.profile_url, userId }) });
310
- setSentIds(prev => new Set([...prev, r.id]));
311
- setPendingList(prev => (prev.some(x => x.id === r.id) ? prev : [...prev, r]));
312
- } catch {}
313
- }}
314
- onUndo={async (r) => {
315
- try {
316
- await fetch('/api/outreach', { method: 'POST', headers: { 'Content-Type': 'application/json' },
317
- body: JSON.stringify({ action: 'undo', connectionId: r.id, userId }) });
318
- setSentIds(prev => { const next = new Set(prev); next.delete(r.id); return next; });
319
- setPendingList(prev => prev.filter(x => x.id !== r.id));
320
- } catch {}
321
- }}
326
+ onSend={(r) => markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {})}
327
+ onUndo={(r) => undoRequest(r).catch(() => {})}
322
328
  />
323
329
  </div>
324
330
  )}
@@ -391,14 +397,7 @@ function QueueInner() {
391
397
  style={{ padding: '4px 8px', borderRadius: 4, fontSize: 9, fontWeight: 700, background: '#0077B5', color: '#fff', textDecoration: 'none' }}>
392
398
  View
393
399
  </a>
394
- <button onClick={async () => {
395
- await fetch('/api/outreach', {
396
- method: 'POST', headers: { 'Content-Type': 'application/json' },
397
- body: JSON.stringify({ action: 'undo', connectionId: p.id }),
398
- });
399
- setPendingList(prev => prev.filter(x => x.id !== p.id));
400
- setSentIds(prev => { const next = new Set(prev); next.delete(p.id); return next; });
401
- }} style={{
400
+ <button onClick={() => { undoRequest(p).catch(() => {}); }} style={{
402
401
  padding: '4px 8px', borderRadius: 4, fontSize: 9, fontWeight: 600,
403
402
  background: 'rgba(255,255,255,0.06)', color: '#666', border: 'none', cursor: 'pointer',
404
403
  }}>Undo</button>
@@ -585,13 +584,9 @@ function QueueInner() {
585
584
  <span style={{ padding: '3px 6px', borderRadius: 3, fontSize: 8, fontWeight: 700, background: 'rgba(255,107,53,0.15)', color: '#FF6B35', flexShrink: 0 }}>Sent</span>
586
585
  ) : (
587
586
  <a href={r.profile_url} target="_blank" rel="noopener noreferrer"
588
- onClick={async (e) => {
587
+ onClick={(e) => {
589
588
  e.stopPropagation();
590
- try {
591
- await fetch('/api/outreach', { method: 'POST', headers: { 'Content-Type': 'application/json' },
592
- body: JSON.stringify({ action: 'mark-sent', connectionId: r.id, profileUrl: r.profile_url }) });
593
- setSentIds(prev => new Set([...prev, r.id]));
594
- } catch {}
589
+ markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {});
595
590
  }}
596
591
  style={{ padding: '3px 6px', borderRadius: 3, fontSize: 8, fontWeight: 700, background: '#0077B5', color: '#fff', textDecoration: 'none', flexShrink: 0 }}>Add</a>
597
592
  )}
@@ -659,17 +654,10 @@ function QueueInner() {
659
654
  }}>⏳ Sent</span>
660
655
  ) : (
661
656
  <a href={r.profile_url} target="_blank" rel="noopener noreferrer"
662
- onClick={async (e) => {
657
+ onClick={(e) => {
663
658
  e.stopPropagation();
664
- // Mark as sent in DB after opening LinkedIn
665
- try {
666
- await fetch('/api/outreach', {
667
- method: 'POST',
668
- headers: { 'Content-Type': 'application/json' },
669
- body: JSON.stringify({ action: 'mark-sent', connectionId: r.id, profileUrl: r.profile_url }),
670
- });
671
- setSentIds(prev => new Set([...prev, r.id]));
672
- } catch {}
659
+ // Opens LinkedIn, and marks the request sent everywhere.
660
+ markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {});
673
661
  }}
674
662
  style={{
675
663
  padding: '4px 8px', borderRadius: 4, fontSize: 9, fontWeight: 700,
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+
3
+ // Scores: how a power score is worked out, and everything you can change about
4
+ // it, in one place (Blake, 2026-09-28: "maybe this should be its own tab").
5
+ // Your field (the sector focus), how tiers are graded, and every company's
6
+ // score. The first two used to live in Settings and the company scores in
7
+ // Paths → Scores; the old links forward here (app/settings/page.js,
8
+ // app/paths/page.js).
9
+
10
+ import { useState } from 'react';
11
+ import Link from 'next/link';
12
+ import OnboardingGate from '../components/OnboardingGate';
13
+ import SectorSection from '../components/settings/SectorSection';
14
+ import TierSection from '../components/settings/TierSection';
15
+ import CompanyScores from '../components/CompanyScores';
16
+ import { Body, LINE, FONT } from '../components/ui';
17
+ import { IS_DEMO } from '../../lib/demo';
18
+
19
+ export default function ScoresPage() {
20
+ return <OnboardingGate><ScoresInner /></OnboardingGate>;
21
+ }
22
+
23
+ const jump = { color: '#3498DB', textDecoration: 'none', fontWeight: 600 };
24
+
25
+ function ScoresInner() {
26
+ // Saving your sector or tiers rescores everyone; the company list below reads
27
+ // its scores again when this changes.
28
+ const [saves, setSaves] = useState(0);
29
+ const saved = () => setSaves((n) => n + 1);
30
+ if (IS_DEMO) {
31
+ return (
32
+ <div style={{
33
+ minHeight: '100vh', background: '#0a0a1a', color: 'rgba(255,255,255,0.7)',
34
+ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
35
+ gap: 12, textAlign: 'center', padding: 24, fontFamily: FONT,
36
+ }}>
37
+ <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700, color: '#fff' }}>Not part of the demo</h2>
38
+ <p style={{ margin: 0, fontSize: 13 }}>The public demo includes the Network Circle and Degrees views only.</p>
39
+ <Link href="/" style={{ fontSize: 12, color: 'rgba(255,255,255,0.5)', textDecoration: 'none' }}>&larr; Back to the network</Link>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ return (
45
+ <div style={{ minHeight: '100vh', background: '#0a0a1a', color: '#fff', fontFamily: FONT }}>
46
+ <header style={{ padding: '16px 24px', borderBottom: LINE, display: 'flex', alignItems: 'center', gap: 16 }}>
47
+ <Link href="/" style={{
48
+ display: 'flex', alignItems: 'center', gap: 6, color: '#888', textDecoration: 'none',
49
+ fontSize: 13, fontWeight: 600, padding: '6px 14px', borderRadius: 6,
50
+ background: 'rgba(255,255,255,0.06)', border: LINE,
51
+ }}>← Back to Map</Link>
52
+ <h1 style={{
53
+ fontSize: 22, fontWeight: 700, margin: 0,
54
+ background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)',
55
+ WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
56
+ }}>Scores</h1>
57
+ </header>
58
+ <main style={{ maxWidth: 980, margin: '0 auto', padding: '8px 24px 64px' }}>
59
+ <Body style={{ marginTop: 16 }}>
60
+ How someone&rsquo;s power score is worked out, and the three things you can change about it:{' '}
61
+ <a href="#sector" style={jump}>your field</a>, <a href="#tiers" style={jump}>how tiers are graded</a> and{' '}
62
+ <a href="#companies" style={jump}>any company&rsquo;s score</a>. A change here rescores everyone.
63
+ </Body>
64
+ <SectorSection onSaved={saved} />
65
+ <TierSection onSaved={saved} />
66
+ <div id="companies" style={{ marginTop: 28 }}>
67
+ <CompanyScores version={saves} />
68
+ </div>
69
+ </main>
70
+ </div>
71
+ );
72
+ }
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { useEffect, useState } from 'react';
4
4
  import Link from 'next/link';
5
+ import { useRouter } from 'next/navigation';
5
6
  import OnboardingGate from '../components/OnboardingGate';
6
7
  import UpdatePanel from '../components/UpdatePanel';
7
- import SectorSection from '../components/settings/SectorSection';
8
8
  import DataSection from '../components/settings/DataSection';
9
9
  import { Section, Body, Mono, LINE, FONT } from '../components/ui';
10
10
  import { IS_DEMO } from '../../lib/demo';
@@ -28,6 +28,13 @@ const KIND_LABEL = {
28
28
  function SettingsInner() {
29
29
  const [info, setInfo] = useState(null);
30
30
  const [error, setError] = useState(null);
31
+ const router = useRouter();
32
+ // Your field and tiers moved to the Scores tab (app/scores): old links to
33
+ // /settings#sector and /settings#tiers go there.
34
+ useEffect(() => {
35
+ const at = window.location.hash;
36
+ if (at === '#sector' || at === '#tiers') router.replace(`/scores${at}`);
37
+ }, [router]);
31
38
 
32
39
  useEffect(() => {
33
40
  fetch('/api/settings')
@@ -71,8 +78,13 @@ function SettingsInner() {
71
78
  {error && <Body style={{ color: '#ff7676', marginTop: 16 }}>{error}</Body>}
72
79
 
73
80
  <UpdatePanel />
74
- <SectorSection />
75
-
81
+ <Section id="scoring" title="Your field and tiers">
82
+ <Body>
83
+ They&rsquo;re on the <Link href="/scores" style={{ color: '#3498DB' }}>Scores</Link> tab now, beside every
84
+ company&rsquo;s score: <Link href="/scores#sector" style={{ color: '#3498DB' }}>your field</Link> and{' '}
85
+ <Link href="/scores#tiers" style={{ color: '#3498DB' }}>how tiers are graded</Link>.
86
+ </Body>
87
+ </Section>
76
88
  <DataSection />
77
89
 
78
90
  <Section id="about" title="About this copy">
@@ -60,6 +60,7 @@ CREATE TABLE IF NOT EXISTS linkedin_connections (
60
60
  unlocked_from_bridge_id TEXT,
61
61
  unlocked_from_name TEXT,
62
62
  outreach_status TEXT,
63
+ mutual_count INTEGER, -- LinkedIn's count of mutual connections (2nd degree), when a scan read one
63
64
  scanned_company TEXT,
64
65
  score_why TEXT,
65
66
  user_id TEXT,