six-degrees 0.3.0 → 0.4.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 (427) 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 +35 -7
  5. package/.next/standalone/.next/required-server-files.json +6 -1
  6. package/.next/standalone/.next/routes-manifest.json +18 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  22. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  24. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  26. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  28. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
  47. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
  51. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +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/unlock/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  61. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  63. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/import.html +1 -1
  68. package/.next/standalone/.next/server/app/import.rsc +13 -13
  69. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
  70. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  71. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
  72. package/.next/standalone/.next/server/app/index.html +1 -1
  73. package/.next/standalone/.next/server/app/index.rsc +13 -13
  74. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
  75. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
  76. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  77. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/launch.html +2 -2
  80. package/.next/standalone/.next/server/app/launch.rsc +13 -13
  81. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
  82. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  83. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
  84. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/paths.html +1 -1
  89. package/.next/standalone/.next/server/app/paths.rsc +13 -13
  90. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
  91. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  92. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
  93. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app/profile.html +1 -1
  96. package/.next/standalone/.next/server/app/profile.rsc +13 -13
  97. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
  98. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  99. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
  100. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/queue.html +1 -1
  103. package/.next/standalone/.next/server/app/queue.rsc +13 -13
  104. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
  105. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  106. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
  107. package/.next/standalone/.next/server/app/scores/page/app-paths-manifest.json +3 -0
  108. package/.next/standalone/.next/server/app/scores/page/build-manifest.json +20 -0
  109. package/.next/standalone/.next/server/app/scores/page/next-font-manifest.json +11 -0
  110. package/.next/standalone/.next/server/app/scores/page/react-loadable-manifest.json +1 -0
  111. package/.next/standalone/.next/server/app/scores/page/server-reference-manifest.json +4 -0
  112. package/.next/standalone/.next/server/app/scores/page.js +13 -0
  113. package/.next/standalone/.next/server/app/scores/page.js.map +5 -0
  114. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -0
  115. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +3 -0
  116. package/.next/standalone/.next/server/app/scores.html +1 -0
  117. package/.next/standalone/.next/server/app/scores.meta +26 -0
  118. package/.next/standalone/.next/server/app/scores.rsc +24 -0
  119. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -0
  120. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +4 -0
  121. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +28 -0
  122. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  123. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  124. package/.next/standalone/.next/server/app/settings.html +1 -1
  125. package/.next/standalone/.next/server/app/settings.rsc +13 -13
  126. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +13 -13
  127. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  128. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
  129. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/app/setup.html +1 -1
  132. package/.next/standalone/.next/server/app/setup.rsc +13 -13
  133. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
  134. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  135. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
  136. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__03ydxxh._.js} +2 -2
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bat8nx._.js +8 -0
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
  157. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
  159. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
  161. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  162. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
  163. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
  164. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
  165. package/.next/standalone/.next/server/chunks/_02twlca._.js +10 -0
  166. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  167. package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
  168. package/.next/standalone/.next/server/chunks/_1_fynia._.js +8 -6
  169. package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
  170. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  171. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  172. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +13 -8
  173. package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
  175. package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
  176. package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
  177. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
  178. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
  179. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
  180. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
  181. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
  182. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
  183. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
  184. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
  185. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
  186. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
  187. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0an6blh._.js → [root-of-the-server]__0zzcbqg._.js} +2 -2
  188. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1z1j3ek._.js → [root-of-the-server]__19ip1qq._.js} +2 -2
  190. package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
  192. package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/ssr/_1cyxocj._.js +3 -0
  196. package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
  198. package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +3 -0
  199. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
  200. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_scores_page_actions_0x-w58n.js +3 -0
  201. package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
  202. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
  203. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
  204. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +4 -0
  205. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  206. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
  207. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +3 -0
  208. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  209. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
  210. package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +3 -0
  211. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0x3i8za._.js +131 -0
  212. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_1bunoni._.js → node_modules_next_dist_1v8aef8._.js} +2 -2
  213. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
  214. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  215. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  216. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  217. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  218. package/.next/standalone/.next/server/pages/404.html +1 -1
  219. package/.next/standalone/.next/server/pages/500.html +1 -1
  220. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  221. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  222. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  223. package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  224. package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  225. package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
  226. package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  227. package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +18 -0
  228. package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +2 -0
  229. package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  230. package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
  231. package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  232. package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  233. package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  234. package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  235. package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +2 -0
  236. package/.next/standalone/CHANGELOG.md +286 -0
  237. package/.next/standalone/CONTRIBUTING.md +15 -4
  238. package/.next/standalone/README.md +50 -29
  239. package/.next/standalone/SECURITY.md +10 -0
  240. package/.next/standalone/app/api/company-scores/legacy/route.js +1 -1
  241. package/.next/standalone/app/api/company-scores/route.js +1 -1
  242. package/.next/standalone/app/api/ingest/route.js +23 -2
  243. package/.next/standalone/app/api/outreach/route.js +107 -44
  244. package/.next/standalone/app/api/scraper/route.js +146 -17
  245. package/.next/standalone/app/api/settings/sector-preview/route.js +3 -2
  246. package/.next/standalone/app/api/settings/sector-suggestions/route.js +1 -1
  247. package/.next/standalone/app/api/update-images/route.js +41 -7
  248. package/.next/standalone/app/avatars/[file]/route.js +6 -1
  249. package/.next/standalone/app/components/Avatar.js +5 -2
  250. package/.next/standalone/app/components/ChainView.js +659 -469
  251. package/.next/standalone/app/components/CompanyScores.js +16 -10
  252. package/.next/standalone/app/components/EmptyState.js +1 -1
  253. package/.next/standalone/app/components/FieldStep.js +94 -0
  254. package/.next/standalone/app/components/ForceGraph.js +183 -768
  255. package/.next/standalone/app/components/GridView.js +9 -7
  256. package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
  257. package/.next/standalone/app/components/LinkedInLimits.js +22 -7
  258. package/.next/standalone/app/components/ListView.js +6 -4
  259. package/.next/standalone/app/components/MappingProgress.js +5 -6
  260. package/.next/standalone/app/components/OnboardingGate.js +1 -1
  261. package/.next/standalone/app/components/OrbitGraph.js +33 -1
  262. package/.next/standalone/app/components/OutlinkQuest.js +5 -4
  263. package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
  264. package/.next/standalone/app/components/RingsView.js +9 -8
  265. package/.next/standalone/app/components/SeparationView.js +96 -14
  266. package/.next/standalone/app/components/Sidebar.js +366 -287
  267. package/.next/standalone/app/components/TheirCircle.js +300 -0
  268. package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
  269. package/.next/standalone/app/components/settings/SectorSection.js +21 -195
  270. package/.next/standalone/app/components/settings/TierSection.js +99 -0
  271. package/.next/standalone/app/components/useRequests.js +12 -0
  272. package/.next/standalone/app/components/useScanner.js +13 -0
  273. package/.next/standalone/app/components/views.js +3 -5
  274. package/.next/standalone/app/globals.css +7 -0
  275. package/.next/standalone/app/launch/page.js +6 -3
  276. package/.next/standalone/app/page.js +183 -74
  277. package/.next/standalone/app/paths/page.js +20 -14
  278. package/.next/standalone/app/profile/page.js +3 -3
  279. package/.next/standalone/app/queue/page.js +56 -60
  280. package/.next/standalone/app/scores/page.js +72 -0
  281. package/.next/standalone/app/settings/page.js +15 -3
  282. package/.next/standalone/app/setup/page.js +423 -260
  283. package/.next/standalone/db/schema.js +2 -1
  284. package/.next/standalone/eslint.config.mjs +3 -3
  285. package/.next/standalone/install.sh +3 -3
  286. package/.next/standalone/lib/chain-layout.js +139 -0
  287. package/.next/standalone/lib/circle.js +169 -0
  288. package/.next/standalone/lib/companies.js +1 -1
  289. package/.next/standalone/lib/csv.js +21 -10
  290. package/.next/standalone/lib/data-export.js +2 -1
  291. package/.next/standalone/lib/data-folder.js +4 -2
  292. package/.next/standalone/lib/db-client.js +1 -0
  293. package/.next/standalone/lib/galaxy.js +12 -0
  294. package/.next/standalone/lib/ingest.js +27 -8
  295. package/.next/standalone/lib/legacy-offer.js +7 -6
  296. package/.next/standalone/lib/legacy-scores.js +2 -2
  297. package/.next/standalone/lib/linkedin-limits.js +30 -8
  298. package/.next/standalone/lib/photos.js +47 -0
  299. package/.next/standalone/lib/quest.js +8 -3
  300. package/.next/standalone/lib/rarity.js +80 -0
  301. package/.next/standalone/lib/reach.js +113 -0
  302. package/.next/standalone/lib/requests-client.js +113 -0
  303. package/.next/standalone/lib/rpc.js +26 -15
  304. package/.next/standalone/lib/scan-state.js +1 -0
  305. package/.next/standalone/lib/scanner-setup.js +20 -1
  306. package/.next/standalone/lib/score-guess.js +60 -0
  307. package/.next/standalone/lib/scoring.js +1761 -69
  308. package/.next/standalone/lib/scraper-client.js +235 -17
  309. package/.next/standalone/lib/sector-directory.js +1 -1
  310. package/.next/standalone/lib/sector-focus.js +17 -3
  311. package/.next/standalone/lib/separation.js +4 -2
  312. package/.next/standalone/lib/settings-client.js +21 -0
  313. package/.next/standalone/lib/settings-effects.js +19 -1
  314. package/.next/standalone/lib/settings.js +7 -2
  315. package/.next/standalone/next.config.mjs +25 -3
  316. package/.next/standalone/package-lock.json +2 -2
  317. package/.next/standalone/package.json +2 -2
  318. package/.next/standalone/public/demo-data.json +1 -1
  319. package/.next/standalone/scripts/build-app.mjs +11 -2
  320. package/.next/standalone/scripts/check-node.mjs +1 -1
  321. package/.next/standalone/scripts/gen-synthetic.mjs +1 -1
  322. package/.next/standalone/scripts/image_store.py +77 -21
  323. package/.next/standalone/scripts/scrape.py +305 -29
  324. package/.next/standalone/server.js +1 -1
  325. package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  326. package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  327. package/.next/static/chunks/0l1568qff0efj.js +1 -0
  328. package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  329. package/.next/static/chunks/0w76iraw7ij3k.js +18 -0
  330. package/.next/static/chunks/135n87ydxjrra.js +2 -0
  331. package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  332. package/.next/static/chunks/28bu26ausqzmn.js +1 -0
  333. package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  334. package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  335. package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  336. package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  337. package/.next/static/chunks/3qh78g2aqhprd.js +2 -0
  338. package/CHANGELOG.md +286 -0
  339. package/README.md +50 -29
  340. package/SECURITY.md +10 -0
  341. package/db/schema.js +2 -1
  342. package/lib/chain-layout.js +139 -0
  343. package/lib/circle.js +169 -0
  344. package/lib/companies.js +1 -1
  345. package/lib/csv.js +21 -10
  346. package/lib/data-export.js +2 -1
  347. package/lib/data-folder.js +4 -2
  348. package/lib/db-client.js +1 -0
  349. package/lib/galaxy.js +12 -0
  350. package/lib/ingest.js +27 -8
  351. package/lib/legacy-offer.js +7 -6
  352. package/lib/legacy-scores.js +2 -2
  353. package/lib/linkedin-limits.js +30 -8
  354. package/lib/photos.js +47 -0
  355. package/lib/quest.js +8 -3
  356. package/lib/rarity.js +80 -0
  357. package/lib/reach.js +113 -0
  358. package/lib/requests-client.js +113 -0
  359. package/lib/rpc.js +26 -15
  360. package/lib/scan-state.js +1 -0
  361. package/lib/scanner-setup.js +20 -1
  362. package/lib/score-guess.js +60 -0
  363. package/lib/scoring.js +1761 -69
  364. package/lib/scraper-client.js +235 -17
  365. package/lib/sector-directory.js +1 -1
  366. package/lib/sector-focus.js +17 -3
  367. package/lib/separation.js +4 -2
  368. package/lib/settings-client.js +21 -0
  369. package/lib/settings-effects.js +19 -1
  370. package/lib/settings.js +7 -2
  371. package/package.json +2 -2
  372. package/public/demo-data.json +1 -1
  373. package/scripts/check-node.mjs +1 -1
  374. package/scripts/image_store.py +77 -21
  375. package/scripts/scrape.py +305 -29
  376. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
  377. package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
  378. package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
  379. package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
  380. package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +0 -3
  381. package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
  382. package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
  383. package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
  384. package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
  385. package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
  386. package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
  387. package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +0 -3
  388. package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +0 -3
  389. package/.next/standalone/.next/server/chunks/ssr/_0942v8n._.js +0 -3
  390. package/.next/standalone/.next/server/chunks/ssr/_0hl_t7r._.js +0 -3
  391. package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +0 -132
  392. package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
  393. package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
  394. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +0 -3
  395. package/.next/standalone/.next/server/chunks/ssr/node_modules_16vay6l._.js +0 -3
  396. package/.next/standalone/.next/server/chunks/ssr/node_modules_1kciwn4._.js +0 -3
  397. package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  398. package/.next/standalone/.next/static/chunks/08k1crid-05dt.js +0 -1
  399. package/.next/standalone/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  400. package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +0 -2
  401. package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +0 -1
  402. package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +0 -44
  403. package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  404. package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +0 -1
  405. package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  406. package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +0 -1
  407. package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  408. package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  409. package/.next/standalone/app/components/BridgeRing.js +0 -753
  410. package/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  411. package/.next/static/chunks/08k1crid-05dt.js +0 -1
  412. package/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  413. package/.next/static/chunks/0q-_khndfxubv.js +0 -2
  414. package/.next/static/chunks/1c79veqedvubt.js +0 -1
  415. package/.next/static/chunks/1chp3nx45meg5.js +0 -44
  416. package/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  417. package/.next/static/chunks/1uadqylrod5_v.js +0 -1
  418. package/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  419. package/.next/static/chunks/289ktj97di6zg.js +0 -1
  420. package/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  421. package/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  422. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
  423. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
  424. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
  425. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
  426. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
  427. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- // Settings → Your sector. Up to three sectors you work in: one of the twelve
