six-degrees 0.4.11 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +9 -9
  4. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +12 -12
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  14. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +11 -11
  15. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  17. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  21. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +1 -1
  25. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/company-scores/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  36. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  38. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  40. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
  44. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  46. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
  48. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/settings/route.js +1 -1
  50. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +1 -1
  54. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/social/route.js +3 -3
  60. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  62. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  65. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  67. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/import.html +1 -1
  72. package/.next/standalone/.next/server/app/import.rsc +14 -14
  73. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +14 -14
  74. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  75. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +13 -13
  76. package/.next/standalone/.next/server/app/index.html +1 -1
  77. package/.next/standalone/.next/server/app/index.rsc +14 -14
  78. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  79. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  80. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  81. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/launch.html +2 -2
  84. package/.next/standalone/.next/server/app/launch.rsc +14 -14
  85. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +14 -14
  86. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  87. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +13 -13
  88. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  90. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/paths.html +1 -1
  93. package/.next/standalone/.next/server/app/paths.rsc +14 -14
  94. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +14 -14
  95. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  96. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +13 -13
  97. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/profile.html +1 -1
  100. package/.next/standalone/.next/server/app/profile.rsc +14 -14
  101. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +14 -14
  102. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  103. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +13 -13
  104. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/queue.html +1 -1
  107. package/.next/standalone/.next/server/app/queue.rsc +14 -14
  108. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +14 -14
  109. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  110. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +13 -13
  111. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  113. package/.next/standalone/.next/server/app/scores.html +1 -1
  114. package/.next/standalone/.next/server/app/scores.rsc +14 -14
  115. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +14 -14
  116. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
  117. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +13 -13
  118. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  119. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/app/settings.html +1 -1
  121. package/.next/standalone/.next/server/app/settings.rsc +14 -14
  122. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  123. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  124. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +13 -13
  125. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  126. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  127. package/.next/standalone/.next/server/app/setup.html +1 -1
  128. package/.next/standalone/.next/server/app/setup.rsc +14 -14
  129. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +14 -14
  130. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  131. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +13 -13
  132. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/app/social.html +1 -1
  135. package/.next/standalone/.next/server/app/social.rsc +14 -14
  136. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +14 -14
  137. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +1 -1
  138. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +13 -13
  139. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__13dx4sd._.js} +2 -2
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
  143. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  145. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +8 -0
  148. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
  149. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
  150. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
  151. package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
  152. package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
  153. package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
  154. package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
  155. package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
  156. package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
  157. package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
  158. package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
  159. package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
  160. package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
  161. package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
  162. package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
  163. package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
  164. package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
  165. package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
  166. package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
  168. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
  169. package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
  170. package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
  171. package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
  172. package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
  173. package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
  174. package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
  175. package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
  176. package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
  177. package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
  178. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
  179. package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
  180. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
  181. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  182. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +6 -5
  184. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +19 -3
  186. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -2
  187. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
  190. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  191. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  192. package/.next/standalone/.next/server/pages/404.html +1 -1
  193. package/.next/standalone/.next/server/pages/500.html +1 -1
  194. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  195. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  196. package/.next/standalone/.next/static/chunks/03bziijw5srol.js +1 -0
  197. package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  198. package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  199. package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  200. package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  201. package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  202. package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  203. package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
  204. package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  205. package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
  206. package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  207. package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  208. package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
  209. package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  210. package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  211. package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  212. package/.next/standalone/CHANGELOG.md +187 -0
  213. package/.next/standalone/app/api/ingest/route.js +40 -1
  214. package/.next/standalone/app/api/scraper/route.js +7 -1
  215. package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
  216. package/.next/standalone/app/components/AppTabs.js +59 -0
  217. package/.next/standalone/app/components/ChainView.js +303 -89
  218. package/.next/standalone/app/components/CirclePanel.js +162 -0
  219. package/.next/standalone/app/components/CompanyScores.js +2 -2
  220. package/.next/standalone/app/components/FieldStep.js +4 -4
  221. package/.next/standalone/app/components/FilterPanel.js +121 -183
  222. package/.next/standalone/app/components/ForceGraph.js +41 -3
  223. package/.next/standalone/app/components/GalaxyLab.js +26 -31
  224. package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
  225. package/.next/standalone/app/components/MapControls.js +154 -0
  226. package/.next/standalone/app/components/NoteDetail.js +118 -0
  227. package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
  228. package/.next/standalone/app/components/PeopleMap.js +308 -0
  229. package/.next/standalone/app/components/ScanRadar.js +129 -0
  230. package/.next/standalone/app/components/ScanStatusBar.js +71 -27
  231. package/.next/standalone/app/components/SeparationView.js +626 -118
  232. package/.next/standalone/app/components/Sidebar.js +28 -2
  233. package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
  234. package/.next/standalone/app/components/settings/SectorSection.js +2 -2
  235. package/.next/standalone/app/components/settings/TierSection.js +1 -1
  236. package/.next/standalone/app/components/settings/TitleSection.js +149 -0
  237. package/.next/standalone/app/components/views.js +15 -14
  238. package/.next/standalone/app/page.js +89 -156
  239. package/.next/standalone/app/paths/page.js +39 -27
  240. package/.next/standalone/app/profile/page.js +33 -5
  241. package/.next/standalone/app/queue/page.js +56 -34
  242. package/.next/standalone/app/scores/page.js +16 -66
  243. package/.next/standalone/app/settings/page.js +27 -13
  244. package/.next/standalone/app/setup/page.js +298 -205
  245. package/.next/standalone/app/social/SocialHub.js +374 -0
  246. package/.next/standalone/app/social/page.js +10 -362
  247. package/.next/standalone/lib/chain-layout.js +105 -35
  248. package/.next/standalone/lib/degrees.js +0 -6
  249. package/.next/standalone/lib/dot-rings.js +25 -0
  250. package/.next/standalone/lib/galaxy-lab.js +65 -6
  251. package/.next/standalone/lib/ingest.js +2 -0
  252. package/.next/standalone/lib/island.js +40 -0
  253. package/.next/standalone/lib/linkedin-limits.js +6 -2
  254. package/.next/standalone/lib/map-heat.js +20 -0
  255. package/.next/standalone/lib/network-health.js +1 -1
  256. package/.next/standalone/lib/notifications.js +121 -0
  257. package/.next/standalone/lib/people-map.js +82 -0
  258. package/.next/standalone/lib/rarity.js +45 -3
  259. package/.next/standalone/lib/reach.js +7 -6
  260. package/.next/standalone/lib/rpc.js +15 -4
  261. package/.next/standalone/lib/scan-pace.js +46 -0
  262. package/.next/standalone/lib/scoring.js +39 -8
  263. package/.next/standalone/lib/scraper-client.js +19 -6
  264. package/.next/standalone/lib/sector-focus.js +4 -4
  265. package/.next/standalone/lib/separation.js +137 -10
  266. package/.next/standalone/lib/settings-effects.js +20 -11
  267. package/.next/standalone/lib/settings.js +3 -0
  268. package/.next/standalone/lib/tier-grid.js +99 -0
  269. package/.next/standalone/lib/title-ranking.js +83 -0
  270. package/.next/standalone/package-lock.json +2 -2
  271. package/.next/standalone/package.json +1 -1
  272. package/.next/standalone/scripts/scrape.py +51 -13
  273. package/.next/static/chunks/03bziijw5srol.js +1 -0
  274. package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  275. package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  276. package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  277. package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  278. package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  279. package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  280. package/.next/static/chunks/1i51yylxdzht1.js +1 -0
  281. package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  282. package/.next/static/chunks/2rqtpccruavf2.js +1 -0
  283. package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  284. package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  285. package/.next/static/chunks/355ryio1b7sm1.js +1 -0
  286. package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  287. package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  288. package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  289. package/CHANGELOG.md +187 -0
  290. package/lib/chain-layout.js +105 -35
  291. package/lib/degrees.js +0 -6
  292. package/lib/dot-rings.js +25 -0
  293. package/lib/galaxy-lab.js +65 -6
  294. package/lib/ingest.js +2 -0
  295. package/lib/island.js +40 -0
  296. package/lib/linkedin-limits.js +6 -2
  297. package/lib/map-heat.js +20 -0
  298. package/lib/network-health.js +1 -1
  299. package/lib/notifications.js +121 -0
  300. package/lib/people-map.js +82 -0
  301. package/lib/rarity.js +45 -3
  302. package/lib/reach.js +7 -6
  303. package/lib/rpc.js +15 -4
  304. package/lib/scan-pace.js +46 -0
  305. package/lib/scoring.js +39 -8
  306. package/lib/scraper-client.js +19 -6
  307. package/lib/sector-focus.js +4 -4
  308. package/lib/separation.js +137 -10
  309. package/lib/settings-effects.js +20 -11
  310. package/lib/settings.js +3 -0
  311. package/lib/tier-grid.js +99 -0
  312. package/lib/title-ranking.js +83 -0
  313. package/package.json +1 -1
  314. package/scripts/scrape.py +51 -13
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
  318. package/.next/standalone/.next/server/chunks/lib_rpc_05gt5ya.js +0 -3
  319. package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
  320. package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
  321. package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
  322. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
  323. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
  324. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
  325. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
  326. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
  327. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
  328. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
  329. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
  330. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
  331. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/_0-85h6g._.js +0 -3
  333. package/.next/standalone/.next/server/chunks/ssr/_08cfj2b._.js +0 -3
  334. package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
  335. package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
  336. package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
  337. package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
  338. package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
  339. package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
  340. package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
  341. package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
  342. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
  343. package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  344. package/.next/standalone/.next/static/chunks/15sjx07xhpn6p.js +0 -1
  345. package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
  346. package/.next/standalone/.next/static/chunks/1a74dky22dxd6.js +0 -1
  347. package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
  348. package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  349. package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
  350. package/.next/standalone/.next/static/chunks/297e1z34yi--z.js +0 -1
  351. package/.next/standalone/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
  352. package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  353. package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  354. package/.next/standalone/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
  355. package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  356. package/.next/standalone/app/components/OrbitGraph.js +0 -737
  357. package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  358. package/.next/static/chunks/15sjx07xhpn6p.js +0 -1
  359. package/.next/static/chunks/160wjb73ddwqd.js +0 -6
  360. package/.next/static/chunks/1a74dky22dxd6.js +0 -1
  361. package/.next/static/chunks/1c_n0973t07l0.js +0 -2
  362. package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  363. package/.next/static/chunks/236f0jtp68j_h.js +0 -1
  364. package/.next/static/chunks/297e1z34yi--z.js +0 -1
  365. package/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
  366. package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  367. package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  368. package/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
  369. package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  370. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  371. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  372. /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
  373. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  374. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  375. /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
