six-degrees 0.7.0 → 0.8.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 (349) 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 +32 -4
  5. package/.next/standalone/.next/routes-manifest.json +6 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.rsc +16 -16
  15. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  17. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/admin-update/route.js +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 +1 -1
  23. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  25. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/data/backup/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/data/csv/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/restore/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 +4 -4
  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/social/crm/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/social/route.js +1 -1
  60. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  62. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  65. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  67. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/import.html +1 -1
  72. package/.next/standalone/.next/server/app/import.rsc +18 -18
  73. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +18 -18
  74. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  75. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -16
  76. package/.next/standalone/.next/server/app/index.html +1 -1
  77. package/.next/standalone/.next/server/app/index.rsc +19 -19
  78. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -16
  79. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +19 -19
  80. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  81. package/.next/standalone/.next/server/app/insights/page/app-paths-manifest.json +3 -0
  82. package/.next/standalone/.next/server/app/insights/page/build-manifest.json +20 -0
  83. package/.next/standalone/.next/server/app/insights/page/next-font-manifest.json +11 -0
  84. package/.next/standalone/.next/server/app/insights/page/react-loadable-manifest.json +1 -0
  85. package/.next/standalone/.next/server/app/insights/page/server-reference-manifest.json +4 -0
  86. package/.next/standalone/.next/server/app/insights/page.js +13 -0
  87. package/.next/standalone/.next/server/app/insights/page.js.map +5 -0
  88. package/.next/standalone/.next/server/app/insights/page.js.nft.json +1 -0
  89. package/.next/standalone/.next/server/app/insights/page_client-reference-manifest.js +3 -0
  90. package/.next/standalone/.next/server/app/insights.html +1 -0
  91. package/.next/standalone/.next/server/app/insights.meta +26 -0
  92. package/.next/standalone/.next/server/app/insights.rsc +35 -0
  93. package/.next/standalone/.next/server/app/insights.segments/_full.segment.rsc +35 -0
  94. package/.next/standalone/.next/server/app/insights.segments/_tree.segment.rsc +4 -0
  95. package/.next/standalone/.next/server/app/insights.segments/insights/__PAGE__.segment.rsc +33 -0
  96. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  98. package/.next/standalone/.next/server/app/launch.html +2 -2
  99. package/.next/standalone/.next/server/app/launch.rsc +18 -18
  100. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +18 -18
  101. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  102. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -16
  103. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  105. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  106. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  107. package/.next/standalone/.next/server/app/paths.html +1 -1
  108. package/.next/standalone/.next/server/app/paths.rsc +19 -19
  109. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +19 -19
  110. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  111. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -16
  112. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  113. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  114. package/.next/standalone/.next/server/app/profile.html +1 -1
  115. package/.next/standalone/.next/server/app/profile.rsc +19 -19
  116. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +19 -19
  117. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  118. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -16
  119. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  121. package/.next/standalone/.next/server/app/queue.html +1 -1
  122. package/.next/standalone/.next/server/app/queue.rsc +19 -19
  123. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +19 -19
  124. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  125. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +21 -20
  126. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  127. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  128. package/.next/standalone/.next/server/app/scores.html +1 -1
  129. package/.next/standalone/.next/server/app/scores.rsc +18 -18
  130. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +18 -18
  131. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  132. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -16
  133. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  135. package/.next/standalone/.next/server/app/settings.html +1 -1
  136. package/.next/standalone/.next/server/app/settings.rsc +19 -19
  137. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +19 -19
  138. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  139. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -16
  140. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  141. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  142. package/.next/standalone/.next/server/app/setup.html +1 -1
  143. package/.next/standalone/.next/server/app/setup.rsc +19 -19
  144. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +19 -19
  145. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  146. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -16
  147. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  148. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  149. package/.next/standalone/.next/server/app/social.html +1 -1
  150. package/.next/standalone/.next/server/app/social.rsc +18 -18
  151. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +18 -18
  152. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
  153. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +16 -16
  154. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  155. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__063s0x7._.js → [root-of-the-server]__0r-0t-9._.js} +1 -1
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ra6xyg._.js +15 -0
  157. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20rwfdw._.js → [root-of-the-server]__0rcpjad._.js} +5 -2
  158. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  159. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/lib_02iz_9q._.js +2 -2
  161. package/.next/standalone/.next/server/chunks/lib_05agvk3._.js +2 -2
  162. package/.next/standalone/.next/server/chunks/lib_0v84u66._.js +2 -2
  163. package/.next/standalone/.next/server/chunks/lib_db-client_0_dch35.js +9 -9
  164. package/.next/standalone/.next/server/chunks/{lib_rpc_0jnfp2e.js → lib_rpc_00nndai.js} +1 -1
  165. package/.next/standalone/.next/server/chunks/{lib_rpc_0b17r6v.js → lib_rpc_03ibb-x.js} +1 -1
  166. package/.next/standalone/.next/server/chunks/{lib_rpc_0doq0t-.js → lib_rpc_03ri3ji.js} +1 -1
  167. package/.next/standalone/.next/server/chunks/{lib_rpc_0fd2rlw.js → lib_rpc_078ce48.js} +1 -1
  168. package/.next/standalone/.next/server/chunks/lib_rpc_0a69mgr.js +3 -0
  169. package/.next/standalone/.next/server/chunks/{lib_rpc_005abye.js → lib_rpc_0cej3t8.js} +1 -1
  170. package/.next/standalone/.next/server/chunks/lib_rpc_0cmh4g6.js +3 -0
  171. package/.next/standalone/.next/server/chunks/lib_rpc_0nx2_pc.js +3 -0
  172. package/.next/standalone/.next/server/chunks/lib_rpc_0r79-w1.js +3 -0
  173. package/.next/standalone/.next/server/chunks/{lib_rpc_0is4qoc.js → lib_rpc_0t552z6.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/lib_rpc_0yx8ynd.js +3 -0
  175. package/.next/standalone/.next/server/chunks/lib_rpc_12p-o9-.js +3 -0
  176. package/.next/standalone/.next/server/chunks/lib_rpc_1466dnb.js +3 -0
  177. package/.next/standalone/.next/server/chunks/lib_rpc_1sryxv6.js +3 -0
  178. package/.next/standalone/.next/server/chunks/lib_rpc_1trn-x5.js +3 -0
  179. package/.next/standalone/.next/server/chunks/lib_rpc_1ygu_mv.js +3 -0
  180. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egamyq._.js +3 -0
  181. package/.next/standalone/.next/server/chunks/ssr/_04am_h8._.js +3 -0
  182. package/.next/standalone/.next/server/chunks/ssr/_07zb7lu._.js +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/_0_79idq._.js +1 -1
  184. package/.next/standalone/.next/server/chunks/ssr/_0gds903._.js +3 -0
  185. package/.next/standalone/.next/server/chunks/ssr/_0klqt9z._.js +3 -0
  186. package/.next/standalone/.next/server/chunks/ssr/{_1k9qfsk._.js → _14jak5s._.js} +6 -2
  187. package/.next/standalone/.next/server/chunks/ssr/_14nxd3q._.js +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/_1cj2yog._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +1 -1
  190. package/.next/standalone/.next/server/chunks/ssr/_1hdqw0-._.js +7 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +1 -1
  192. package/.next/standalone/.next/server/chunks/ssr/{_0gy11ar._.js → _1r62six._.js} +6 -2
  193. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_insights_page_actions_0-0tf75.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +2 -2
  195. package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +2 -2
  196. package/.next/standalone/.next/server/chunks/ssr/{_01o3i5y._.js → app_components_settings_SectorPicker_1c-29qw.js} +2 -6
  197. package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
  198. package/.next/standalone/.next/server/chunks/ssr/app_insights_page_1d_chja.js +3 -0
  199. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +14 -14
  200. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +3 -3
  201. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  202. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +3 -3
  203. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  204. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  205. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  206. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  207. package/.next/standalone/.next/server/pages/404.html +1 -1
  208. package/.next/standalone/.next/server/pages/500.html +1 -1
  209. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  210. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  211. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  212. package/.next/standalone/.next/static/chunks/02wnf8dkxegxg.js +34 -0
  213. package/.next/standalone/.next/static/chunks/0838_s6w-i_ik.js +1 -0
  214. package/.next/{static/chunks/2eo9pqq9gay3r.js → standalone/.next/static/chunks/0ka4prpk3w9wn.js} +4 -4
  215. package/.next/standalone/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
  216. package/.next/standalone/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
  217. package/.next/standalone/.next/static/chunks/112mv-com-mqa.js +21 -0
  218. package/.next/{static/chunks/44yn0tcd9dfk3.js → standalone/.next/static/chunks/1bkt_hhjzvbh8.js} +1 -1
  219. package/.next/standalone/.next/static/chunks/1cmphb3kh2ers.css +3 -0
  220. package/.next/{static/chunks/1qbjermbf68xj.js → standalone/.next/static/chunks/1d8yv3k_atka7.js} +1 -1
  221. package/.next/standalone/.next/static/chunks/23hc51oa4vkhc.js +5 -0
  222. package/.next/standalone/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
  223. package/.next/standalone/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
  224. package/.next/{static/chunks/2zf4jmz9l3li2.js → standalone/.next/static/chunks/2w-y83mjlpnse.js} +1 -1
  225. package/.next/standalone/.next/static/chunks/2ws1xf293j-tm.js +5 -0
  226. package/.next/standalone/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
  227. package/.next/standalone/.next/static/chunks/3opgg1efrx8q6.js +1 -0
  228. package/.next/standalone/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
  229. package/.next/{static/chunks/0an_b6zi1r_qq.js → standalone/.next/static/chunks/3y40z6of3r9py.js} +1 -5
  230. package/.next/standalone/.next/static/chunks/403dpflln8ah5.js +1 -0
  231. package/.next/standalone/CHANGELOG.md +60 -0
  232. package/.next/standalone/README.md +25 -1
  233. package/.next/standalone/SECURITY.md +8 -0
  234. package/.next/standalone/app/api/outreach/route.js +13 -73
  235. package/.next/standalone/app/api/scraper/route.js +97 -4
  236. package/.next/standalone/app/components/AppHeader.js +22 -6
  237. package/.next/standalone/app/components/AppTabs.js +4 -0
  238. package/.next/standalone/app/components/AutoConnect.js +286 -0
  239. package/.next/standalone/app/components/ChainView.js +75 -2
  240. package/.next/standalone/app/components/FilterPanel.js +8 -1
  241. package/.next/standalone/app/components/LinkedInLimits.js +38 -13
  242. package/.next/standalone/app/components/OutlinkQuest.js +32 -11
  243. package/.next/standalone/app/components/ScanStatusBar.js +18 -3
  244. package/.next/standalone/app/components/ScanTrail.js +13 -3
  245. package/.next/standalone/app/components/SeparationView.js +6 -22
  246. package/.next/standalone/app/components/Sidebar.js +147 -38
  247. package/.next/standalone/app/components/insights/Boards.js +275 -0
  248. package/.next/standalone/app/components/insights/PeopleView.js +266 -0
  249. package/.next/standalone/app/components/insights/Rail.js +156 -0
  250. package/.next/standalone/app/components/insights/Report.js +382 -0
  251. package/.next/standalone/app/components/insights/parts.js +239 -0
  252. package/.next/standalone/app/components/settings/UsageSection.js +50 -3
  253. package/.next/standalone/app/globals.css +2 -3
  254. package/.next/standalone/app/insights/page.js +220 -0
  255. package/.next/standalone/app/page.js +1 -17
  256. package/.next/standalone/app/queue/page.js +30 -18
  257. package/.next/standalone/app/setup/page.js +16 -9
  258. package/.next/standalone/lib/auto-connect.js +145 -0
  259. package/.next/standalone/lib/companies.js +1 -1
  260. package/.next/standalone/lib/in-progress.js +46 -0
  261. package/.next/standalone/lib/insights-board.js +613 -0
  262. package/.next/standalone/lib/linkedin-limits.js +36 -11
  263. package/.next/standalone/lib/requests-client.js +10 -0
  264. package/.next/standalone/lib/requests.js +124 -0
  265. package/.next/standalone/lib/scan-state.js +1 -0
  266. package/.next/standalone/lib/scraper-client.js +18 -5
  267. package/.next/standalone/lib/settings.js +3 -0
  268. package/.next/standalone/lib/usage.js +5 -0
  269. package/.next/standalone/package-lock.json +2 -2
  270. package/.next/standalone/package.json +1 -1
  271. package/.next/standalone/scripts/scrape.py +569 -9
  272. package/.next/static/chunks/02wnf8dkxegxg.js +34 -0
  273. package/.next/static/chunks/0838_s6w-i_ik.js +1 -0
  274. package/.next/{standalone/.next/static/chunks/2eo9pqq9gay3r.js → static/chunks/0ka4prpk3w9wn.js} +4 -4
  275. package/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
  276. package/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
  277. package/.next/static/chunks/112mv-com-mqa.js +21 -0
  278. package/.next/{standalone/.next/static/chunks/44yn0tcd9dfk3.js → static/chunks/1bkt_hhjzvbh8.js} +1 -1
  279. package/.next/static/chunks/1cmphb3kh2ers.css +3 -0
  280. package/.next/{standalone/.next/static/chunks/1qbjermbf68xj.js → static/chunks/1d8yv3k_atka7.js} +1 -1
  281. package/.next/static/chunks/23hc51oa4vkhc.js +5 -0
  282. package/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
  283. package/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
  284. package/.next/{standalone/.next/static/chunks/2zf4jmz9l3li2.js → static/chunks/2w-y83mjlpnse.js} +1 -1
  285. package/.next/static/chunks/2ws1xf293j-tm.js +5 -0
  286. package/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
  287. package/.next/static/chunks/3opgg1efrx8q6.js +1 -0
  288. package/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
  289. package/.next/{standalone/.next/static/chunks/0an_b6zi1r_qq.js → static/chunks/3y40z6of3r9py.js} +1 -5
  290. package/.next/static/chunks/403dpflln8ah5.js +1 -0
  291. package/CHANGELOG.md +60 -0
  292. package/README.md +25 -1
  293. package/SECURITY.md +8 -0
  294. package/lib/auto-connect.js +145 -0
  295. package/lib/companies.js +1 -1
  296. package/lib/in-progress.js +46 -0
  297. package/lib/insights-board.js +613 -0
  298. package/lib/linkedin-limits.js +36 -11
  299. package/lib/requests-client.js +10 -0
  300. package/lib/requests.js +124 -0
  301. package/lib/scan-state.js +1 -0
  302. package/lib/scraper-client.js +18 -5
  303. package/lib/settings.js +3 -0
  304. package/lib/usage.js +5 -0
  305. package/package.json +1 -1
  306. package/scripts/scrape.py +569 -9
  307. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_4y8z7._.js +0 -8
  308. package/.next/standalone/.next/server/chunks/_02twlca._.js +0 -10
  309. package/.next/standalone/.next/server/chunks/lib_rpc_0nef_n7.js +0 -3
  310. package/.next/standalone/.next/server/chunks/lib_rpc_0plrzra.js +0 -3
  311. package/.next/standalone/.next/server/chunks/lib_rpc_13a4m0c.js +0 -3
  312. package/.next/standalone/.next/server/chunks/lib_rpc_1if17c-.js +0 -3
  313. package/.next/standalone/.next/server/chunks/lib_rpc_1j-zw7v.js +0 -3
  314. package/.next/standalone/.next/server/chunks/lib_rpc_1sngt-q.js +0 -3
  315. package/.next/standalone/.next/server/chunks/lib_rpc_1v2sgme.js +0 -3
  316. package/.next/standalone/.next/server/chunks/lib_rpc_1vh-tgx.js +0 -3
  317. package/.next/standalone/.next/server/chunks/lib_rpc_1zaubz7.js +0 -3
  318. package/.next/standalone/.next/server/chunks/ssr/_02sband._.js +0 -7
  319. package/.next/standalone/.next/server/chunks/ssr/_189g8lg._.js +0 -3
  320. package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +0 -3
  321. package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +0 -3
  322. package/.next/standalone/.next/static/chunks/0e3g0sil79cus.js +0 -21
  323. package/.next/standalone/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
  324. package/.next/standalone/.next/static/chunks/163b-20vwa06i.js +0 -34
  325. package/.next/standalone/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
  326. package/.next/standalone/.next/static/chunks/23_1z47al92nz.js +0 -57
  327. package/.next/standalone/.next/static/chunks/260bomtr214m8.js +0 -34
  328. package/.next/standalone/.next/static/chunks/2xk2ra4piiuod.js +0 -34
  329. package/.next/standalone/.next/static/chunks/3b0-14cxqsprt.js +0 -1
  330. package/.next/standalone/.next/static/chunks/3jarus1_38s2z.js +0 -1
  331. package/.next/standalone/.next/static/chunks/3ssu341hav_sq.css +0 -3
  332. package/.next/standalone/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
  333. package/.next/static/chunks/0e3g0sil79cus.js +0 -21
  334. package/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
  335. package/.next/static/chunks/163b-20vwa06i.js +0 -34
  336. package/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
  337. package/.next/static/chunks/23_1z47al92nz.js +0 -57
  338. package/.next/static/chunks/260bomtr214m8.js +0 -34
  339. package/.next/static/chunks/2xk2ra4piiuod.js +0 -34
  340. package/.next/static/chunks/3b0-14cxqsprt.js +0 -1
  341. package/.next/static/chunks/3jarus1_38s2z.js +0 -1
  342. package/.next/static/chunks/3ssu341hav_sq.css +0 -3
  343. package/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
  344. /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
  345. /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
  346. /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
  347. /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
  348. /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
  349. /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
@@ -0,0 +1,275 @@
1
+ 'use client';
2
+
3
+ // Insights → Kingmakers, Gatekeepers, Companies and Industries: each board in
4
+ // full, a hundred rows at a time. The numbers are lib/insights-board.js's,
5
+ // with where they came from under each.
6
+
7
+ import { useState } from 'react';
8
+ import Avatar from '../Avatar';
9
+ import { TIER_COLORS } from '../../../lib/themes';
10
+ import { shortName } from '../../../lib/separation';
11
+ import {
12
+ fmt, one, pc, Hero, PersonCell, Src, NeedsCircles, OpenCircle, Bars, Card, LINE, SOFT_LINE, CARD_BG, ELLIPSIS,
13
+ } from './parts';
14
+ import { Logo } from './Rail';
15
+ import { PAGE } from './PeopleView';
16
+
17
+ /** A board's table: the header row on a computer, the rows, and Show more. */
18
+ function Table({ cols, head, right = [], rows, render, isMobile, source }) {
19
+ const step = isMobile ? 25 : PAGE;
20
+ const [pages, setPages] = useState(1);
21
+ const shown = pages * step;
22
+ const list = rows.slice(0, shown);
23
+ const left = rows.length - list.length;
24
+ return (
25
+ <div style={{ border: `1px solid ${LINE}`, borderRadius: 14, background: CARD_BG, overflow: 'hidden' }}>
26
+ {!isMobile && (
27
+ <div style={{ display: 'grid', gridTemplateColumns: cols, gap: 12, padding: '0 16px', alignItems: 'center', height: 36, borderBottom: `1px solid ${LINE}`, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.02)', fontSize: 10.5, color: 'var(--sd-fg-4, #6f7a88)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 700 }}>
28
+ {head.map((h, i) => <span key={i} style={{ textAlign: right.includes(i) ? 'right' : undefined }}>{h}</span>)}
29
+ </div>
30
+ )}
31
+ {list.map((r, i) => (
32
+ <div key={r.key ?? i} style={isMobile
33
+ ? { padding: '10px 12px', borderBottom: `1px solid ${SOFT_LINE}` }
34
+ : { display: 'grid', gridTemplateColumns: cols, gap: 12, padding: '0 16px', alignItems: 'center', minHeight: 54, borderBottom: `1px solid ${SOFT_LINE}` }}>
35
+ {render(r, i)}
36
+ </div>
37
+ ))}
38
+ {left > 0 && (
39
+ <button type="button" onClick={() => setPages((n) => n + 1)} style={{ display: 'block', width: '100%', textAlign: 'left', padding: '12px 16px', border: 'none', borderBottom: `1px solid ${LINE}`, background: 'transparent', cursor: 'pointer', fontSize: 12.5, fontWeight: 600, color: 'var(--sd-blue, #3498DB)' }}>
40
+ Show {fmt(Math.min(step, left))} more → <span style={{ color: 'var(--sd-fg-4, #6f7a88)', fontWeight: 400, marginLeft: 6 }}>{fmt(left)} to go</span>
41
+ </button>
42
+ )}
43
+ <Src style={{ padding: '10px 16px 14px', marginTop: 0 }}>{source}</Src>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ const Rank = ({ n, isMobile }) => <span style={{ fontSize: isMobile ? 11.5 : 13, fontWeight: 700, color: n <= 3 ? 'var(--sd-gold, #FFD700)' : 'var(--sd-fg-3, #8b9a9a)', fontVariantNumeric: 'tabular-nums' }}>#{fmt(n)}</span>;
49
+ const Num = ({ children, color, big }) => <b style={{ fontSize: big ? 16 : 13.5, fontWeight: big ? 800 : 700, color: color || 'var(--sd-fg-1, #fff)', fontVariantNumeric: 'tabular-nums', display: 'block', textAlign: 'right' }}>{children}</b>;
50
+ const Mute = ({ children, right }) => <span style={{ fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)', fontVariantNumeric: 'tabular-nums', ...(right ? { display: 'block', textAlign: 'right' } : null) }}>{children}</span>;
51
+
52
+ /** S and A behind someone, as one bar with the letters on it. */
53
+ function SABar({ S, A, max }) {
54
+ return <Bars max={max} parts={[
55
+ { value: S, color: TIER_COLORS.S, label: 'S', ink: TIER_COLORS.S, title: `${fmt(S)} S` },
56
+ { value: A, color: TIER_COLORS.A, label: 'A', ink: TIER_COLORS.A, title: `${fmt(A)} A` },
57
+ ]} />;
58
+ }
59
+
60
+ // ── Kingmakers ──────────────────────────────────────────────────────────────
61
+
62
+ export function KingmakersBoard({ data, source, isMobile }) {
63
+ const { kings } = data;
64
+ const cols = '52px minmax(0,1.5fr) 80px 56px 56px minmax(0,1.3fr) 70px 100px';
65
+ const max = kings.list[0]?.SA || 1;
66
+ return (
67
+ <>
68
+ <Hero title="Kingmakers" isMobile={isMobile}>
69
+ Your connections by the S and A people in their circle: the doors with the most power behind them. A title says what someone holds; a circle says what they can open.
70
+ </Hero>
71
+ {kings.scanned === 0 ? <Card><NeedsCircles source={source} /></Card> : (
72
+ <Table isMobile={isMobile} cols={cols} rows={kings.list.map((k) => ({ ...k, key: k.row.id }))} source={kings.source}
73
+ head={['Rank', 'Connection', 'Circle', 'S', 'A', 'Behind them', 'S+A', '']} right={[2, 3, 4, 6]}
74
+ render={(k, i) => (isMobile ? (
75
+ <div style={{ display: 'grid', gridTemplateColumns: '36px minmax(0,1fr) auto', gap: 8, alignItems: 'center' }}>
76
+ <Rank n={i + 1} isMobile />
77
+ <PersonCell row={k.row} size={30} />
78
+ <Num big>{fmt(k.SA)}</Num>
79
+ <span />
80
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><SABar S={k.S} A={k.A} max={max} /><Mute>of {fmt(k.size)}</Mute></span>
81
+ <OpenCircle id={k.row.id} compact />
82
+ </div>
83
+ ) : (
84
+ <>
85
+ <Rank n={i + 1} />
86
+ <PersonCell row={k.row} />
87
+ <Mute right>{fmt(k.size)}</Mute>
88
+ <Num color={TIER_COLORS.S}>{fmt(k.S)}</Num>
89
+ <Num color={TIER_COLORS.A}>{fmt(k.A)}</Num>
90
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><SABar S={k.S} A={k.A} max={max} /><Mute>{pc(100 * k.share)}</Mute></span>
91
+ <Num big>{fmt(k.SA)}</Num>
92
+ <span style={{ justifySelf: 'end' }}><OpenCircle id={k.row.id} /></span>
93
+ </>
94
+ ))} />
95
+ )}
96
+ </>
97
+ );
98
+ }
99
+
100
+ // ── Gatekeepers ─────────────────────────────────────────────────────────────
101
+
102
+ export function GatekeepersBoard({ data, source, isMobile }) {
103
+ const { gates, kings } = data;
104
+ const cols = '52px minmax(0,1.6fr) 70px 110px 76px minmax(0,1fr) 100px';
105
+ const others = Math.max(0, 100 - gates.top3Share);
106
+ return (
107
+ <>
108
+ <Hero title="Gatekeepers" isMobile={isMobile}>
109
+ {kings.scanned === 0 ? 'Who your S and A reach hangs on: the people who reach you through one connection only.'
110
+ : <><b style={{ color: 'var(--sd-fg-1, #fff)' }}>{pc(gates.onlyShare)}</b> of the S and A people you can reach have one way in. Three connections carry <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{pc(gates.top3Share)}</b> of your S and A reach.</>}
111
+ </Hero>
112
+ {kings.scanned === 0 ? <Card><NeedsCircles source={source} /></Card> : (
113
+ <>
114
+ <div style={{ marginBottom: 14 }}>
115
+ <div style={{ display: 'flex', gap: 2, height: 30, borderRadius: 8, overflow: 'hidden' }}>
116
+ {gates.top3.map((g, i) => (
117
+ <span key={g.row.id} title={`${g.row.name}: ${pc(g.share)} of your S and A reach`} style={{ width: `${g.share}%`, background: ['#00ff88', '#00c46a', '#00984f'][i], display: 'flex', alignItems: 'center', paddingLeft: 10, overflow: 'hidden' }}>
118
+ <em style={{ fontStyle: 'normal', fontSize: 12, fontWeight: 750, color: '#00170b', whiteSpace: 'nowrap' }}>{shortName(g.row.name)} {pc(g.share)}</em>
119
+ </span>
120
+ ))}
121
+ <span style={{ width: `${others}%`, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.1)', display: 'flex', alignItems: 'center', paddingLeft: 10, overflow: 'hidden' }}>
122
+ <em style={{ fontStyle: 'normal', fontSize: 12, fontWeight: 600, color: 'var(--sd-fg-2, #b8c4c4)', whiteSpace: 'nowrap' }}>{fmt(Math.max(0, gates.list.length - 3))} others {pc(others)}</em>
123
+ </span>
124
+ </div>
125
+ </div>
126
+ <Table isMobile={isMobile} cols={cols} rows={gates.list.map((g) => ({ ...g, key: g.row.id }))} source={gates.source}
127
+ head={['Rank', 'Connection', 'Circle', 'Only via them', 'Of them S', 'Share of your S and A reach', '']} right={[2, 3, 4]}
128
+ render={(g, i) => (isMobile ? (
129
+ <div style={{ display: 'grid', gridTemplateColumns: '36px minmax(0,1fr) auto', gap: 8, alignItems: 'center' }}>
130
+ <Rank n={i + 1} isMobile />
131
+ <PersonCell row={g.row} size={30} />
132
+ <span style={{ textAlign: 'right' }}><Num big color="var(--sd-green, #00ff88)">{fmt(g.onlySA)}</Num><div style={{ fontSize: 10, color: 'var(--sd-fg-4, #6f7a88)' }}>only via them</div></span>
133
+ <span />
134
+ <Mute>{fmt(g.onlyS)} S · circle of {fmt(g.size)} · {pc((100 * g.credit) / Math.max(1, gates.totalSA))} of your reach</Mute>
135
+ <OpenCircle id={g.row.id} compact />
136
+ </div>
137
+ ) : (
138
+ <>
139
+ <Rank n={i + 1} />
140
+ <PersonCell row={g.row} />
141
+ <Mute right>{fmt(g.size)}</Mute>
142
+ <Num big color="var(--sd-green, #00ff88)">{fmt(g.onlySA)}</Num>
143
+ <Num color={TIER_COLORS.S}>{fmt(g.onlyS)}</Num>
144
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
145
+ <Bars max={gates.top3[0]?.credit || 1} parts={[{ value: g.credit, color: 'var(--sd-green, #00ff88)' }]} height={8} />
146
+ <Mute>{pc((100 * g.credit) / Math.max(1, gates.totalSA))}</Mute>
147
+ </span>
148
+ <span style={{ justifySelf: 'end' }}><OpenCircle id={g.row.id} /></span>
149
+ </>
150
+ ))} />
151
+ </>
152
+ )}
153
+ </>
154
+ );
155
+ }
156
+
157
+ // ── Companies ───────────────────────────────────────────────────────────────
158
+
159
+ const SCORE_FROM = { yours: 'your score', known: 'curated list', data: 'public dataset', estimate: 'estimated' };
160
+
161
+ export function CompaniesBoard({ heavy, isMobile }) {
162
+ const cols = '52px minmax(0,1.6fr) 96px 56px 56px 96px minmax(0,1.2fr) 92px';
163
+ if (!heavy) return <><Hero title="Company power" isMobile={isMobile}>Every company within two steps of you, with everyone’s power added up.</Hero><Card><div style={{ fontSize: 13, color: 'var(--sd-fg-3, #8b9a9a)' }}>Counting…</div></Card></>;
164
+ const { companies } = heavy;
165
+ return (
166
+ <>
167
+ <Hero title="Company power" isMobile={isMobile}>
168
+ <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{fmt(companies.count)}</b> companies someone within two steps of you works at now, with everyone’s power added up: where the power you can reach sits.
169
+ </Hero>
170
+ <Table isMobile={isMobile} cols={cols} rows={companies.list.map((c) => ({ ...c, key: c.name }))} source={`${companies.source} ${companies.bigSource}`}
171
+ head={['Rank', 'Company', 'People', 'S', 'A', 'Total power', 'Ways in', 'Score']} right={[2, 3, 4, 5, 7]}
172
+ render={(c, i) => {
173
+ const ways = (
174
+ <span style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0, overflow: 'hidden' }} title={c.bridges.map((b) => `${b.bridge.name}: ${fmt(b.people)} there`).join('\n')}>
175
+ <Mute>{fmt(c.waysIn)}</Mute>
176
+ {c.bridges.slice(0, 2).map((b) => (
177
+ <span key={b.bridge.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 11.5, color: 'var(--sd-fg-2, #b8c4c4)', whiteSpace: 'nowrap' }}>
178
+ <Avatar person={b.bridge} size={16} tierColors={TIER_COLORS} />{shortName(b.bridge.name)}
179
+ </span>
180
+ ))}
181
+ {c.d1 > 0 && <span style={{ fontSize: 11, color: 'var(--sd-green, #7dffc0)', whiteSpace: 'nowrap' }}>{fmt(c.d1)} yours</span>}
182
+ </span>
183
+ );
184
+ const name = (
185
+ <span style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
186
+ <Logo name={c.name} size={30} />
187
+ <span style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
188
+ <b style={{ fontSize: 13.5, fontWeight: 650, ...ELLIPSIS }}>{c.name}</b>
189
+ <span style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 11.5, color: 'var(--sd-fg-3, #8b9a9a)', minWidth: 0 }}>
190
+ <i className="sd-dot-html" style={{ width: 7, height: 7, borderRadius: '50%', background: c.industry.color, flexShrink: 0 }} />
191
+ <span style={ELLIPSIS}>{c.industry.label}</span>
192
+ </span>
193
+ </span>
194
+ </span>
195
+ );
196
+ return isMobile ? (
197
+ <div style={{ display: 'grid', gridTemplateColumns: '36px minmax(0,1fr) auto', gap: 8, alignItems: 'center' }}>
198
+ <Rank n={i + 1} isMobile />
199
+ {name}
200
+ <span style={{ textAlign: 'right' }}><Num big>{fmt(Math.round(c.total))}</Num><div style={{ fontSize: 10, color: 'var(--sd-fg-4, #6f7a88)' }}>total power</div></span>
201
+ <span />
202
+ <Mute><span style={{ color: TIER_COLORS.S }}>{fmt(c.S)} S</span> · <span style={{ color: TIER_COLORS.A }}>{fmt(c.A)} A</span> · {fmt(c.people)} people · {fmt(c.waysIn)} ways in</Mute>
203
+ <Mute>{one(c.score).replace(/\.0$/, '')}/10</Mute>
204
+ </div>
205
+ ) : (
206
+ <>
207
+ <Rank n={i + 1} />
208
+ {name}
209
+ <Mute right>{fmt(c.people)}</Mute>
210
+ <Num color={TIER_COLORS.S}>{fmt(c.S)}</Num>
211
+ <Num color={TIER_COLORS.A}>{fmt(c.A)}</Num>
212
+ <Num big>{fmt(Math.round(c.total))}</Num>
213
+ {ways}
214
+ <span style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
215
+ <Num>{one(c.score).replace(/\.0$/, '')}/10</Num>
216
+ <span style={{ fontSize: 10, color: 'var(--sd-fg-4, #6f7a88)', whiteSpace: 'nowrap' }}>{SCORE_FROM[c.scoreSource]}</span>
217
+ </span>
218
+ </>
219
+ );
220
+ }} />
221
+ </>
222
+ );
223
+ }
224
+
225
+ // ── Industries ──────────────────────────────────────────────────────────────
226
+
227
+ export function IndustriesBoard({ heavy, isMobile }) {
228
+ const cols = 'minmax(0,1.5fr) 90px 90px 64px 64px minmax(0,1.4fr) 100px';
229
+ if (!heavy) return <><Hero title="Industries" isMobile={isMobile}>Where the power you can reach sits, by industry.</Hero><Card><div style={{ fontSize: 13, color: 'var(--sd-fg-3, #8b9a9a)' }}>Counting…</div></Card></>;
230
+ const { industries } = heavy;
231
+ const top = industries.list[0];
232
+ const max = Math.max(1, ...industries.list.map((e) => e.share));
233
+ return (
234
+ <>
235
+ <Hero title="Industries" isMobile={isMobile}>
236
+ Where the power you can reach sits.{top ? <> <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{top.label}</b> holds {pc(top.share)} of your S and A.</> : null}
237
+ </Hero>
238
+ <Table isMobile={isMobile} cols={cols} rows={industries.list} source={industries.source}
239
+ head={['Industry', 'Companies', 'People', 'S', 'A', 'Share of your S and A', 'Total power']} right={[1, 2, 3, 4, 6]}
240
+ render={(e) => {
241
+ const label = (
242
+ <span style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
243
+ <i className="sd-dot-html" style={{ width: 11, height: 11, borderRadius: 3, background: e.color, flexShrink: 0 }} />
244
+ <b style={{ fontSize: 13.5, fontWeight: 650, ...ELLIPSIS }}>{e.label}</b>
245
+ </span>
246
+ );
247
+ const bar = (
248
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
249
+ <Bars max={max} parts={[{ value: e.share, color: e.color, ink: e.color }]} height={10} />
250
+ <Mute>{pc(e.share)}</Mute>
251
+ </span>
252
+ );
253
+ return isMobile ? (
254
+ <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) auto', gap: 6, alignItems: 'center' }}>
255
+ {label}
256
+ <Num big>{pc(e.share)}</Num>
257
+ <Mute>{fmt(e.companies)} companies · {fmt(e.people)} people · <span style={{ color: TIER_COLORS.S }}>{fmt(e.S)} S</span> · <span style={{ color: TIER_COLORS.A }}>{fmt(e.A)} A</span></Mute>
258
+ <span />
259
+ </div>
260
+ ) : (
261
+ <>
262
+ {label}
263
+ <Mute right>{fmt(e.companies)}</Mute>
264
+ <Mute right>{fmt(e.people)}</Mute>
265
+ <Num color={TIER_COLORS.S}>{fmt(e.S)}</Num>
266
+ <Num color={TIER_COLORS.A}>{fmt(e.A)}</Num>
267
+ {bar}
268
+ <Num big>{fmt(e.total)}</Num>
269
+ </>
270
+ );
271
+ }} />
272
+ </>
273
+ );
274
+ }
275
+
@@ -0,0 +1,266 @@
1
+ 'use client';
2
+
3
+ // Insights → People: the Power Index. Everyone within two steps of you, each
4
+ // once, ranked by power, with the filters above it and the rail of boards
5
+ // beside it (mock-up B). The table shows a hundred rows at a time (25 on a
6
+ // phone), so a network of thousands stays quick; ranks are the whole list's,
7
+ // so a filter never renumbers anyone.
8
+
9
+ import { memo, useDeferredValue, useMemo, useState } from 'react';
10
+ import { filterIndex, bandText } from '../../../lib/insights-board';
11
+ import { hasRequest } from '../../../lib/requests-client';
12
+ import { shortName, TIERS } from '../../../lib/separation';
13
+ import { RARITY, toggle } from '../../../lib/rarity';
14
+ import Avatar from '../Avatar';
15
+ import { TIER_COLORS } from '../../../lib/themes';
16
+ import {
17
+ fmt, one, TierChip, DegreeChip, RarityTag, PersonCell, Src, Hero, OpenCircle, ScanCircle, AskButton,
18
+ LINE, SOFT_LINE, CARD_BG, ELLIPSIS,
19
+ } from './parts';
20
+ import Rail from './Rail';
21
+
22
+ export const PAGE = 100;
23
+ const COLS = '58px minmax(0,1.55fr) 148px minmax(0,1.45fr) 118px 100px';
24
+
25
+ export default function PeopleView({ data, heavy, open, net, source, isMobile, onView, requests }) {
26
+ const { index } = data;
27
+ const [degree, setDegree] = useState('all');
28
+ const [tiers, setTiers] = useState(() => new Set());
29
+ const [rarities, setRarities] = useState(() => new Set());
30
+ const [query, setQuery] = useState('');
31
+ const q = useDeferredValue(query);
32
+ // A hundred rows at a time on a computer, twenty-five on a phone, where the boards sit under the table.
33
+ const step = isMobile ? 25 : PAGE;
34
+ const [pages, setPages] = useState(1);
35
+ const shown = pages * step;
36
+ const filtering = degree !== 'all' || tiers.size > 0 || rarities.size > 0 || q.trim() !== '';
37
+ const list = useMemo(() => (filtering ? filterIndex(index, { degree, tiers, rarities, query: q }) : index.people),
38
+ [index, filtering, degree, tiers, rarities, q]);
39
+ const bandAt = useMemo(() => new Map(index.bands.map((b) => [b.start, b])), [index]);
40
+ // A new filter starts from the top again.
41
+ const filterKey = `${degree}|${[...tiers].join()}|${[...rarities].join()}|${q}`;
42
+ const [lastKey, setLastKey] = useState(filterKey);
43
+ if (lastKey !== filterKey) { setLastKey(filterKey); setPages(1); }
44
+
45
+ const rows = list.slice(0, shown);
46
+ const left = list.length - rows.length;
47
+ const next = list[rows.length];
48
+ const sameTieLeft = next && rows.length && rows[rows.length - 1].score === next.score
49
+ ? list.slice(rows.length).filter((p) => p.score === next.score).length : 0;
50
+ const scoped = source === 'own';
51
+ // No 2nd degree (a CSV import, or nothing scanned yet): no degree or rarity to filter by.
52
+ const second = index.counts.degree[2] > 0;
53
+
54
+ // "All →" on Hidden giants: the table, narrowed to them.
55
+ const showHidden = () => { setDegree(2); setTiers(new Set(['S'])); setRarities(new Set(['only'])); setQuery(''); window.scrollTo(0, 0); };
56
+
57
+ const filters = (
58
+ <div style={{
59
+ display: 'flex', alignItems: 'center', gap: isMobile ? 8 : 14, flexWrap: 'wrap', padding: isMobile ? '8px 10px' : '10px 12px',
60
+ border: `1px solid ${LINE}`, borderRadius: 12, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.025)', marginBottom: 14,
61
+ }}>
62
+ {second && (
63
+ <Group label="Degree" isMobile={isMobile}>
64
+ {[['all', 'All', index.total], [1, '1st', index.counts.degree[1]], [2, '2nd', index.counts.degree[2]]].map(([k, label, n]) => (
65
+ <Chip key={k} on={degree === k} onClick={() => setDegree(k)} count={n}>{label}</Chip>
66
+ ))}
67
+ </Group>
68
+ )}
69
+ <Group label="Tier" isMobile={isMobile}>
70
+ {TIERS.map((t) => (
71
+ <Chip key={t} on={tiers.has(t)} onClick={() => setTiers((s) => toggle(s, t))} count={index.counts.tiers[t]} title={`${t} tier`}>
72
+ <TierChip tier={t} size={16} />
73
+ </Chip>
74
+ ))}
75
+ </Group>
76
+ {second && (
77
+ <Group label="Rarity" isMobile={isMobile}>
78
+ {RARITY.map((r) => (
79
+ <Chip key={r.key} on={rarities.has(r.key)} onClick={() => setRarities((s) => toggle(s, r.key))} count={index.counts.rarity[r.key]}
80
+ title={`${r.label}: ${r.range} mutual connection${r.range === '1' ? '' : 's'}`}>
81
+ <i className="sd-dot-html" style={{ width: 7, height: 7, borderRadius: '50%', background: r.color, display: 'inline-block' }} />{r.label}
82
+ </Chip>
83
+ ))}
84
+ </Group>
85
+ )}
86
+ <input type="search" value={query} onChange={(e) => setQuery(e.target.value)} aria-label="Search a name, company, or who knows them"
87
+ placeholder="Search a name or company" style={{
88
+ marginLeft: isMobile ? 0 : 'auto', width: isMobile ? '100%' : 240, height: isMobile ? 40 : 32, padding: '0 12px', borderRadius: 8, outline: 'none',
89
+ fontSize: isMobile ? 16 : 12.5, color: 'var(--sd-fg-1, #fff)', border: `1px solid ${LINE}`, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.04)',
90
+ }} />
91
+ </div>
92
+ );
93
+
94
+ return (
95
+ <>
96
+ <Hero title="The Power Index" isMobile={isMobile}>
97
+ {second
98
+ ? <>Everyone within two steps of you, ranked by power: <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{fmt(index.total)}</b> people, each once.</>
99
+ : <>Your connections, ranked by power: <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{fmt(index.total)}</b> people.{' '}
100
+ {source === 'csv' ? 'A CSV import has no circles, so the people one step further out aren’t here.' : 'Scan their circles to add the people one step further out.'}</>}
101
+ </Hero>
102
+ {filters}
103
+ <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'minmax(0,1fr) 370px', gap: 16, alignItems: 'start' }}>
104
+ <div style={{ border: `1px solid ${LINE}`, borderRadius: 14, background: CARD_BG, overflow: 'hidden', minWidth: 0 }}>
105
+ {!isMobile && (
106
+ <div style={{ display: 'grid', gridTemplateColumns: COLS, gap: 10, padding: '0 16px', alignItems: 'center', height: 36, borderBottom: `1px solid ${LINE}`, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.02)', fontSize: 10.5, color: 'var(--sd-fg-4, #6f7a88)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 700 }}>
107
+ <span>Rank</span><span>Person</span><span>Power</span><span>Way in</span><span>Rarity</span><span />
108
+ </div>
109
+ )}
110
+ {rows.length === 0 && (
111
+ <div style={{ padding: '28px 16px', fontSize: 13, color: 'var(--sd-fg-3, #8b9a9a)' }}>Nobody matches these filters.</div>
112
+ )}
113
+ {rows.map((p, i) => {
114
+ const band = !filtering ? bandAt.get(i) : null;
115
+ return (
116
+ <div key={p.key}>
117
+ {band && <Band band={band} isMobile={isMobile} />}
118
+ <Row p={p} isMobile={isMobile} scoped={scoped} csv={source === 'csv'} asked={p.degree === 2 && hasRequest(p.row, requests)} />
119
+ </div>
120
+ );
121
+ })}
122
+ {left > 0 && (
123
+ <button type="button" onClick={() => setPages((n) => n + 1)} style={{
124
+ display: 'block', width: '100%', textAlign: 'left', padding: '12px 16px', border: 'none', borderBottom: `1px solid ${LINE}`,
125
+ background: 'transparent', cursor: 'pointer', fontSize: 12.5, fontWeight: 600, color: 'var(--sd-blue, #3498DB)',
126
+ }}>
127
+ Show {fmt(Math.min(step, left))} more →
128
+ <span style={{ color: 'var(--sd-fg-4, #6f7a88)', fontWeight: 400, marginLeft: 8 }}>
129
+ {sameTieLeft ? `${fmt(sameTieLeft)} more at ${one(next.score)}, ${fmt(left)} in all` : `${fmt(left)} to go`}
130
+ </span>
131
+ </button>
132
+ )}
133
+ <Src style={{ padding: '10px 16px 14px', marginTop: 0 }}>{index.source}</Src>
134
+ </div>
135
+ <Rail data={data} heavy={heavy} open={open} source={source} onView={onView} onHidden={showHidden} isMobile={isMobile} net={net} />
136
+ </div>
137
+ </>
138
+ );
139
+ }
140
+
141
+ function Group({ label, children, isMobile }) {
142
+ // On a phone each group is one row that scrolls sideways, rather than four rows of chips.
143
+ return (
144
+ <div style={{ display: 'flex', gap: 5, alignItems: 'center', flexWrap: isMobile ? 'nowrap' : 'wrap', minWidth: 0, ...(isMobile ? { overflowX: 'auto', width: '100%', scrollbarWidth: 'none' } : null) }}>
145
+ <span style={{ fontSize: 10.5, color: 'var(--sd-fg-4, #6f7a88)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 700, marginRight: 3 }}>{label}</span>
146
+ {children}
147
+ </div>
148
+ );
149
+ }
150
+
151
+ function Chip({ on, onClick, count, children, title }) {
152
+ return (
153
+ <button type="button" aria-pressed={on} onClick={onClick} title={title} style={{
154
+ fontSize: 12, padding: '4px 9px', borderRadius: 16, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap',
155
+ border: `1px solid ${on ? 'rgba(var(--sd-ink, 255, 255, 255), 0.55)' : LINE}`,
156
+ background: on ? 'rgba(var(--sd-ink, 255, 255, 255), 0.12)' : 'rgba(var(--sd-ink, 255, 255, 255), 0.03)',
157
+ color: on ? 'var(--sd-fg-1, #fff)' : 'var(--sd-fg-2, #b8c4c4)', fontWeight: on ? 700 : 500,
158
+ }}>
159
+ {children}
160
+ {count != null && <small style={{ color: 'var(--sd-fg-4, #6f7a88)', fontSize: 11 }}>{fmt(count)}</small>}
161
+ </button>
162
+ );
163
+ }
164
+
165
+ /** A tie of many: how many share the score, and the working most of them share. */
166
+ function Band({ band, isMobile }) {
167
+ const t = bandText(band);
168
+ const mixed = band.yours > 0 && band.yours < band.count;
169
+ return (
170
+ <div style={{
171
+ display: 'flex', alignItems: isMobile ? 'flex-start' : 'center', gap: 12, padding: isMobile ? '10px 12px' : '10px 16px',
172
+ background: 'linear-gradient(90deg, rgba(255,215,0,0.1), rgba(255,215,0,0.02))',
173
+ borderTop: '1px solid rgba(255,215,0,0.22)', borderBottom: '1px solid rgba(255,215,0,0.22)',
174
+ }}>
175
+ <span style={{ fontSize: 15, fontWeight: 800, color: 'var(--sd-gold, #FFD700)', fontVariantNumeric: 'tabular-nums', flexShrink: 0 }}>#{fmt(band.rank)}=</span>
176
+ <span style={{ fontSize: 12.5, color: 'var(--sd-fg-1, #e9edf3)', lineHeight: 1.45 }}>
177
+ <b>{t.head}</b> {t.rest}{' '}
178
+ <span style={{ color: 'var(--sd-fg-3, #8b9a9a)' }}>{mixed ? 'Yours first, then most ways in.' : band.yours ? 'All yours.' : 'Most ways in first.'}</span>
179
+ </span>
180
+ </div>
181
+ );
182
+ }
183
+
184
+ /** One person: rank, who, power, way in, rarity and what to do. */
185
+ const Row = memo(function Row({ p, isMobile, scoped, csv, asked }) {
186
+ const rank = `#${fmt(p.rank)}${p.tied ? '=' : ''}`;
187
+ const podium = p.rank <= 5;
188
+ const power = (
189
+ <span style={{ display: 'flex', alignItems: 'center', gap: 7, flexShrink: 0 }}>
190
+ <b style={{ fontSize: isMobile ? 16 : 17, fontWeight: 800, minWidth: 30, textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--sd-fg-1, #fff)' }}>{one(p.score)}</b>
191
+ <TierChip tier={p.tier} />
192
+ {!isMobile && p.boost > 0 && (
193
+ <span title="Added for a strong circle: their scanned circle holds many S and A people (lib/scoring.js bridge boost, up to +2)" style={{
194
+ fontSize: 10, fontWeight: 700, color: 'var(--sd-green, #00ff88)', background: 'rgba(0,255,136,0.08)', border: '1px solid rgba(0,255,136,0.28)',
195
+ padding: '1px 6px', borderRadius: 10, whiteSpace: 'nowrap',
196
+ }}>+{p.boost} circle</span>
197
+ )}
198
+ </span>
199
+ );
200
+ const way = <Way p={p} max={isMobile ? 1 : 2} csv={csv} />;
201
+ const rarity = p.degree === 1
202
+ ? <span style={{ fontSize: 11.5, color: 'var(--sd-fg-4, #6f7a88)', whiteSpace: 'nowrap' }}>Your connection</span>
203
+ : <RarityTag rarity={p.rarity} count={!isMobile} small={isMobile} />;
204
+ const action = <Action p={p} scoped={scoped} asked={asked} compact={isMobile} />;
205
+
206
+ if (isMobile) {
207
+ return (
208
+ <div style={{ display: 'grid', gridTemplateColumns: '40px minmax(0,1fr) 66px', columnGap: 8, rowGap: 6, padding: '10px 12px', borderBottom: `1px solid ${SOFT_LINE}` }}>
209
+ <span style={{ gridRow: '1 / span 2', paddingTop: 9, fontSize: 11.5, fontWeight: 700, color: podium ? 'var(--sd-gold, #FFD700)' : 'var(--sd-fg-3, #8b9a9a)', whiteSpace: 'nowrap' }}>{rank}</span>
210
+ <PersonCell row={p.row} size={32} chip={false} />
211
+ <span style={{ justifySelf: 'end' }}>{power}</span>
212
+ <div style={{ gridColumn: '2 / span 2', display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
213
+ <span style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0, flex: 1, overflow: 'hidden', fontSize: 12, color: 'var(--sd-fg-2, #b8c4c4)' }}>{way}</span>
214
+ {p.degree === 2 && rarity}
215
+ {action}
216
+ </div>
217
+ </div>
218
+ );
219
+ }
220
+ return (
221
+ <div style={{ display: 'grid', gridTemplateColumns: COLS, gap: 10, padding: '0 16px', alignItems: 'center', minHeight: 56, borderBottom: `1px solid ${SOFT_LINE}` }}>
222
+ <span style={{ fontSize: 13, fontWeight: 700, color: podium ? 'var(--sd-gold, #FFD700)' : 'var(--sd-fg-3, #8b9a9a)', fontVariantNumeric: 'tabular-nums' }}>{rank}</span>
223
+ <PersonCell row={p.row} chip={false} />
224
+ {power}
225
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, fontSize: 12, color: 'var(--sd-fg-2, #b8c4c4)' }}>{way}</span>
226
+ <span style={{ minWidth: 0 }}>{rarity}</span>
227
+ <span style={{ justifySelf: 'end' }}>{action}</span>
228
+ </div>
229
+ );
230
+ });
231
+
232
+ /** Yours: their circle. 2nd degree: who of yours knows them, best first. */
233
+ function Way({ p, max = 2, csv }) {
234
+ if (p.degree === 1) {
235
+ const c = p.circle;
236
+ // A CSV import has no circles to scan: the chip says enough.
237
+ if (csv) return <DegreeChip degree={1} />;
238
+ const text = c.state === 'scanned'
239
+ ? <span style={{ whiteSpace: 'nowrap' }}>Circle {fmt(c.size)} · <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{fmt(c.S + c.A)}</b> S+A</span>
240
+ : <span style={{ color: 'var(--sd-fg-4, #6f7a88)', fontSize: 11.5, whiteSpace: 'nowrap' }}>{c.state === 'hidden' ? 'Their list is hidden' : 'Circle not scanned'}</span>;
241
+ return <><DegreeChip degree={1} />{text}</>;
242
+ }
243
+ const named = p.routes.filter((r) => r.bridge);
244
+ const extra = p.routes.length - Math.min(max, named.length);
245
+ return (
246
+ <>
247
+ <DegreeChip degree={2} />
248
+ <span style={{ display: 'flex', gap: 8, minWidth: 0, overflow: 'hidden' }} title={p.routes.map((r) => (r.bridge ? r.bridge.name : 'a connection the app can’t name')).join('\n')}>
249
+ {named.slice(0, max).map((r) => (
250
+ <span key={r.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12, color: 'var(--sd-fg-2, #b8c4c4)', whiteSpace: 'nowrap' }}>
251
+ <Avatar person={r.bridge} size={18} tierColors={TIER_COLORS} />{shortName(r.bridge.name)}
252
+ </span>
253
+ ))}
254
+ {!named.length && <span style={{ fontSize: 11.5, color: 'var(--sd-fg-4, #6f7a88)', ...ELLIPSIS }}>a connection the app can’t name</span>}
255
+ {extra > 0 && <span style={{ fontSize: 11.5, color: 'var(--sd-fg-4, #6f7a88)', whiteSpace: 'nowrap' }}>+{fmt(extra)}</span>}
256
+ </span>
257
+ </>
258
+ );
259
+ }
260
+
261
+ function Action({ p, scoped, asked, compact }) {
262
+ if (p.degree === 2) return <AskButton row={p.row} bridgeId={p.routes[0]?.id ?? p.row.source_connection_id} asked={asked} compact={compact} canAuto={scoped} />;
263
+ if (p.circle.state === 'scanned') return <OpenCircle id={p.row.id} compact={compact} />;
264
+ if (p.circle.state === 'todo' && scoped) return <ScanCircle id={p.row.id} compact={compact} />;
265
+ return null;
266
+ }