3
+ // Scores → Your sector (app/scores; it was Settings → Your sector). Up to three sectors you work in: one of the twelve
4
4
  // broad industries, or a narrower sector from the app's directory (Dental,
5
5
  // Real Estate, Software & SaaS…). Companies in them count for more when your
6
6
  // scanned network is scored, like setting their scores by hand on Paths →
@@ -10,17 +10,19 @@
10
10
  // Before you save, the page asks the app what would change (a dry run that
11
11
  // writes nothing). Saving stores the choice with your network and rescores
12
12
  // everyone, the way a company score change does. Suggestions come from the
13
- // people you've scanned (/api/settings/sector-suggestions).
13
+ // people you've scanned (/api/settings/sector-suggestions). The picks
14
+ // themselves are SectorPicker.js, which the Scan page's question about your
15
+ // field, before the first scan, shows too.
14
16
 
15
- import { useEffect, useMemo, useRef, useState } from 'react';
16
- import Link from 'next/link';
17
+ import { useEffect, useRef, useState } from 'react';
17
18
  import { Section, Body, Status, Btn, LINE } from '../ui';
18
- import { INDUSTRIES } from '../../../lib/companies';
19
- import { MAX_SECTORS, sameFocus, focusFingerprint } from '../../../lib/sector-focus';
20
- import { DIRECTORY, SECTOR_KEYS, sectorByKey } from '../../../lib/sector-directory';
19
+ import SectorPicker from './SectorPicker';
20
+ import { sameFocus, focusFingerprint } from '../../../lib/sector-focus';
21
+ import { DIRECTORY, sectorByKey } from '../../../lib/sector-directory';
21
22
  import { SECTOR_BONUS } from '../../../lib/scoring';