@@ -7,17 +7,21 @@ import useScanner from './components/useScanner';
7
7
  import useRequests from './components/useRequests';
8
8
  import { requestCount } from '../lib/requests-client';
9
9
  import { loadNetwork } from '../lib/network';
10
- import { VIEWS, resolveView } from './components/views';
10
+ import { resolveView } from './components/views';
11
11
  import AutoScanButton from './components/AutoScanButton';
12
- import { peopleByDegree, tierCountsOf } from '../lib/degrees';
12
+ import { peopleByDegree } from '../lib/degrees';
13
+ import { NETWORK_GRID, DEGREES_GRID, shows, gridCounts } from '../lib/tier-grid';
13
14
  import Sidebar from './components/Sidebar';
14
15
  import FilterPanel from './components/FilterPanel';
16
+ import { viewsForMode } from './components/views';
17
+ import { setNotchTabs } from '../lib/island';
15
18
  import OnboardingGate from './components/OnboardingGate';
16
19
  import EmptyState from './components/EmptyState';
17
20
  import { useUser } from './components/UserProvider';
18
21
  import { IS_DEMO, loadDemoNetwork } from '../lib/demo';
19
22
  import { hasCsvNetwork, loadCsvNetwork, clearCsvNetwork } from '../lib/csv';
20
23
  import Link from 'next/link';