22
23
  import { TIER_COLORS_CLASSIC } from '../../../lib/tiers';
23
24
  import { csvNetworkSource } from '../../../lib/csv';
25
+ import { saveSettings } from '../../../lib/settings-client';
24
26
 
25
27
  const STRENGTH_LABEL = { lean: 'Lean', strong: 'Strong' };
26
28
  // What says the reader has taken over the page (see the jump to #sector).
@@ -28,33 +30,10 @@ const READER_INPUT = ['wheel', 'touchstart', 'keydown', 'pointerdown'];
28
30
  const DEGREE = { 1: '1st', 2: '2nd', 3: '3rd' };
29
31
  const plural = (n, one, many = `${one}s`) => `${n.toLocaleString()} ${n === 1 ? one : many}`;
30
32
 
31
- // Each industry's narrower sectors, in the directory's order.
32
- const SECTORS_OF = new Map(INDUSTRIES.map((g) => [g.key, DIRECTORY.filter((s) => s.group === g.key)]));
33
- const GROUP_OF = new Map(DIRECTORY.map((s) => [s.key, s.group]));
34
- // What the search box looks through: each sector's name, its words, jobs and
35
- // companies, as they read (without the list's "(s)" and "$" marks), so
36
- // "recruiter" finds HR & Recruiting.
37
- const SEARCHABLE = DIRECTORY.map((s) => ({
38
- sector: s,
39
- label: s.label.toLowerCase(),
40
- words: [...s.words, ...(s.roles || []), ...(s.names || []), ...s.companies].map((w) => ` ${w.replace(/\(s\)$|\$$/, '').toLowerCase()}`),
41
- }));
42
- // A sector of work every company has (HR & Recruiting, Legal…) counts firms in
43
- // that line, not every company that employs someone doing it.
44
- const FUNCTION_TITLE = 'Counts firms in this line of work, not every company with someone in the job';
33
+ // The sectors that are work every kind of company has (HR & Recruiting, Legal…), for the notes below.
45
34
  const FUNCTIONS = DIRECTORY.filter((s) => s.kind === 'function').map((s) => s.label);