24
+ import AppTabs from './components/AppTabs';
21
25
 
22
26
  // One shared empty list, so "no 2nd-degree data" is the same value every render.
23
27
  const NO_DEGREE2 = [];
@@ -72,27 +76,33 @@ function HomeInner() {
72
76
  const [degree2, setDegree2] = useState([]);
73
77
  // People found only by company scans: 3rd degree in Network Circle's filter.
74
78
  const [degree3, setDegree3] = useState([]);
75
- // Which degrees Network Circle draws: your connections alone until you pick more.
76
- const [degrees, setDegrees] = useState([1]);
77
- // Tiers switched off in Network Circle's Filter panel (C and D make a lot of noise).
78
- const [hiddenTiers, setHiddenTiers] = useState([]);
79
+ // The Filters panel's grid, one for each tab: which tiers show, at which
80
+ // degrees (lib/tier-grid.js). Network Circle starts with your connections
81
+ // alone; Degrees with everything it has.
82
+ const [grids, setGrids] = useState({ network: NETWORK_GRID, degrees: DEGREES_GRID });
79
83
  const [selected, setSelected] = useState(null);
80
84
  const focusNodeRef = useRef(null);
81
- const [filter, setFilter] = useState('all');
82
85
  // A link to someone's circle in Bridge Chains, /?chain=<id> (the Scan page's
83
86
  // "watch it fill in"), opens Degrees on it. The router's search params rather
84
87
  // than window.location: on a click from another page the address bar only
85
88
  // changes after this page has rendered, and the map opened on the Galaxy.
86
- const linkedChain = useSearchParams().get('chain');
89
+ const params = useSearchParams();
90
+ const linkedChain = params.get('chain');
91
+ // Another page's tab row links back here with the tab named: /?mode=separation (app/components/AppTabs.js).
92
+ const linkedMode = ['network', 'degrees', 'separation'].includes(params.get('mode')) ? params.get('mode') : null;
87
93
  const [chainOpen, setChainOpen] = useState(linkedChain);
88
- const [mode, setMode] = useState(() => (chainOpen ? 'degrees' : 'network'));
94
+ // Whose circle is open in Bridge Chains right now: the right panel follows it.
95
+ const [chainIn, setChainIn] = useState(null);
96
+ const [mode, setMode] = useState(() => (chainOpen ? 'degrees' : linkedMode || 'network'));
89
97
  const [loading, setLoading] = useState(true);
90
98
  const [stats, setStats] = useState(null);
91
99
  const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
92
100
  const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(true);
93
- const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : 'galaxy'));
101
+ const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : linkedMode === 'degrees' ? 'chain' : linkedMode === 'separation' ? 'separation' : 'galaxy'));
94
102
  const [notifications, setNotifications] = useState([]);
95
103
  const [showNotifs, setShowNotifs] = useState(false);
104
+ // A notification opened in the right panel (app/components/NoteDetail.js).
105
+ const [openNote, setOpenNote] = useState(null);
96
106
  const [csvMode, setCsvMode] = useState(false);
97
107
  const [csvSource, setCsvSource] = useState('csv');
98
108
  // What the scanner noted: hidden lists, and lists read with nothing new in
@@ -158,7 +168,7 @@ function HomeInner() {
158
168
  const [separationPreset, setSeparationPreset] = useState(null);
159
169
  const showInSeparation = useCallback((preset) => {
160
170
  setSeparationPreset({ ...preset, id: `${preset.query}|${preset.tier || ''}|${preset.rarity || ''}|${Math.random()}` });
161
- setMode('degrees');
171
+ setMode('separation');
162
172
  setVisualMode('separation');
163
173
  }, []);
164
174
  // A card's Insights → that person's circle in Bridge Chains.
@@ -172,7 +182,8 @@ function HomeInner() {
172
182
  try { window.history.replaceState(null, '', window.location.pathname); } catch { /* the link stays */ }
173
183
  }, []);
174
184
 
175
- const isDegreesMode = mode === 'degrees';
185
+ // Separation is its own tab, built on the same rows as Degrees: your bridges and their circles.
186
+ const isDegreesMode = mode === 'degrees' || mode === 'separation';
176
187
  const connections = degree1;
177
188
 
178
189
  // What the views draw. Memoised because the graph views rebuild their whole