46
35
  const listed = (labels) => (labels.length > 1 ? `${labels.slice(0, -1).join(', ')} and ${labels.at(-1)}` : labels.join(''));
47
36
 
48
- /** Sectors (and whole industries) whose name has the query, or a word or company that starts with it. */
49
- function searchSectors(query) {
50
- const q = query.trim().toLowerCase().replace(/\s+/g, ' ');
51
- if (!q) return null;
52
- return {
53
- industries: INDUSTRIES.filter((g) => g.label.toLowerCase().includes(q)),
54
- sectors: SEARCHABLE.filter((s) => s.label.includes(q) || s.words.some((w) => w.includes(` ${q}`))).map((s) => s.sector),
55
- };
56
- }
57
-
58
37
  /** "120 people move up a tier and 4 move down." (or "moved", after a save) */
59
38
  function tierLine(up, down, past) {
60
39
  const [goes, go] = past ? ['moved', 'moved'] : ['moves', 'move'];
@@ -64,7 +43,7 @@ function tierLine(up, down, past) {
64
43
  return up ? `${plural(up, 'person', 'people')} ${verb(up)} up a tier.` : `${plural(down, 'person', 'people')} ${verb(down)} down a tier.`;
65
44
  }
66
45
 
67
- export default function SectorSection() {
46
+ export default function SectorSection({ onSaved } = {}) {
68
47
  const [saved, setSaved] = useState(null);
69
48
  const [draft, setDraft] = useState(null);
70
49
  const [preview, setPreview] = useState(null);
@@ -72,9 +51,6 @@ export default function SectorSection() {
72
51
  const [result, setResult] = useState(null);
73
52
  const [loadError, setLoadError] = useState(null);
74
53
  const [suggest, setSuggest] = useState({ loading: true });
75
- const [query, setQuery] = useState('');
76
- // Industries shown open; those holding a saved pick start open.
77
- const [open, setOpen] = useState(() => new Set());
78
54
  // A CSV import or the sample network held in this window. This section only
79
55
  // renders in the browser (the page waits in OnboardingGate), so reading
80
56
  // sessionStorage here can't disagree with a server render.
@@ -92,7 +68,6 @@ export default function SectorSection() {
92
68
  const focus = d.settings?.sectorFocus || { sectors: [], strength: 'lean' };
93
69
  setSaved(focus);
94
70
  setDraft(focus);
95
- setOpen(new Set(focus.sectors.map((k) => GROUP_OF.get(k) || k)));
96
71
  })
97
72
  .catch((e) => { if (!off) setLoadError(e.message); });
98
73
  // Suggestions read every scanned row, so they load on their own and never hold up the rest.
@@ -103,7 +78,7 @@ export default function SectorSection() {
103
78
  return () => { off = true; clearTimeout(timer.current); };
104
79
  }, []);
105
80
 
106
- // Links to /settings#sector (the profile's card, Paths → Scores) arrive
81
+ // Links to /scores#sector (the profile's card, the old /settings#sector) arrive
107
82
  // before this section has loaded, so the browser's own jump to it finds
108
83
  // nothing. Jump once it's here, then keep it in place while what loads above
109
84
  // it (Updates) pushes it down: the browser only holds steady what's already
@@ -128,8 +103,6 @@ export default function SectorSection() {
128
103
  return stop;
129
104
  }, [draft, loadError]);
130
105
 
131
- const results = useMemo(() => searchSectors(query), [query]);
132
-
133
106
  // Every change asks for a fresh dry run, a moment after the last click.
134
107
  // Only the newest answer is kept.
135
108
  function change(next) {
@@ -154,55 +127,20 @@ export default function SectorSection() {
154
127
  }, 250);
155
128
  }
156
129
 
157
- function toggle(key) {
158
- const on = draft.sectors.includes(key);
159
- if (!on && draft.sectors.length >= MAX_SECTORS) return;
160
- const sectors = on ? draft.sectors.filter((k) => k !== key) : SECTOR_KEYS.filter((k) => k === key || draft.sectors.includes(k));
161
- // A sector picked from the suggestions or a search shows up in its industry too.
162
- if (!on && GROUP_OF.has(key)) setOpen((was) => new Set(was).add(GROUP_OF.get(key)));
163
- change({ ...draft, sectors });
164
- }
165
-
166
- function flip(group) {
167
- setOpen((was) => {
168
- const next = new Set(was);
169
- if (next.has(group)) next.delete(group); else next.add(group);
170
- return next;
171
- });
172
- }
173
-
174
130
  async function save() {
175
131
  setSaving(true);
176
132
  setResult(null);
177
133
  clearTimeout(timer.current);
178
134
  ticket.current++;
179
- try {
180
- const r = await fetch('/api/settings', {
181
- method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ settings: { sectorFocus: draft } }),
182
- });
183
- const d = await r.json();
184
- if (d.settings) { setSaved(d.settings.sectorFocus); setDraft(d.settings.sectorFocus); setPreview(null); }
185
- if (!r.ok) { setResult({ tone: 'bad', text: d.error || 'Could not save.' }); return; }
186
- setResult({ tone: 'ok', text: savedLine(d.settings.sectorFocus, d.effects?.sectorFocus) });
187
- } catch {
188
- setResult({ tone: 'bad', text: 'Could not reach the app. Reload this page to see what is saved.' });
189
- } finally {
190
- setSaving(false);
191
- }
135
+ const d = await saveSettings({ sectorFocus: draft });
136
+ setSaving(false);
137
+ if (d.settings) { setSaved(d.settings.sectorFocus); setDraft(d.settings.sectorFocus); setPreview(null); }
138
+ if (d.error) { setResult({ tone: 'bad', text: d.error }); return; }
139
+ setResult({ tone: 'ok', text: savedLine(d.settings.sectorFocus, d.effects?.sectorFocus) });
140
+ onSaved?.();
192
141
  }
193
142
 
194
143
  const changed = draft && saved && !sameFocus(draft, saved);
195
- const full = draft && draft.sectors.length >= MAX_SECTORS;
196
- // One chip for any pick: an industry or a sector, wherever it's shown.
197
- const chip = (key, label, { note, title } = {}) => {
198
- const on = draft.sectors.includes(key);
199
- return (
200
- <Chip key={key} color={sectorByKey(key).color} on={on} blocked={!on && full} disabled={saving} title={title}
201
- onClick={() => toggle(key)}>
202
- {label}{note && <span style={{ fontWeight: 400, color: on ? '#dfe7e7' : '#8b9a9a' }}>{note}</span>}
203
- </Chip>
204
- );
205
- };
206
144
 