@@ -182,14 +193,13 @@ function HomeInner() {
182
193
  // Network Circle draws the degrees picked in the Filter panel, each person
183
194
  // once, at the nearest degree they're found (lib/degrees.js).
184
195
  const byDegree = useMemo(() => peopleByDegree(degree1, degree2, degree3), [degree1, degree2, degree3]);
185
- const networkRows = useMemo(
186
- () => (isDegreesMode ? connections : degrees.flatMap((d) => byDegree[d] || [])),
187
- [isDegreesMode, connections, degrees, byDegree],
188
- );
196
+ const grid = isDegreesMode ? grids.degrees : grids.network;
197
+ // Degrees draws your connections (the bridges among them) and their circles;
198
+ // Network Circle draws whoever the grid shows, at any degree.
189
199
  const filtered = useMemo(() => {
190
- if (!isDegreesMode) return hiddenTiers.length ? networkRows.filter(c => !hiddenTiers.includes(c.tier)) : networkRows;
191
- return filter === 'all' ? networkRows : networkRows.filter(c => c.tier === filter);
192
- }, [isDegreesMode, networkRows, filter, hiddenTiers]);
200
+ if (isDegreesMode) return connections.filter((c) => shows(grid, c.tier, 1));
201
+ return [1, 2, 3].flatMap((d) => (byDegree[d] || []).filter((c) => shows(grid, c.tier, d)));
202
+ }, [isDegreesMode, connections, byDegree, grid]);
193
203
  // Lookups built once per load. Every click re-renders this component, and the
194
204
  // three places below used to scan one list inside another — degree1.find per
195
205
  // 2nd-degree row, degree2.some per connection — about 16M comparisons, twice
@@ -199,21 +209,39 @@ function HomeInner() {
199
209
  () => new Set(degree2.map(d => d.source_connection_id).filter(Boolean)),
200
210
  [degree2],
201
211
  );
202
- const bridgeTierCounts = useMemo(() => {
203
- const counts = {};
204
- degree1.forEach(c => { if (bridgeIds.has(c.id)) counts[c.tier] = (counts[c.tier] || 0) + 1; });
205
- return counts;
206
- }, [degree1, bridgeIds]);
212
+ // A 2nd-degree row shows when its own tier does at 2nd degree, and the bridge
213
+ // it came through shows at 1st.
207
214
  const filteredD2 = useMemo(() => {
208
215
  if (!isDegreesMode) return NO_DEGREE2;
209
- if (filter === 'all') return degree2;
210
- return degree2.filter(c => d1ById.get(c.source_connection_id)?.tier === filter);
211
- }, [isDegreesMode, filter, d1ById, degree2]);
216
+ return degree2.filter((c) => shows(grid, c.tier, 2) && shows(grid, d1ById.get(c.source_connection_id)?.tier, 1));
217
+ }, [isDegreesMode, grid, d1ById, degree2]);
218
+ // How many people stand behind each dot of the grid. In Degrees, 1st degree
219
+ // counts the bridges, since those are the connections it draws.
220
+ const panelCounts = useMemo(() => gridCounts(isDegreesMode
221
+ ? { 1: degree1.filter((c) => bridgeIds.has(c.id)), 2: byDegree[2] }
222
+ : byDegree), [isDegreesMode, degree1, bridgeIds, byDegree]);
212
223
  const selectHandler = useCallback((node) => {
213
224
  setSelected(node);
214
- if (node) setSidebarCollapsed(false);
225
+ if (node) { setSidebarCollapsed(false); setOpenNote(null); }
215
226
  }, []);
216
227
 
228
+ // Resolved once: the renderer below and the notch's buttons both need it.
229
+ // (Above the loading screen's early return, because the effect is a hook.)
230
+ const view = resolveView(visualMode, mode);
231
+
232
+ // This tab's views live in the notch under the header (app/components/ScanStatusBar.js);
233
+ // the Filters panel only filters.
234
+ const hasNetwork = degree1.length > 0;
235
+ useEffect(() => {
236
+ if (!hasNetwork) return undefined;
237
+ setNotchTabs({
238
+ items: viewsForMode(mode).map((v) => ({ key: v.key, label: v.label, icon: v.icon, title: v.desc })),
239
+ current: view.key,
240
+ onPick: setVisualMode,
241
+ });
242
+ return () => setNotchTabs(null);
243
+ }, [mode, view.key, hasNetwork]);
244
+
217
245
  if (loading) {
218
246
  return (
219
247
  <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
@@ -223,9 +251,6 @@ function HomeInner() {
223
251
  );
224
252
  }
225
253
 
226
- // Resolved once, because two places care: the renderer below, and the Orbit
227
- // toggle, which hides over Separation.
228
- const view = resolveView(visualMode, mode);
229
254
  // Scanning and the Scan page belong to your own network, not the sample or a CSV.
230
255
  const canScan = !IS_DEMO && !csvMode;
231
256
 
@@ -236,28 +261,13 @@ function HomeInner() {
236
261
  <h1 style={{ fontSize: isMobile ? 16 : 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
237
262
  Six Degrees
238
263
  </h1>
239
- <div style={{ display: 'flex', gap: isMobile ? 2 : 4, background: 'rgba(255,255,255,0.08)', borderRadius: 8, padding: isMobile ? 2 : 3, flexWrap: isMobile ? 'wrap' : 'nowrap' }}>
240
- <button
241
- onClick={() => { setMode('network'); setSelected(null); setFilter('all'); setVisualMode('galaxy'); }}
242
- style={{
243
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600, cursor: 'pointer',
244
- background: mode === 'network' ? '#fff' : 'transparent',
245
- color: mode === 'network' ? '#000' : '#888',
246
- }}
247
- >
248
- {isMobile ? 'Circle' : 'Network Circle'}
249
- </button>
250
- <button
251
- onClick={() => { setMode('degrees'); setSelected(null); setFilter('all'); setVisualMode('chain'); }}
252
- style={{
253
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600, cursor: 'pointer',
254
- background: mode === 'degrees' ? 'linear-gradient(135deg, #FFD700, #FF6B35)' : 'rgba(255,255,255,0.12)',
255
- color: mode === 'degrees' ? '#000' : '#fff',
256
- }}
257
- >
258
- Degrees
259
- </button>
260
-
264
+ <AppTabs
265
+ active={mode}
266
+ isMobile={isMobile}
267
+ csvMode={csvMode}
268
+ onMode={(m) => { setMode(m); setSelected(null); setVisualMode(m === 'network' ? 'galaxy' : m === 'degrees' ? 'chain' : 'separation'); }}
269
+ after={!IS_DEMO && !csvMode ? <AutoScanButton isMobile={isMobile} /> : null}
270
+ >
261
271
  {/* Which network you are looking at, and the way back out of it.
262
272
  Loading the sample used to be a one-way door: it lives in
263
273
  sessionStorage, and nothing in the UI cleared it. */}
@@ -288,63 +298,7 @@ function HomeInner() {
288
298
  </button>
289
299
  </div>
290
300
  )}
291
- {!IS_DEMO && <Link
292
- href="/paths"
293
- style={{
294
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
295
- background: 'rgba(255,255,255,0.06)', color: '#00ff88', textDecoration: 'none',
296
- display: 'flex', alignItems: 'center', gap: 4,
297
- }}
298
- >
299
- Paths
300
- </Link>}
301
- {!IS_DEMO && <Link
302
- href="/social"
303
- title="Experimental: what your own LinkedIn data says about your relationships"
304
- style={{
305
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
306
- background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
307
- display: 'flex', alignItems: 'center', gap: 4,
308
- }}
309
- >
310
- Social<span style={{ fontSize: 9, opacity: 0.7, marginLeft: 2 }}>beta</span>
311
- </Link>}
312
- {!IS_DEMO && !csvMode && <Link
313
- href="/scores"
314
- style={{
315
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
316
- background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
317
- display: 'flex', alignItems: 'center', gap: 4,
318
- }}
319
- >
320
- Scores
321
- </Link>}
322
- {!IS_DEMO && !csvMode && <Link
323
- href="/queue"
324
- style={{
325
- padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
326
- background: 'rgba(255,255,255,0.06)', color: '#FF6B35', textDecoration: 'none',
327
- display: 'flex', alignItems: 'center', gap: 4,
328
- }}
329
- >
330
- Outlink
331
- </Link>}
332
- {/* Named "Scan" everywhere — nav, page title, README and docs. It is
333
- also the only route to 2nd-degree data, so it stays visible on
334
- mobile rather than being the one thing a phone user cannot find. */}
335
- {!IS_DEMO && !csvMode && <Link
336
- href="/setup"
337
- style={{
338
- padding: isMobile ? '8px 12px' : '8px 16px', borderRadius: 6, border: 'none',
339
- fontSize: 13, fontWeight: 600,
340
- background: 'rgba(255,255,255,0.06)', color: '#666', textDecoration: 'none',
341
- display: 'flex', alignItems: 'center', gap: 4,
342
- }}
343
- >
344
- Scan
345
- </Link>}
346
- {!IS_DEMO && !csvMode && <AutoScanButton isMobile={isMobile} />}
347
- </div>
301
+ </AppTabs>
348
302
  {/* Settings — also in CSV/sample mode and on phones, so updates stay reachable */}
349
303
  {!IS_DEMO && <Link
350
304
  href="/settings"
@@ -402,14 +356,26 @@ function HomeInner() {
402
356
  {notifications.length === 0 ? (
403
357
  <div style={{ padding: 20, textAlign: 'center', color: '#555', fontSize: 12 }}>No notifications yet</div>
404
358
  ) : notifications.slice(0, 20).map(n => (
405
- <div key={n.id} style={{
406
- padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.04)',
359
+ <div key={n.id} role="button" tabIndex={0}
360
+ onClick={() => {
361
+ // Open it in the right panel, and count it as read.
362
+ if (!n.seen) {
363
+ fetch('/api/notifications', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'mark-seen', id: n.id }) }).catch(() => {});
364
+ setNotifications(prev => prev.map(x => (x.id === n.id ? { ...x, seen: true } : x)));
365
+ }
366
+ setOpenNote(n); setShowNotifs(false); setSidebarCollapsed(false);
367
+ }}
368
+ onKeyDown={(e) => { if (e.key === 'Enter') e.currentTarget.click(); }}
369
+ onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(255,255,255,0.06)'; }}
370
+ onMouseLeave={(e) => { e.currentTarget.style.background = n.seen ? 'transparent' : 'rgba(255,215,0,0.04)'; }}
371
+ style={{
372
+ padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.04)', cursor: 'pointer',
407
373
  background: n.seen ? 'transparent' : 'rgba(255,215,0,0.04)',
408
374
  }}>
409
375
  <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
410
376
  <span style={{ fontSize: 16 }}>{n.icon || '📌'}</span>
411
377
  <div style={{ flex: 1 }}>
412
- <div style={{ fontSize: 12, fontWeight: 600, color: n.seen ? '#888' : '#fff' }}>{n.title}</div>
378
+ <div style={{ fontSize: 12, fontWeight: 600, color: n.seen ? '#888' : '#fff' }}>{n.title}<span style={{ color: '#556', marginLeft: 6 }}>›</span></div>
413
379
  {n.message && <div style={{ fontSize: 10, color: '#666', marginTop: 2 }}>{n.message}</div>}
414
380
  <div style={{ fontSize: 9, color: '#444', marginTop: 3 }}>{new Date(n.created_at).toLocaleDateString()}</div>
415
381
  </div>
@@ -458,18 +424,11 @@ function HomeInner() {
458
424
  <FilterPanel
459
425
  collapsed={filterPanelCollapsed}
460
426
  onToggle={() => setFilterPanelCollapsed(!filterPanelCollapsed)}
461
- mode={mode}
462
- filter={filter}
463
- onFilterChange={setFilter}
427
+ mode={isDegreesMode ? 'degrees' : 'network'}
464
428
  visualMode={view.key}
465
- onVisualModeChange={setVisualMode}
466
- tierCounts={isDegreesMode ? stats?.tiers || {} : tierCountsOf(networkRows)}
467
- degrees={degrees}
468
- onDegreesChange={setDegrees}
469
- hiddenTiers={hiddenTiers}
470
- onHiddenTiersChange={setHiddenTiers}
471
- degreeCounts={{ 1: byDegree[1].length, 2: byDegree[2].length, 3: byDegree[3].length }}
472
- bridgeTierCounts={bridgeTierCounts}
429
+ grid={grid}
430
+ gridCounts={panelCounts}
431
+ onGridChange={(next) => setGrids((g) => ({ ...g, [isDegreesMode ? 'degrees' : 'network']: next }))}
473
432
  />
474
433
  {/* Visualization — switches based on visualMode */}
475
434
  {(() => {
@@ -525,6 +484,7 @@ function HomeInner() {
525
484
  canScan,
526
485
  chainOpen,
527
486
  onChainOpened: chainOpened,
487
+ onCircle: setChainIn,
528
488
  preset: separationPreset,
529
489
  };
530
490
  return <View {...viewProps} />;
@@ -535,12 +495,14 @@ function HomeInner() {
535
495
  tierColors={TIER_COLORS}
536
496
  connections={degree1}
537
497
  degree2={degree2}
538
- mode={mode}
498
+ mode={isDegreesMode ? 'degrees' : 'network'}
539
499
  collapsed={sidebarCollapsed}
540
- filter={filter}
541
500
  onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
542
501
  onSelect={(node) => { setSelected(node || null); }}
543
- onSwitchMode={(newMode) => { setMode(newMode); setFilter('all'); }}
502
+ onSwitchMode={(newMode) => { setMode(newMode); }}
503
+ note={openNote}
504
+ onCloseNote={() => setOpenNote(null)}
505
+ circle={view.key === 'chain' ? chainIn : null}
544
506
  onOpenCircle={openCircle}
545
507
  onShowInSeparation={showInSeparation}
546
508
  onFocusNode={(nodeId) => { if (focusNodeRef.current) focusNodeRef.current(nodeId); }}
@@ -550,35 +512,6 @@ function HomeInner() {
550
512
  />
551
513
  {canScan && <NetworkRefresh onChange={reload} live={view.key === 'chain'} />}
552
514
 
553
- {/* Orbit, one tap from Bridge Chains — bottom right, Degrees mode only.
554
- It used to say "Galaxy" and show Orbit: the Galaxy is a Network
555
- Circle view, so asking for it here fell back to Orbit. Hidden over
556
- Separation, where on a phone it sat on top of the score column. */}
557
- {isDegreesMode && view.key !== 'separation' && (
558
- <div style={{
559
- position: 'absolute', bottom: 20, right: 20, zIndex: 20,
560
- display: 'flex', alignItems: 'center', gap: 8,
561
- background: 'rgba(0,0,0,0.7)', borderRadius: 20, padding: '6px 14px',
562
- backdropFilter: 'blur(8px)', border: '1px solid rgba(255,255,255,0.08)',
563
- }}>
564
- <span style={{ fontSize: 10, color: '#666' }}>{VIEWS.orbit.icon} {VIEWS.orbit.label}</span>
565
- <div
566
- onClick={() => setVisualMode(view.key === 'orbit' ? 'chain' : 'orbit')}
567
- style={{
568
- width: 32, height: 18, borderRadius: 9, cursor: 'pointer',
569
- background: view.key === 'orbit' ? '#FF6B35' : 'rgba(255,255,255,0.15)',
570
- position: 'relative', transition: 'background 0.2s',
571
- }}
572
- >
573
- <div style={{
574
- width: 14, height: 14, borderRadius: '50%', background: '#fff',
575
- position: 'absolute', top: 2,
576
- left: view.key === 'orbit' ? 16 : 2,
577
- transition: 'left 0.2s',
578
- }} />
579
- </div>
580
- </div>
581
- )}
582
515
  </div>
583
516
  </div>
584
517
  );
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { Suspense, useEffect, useState } from 'react';
4
+ import AppTabs from '../components/AppTabs';
5
+ import { setNotchTabs } from '../../lib/island';
4
6
  import { useRouter, useSearchParams } from 'next/navigation';
5
7
  import { runScrape, scraperStatus, notReadyMessage, busyReason } from '../../lib/scraper-client';
6
8
  import { loadNetwork } from '../../lib/network';
@@ -15,7 +17,10 @@ import { companyOf, getSeniority } from '../../lib/companies';
15
17
  import { localPhoto } from '../../lib/photos';
16
18
 
17
19
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
18
- const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies']];
20
+ // Map and Companies (Blake, 2026-10-02: companies are worth more than industries,
21
+ // and an industry's detail opens from a company's card). Whether the map's bubbles
22
+ // are companies or people is a switch in its Filters panel.
23
+ const TABS = [['map', 'Map'], ['companies', 'Companies']];
19
24
 
20
25
  export default function PathsPage() {
21
26
  // Suspense because PathsInner reads the address's ?tab= (useSearchParams),
@@ -32,15 +37,24 @@ function PathsInner() {
32
37
  const [d1Data, setD1Data] = useState([]);
33
38
  const [d2Data, setD2Data] = useState([]);
34
39
  const [d3Data, setD3Data] = useState([]);
35
- // Map and Industries are the analyzer; Companies is the list it grew from.
40
+ // Map is the analyzer; Companies is the list it grew from.
36
41
  // A link can open a tab (/paths?tab=companies). Company scores have a tab of
37
- // their own now (app/scores): an old link to /paths?tab=scores goes there.
42
+ // their own (app/scores), and now live in Settings: an old link to /paths?tab=scores goes there.
38
43
  // The router's search params rather than window.location: on a click from
39
44
  // another page the address bar only changes after this page has rendered.
45
+ // Old links still land: ?tab=people opens the map with people as the bubbles; ?tab=industries the map.
40
46
  const asked = useSearchParams().get('tab');
41
47
  const [tab, setTab] = useState(() => (TABS.some(([k]) => k === asked) ? asked : 'map'));
48
+ const initialShow = asked === 'people' ? 'people' : 'companies';
42
49
  const router = useRouter();
43
- useEffect(() => { if (asked === 'scores') router.replace('/scores#companies'); }, [asked, router]);
50
+ useEffect(() => { if (asked === 'scores') router.replace('/settings#companies'); }, [asked, router]);
51
+ // Map and Companies in the notch under the tabs, as the
52
+ // map page's views are; not while one company is open (its own bar shows).
53
+ useEffect(() => {
54
+ if (selectedCompany) { setNotchTabs(null); return undefined; }
55
+ setNotchTabs({ items: TABS.map(([key, label]) => ({ key, label })), current: tab, onPick: setTab });
56
+ return () => setNotchTabs(null);
57
+ }, [tab, selectedCompany]);
44
58
  // Bumped when company scores change, so the network reloads with new tiers.
45
59
  const [reloadKey, setReloadKey] = useState(0);
46
60
  // The sample or a CSV import: they live only in this tab, so a company scan could only fail.
@@ -246,28 +260,25 @@ function PathsInner() {
246
260
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
247
261
  display: 'flex', flexDirection: 'column', overflow: 'hidden',
248
262
  }}>
249
- <header style={{ padding: '14px 24px', borderBottom: '1px solid rgba(255,255,255,0.1)', flexShrink: 0 }}>
263
+ {/* The same header as every page: the app's tabs, Paths lit; Map, People,
264
+ Industries and Companies sit in the notch under it. */}
265
+ <header style={{ padding: '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)', flexShrink: 0 }}>
266
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
267
+ <Link href="/" style={{ textDecoration: 'none' }}>
268
+ <h1 style={{ fontSize: 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
269
+ Six Degrees
270
+ </h1>
271
+ </Link>
272
+ <AppTabs active="paths" csvMode={localOnly} />
273
+ <Link href="/settings" title="Settings" aria-label="Settings" style={{
274
+ width: 32, height: 32, borderRadius: '50%', flexShrink: 0, background: 'rgba(255,255,255,0.06)', color: '#888',
275
+ fontSize: 16, textDecoration: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center',
276
+ }}>⚙</Link>
277
+ </div>
278
+ </header>
279
+ {selectedCompany && (
280
+ <div style={{ padding: '10px 24px', borderBottom: '1px solid rgba(255,255,255,0.08)', flexShrink: 0 }}>
250
281
  <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
251
- <Link href="/" style={{
252
- display: 'flex', alignItems: 'center', gap: 6, color: '#888', textDecoration: 'none',
253
- fontSize: 13, fontWeight: 600, padding: '6px 14px', borderRadius: 6,
254
- background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)',
255
- }}><span style={{ fontSize: 16 }}>&larr;</span> Map</Link>
256
- <h1 style={{
257
- fontSize: 20, fontWeight: 700, margin: 0,
258
- background: 'linear-gradient(135deg, #00ff88, #3498DB)',
259
- WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
260
- }}>Paths</h1>
261
- {!selectedCompany && (
262
- <div style={{ display: 'flex', gap: 4, marginLeft: 8, padding: 3, borderRadius: 8, background: 'rgba(255,255,255,0.05)' }}>
263
- {TABS.map(([k, label]) => (
264
- <button key={k} onClick={() => setTab(k)} style={{
265
- padding: '5px 12px', borderRadius: 6, border: 'none', cursor: 'pointer', fontSize: 12.5, fontWeight: 700,
266
- background: tab === k ? 'linear-gradient(135deg, #00ff88, #3498DB)' : 'transparent', color: tab === k ? '#000' : '#aab',
267
- }}>{label}</button>
268
- ))}
269
- </div>
270
- )}
271
282
  {selectedCompany && (
272
283
  <>
273
284
  <button onClick={() => { setSelectedCompany(null); setCompanyPeople([]); }}
@@ -301,10 +312,11 @@ function PathsInner() {
301
312
  </>
302
313
  )}
303
314
  </div>
304
- </header>
315
+ </div>
316
+ )}
305
317
 
306
318
  {!selectedCompany && tab !== 'companies' ? (
307
- <PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data} tab={tab}
319
+ <PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data} initialShow={initialShow}
308
320
  onOpenCompany={(co) => selectCompany({ ...co, sCount: co.S, aCount: co.A })} />
309
321
  ) : (
310
322
  <div style={{ flex: 1, overflow: 'auto', padding: '16px 24px' }}>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
3
+ import { Suspense, useEffect, useState } from 'react';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
4
5
  import { loadNetwork } from '../../lib/network';
5
6
  import MappingProgress from '../components/MappingProgress';
6
7
  import { IS_DEMO } from '../../lib/demo';
@@ -8,6 +9,7 @@ import OnboardingGate from '../components/OnboardingGate';
8
9
  import { useUser } from '../components/UserProvider';
9
10
  import { sectorByKey } from '../../lib/sector-labels';
10
11
  import Link from 'next/link';
12
+ import NetworkHealthSection from '../components/settings/NetworkHealthSection';
11
13
 
12
14
  const LEVEL_NAMES = {
13
15
  1: 'Observer', 11: 'Connector', 31: 'Networker', 51: 'Strategist', 76: 'Architect',
@@ -22,18 +24,25 @@ function getLevelName(level) {
22
24
  }
23
25
 
24
26
  export default function ProfilePage() {
25
- return <OnboardingGate><ProfileInner /></OnboardingGate>;
27
+ // useSearchParams (which view) needs a Suspense boundary to build.
28
+ return <Suspense fallback={null}><OnboardingGate><ProfileInner /></OnboardingGate></Suspense>;
26
29
  }
27
30
 
28
31
  function ProfileInner() {
29
32
  const { userId, userName, userProfile } = useUser();
33
+ // Profile, or Insights: how your network holds together (Blake, 2026-10-02:
34
+ // "having the insights button into profile having network health"). A link
35
+ // can open Insights: /profile?view=insights (Settings, the old Scores tab).
36
+ const router = useRouter();
37
+ const view = useSearchParams().get('view') === 'insights' ? 'insights' : 'profile';
38
+ const showView = (v) => router.replace(v === 'insights' ? '/profile?view=insights' : '/profile', { scroll: false });
30
39
  const profile = userProfile || { name: userName || 'User', headline: '', sectors: [], goals: [] };
31
40
  const [stats, setStats] = useState(null);
32
41
  const [loading, setLoading] = useState(true);
33
42
  const [notifications, setNotifications] = useState([]);
34
43
  const [queueStats, setQueueStats] = useState({ total: 0, sent: 0, accepted: 0 });
35
44
  const [mapping, setMapping] = useState({ degree1: [], degree2: [], skips: [] });
36
- // "Your Sectors" is what you picked in Scores → Your sector. (users.sectors,
45
+ // "Your Sectors" is what you picked in Settings → Scores → Your field. (users.sectors,
37
46
  // which this card used to show, is never written by anything; the Sidebar
38
47
  // and Outlink still read it as free text.) null while it loads; a load that
39
48
  // fails says so rather than showing "none picked" (TRAPS §7).
@@ -163,9 +172,27 @@ function ProfileInner() {
163
172
  background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)',
164
173
  WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
165
174
  }}>Profile</h1>
175
+ {/* Profile, or Insights: network health, built from your own scans */}
176
+ <div role="tablist" aria-label="Profile or Insights" style={{ display: 'flex', gap: 3, marginLeft: 8, padding: 3, borderRadius: 9, background: 'rgba(255,255,255,0.06)' }}>
177
+ {[['profile', 'Profile'], ['insights', 'Insights']].map(([k, label]) => (
178
+ <button key={k} type="button" role="tab" aria-selected={view === k} onClick={() => showView(k)} style={{
179
+ padding: '6px 14px', borderRadius: 7, border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 700,
180
+ background: view === k ? 'linear-gradient(135deg, #FFD700, #FF6B35)' : 'transparent', color: view === k ? '#000' : '#aab',
181
+ }}>{k === 'insights' ? '✦ ' : ''}{label}</button>
182
+ ))}
183
+ </div>
166
184
  </header>
167
185
 
168
186
  <div style={{ flex: 1, overflow: 'auto', padding: '30px 20px' }}>
187
+ {view === 'insights' ? (
188
+ <div style={{ maxWidth: 820, margin: '0 auto' }}>
189
+ <div style={{ fontSize: 13, color: '#8a8fa8', lineHeight: 1.6 }}>
190
+ How your network holds together, from the circles you&rsquo;ve scanned. Only your own numbers, never a
191
+ comparison with anyone else&rsquo;s.
192
+ </div>
193
+ <NetworkHealthSection />
194
+ </div>
195
+ ) : (
169
196
  <div style={{ maxWidth: 700, margin: '0 auto' }}>
170
197
 
171
198
  {/* === LEVEL CARD === */}
@@ -376,8 +403,8 @@ function ProfileInner() {
376
403
  }}>
377
404
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
378
405
  <h3 style={{ fontSize: 14, fontWeight: 700, margin: 0 }}>Your Sectors</h3>
379
- <Link href="/scores#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
380
- {sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick on Scores' : 'Scores'} &rarr;
406
+ <Link href="/settings#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
407
+ {sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick in Settings' : 'Settings'} &rarr;
381
408
  </Link>
382
409
  </div>
383
410
  {!sectorFocus ? (
@@ -409,6 +436,7 @@ function ProfileInner() {
409
436
  </div>
410
437
 
411
438
  </div>
439
+ )}
412
440
  </div>
413
441
  </div>
414
442
  );