207
145
  return (
208
146
  <Section id="sector" title="Your sector"
@@ -212,82 +150,8 @@ export default function SectorSection() {
212
150
 
213
151
  {draft && (
214
152
  <>
215
- <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
216
- <span style={{ fontSize: 13, color: '#8b9a9a' }}>Your picks ({draft.sectors.length} of {MAX_SECTORS}):</span>
217
- {!draft.sectors.length && <span style={{ fontSize: 13, color: '#667' }}>none yet</span>}
218
- {draft.sectors.map((k) => {
219
- const s = sectorByKey(k);
220
- return (
221
- <button key={k} type="button" disabled={saving} onClick={() => toggle(k)} aria-label={`Remove ${s.label}`}
222
- style={{
223
- display: 'flex', alignItems: 'center', gap: 6, padding: '4px 10px', borderRadius: 999, fontSize: 12.5,
224
- fontWeight: 600, color: '#fff', background: `${s.color}33`, border: `1px solid ${s.color}`, cursor: 'pointer',
225
- }}>
226
- {s.label} <span aria-hidden="true" style={{ color: '#cfd8d8' }}>×</span>
227
- </button>
228
- );
229
- })}
230
- </div>
231
-
232
- <Suggestions state={suggest} chip={chip} />
233
-
234
- <label htmlFor="sector-search" style={{ display: 'block', fontSize: 13, color: '#8b9a9a', margin: '16px 0 6px' }}>
235
- Find a sector
236
- </label>
237
- <div style={{ display: 'flex', gap: 8 }}>
238
- <input id="sector-search" type="search" value={query} onChange={(e) => setQuery(e.target.value)}
239
- placeholder="Try dentist, bakery or Stripe"
240
- autoComplete="off" spellCheck={false}
241
- style={{
242
- flex: 1, minWidth: 0, padding: '8px 12px', borderRadius: 7, fontSize: 14, color: '#fff',
243
- background: 'rgba(255,255,255,0.05)', border: LINE, outline: 'none',
244
- }} />
245
- {query && <Btn onClick={() => setQuery('')}>Clear</Btn>}
246
- </div>
247
-
248
- {results ? (
249
- <div style={{ marginTop: 10 }} aria-live="polite">
250
- {results.industries.length + results.sectors.length === 0 ? (
251
- <Body style={{ fontSize: 12.5 }}>
252
- Nothing in the list matches &ldquo;{query.trim()}&rdquo;. Try a job title, a kind of business or a company, or clear
253
- the search and pick one of the twelve broad industries.
254
- </Body>
255
- ) : (
256
- <div role="group" aria-label="Sectors that match your search" style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
257
- {results.industries.map((g) => chip(g.key, g.label, { note: ' · the whole industry' }))}
258
- {results.sectors.map((s) => chip(s.key, s.label, { title: s.kind === 'function' ? FUNCTION_TITLE : `In ${sectorByKey(s.group).label}` }))}
259
- </div>
260
- )}
261
- </div>
262
- ) : (
263
- <div style={{ marginTop: 10 }}>
264
- {INDUSTRIES.map((g) => {
265
- const sectors = SECTORS_OF.get(g.key);
266
- const isOpen = open.has(g.key);
267
- const inside = sectors.filter((s) => draft.sectors.includes(s.key)).length;
268
- return (
269
- <div key={g.key} style={{ borderTop: LINE, padding: '8px 0' }}>
270
- <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
271
- {chip(g.key, g.label, { title: 'The whole industry: companies its sectors place, or whose name or the well-known list says so' })}
272
- <button type="button" aria-expanded={isOpen} aria-controls={`sectors-${g.key}`} onClick={() => flip(g.key)}
273
- style={{
274
- padding: '4px 6px', fontSize: 12.5, color: inside ? '#9fd3ff' : '#8b9a9a', background: 'none', border: 'none',
275
- cursor: 'pointer',
276
- }}>
277
- {isOpen ? '▾' : '▸'} {plural(sectors.length, 'sector')}{inside ? ` · ${inside} picked` : ''}
278
- </button>
279
- </div>
280
- {isOpen && (
281
- <div id={`sectors-${g.key}`} role="group" aria-label={`Sectors in ${g.label}`}
282
- style={{ display: 'flex', flexWrap: 'wrap', gap: 8, margin: '8px 0 2px 14px' }}>
283
- {sectors.map((s) => chip(s.key, s.label, s.kind === 'function' ? { title: FUNCTION_TITLE } : undefined))}
284
- </div>
285
- )}
286
- </div>
287
- );
288
- })}
289
- </div>
290
- )}
153
+ <SectorPicker sectors={draft.sectors} onChange={(sectors) => change({ ...draft, sectors })} disabled={saving}
154
+ suggest={suggest} />
291
155
 
292
156
  <div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8, marginTop: 14 }}>
293
157
  <span style={{ fontSize: 13, color: '#8b9a9a' }}>How much:</span>
@@ -321,7 +185,7 @@ export default function SectorSection() {
321
185
 
322
186
  <ul style={{ margin: '16px 0 0', paddingLeft: 18, fontSize: 12.5, color: '#778', lineHeight: 1.7 }}>
323
187
  <li>Tiers rank how reachable someone is through your network, not the people themselves.</li>
324
- <li>A company score you set on <Link href="/paths?tab=scores" style={{ color: '#3498DB' }}>Paths → Scores</Link> always wins. Your sector never changes it.</li>
188
+ <li>A company score you set on <a href="#companies" style={{ color: '#3498DB' }}>Company scores</a>, below, always wins. Your sector never changes it.</li>
325
189
  <li>If you scan highest tier first, people in your sectors come up sooner. Scanning newest first, the default, doesn&rsquo;t go by tier.</li>
326
190
  <li>
327
191
  An industry includes its sectors. It counts a company in any of its sectors (Healthcare &amp; Biotech counts a
@@ -352,44 +216,6 @@ export default function SectorSection() {
352
216
  );
353
217
  }
354
218
 
355
- /** A pick you can turn on and off: an industry or a sector, with its industry's colour. */
356
- function Chip({ color, on, blocked, disabled, title, onClick, children }) {
357
- return (
358
- <button type="button" aria-pressed={on} disabled={blocked || disabled} onClick={onClick}
359
- title={blocked ? `Up to ${MAX_SECTORS} sectors. Turn one off first.` : title}
360
- style={{
361
- display: 'flex', alignItems: 'center', gap: 7, padding: '6px 12px', borderRadius: 999, maxWidth: '100%',
362
- fontSize: 12.5, fontWeight: 600, textAlign: 'left', cursor: blocked ? 'not-allowed' : 'pointer',
363
- color: on ? '#fff' : blocked ? '#556' : '#b8c4c4',
364
- background: on ? `${color}33` : 'rgba(255,255,255,0.04)',
365
- border: on ? `1px solid ${color}` : LINE,
366
- }}>
367
- <span style={{ width: 8, height: 8, borderRadius: '50%', background: color, opacity: blocked ? 0.4 : 1, flexShrink: 0 }} />
368
- <span>{children}</span>
369
- </button>
370
- );
371
- }
372
-
373
- /** "Suggested from your network": the directory's sectors most of your scanned people work in. */
374
- function Suggestions({ state, chip }) {
375
- const note = { fontSize: 12.5, marginTop: 10 };
376
- if (state.loading) return <Body style={note}>Looking through your network for suggestions…</Body>;
377
- if (state.error) return <Body style={note}>Couldn&rsquo;t load suggestions ({state.error}). You can still pick below.</Body>;
378
- if (!state.scored) return <Body style={note}>Suggestions appear here once you&rsquo;ve scanned your network.</Body>;
379
- if (!state.list.length) return <Body style={note}>None of the list&rsquo;s sectors stands out among the people you&rsquo;ve scanned yet.</Body>;
380
- return (
381
- <div style={{ marginTop: 14 }}>
382
- <div style={{ fontSize: 13, color: '#8b9a9a', marginBottom: 6 }}>Suggested from your network</div>
383
- <div role="group" aria-label="Suggested from your network" style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
384
- {state.list.map((s) => chip(s.key, s.label, {
385
- note: `: ${plural(s.people, 'person', 'people')} at ${plural(s.companies, 'company', 'companies')}`,
386
- title: `In ${sectorByKey(s.group).label}`,
387
- }))}
388
- </div>
389
- </div>
390
- );
391
- }
392
-
393
219
  /** "Saved: …. Rescored your network (N people). …" */
394
220
  function savedLine(focus, effect) {
395
221
  // The labels have commas of their own ("Tech, Software & AI"), so a dot
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+
3
+ // Scores → Tiers (app/scores; it was Settings → Tiers). How the S to D rings are drawn: on your network's curve
4
+ // (your top 3% are S, and so on down), or on the fixed scale, the same lines
5
+ // for everyone. The curve only lifts, so a network full of companies the
6
+ // curated list knows keeps its tiers. The model is lib/scoring.js (CURVE,
7
+ // curvedTier); saving rescores everyone, as a new sector focus does, and says
8
+ // how many people changed tier (lib/settings-effects.js).
9
+
10
+ import { useEffect, useState } from 'react';
11
+ import { Section, Body, Status, LINE } from '../ui';
12
+ import { CURVE, CURVE_FLOOR } from '../../../lib/scoring';
13
+ import { csvNetworkSource } from '../../../lib/csv';
14
+ import { saveSettings } from '../../../lib/settings-client';
15
+
16
+ const pct = (x) => `${Math.round(x * 100)}%`;
17
+ const [S, A, B] = CURVE.map(([, share]) => share);
18
+ const plural = (n, one, many) => `${n.toLocaleString()} ${n === 1 ? one : many}`;
19
+
20
+ const OPTIONS = [
21
+ {
22
+ key: 'curve',
23
+ label: "On your network's curve",
24
+ note: `Your top ${pct(S)} are S, the next ${pct(A - S)} A and the next ${pct(B - A)} B, so any network has a top, even one full of companies the app doesn't know. It only lifts: nobody drops a tier because their network is strong, and nobody under ${CURVE_FLOOR} is lifted into S or A.`,
25
+ },
26
+ {
27
+ key: 'fixed',
28
+ label: 'On the fixed scale',
29
+ note: 'The same lines for everyone: S from 7.5, A from 5.5, B from 4, C from 2.5. Easy to compare from one network to another, but a network outside well-known companies has almost nobody in S or A.',
30
+ },
31
+ ];
32
+
33
+ export default function TierSection({ onSaved } = {}) {
34
+ const [saved, setSaved] = useState(null);
35
+ const [saving, setSaving] = useState(false);
36
+ const [result, setResult] = useState(null);
37
+ const [loadError, setLoadError] = useState(null);
38
+ // A CSV import or the sample network held in this window: neither is changed
39
+ // by this (a CSV is always graded on its own curve; the sample keeps its
40
+ // tiers). This section only renders in the browser (OnboardingGate), so
41
+ // reading sessionStorage here can't disagree with a server render.
42
+ const [onScreen] = useState(() => (typeof window === 'undefined' ? null : csvNetworkSource()));
43
+
44
+ useEffect(() => {
45
+ let off = false;
46
+ fetch('/api/settings')
47
+ .then((r) => r.json().then((d) => (r.ok ? d : Promise.reject(new Error(d.error || 'Could not load how tiers are graded.')))))
48
+ .then((d) => { if (!off) setSaved(d.settings?.tierScale || 'curve'); })
49
+ .catch((e) => { if (!off) setLoadError(e.message); });
50
+ return () => { off = true; };
51
+ }, []);
52
+
53
+ async function choose(key) {
54
+ if (saving || key === saved) return;
55
+ setSaving(true);
56
+ setResult(null);
57
+ const { settings, effects, error } = await saveSettings({ tierScale: key });
58
+ if (settings) setSaved(settings.tierScale);
59
+ if (error) setResult({ tone: 'bad', text: error });
60
+ else {
61
+ const e = effects?.tierScale;
62
+ setResult({ tone: 'ok', text: e ? `Saved. ${plural(e.moved, 'person', 'people')} changed tier.` : 'Saved.' });
63
+ onSaved?.();
64
+ }
65
+ setSaving(false);
66
+ }
67
+
68
+ return (
69
+ <Section id="tiers" title="Tiers" intro="How the S to D rings are drawn. A tier says where someone sits in your network, not what they're worth.">
70
+ {loadError && <Status tone="bad">{loadError}</Status>}
71
+ {saved && (
72
+ <div role="radiogroup" aria-label="How tiers are graded" style={{ display: 'grid', gap: 8 }}>
73
+ {OPTIONS.map((o) => {
74
+ const on = saved === o.key;
75
+ return (
76
+ <button key={o.key} type="button" role="radio" aria-checked={on} disabled={saving} onClick={() => choose(o.key)} style={{
77
+ textAlign: 'left', padding: '12px 14px', borderRadius: 8, cursor: saving ? 'wait' : on ? 'default' : 'pointer', color: '#fff',
78
+ background: on ? 'rgba(155,89,182,0.14)' : 'rgba(255,255,255,0.03)',
79
+ border: on ? '1px solid rgba(155,89,182,0.6)' : LINE,
80
+ }}>
81
+ <div style={{ fontSize: 14, fontWeight: 650 }}>{on ? '● ' : '○ '}{o.label}{o.key === 'curve' ? ' (recommended)' : ''}</div>
82
+ <Body style={{ marginTop: 4 }}>{o.note}</Body>
83
+ </button>
84
+ );
85
+ })}
86
+ </div>
87
+ )}
88
+ {onScreen && (
89
+ <Body style={{ color: '#FFD700', marginTop: 10 }}>
90
+ {onScreen === 'sample'
91
+ ? 'The sample network open in this window keeps its own tiers (they’re the same either way).'
92
+ : 'The CSV import open in this window is always graded on its own curve.'}{' '}
93
+ This applies to networks you&rsquo;ve scanned.
94
+ </Body>
95
+ )}
96
+ {result && <Status tone={result.tone}>{result.text}</Status>}
97
+ </Section>
98
+ );
99
+ }
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+
3
+ import { useSyncExternalStore } from 'react';
4
+ import { watchRequests, requestsNow, REQUESTS_UNKNOWN } from '../../lib/requests-client';
5
+
6
+ /**
7
+ * Who you've sent a request to, shared by every view (lib/requests-client.js).
8
+ * Pass the answer to hasRequest(row, state) and requestCount(state).
9
+ */
10
+ export default function useRequests() {
11
+ return useSyncExternalStore(watchRequests, requestsNow, () => REQUESTS_UNKNOWN);
12
+ }
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import { useSyncExternalStore } from 'react';
4
+ import { watchScanner, scannerNow, SCANNER_UNKNOWN } from '../../lib/scraper-client';
5
+
6
+ /**
7
+ * What the scanner is running, shared by every Scan button on the page: one
8
+ * answer, asked once (lib/scraper-client.js). Any button that starts a scan
9
+ * greys out while `running`, and says why with busyReason().
10
+ */
11
+ export default function useScanner() {
12
+ return useSyncExternalStore(watchScanner, scannerNow, () => SCANNER_UNKNOWN);
13
+ }
@@ -13,7 +13,6 @@
13
13
  import ForceGraph from './ForceGraph';
14
14
  import OrbitGraph from './OrbitGraph';
15
15
  import ChainView from './ChainView';
16
- import BridgeRing from './BridgeRing';
17
16
  import GridView from './GridView';
18
17
  import ListView from './ListView';
19
18
  import RingsView from './RingsView';
@@ -31,18 +30,17 @@ export const VIEWS = {
31
30
  orbit: { component: OrbitGraph, modes: ['network', 'degrees'], label: 'Orbit', icon: '🪐', desc: 'Tier orbits + circle dots; in Degrees, every mapped circle fanned out', allDegree2: true },
32
31
  separation: { component: SeparationView, modes: ['degrees'], label: 'Separation', icon: '🏆', desc: 'Every 2nd degree, ranked · every way in' },
33
32
  chain: { component: ChainView, modes: ['degrees'], label: 'Bridge Chains', icon: '🔗', desc: 'Degree paths' },
34
- revolver: { component: BridgeRing, modes: ['degrees'], label: 'Revolver', icon: '🎯', desc: 'Rotary dial · spin to switch' },
35
33
  rings: { component: RingsView, modes: ['network', 'degrees'], label: 'Pyramid', icon: '🔺', desc: 'Tier hierarchy' },
36
34
  list: { component: ListView, modes: ['network', 'degrees'], label: 'List', icon: '☰', desc: 'Ranked power list' },
37
35
  grid: { component: GridView, modes: ['network', 'degrees'], label: 'Grid', icon: '▦', desc: 'Cards' },
38
36
  };
39
37
 
40
38
  /**
41
- * The order they appear in the menu. Separation is the first Degrees view, so
42
- * it is also where resolveView lands when the chosen view isn't a Degrees one
39
+ * The order they appear in the menu. Orbit is the first Degrees view, so it is
40
+ * also where resolveView lands when the chosen view isn't a Degrees one
43
41
  * (Galaxy, say, carried over from Network Circle).
44
42
  */
45
- const ORDER = ['galaxy', 'orbit', 'separation', 'chain', 'revolver', 'rings', 'list'];
43
+ const ORDER = ['galaxy', 'orbit', 'separation', 'chain', 'rings', 'list'];
46
44
 
47
45
  export function viewsForMode(mode) {
48
46
  return ORDER
@@ -19,6 +19,13 @@ body:has(> div > [data-map]) {
19
19
  overflow: hidden;
20
20
  }
21
21
 
22
+ /* The Scan page's field question holds Continue and Skip for now at the foot
23
+ of the window (app/components/FieldStep.js). Without this, a pick reached
24
+ with Tab can stop behind them. */
25
+ html:has([data-field-question]) {
26
+ scroll-padding-bottom: 80px;
27
+ }
28
+
22
29
  ::-webkit-scrollbar {
23
30
  width: 6px;
24
31
  }
@@ -125,7 +125,7 @@ function HeroSection() {
125
125
  transition: 'all 1s cubic-bezier(0.16, 1, 0.3, 1) 0.15s',
126
126
  letterSpacing: '-0.03em',
127
127
  }}>
128
- 6 Degrees of<br />Separation
128
+ Six Degrees of<br />Separation
129
129
  </h1>
130
130
 
131
131
  {/* Subtitle */}
@@ -315,7 +315,7 @@ function GalaxySection() {
315
315
  }
316
316
 
317
317
  // ═══════════════════════════════════════════════════════════════
318
- // BRIDGE CHAINS — 6 degrees visualization
318
+ // BRIDGE CHAINS — six degrees visualization
319
319
  // ═══════════════════════════════════════════════════════════════
320
320
  function ChainSection() {
321
321
  const [ref, inView] = useInView();
@@ -474,6 +474,9 @@ function ScoringSection() {
474
474
  </div>
475
475
  ))}
476
476
  </div>
477
+ <p style={{ textAlign: 'center', fontSize: 12, color: '#555', marginTop: 24 }}>
478
+ These are the fixed scale&rsquo;s lines. By default tiers are graded on your own network&rsquo;s curve: your top 3% are S, the next 12% A.
479
+ </p>
477
480
  </div>
478
481
  </section>
479
482
  );
@@ -615,7 +618,7 @@ function TechSection() {
615
618
  // The model in lib/scoring.js: a product, so a title counts for more at a bigger company.
616
619
  { sym: 'T', label: 'Title', desc: 'C-suite 10, VP 9, Director 7.5, Manager 6.5', note: 'Multiplied by the company', color: TIER.S },
617
620
  { sym: 'C', label: 'Company', desc: 'Well-known 7–10, else estimated from your network', note: 'Weight 0.5 to 1.0', color: TIER.A },
618
- { sym: 'R + B', label: 'Reach & bridge', desc: 'Investor, YC, an audience in the millions; a strong circle', note: 'Up to +1.5 and +1', color: TIER.B },
621
+ { sym: 'R + B', label: 'Reach & bridge', desc: 'Investor, YC, an audience in the millions; a strong circle', note: 'Up to +1.5 and +2', color: TIER.B },
619
622
  ].map((item, i) => (
620
623
  <div key={i} style={{
621
624
  padding: 16, borderRadius: 12, background: `${item.color}08`,