six-degrees 0.5.0 → 0.6.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 (366) 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 +14 -14
  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 +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.rsc +20 -19
  14. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +20 -19
  15. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +16 -15
  16. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  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.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/update/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/import/page.js +1 -1
  50. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/import.html +1 -1
  53. package/.next/standalone/.next/server/app/import.rsc +24 -23
  54. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +24 -23
  55. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  56. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +18 -17
  57. package/.next/standalone/.next/server/app/index.html +1 -1
  58. package/.next/standalone/.next/server/app/index.rsc +22 -21
  59. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +18 -17
  60. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +22 -21
  61. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/launch/page.js +1 -1
  63. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/launch.html +2 -2
  66. package/.next/standalone/.next/server/app/launch.rsc +24 -23
  67. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +24 -23
  68. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  69. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +18 -17
  70. package/.next/standalone/.next/server/app/page.js +1 -1
  71. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/paths/page.js +1 -1
  74. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/paths.html +1 -1
  77. package/.next/standalone/.next/server/app/paths.rsc +24 -23
  78. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +24 -23
  79. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  80. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +18 -17
  81. package/.next/standalone/.next/server/app/profile/page.js +1 -1
  82. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/profile.html +1 -1
  85. package/.next/standalone/.next/server/app/profile.rsc +24 -23
  86. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +24 -23
  87. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  88. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +18 -17
  89. package/.next/standalone/.next/server/app/queue/page.js +1 -1
  90. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/queue.html +1 -1
  93. package/.next/standalone/.next/server/app/queue.rsc +24 -23
  94. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +24 -23
  95. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  96. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +18 -17
  97. package/.next/standalone/.next/server/app/scores/page.js +1 -1
  98. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  100. package/.next/standalone/.next/server/app/scores.html +1 -1
  101. package/.next/standalone/.next/server/app/scores.rsc +24 -23
  102. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -23
  103. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  104. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +18 -17
  105. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  106. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/settings.html +1 -1
  109. package/.next/standalone/.next/server/app/settings.rsc +24 -23
  110. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +24 -23
  111. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  112. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +18 -17
  113. package/.next/standalone/.next/server/app/setup/page.js +1 -1
  114. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/setup.html +1 -1
  117. package/.next/standalone/.next/server/app/setup.rsc +24 -23
  118. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +24 -23
  119. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  120. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +18 -17
  121. package/.next/standalone/.next/server/app/social/page.js +1 -1
  122. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  123. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  124. package/.next/standalone/.next/server/app/social.html +1 -1
  125. package/.next/standalone/.next/server/app/social.rsc +24 -23
  126. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +24 -23
  127. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
  128. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +18 -17
  129. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05_3-22._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13dx4sd._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__18bcnu4._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tir0vc._.js +2 -2
  157. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +2 -2
  159. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  160. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +2 -2
  161. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +5 -5
  162. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +5 -5
  163. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +5 -5
  164. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +9 -9
  165. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08oxiqh._.js +3 -0
  166. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/_08g31gm._.js +3 -0
  168. package/.next/standalone/.next/server/chunks/ssr/_0gk_ofx._.js +3 -0
  169. package/.next/standalone/.next/server/chunks/ssr/_0nwsf93._.js +3 -0
  170. package/.next/standalone/.next/server/chunks/ssr/_0vho1lb._.js +3 -0
  171. package/.next/standalone/.next/server/chunks/ssr/{_0jdkqgi._.js → _1-658fq._.js} +2 -2
  172. package/.next/standalone/.next/server/chunks/ssr/_1bc46kz._.js +3 -0
  173. package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +3 -0
  174. package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +9 -0
  175. package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +9 -0
  176. package/.next/standalone/.next/server/chunks/ssr/_2105jz8._.js +3 -0
  177. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +1 -1
  178. package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +3 -0
  179. package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +13 -0
  180. package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +13 -0
  181. package/.next/standalone/.next/server/chunks/ssr/{_0gtv9jb._.js → app_components_CompanyScores_1g0mytz.js} +2 -2
  182. package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/app_import_page_1fmu6_l.js +1 -1
  184. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +14 -12
  185. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  186. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  187. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +6 -6
  188. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  189. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +4 -4
  190. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -1
  191. package/.next/standalone/.next/server/chunks/ssr/{lib_0i8v-s3._.js → lib_1c4ieuw._.js} +2 -2
  192. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
  193. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  194. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  195. package/.next/standalone/.next/server/pages/404.html +1 -1
  196. package/.next/standalone/.next/server/pages/500.html +1 -1
  197. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  198. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  199. package/.next/standalone/.next/static/chunks/02pq76u7y4v9-.js +22 -0
  200. package/.next/standalone/.next/static/chunks/0cmq0exd4d0h9.js +1 -0
  201. package/.next/{static/chunks/2wgz9ietkqqe6.js → standalone/.next/static/chunks/0i02yxppzds1o.js} +1 -1
  202. package/.next/standalone/.next/static/chunks/0m_p4k-q2qh--.js +1 -0
  203. package/.next/standalone/.next/static/chunks/0oyslj62jkubd.js +1 -0
  204. package/.next/standalone/.next/static/chunks/{1x_ysly42-teq.css → 0vo-l8hxneba6.css} +1 -1
  205. package/.next/{static/chunks/3sowbvbaj1s6n.js → standalone/.next/static/chunks/0ws7zy0w57ufi.js} +1 -1
  206. package/.next/standalone/.next/static/chunks/0yh8z2tbrc5i_.js +1 -0
  207. package/.next/standalone/.next/static/chunks/172e10mi-tpo_.js +34 -0
  208. package/.next/standalone/.next/static/chunks/1ei5vbe2uw6fl.js +1 -0
  209. package/.next/standalone/.next/static/chunks/1f7_pl_wq7lh3.js +11 -0
  210. package/.next/standalone/.next/static/chunks/1jzbe7zdx9vbj.js +2 -0
  211. package/.next/standalone/.next/static/chunks/1m3017gp790pw.js +45 -0
  212. package/.next/standalone/.next/static/chunks/1t20e8u0st7ep.js +1 -0
  213. package/.next/standalone/.next/static/chunks/2-ch9rm27eril.js +11 -0
  214. package/.next/standalone/.next/static/chunks/2ldv-v3s9yaif.js +1 -0
  215. package/.next/standalone/.next/static/chunks/3bp284gjf7dyq.js +1 -0
  216. package/.next/standalone/.next/static/chunks/3k1f6clbn3gj3.js +7 -0
  217. package/.next/standalone/.next/static/chunks/3l4t8je6iguzc.js +1 -0
  218. package/.next/standalone/.next/static/chunks/{0muwt9bykzr-1.js → 3tplzuc9jk-tl.js} +1 -1
  219. package/.next/standalone/.next/static/chunks/42m0bqr8pbovz.js +11 -0
  220. package/.next/standalone/CHANGELOG.md +61 -0
  221. package/.next/standalone/README.md +22 -8
  222. package/.next/standalone/app/api/scraper/route.js +40 -3
  223. package/.next/standalone/app/api/update/route.js +12 -6
  224. package/.next/standalone/app/components/AppHeader.js +257 -0
  225. package/.next/standalone/app/components/Avatar.js +2 -1
  226. package/.next/standalone/app/components/ChainView.js +12 -12
  227. package/.next/standalone/app/components/FilterPanel.js +20 -12
  228. package/.next/standalone/app/components/ForceGraph.js +163 -25
  229. package/.next/standalone/app/components/GalaxyLab.js +43 -12
  230. package/.next/standalone/app/components/GridView.js +4 -4
  231. package/.next/standalone/app/components/LinkedInLimits.js +2 -1
  232. package/.next/standalone/app/components/ListView.js +3 -3
  233. package/.next/standalone/app/components/NoteDetail.js +2 -1
  234. package/.next/standalone/app/components/OnboardingGate.js +2 -2
  235. package/.next/standalone/app/components/OutlinkQuest.js +2 -1
  236. package/.next/standalone/app/components/PathsAnalyzer.js +3 -2
  237. package/.next/standalone/app/components/PeopleMap.js +2 -1
  238. package/.next/standalone/app/components/RingsView.js +3 -3
  239. package/.next/standalone/app/components/ScanTrail.js +85 -0
  240. package/.next/standalone/app/components/SeparationView.js +100 -77
  241. package/.next/standalone/app/components/Sidebar.js +4 -4
  242. package/.next/standalone/app/components/TheirCircle.js +2 -1
  243. package/.next/standalone/app/components/ThemeLoader.js +9 -0
  244. package/.next/standalone/app/components/UpdatePanel.js +20 -2
  245. package/.next/standalone/app/components/settings/AppearanceSection.js +226 -0
  246. package/.next/standalone/app/components/settings/NetworkHealthSection.js +2 -1
  247. package/.next/standalone/app/components/ui.js +2 -1
  248. package/.next/standalone/app/globals.css +70 -2
  249. package/.next/standalone/app/import/page.js +3 -3
  250. package/.next/standalone/app/layout.js +11 -2
  251. package/.next/standalone/app/page.js +69 -215
  252. package/.next/standalone/app/paths/page.js +8 -21
  253. package/.next/standalone/app/profile/page.js +11 -10
  254. package/.next/standalone/app/queue/page.js +8 -21
  255. package/.next/standalone/app/scores/page.js +1 -1
  256. package/.next/standalone/app/settings/page.js +7 -3
  257. package/.next/standalone/app/setup/page.js +83 -88
  258. package/.next/standalone/app/social/Crm.js +2 -1
  259. package/.next/standalone/app/social/SocialHub.js +2 -2
  260. package/.next/standalone/app/social/page.js +1 -1
  261. package/.next/standalone/lib/data-import.js +1 -0
  262. package/.next/standalone/lib/galaxy-export.js +2 -1
  263. package/.next/standalone/lib/galaxy-lab.js +24 -8
  264. package/.next/standalone/lib/level.js +51 -0
  265. package/.next/standalone/lib/release.js +13 -7
  266. package/.next/standalone/lib/scanner-python.js +38 -8
  267. package/.next/standalone/lib/scraper-client.js +2 -1
  268. package/.next/standalone/lib/separation.js +4 -2
  269. package/.next/standalone/lib/theme-store.js +61 -0
  270. package/.next/standalone/lib/themes.js +186 -0
  271. package/.next/standalone/lib/tiers.js +2 -8
  272. package/.next/standalone/package-lock.json +2 -2
  273. package/.next/standalone/package.json +4 -4
  274. package/.next/standalone/scripts/build-desktop.mjs +325 -0
  275. package/.next/standalone/scripts/build-site.mjs +85 -15
  276. package/.next/standalone/scripts/gen-test-network.mjs +93 -0
  277. package/.next/standalone/scripts/next.mjs +20 -0
  278. package/.next/standalone/scripts/readme_buttons.py +3 -0
  279. package/.next/standalone/scripts/requirements.txt +18 -1
  280. package/.next/standalone/scripts/site-news.mjs +54 -9
  281. package/.next/standalone/scripts/smoke-desktop.mjs +177 -0
  282. package/.next/standalone/scripts/windows/six-degrees.iss +100 -0
  283. package/.next/static/chunks/02pq76u7y4v9-.js +22 -0
  284. package/.next/static/chunks/0cmq0exd4d0h9.js +1 -0
  285. package/.next/{standalone/.next/static/chunks/2wgz9ietkqqe6.js → static/chunks/0i02yxppzds1o.js} +1 -1
  286. package/.next/static/chunks/0m_p4k-q2qh--.js +1 -0
  287. package/.next/static/chunks/0oyslj62jkubd.js +1 -0
  288. package/.next/static/chunks/{1x_ysly42-teq.css → 0vo-l8hxneba6.css} +1 -1
  289. package/.next/{standalone/.next/static/chunks/3sowbvbaj1s6n.js → static/chunks/0ws7zy0w57ufi.js} +1 -1
  290. package/.next/static/chunks/0yh8z2tbrc5i_.js +1 -0
  291. package/.next/static/chunks/172e10mi-tpo_.js +34 -0
  292. package/.next/static/chunks/1ei5vbe2uw6fl.js +1 -0
  293. package/.next/static/chunks/1f7_pl_wq7lh3.js +11 -0
  294. package/.next/static/chunks/1jzbe7zdx9vbj.js +2 -0
  295. package/.next/static/chunks/1m3017gp790pw.js +45 -0
  296. package/.next/static/chunks/1t20e8u0st7ep.js +1 -0
  297. package/.next/static/chunks/2-ch9rm27eril.js +11 -0
  298. package/.next/static/chunks/2ldv-v3s9yaif.js +1 -0
  299. package/.next/static/chunks/3bp284gjf7dyq.js +1 -0
  300. package/.next/static/chunks/3k1f6clbn3gj3.js +7 -0
  301. package/.next/static/chunks/3l4t8je6iguzc.js +1 -0
  302. package/.next/static/chunks/{0muwt9bykzr-1.js → 3tplzuc9jk-tl.js} +1 -1
  303. package/.next/static/chunks/42m0bqr8pbovz.js +11 -0
  304. package/CHANGELOG.md +61 -0
  305. package/README.md +22 -8
  306. package/lib/data-import.js +1 -0
  307. package/lib/galaxy-export.js +2 -1
  308. package/lib/galaxy-lab.js +24 -8
  309. package/lib/level.js +51 -0
  310. package/lib/release.js +13 -7
  311. package/lib/scanner-python.js +38 -8
  312. package/lib/scraper-client.js +2 -1
  313. package/lib/separation.js +4 -2
  314. package/lib/theme-store.js +61 -0
  315. package/lib/themes.js +186 -0
  316. package/lib/tiers.js +2 -8
  317. package/package.json +4 -4
  318. package/scripts/requirements.txt +18 -1
  319. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +0 -3
  320. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16hs8kw._.js +0 -3
  321. package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +0 -3
  322. package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +0 -3
  323. package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +0 -3
  324. package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +0 -9
  325. package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +0 -3
  326. package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +0 -3
  327. package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +0 -3
  328. package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +0 -9
  329. package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +0 -3
  330. package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +0 -3
  331. package/.next/standalone/.next/static/chunks/03bziijw5srol.js +0 -1
  332. package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +0 -7
  333. package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +0 -1
  334. package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +0 -34
  335. package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +0 -22
  336. package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +0 -2
  337. package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +0 -1
  338. package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +0 -1
  339. package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +0 -43
  340. package/.next/standalone/.next/static/chunks/2dwtoszeq91us.js +0 -1
  341. package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +0 -1
  342. package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +0 -1
  343. package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +0 -1
  344. package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +0 -1
  345. package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +0 -1
  346. package/.next/static/chunks/03bziijw5srol.js +0 -1
  347. package/.next/static/chunks/0bgq8wmopl_ig.js +0 -7
  348. package/.next/static/chunks/0ezus6p2eb-vg.js +0 -1
  349. package/.next/static/chunks/0g_fy2034bp0x.js +0 -34
  350. package/.next/static/chunks/0q5dru4q6gxl5.js +0 -22
  351. package/.next/static/chunks/0w-byiwb2zax8.js +0 -2
  352. package/.next/static/chunks/1g7jcovdejc9-.js +0 -1
  353. package/.next/static/chunks/1i51yylxdzht1.js +0 -1
  354. package/.next/static/chunks/1zf8swaasjf1e.js +0 -43
  355. package/.next/static/chunks/2dwtoszeq91us.js +0 -1
  356. package/.next/static/chunks/2rqtpccruavf2.js +0 -1
  357. package/.next/static/chunks/30x3oq_st7a5r.js +0 -1
  358. package/.next/static/chunks/355ryio1b7sm1.js +0 -1
  359. package/.next/static/chunks/3ap5hgma7n0o7.js +0 -1
  360. package/.next/static/chunks/3fci6qi39-1n5.js +0 -1
  361. /package/.next/standalone/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_buildManifest.js +0 -0
  362. /package/.next/standalone/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_clientMiddlewareManifest.js +0 -0
  363. /package/.next/standalone/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_ssgManifest.js +0 -0
  364. /package/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_buildManifest.js +0 -0
  365. /package/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_clientMiddlewareManifest.js +0 -0
  366. /package/.next/static/{I-cj79yE2IZ8taX2dZGp3 → dQUvvVSXqbkEFC3-CNcwT}/_ssgManifest.js +0 -0
@@ -2,13 +2,12 @@
2
2
 
3
3
  import { Suspense, useEffect, useState, useRef, useMemo, useCallback } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
- import { scraperStatus, beginScrape, notReadyMessage, busyReason, isCircleScan, loadScanNotes, NO_SCAN_NOTES } from '../lib/scraper-client';
5
+ import { isCircleScan, loadScanNotes, NO_SCAN_NOTES } from '../lib/scraper-client';
6
6
  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
10
  import { resolveView } from './components/views';
11
- import AutoScanButton from './components/AutoScanButton';
12
11
  import { peopleByDegree } from '../lib/degrees';
13
12
  import { NETWORK_GRID, DEGREES_GRID, shows, gridCounts } from '../lib/tier-grid';
14
13
  import Sidebar from './components/Sidebar';
@@ -21,7 +20,9 @@ import { useUser } from './components/UserProvider';
21
20
  import { IS_DEMO, loadDemoNetwork } from '../lib/demo';
22
21
  import { hasCsvNetwork, loadCsvNetwork, clearCsvNetwork } from '../lib/csv';
23
22
  import Link from 'next/link';
24
- import AppTabs from './components/AppTabs';
23
+ import AppHeader from './components/AppHeader';
24
+ import { networkLevel } from '../lib/level';
25
+ import { TIER_COLORS } from '../lib/themes'; // the theme's dot colours, filled before anything draws
25
26
 
26
27
  // One shared empty list, so "no 2nd-degree data" is the same value every render.
27
28
  const NO_DEGREE2 = [];
@@ -38,13 +39,6 @@ function statsFor(d1, d2) {
38
39
  return { total: d1.length, tiers: tierCounts, d2Total: d2.length, d2Tiers: d2TierCounts };
39
40
  }
40
41
 
41
- const TIER_COLORS = {
42
- S: '#FFD700',
43
- A: '#9B59B6',
44
- B: '#3498DB',
45
- C: '#95A5A6',
46
- D: '#BDC3C7',
47
- };
48
42
 
49
43
  export default function Home() {
50
44
  // Suspense because HomeInner reads the address's ?chain= (useSearchParams),
@@ -99,10 +93,11 @@ function HomeInner() {
99
93
  const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
100
94
  const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(true);
101
95
  const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : linkedMode === 'degrees' ? 'chain' : linkedMode === 'separation' ? 'separation' : 'galaxy'));
102
- const [notifications, setNotifications] = useState([]);
103
- const [showNotifs, setShowNotifs] = useState(false);
104
- // A notification opened in the right panel (app/components/NoteDetail.js).
96
+ // A notification opened in the right panel (app/components/NoteDetail.js);
97
+ // one picked on another page arrives as /?note=<id> (app/components/AppHeader.js).
105
98
  const [openNote, setOpenNote] = useState(null);
99
+ const linkedNote = params.get('note');
100
+ const openNoteHere = useCallback((n) => { setOpenNote(n); setSidebarCollapsed(false); }, []);
106
101
  const [csvMode, setCsvMode] = useState(false);
107
102
  const [csvSource, setCsvSource] = useState('csv');
108
103
  // What the scanner noted: hidden lists, and lists read with nothing new in
@@ -138,8 +133,6 @@ function HomeInner() {
138
133
  load();
139
134
  if (!IS_DEMO && !hasCsvNetwork()) {
140
135
  loadScanNotes().then(setScanNotes);
141
- // Fetch notifications
142
- fetch(`/api/notifications?userId=${userId}`).then(r => r.json()).then(d => setNotifications(d.notifications || [])).catch(() => {});
143
136
  }
144
137
  }, [userId]);
145
138
 
@@ -188,7 +181,7 @@ function HomeInner() {
188
181
 
189
182
  // What the views draw. Memoised because the graph views rebuild their whole
190
183
  // scene when these change identity: computed inline, every re-render — a click,
191
- // the sidebar opening, notifications arriving — reset the galaxy's layout. The
184
+ // the sidebar opening, a notification arriving — reset the galaxy's layout. The
192
185
  // empty list in network mode was a new [] each time, which was enough on its own.
193
186
  // Network Circle draws the degrees picked in the Filter panel, each person
194
187
  // once, at the nearest degree they're found (lib/degrees.js).
@@ -196,30 +189,42 @@ function HomeInner() {
196
189
  const grid = isDegreesMode ? grids.degrees : grids.network;
197
190
  // Degrees draws your connections (the bridges among them) and their circles;
198
191
  // Network Circle draws whoever the grid shows, at any degree.
192
+ // Separation ranks the people two steps away, and every connection is a way
193
+ // in to them whatever its tier, so there the tiers pick who's ranked and never
194
+ // which connections lead to them (Blake, 2026-10-03: hiding a tier took the
195
+ // purple A-tier mutuals off the map; "the filter should only apply to the output").
196
+ const isSeparation = mode === 'separation';
199
197
  const filtered = useMemo(() => {
198
+ if (isSeparation) return connections;
200
199
  if (isDegreesMode) return connections.filter((c) => shows(grid, c.tier, 1));
201
200
  return [1, 2, 3].flatMap((d) => (byDegree[d] || []).filter((c) => shows(grid, c.tier, d)));
202
- }, [isDegreesMode, connections, byDegree, grid]);
201
+ }, [isSeparation, isDegreesMode, connections, byDegree, grid]);
203
202
  // Lookups built once per load. Every click re-renders this component, and the
204
203
  // three places below used to scan one list inside another — degree1.find per
205
204
  // 2nd-degree row, degree2.some per connection — about 16M comparisons, twice
206
205
  // a click, at 20k rows.
207
206
  const d1ById = useMemo(() => new Map(degree1.map(c => [c.id, c])), [degree1]);
207
+ // Your level, in the round button at the top right (lib/level.js).
208
+ const level = useMemo(() => networkLevel(degree1, degree2), [degree1, degree2]);
208
209
  const bridgeIds = useMemo(
209
210
  () => new Set(degree2.map(d => d.source_connection_id).filter(Boolean)),
210
211
  [degree2],
211
212
  );
212
- // A 2nd-degree row shows when its own tier does at 2nd degree, and the bridge
213
- // it came through shows at 1st.
213
+ // A 2nd-degree row shows when its own tier does at 2nd degree, and (in
214
+ // Degrees, not Separation) the bridge it came through shows at 1st.
214
215
  const filteredD2 = useMemo(() => {
215
216
  if (!isDegreesMode) return NO_DEGREE2;
217
+ if (isSeparation) return degree2.filter((c) => shows(grid, c.tier, 2));
216
218
  return degree2.filter((c) => shows(grid, c.tier, 2) && shows(grid, d1ById.get(c.source_connection_id)?.tier, 1));
217
- }, [isDegreesMode, grid, d1ById, degree2]);
219
+ }, [isDegreesMode, isSeparation, grid, d1ById, degree2]);
218
220
  // 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]);
221
+ // counts the bridges, since those are the connections it draws; Separation
222
+ // only ranks the 2nd.
223
+ const panelCounts = useMemo(() => gridCounts(isSeparation
224
+ ? { 2: byDegree[2] }
225
+ : isDegreesMode
226
+ ? { 1: degree1.filter((c) => bridgeIds.has(c.id)), 2: byDegree[2] }
227
+ : byDegree), [isSeparation, isDegreesMode, degree1, bridgeIds, byDegree]);
223
228
  const selectHandler = useCallback((node) => {
224
229
  setSelected(node);
225
230
  if (node) { setSidebarCollapsed(false); setOpenNote(null); }
@@ -244,7 +249,7 @@ function HomeInner() {
244
249
 
245
250
  if (loading) {
246
251
  return (
247
- <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
252
+ <div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: 'var(--sd-page)', color: '#fff' }}>
248
253
  <div style={{ fontSize: 24, fontWeight: 700 }}>Loading Six Degrees…</div>
249
254
  <div style={{ fontSize: 14, color: '#888', marginTop: 8 }}>Mapping your LinkedIn network</div>
250
255
  </div>
@@ -255,157 +260,46 @@ function HomeInner() {
255
260
  const canScan = !IS_DEMO && !csvMode;
256
261
 
257
262
  return (
258
- <div data-map style={{ height: '100vh', overflow: 'hidden', background: '#0a0a1a', color: '#fff', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
259
- <header style={{ padding: isMobile ? '10px 12px' : '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>
260
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: isMobile ? 4 : 8, flexWrap: isMobile ? 'wrap' : 'nowrap', gap: isMobile ? 6 : 0 }}>
261
- <h1 style={{ fontSize: isMobile ? 16 : 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
262
- Six Degrees
263
- </h1>
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
- >
271
- {/* Which network you are looking at, and the way back out of it.
272
- Loading the sample used to be a one-way door: it lives in
273
- sessionStorage, and nothing in the UI cleared it. */}
274
- {csvMode && (
275
- <div style={{
276
- display: 'flex', alignItems: 'center', gap: 6,
277
- marginLeft: isMobile ? 4 : 10, padding: isMobile ? '4px 8px' : '5px 10px',
278
- borderRadius: 999, background: 'rgba(255,255,255,0.05)',
279
- border: '1px solid rgba(255,255,255,0.12)',
280
- }}>
281
- <span style={{
282
- width: 6, height: 6, borderRadius: '50%',
283
- background: csvSource === 'sample' ? '#9B59B6' : '#2ecc71',
284
- }} />
285
- <span style={{ fontSize: isMobile ? 10 : 12, color: 'rgba(255,255,255,0.65)', whiteSpace: 'nowrap' }}>
286
- {csvSource === 'sample' ? 'Sample network' : 'Your CSV'}
287
- </span>
288
- <button
289
- onClick={() => { clearCsvNetwork(); window.location.href = '/'; }}
290
- title={csvSource === 'sample' ? 'Leave the sample network' : 'Clear this import'}
291
- style={{
292
- border: 'none', background: 'transparent', cursor: 'pointer',
293
- color: '#888', fontSize: isMobile ? 13 : 15, lineHeight: 1,
294
- padding: '0 0 0 2px',
295
- }}
296
- >
297
- &times;
298
- </button>
299
- </div>
300
- )}
301
- </AppTabs>
302
- {/* Settings — also in CSV/sample mode and on phones, so updates stay reachable */}
303
- {!IS_DEMO && <Link
304
- href="/settings"
305
- title="Settings"
306
- aria-label="Settings"
307
- style={{
308
- width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', flexShrink: 0,
309
- background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 14 : 16, textDecoration: 'none',
310
- display: 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: isMobile ? 4 : 8,
311
- }}
312
- >
313
- {'\u2699\uFE0E'}
314
- </Link>}
315
- {/* Notification bell */}
316
- {!IS_DEMO && !csvMode && <div style={{ position: 'relative', marginLeft: isMobile ? 4 : 8 }}>
263
+ <div data-map style={{ height: '100vh', overflow: 'hidden', background: 'var(--sd-page)', color: '#fff', fontFamily: 'var(--sd-font)' }}>
264
+ <AppHeader
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
+ level={level}
270
+ onOpenNote={openNoteHere}
271
+ openNoteId={linkedNote}
272
+ // Which network you are looking at, and the way back out of it. Loading
273
+ // the sample used to be a one-way door: it lives in sessionStorage, and
274
+ // nothing in the UI cleared it.
275
+ chips={csvMode && (
276
+ <div style={{
277
+ display: 'flex', alignItems: 'center', gap: 6,
278
+ marginLeft: isMobile ? 4 : 10, padding: isMobile ? '4px 8px' : '5px 10px',
279
+ borderRadius: 999, background: 'rgba(255,255,255,0.05)',
280
+ border: '1px solid rgba(255,255,255,0.12)',
281
+ }}>
282
+ <span style={{
283
+ width: 6, height: 6, borderRadius: '50%',
284
+ background: csvSource === 'sample' ? '#9B59B6' : '#2ecc71',
285
+ }} />
286
+ <span style={{ fontSize: isMobile ? 10 : 12, color: 'rgba(255,255,255,0.65)', whiteSpace: 'nowrap' }}>
287
+ {csvSource === 'sample' ? 'Sample network' : 'Your CSV'}
288
+ </span>
317
289
  <button
318
- onClick={() => setShowNotifs(!showNotifs)}
290
+ onClick={() => { clearCsvNetwork(); window.location.href = '/'; }}
291
+ title={csvSource === 'sample' ? 'Leave the sample network' : 'Clear this import'}
319
292
  style={{
320
- width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none', cursor: 'pointer',
321
- background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
322
- display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative',
293
+ border: 'none', background: 'transparent', cursor: 'pointer',
294
+ color: '#888', fontSize: isMobile ? 13 : 15, lineHeight: 1,
295
+ padding: '0 0 0 2px',
323
296
  }}
324
297
  >
325
- 🔔
326
- {notifications.filter(n => !n.seen).length > 0 && (
327
- <span style={{
328
- position: 'absolute', top: -2, right: -2, width: 16, height: 16, borderRadius: '50%',
329
- background: '#ff5050', color: '#fff', fontSize: 9, fontWeight: 700,
330
- display: 'flex', alignItems: 'center', justifyContent: 'center',
331
- }}>
332
- {notifications.filter(n => !n.seen).length}
333
- </span>
334
- )}
298
+ &times;
335
299
  </button>
336
- {showNotifs && (
337
- <div style={{
338
- position: 'absolute', top: 40, right: 0, width: 320, maxHeight: 400,
339
- background: 'rgba(10,10,26,0.98)', border: '1px solid rgba(255,255,255,0.1)',
340
- borderRadius: 12, overflow: 'hidden', zIndex: 100,
341
- backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
342
- boxShadow: '0 8px 32px rgba(0,0,0,0.5)',
343
- }}>
344
- <div style={{ padding: '12px 16px', borderBottom: '1px solid rgba(255,255,255,0.08)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
345
- <span style={{ fontWeight: 700, fontSize: 14 }}>Notifications</span>
346
- {notifications.filter(n => !n.seen).length > 0 && (
347
- <button onClick={() => {
348
- fetch('/api/notifications', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'mark-all-seen' }) });
349
- setNotifications(prev => prev.map(n => ({ ...n, seen: true })));
350
- }} style={{ background: 'none', border: 'none', color: '#3498DB', fontSize: 11, cursor: 'pointer', fontWeight: 600 }}>
351
- Mark all read
352
- </button>
353
- )}
354
- </div>
355
- <div style={{ overflowY: 'auto', maxHeight: 340 }}>
356
- {notifications.length === 0 ? (
357
- <div style={{ padding: 20, textAlign: 'center', color: '#555', fontSize: 12 }}>No notifications yet</div>
358
- ) : notifications.slice(0, 20).map(n => (
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',
373
- background: n.seen ? 'transparent' : 'rgba(255,215,0,0.04)',
374
- }}>
375
- <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
376
- <span style={{ fontSize: 16 }}>{n.icon || '📌'}</span>
377
- <div style={{ flex: 1 }}>
378
- <div style={{ fontSize: 12, fontWeight: 600, color: n.seen ? '#888' : '#fff' }}>{n.title}<span style={{ color: '#556', marginLeft: 6 }}>›</span></div>
379
- {n.message && <div style={{ fontSize: 10, color: '#666', marginTop: 2 }}>{n.message}</div>}
380
- <div style={{ fontSize: 9, color: '#444', marginTop: 3 }}>{new Date(n.created_at).toLocaleDateString()}</div>
381
- </div>
382
- </div>
383
- </div>
384
- ))}
385
- </div>
386
- </div>
387
- )}
388
- </div>}
389
- {/* Refresh button — Check for new, once you say yes; notifies */}
390
- {!IS_DEMO && !csvMode && <RefreshButton isMobile={isMobile} />}
391
- {/* Profile icon — top right */}
392
- <a href={IS_DEMO ? '/launch' : csvMode ? '/import' : '/profile'} style={{
393
- width: isMobile ? 30 : 36, height: isMobile ? 30 : 36, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
394
- background: 'linear-gradient(135deg, #FFD700, #FF6B35)', color: '#000', fontWeight: 800, fontSize: isMobile ? 11 : 14,
395
- textDecoration: 'none', marginLeft: isMobile ? 6 : 12, flexShrink: 0,
396
- }}>
397
- {(() => {
398
- const sCount = degree1.filter(c => c.tier === 'S').length;
399
- const aCount = degree1.filter(c => c.tier === 'A').length;
400
- const bCount = degree1.filter(c => c.tier === 'B').length;
401
- const clusters = degree1.filter(c => bridgeIds.has(c.id)).length;
402
- const d2S = degree2.filter(c => c.tier === 'S').length;
403
- const np = (sCount*100)+(aCount*40)+(bCount*15)+(clusters*200)+(d2S*50)+degree1.length;
404
- return Math.floor(Math.sqrt(np / 10));
405
- })()}
406
- </a>
407
- </div>
408
-
300
+ </div>
301
+ )}
302
+ >
409
303
  {/* Pending count badge — in header: everyone with a request out, once each (lib/requests-client.js) */}
410
304
  {isDegreesMode && !csvMode && pendingCount > 0 && (
411
305
  <Link href="/queue" style={{
@@ -418,13 +312,13 @@ function HomeInner() {
418
312
  {pendingCount} Pending
419
313
  </Link>
420
314
  )}
421
- </header>
315
+ </AppHeader>
422
316
 
423
317
  <div style={{ display: 'flex', height: isMobile ? 'calc(100vh - 70px)' : 'calc(100vh - 130px)', position: 'relative' }}>
424
318
  <FilterPanel
425
319
  collapsed={filterPanelCollapsed}
426
320
  onToggle={() => setFilterPanelCollapsed(!filterPanelCollapsed)}
427
- mode={isDegreesMode ? 'degrees' : 'network'}
321
+ mode={isSeparation ? 'separation' : isDegreesMode ? 'degrees' : 'network'}
428
322
  visualMode={view.key}
429
323
  grid={grid}
430
324
  gridCounts={panelCounts}
@@ -536,7 +430,8 @@ function networkShape(d1, d2) {
536
430
  // Looks at the network again when a scan has changed it: once when any scan
537
431
  // ends, and every 20 seconds while a circle is being scanned with Bridge Chains
538
432
  // open, since the scanner saves every 10 pages and the circle fills in as it
539
- // does. Its own component for the same reason as RefreshButton below.
433
+ // does. Its own component so the scanner's answer, which changes every second or
434
+ // two while a scan runs, re-renders it and not the whole map.
540
435
  function NetworkRefresh({ onChange, live }) {
541
436
  const scan = useScanner();
542
437
  const ended = scan.finished.find(CHANGES_NETWORK)?.startedAt ?? null;
@@ -552,44 +447,3 @@ function NetworkRefresh({ onChange, live }) {
552
447
  return null;
553
448
  }
554
449
 
555
- // Its own component so the scanner's answer, which changes every second or two
556
- // while a scan runs, re-renders this button and not the whole map.
557
- //
558
- // It asks first, as a company scan does, and says what it will do and what it
559
- // costs: one click on a small icon used to open a Chrome window on LinkedIn
560
- // with no word of what it was about to do.
561
- function RefreshButton({ isMobile }) {
562
- const scan = useScanner();
563
- const busy = busyReason(scan);
564
- return (
565
- <button
566
- onClick={async () => {
567
- try {
568
- const blocked = notReadyMessage(await scraperStatus());
569
- if (blocked) { alert(blocked); return; }
570
- const ok = window.confirm(
571
- 'Check for new connections?\n\nThis opens a Chrome window on your LinkedIn connections ' +
572
- 'list and reads it from the newest, stopping once it reaches people already saved. It ' +
573
- 'usually takes under a minute. It reads your own list, not a search, so it doesn’t ' +
574
- 'use your search budget, but like any scan it is LinkedIn traffic from your account.',
575
- );
576
- if (!ok) return;
577
- await beginScrape('refresh');
578
- alert('Checking for new connections — watch it on the Scan page.');
579
- } catch (e) {
580
- alert(e.message);
581
- }
582
- }}
583
- disabled={Boolean(busy)}
584
- title={busy ? `${busy}. One scan at a time.` : 'Check for new connections'}
585
- style={{
586
- width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none',
587
- cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.4 : 1,
588
- background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
589
- display: isMobile ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: 8,
590
- }}
591
- >
592
- ↻
593
- </button>
594
- );
595
- }
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Suspense, useEffect, useState } from 'react';
4
- import AppTabs from '../components/AppTabs';
4
+ import AppHeader from '../components/AppHeader';
5
5
  import { setNotchTabs } from '../../lib/island';
6
6
  import { useRouter, useSearchParams } from 'next/navigation';
7
7
  import { runScrape, scraperStatus, notReadyMessage, busyReason } from '../../lib/scraper-client';
@@ -15,8 +15,8 @@ import useScanner from '../components/useScanner';
15
15
  import Link from 'next/link';
16
16
  import { companyOf, getSeniority } from '../../lib/companies';
17
17
  import { localPhoto } from '../../lib/photos';
18
+ import { TIER_COLORS } from '../../lib/themes';
18
19
 
19
- const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
20
20
  // Map and Companies (Blake, 2026-10-02: companies are worth more than industries,
21
21
  // and an industry's detail opens from a company's card). Whether the map's bubbles
22
22
  // are companies or people is a switch in its Filters panel.
@@ -240,8 +240,8 @@ function PathsInner() {
240
240
  return (
241
241
  <div style={{
242
242
  minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
243
- gap: 12, background: '#0a0a1a', color: 'rgba(255,255,255,0.7)',
244
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
243
+ gap: 12, background: 'var(--sd-page)', color: 'rgba(255,255,255,0.7)',
244
+ fontFamily: 'var(--sd-font)',
245
245
  }}>
246
246
  <h1 style={{ fontSize: 20, fontWeight: 700, margin: 0, color: '#fff' }}>Not part of the demo</h1>
247
247
  <p style={{ fontSize: 13, margin: 0 }}>The public demo includes the Network Circle and Degrees views only.</p>
@@ -251,31 +251,18 @@ function PathsInner() {
251
251
  }
252
252
 
253
253
  if (loading) {
254
- return <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>Loading...</div>;
254
+ return <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--sd-page)', color: '#fff' }}>Loading...</div>;
255
255
  }
256
256
 
257
257
  return (
258
258
  <div style={{
259
- height: '100vh', background: '#0a0a1a', color: '#fff',
260
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
259
+ height: '100vh', background: 'var(--sd-page)', color: '#fff',
260
+ fontFamily: 'var(--sd-font)',
261
261
  display: 'flex', flexDirection: 'column', overflow: 'hidden',
262
262
  }}>
263
263
  {/* The same header as every page: the app's tabs, Paths lit; Map, People,
264
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>
265
+ <AppHeader active="paths" csvMode={localOnly} />
279
266
  {selectedCompany && (
280
267
  <div style={{ padding: '10px 24px', borderBottom: '1px solid rgba(255,255,255,0.08)', flexShrink: 0 }}>
281
268
  <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
@@ -9,6 +9,8 @@ import OnboardingGate from '../components/OnboardingGate';
9
9
  import { useUser } from '../components/UserProvider';
10
10
  import { sectorByKey } from '../../lib/sector-labels';
11
11
  import Link from 'next/link';
12
+ import { networkPower as powerOf, levelOf } from '../../lib/level';
13
+ import { TIER_COLORS } from '../../lib/themes';
12
14
  import NetworkHealthSection from '../components/settings/NetworkHealthSection';
13
15
 
14
16
  const LEVEL_NAMES = {
@@ -74,10 +76,9 @@ function ProfileInner() {
74
76
  d1.forEach(c => { if (c.company) companies[c.company] = (companies[c.company] || 0) + 1; });
75
77
  const topCompanies = Object.entries(companies).sort((a, b) => b[1] - a[1]).slice(0, 10);
76
78
 
77
- // Network Power
78
- const networkPower = (tiers.S * 100) + (tiers.A * 40) + (tiers.B * 15) + (tiers.C * 5) + (tiers.D * 1)
79
- + (clusters * 200) + (catalysts * 150) + (d2Tiers.S * 50) + d1.length;
80
- const level = Math.floor(Math.sqrt(networkPower / 10));
79
+ // Network Power, and the level the header's round button shows (lib/level.js)
80
+ const networkPower = powerOf(d1, d2);
81
+ const level = levelOf(networkPower);
81
82
  const currentLevelPower = level * level * 10;
82
83
  const nextLevelPower = (level + 1) * (level + 1) * 10;
83
84
  const xpProgress = ((networkPower - currentLevelPower) / (nextLevelPower - currentLevelPower)) * 100;
@@ -115,10 +116,10 @@ function ProfileInner() {
115
116
  if (IS_DEMO) {
116
117
  return (
117
118
  <div style={{
118
- minHeight: '100vh', background: '#0a0a1a', color: 'rgba(255,255,255,0.7)',
119
+ minHeight: '100vh', background: 'var(--sd-page)', color: 'rgba(255,255,255,0.7)',
119
120
  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
120
121
  gap: 12, textAlign: 'center', padding: 24,
121
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
122
+ fontFamily: 'var(--sd-font)',
122
123
  }}>
123
124
  <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700, color: '#fff' }}>Not part of the demo</h2>
124
125
  <p style={{ margin: 0, fontSize: 13 }}>The public demo includes the Network Circle and Degrees views only.</p>
@@ -129,13 +130,13 @@ function ProfileInner() {
129
130
 
130
131
  if (loading) {
131
132
  return (
132
- <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
133
+ <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--sd-page)', color: '#fff' }}>
133
134
  Loading profile...
134
135
  </div>
135
136
  );
136
137
  }
137
138
 
138
- const tierColors = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
139
+ const tierColors = TIER_COLORS; // the theme's (lib/themes.js)
139
140
  const s = stats;
140
141
 
141
142
  // Milestones
@@ -154,8 +155,8 @@ function ProfileInner() {
154
155
 
155
156
  return (
156
157
  <div style={{
157
- height: '100vh', background: '#0a0a1a', color: '#fff',
158
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
158
+ height: '100vh', background: 'var(--sd-page)', color: '#fff',
159
+ fontFamily: 'var(--sd-font)',
159
160
  display: 'flex', flexDirection: 'column', overflow: 'hidden',
160
161
  }}>
161
162
  {/* Header */}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Suspense, useEffect, useMemo, useState } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
- import AppTabs from '../components/AppTabs';
5
+ import AppHeader from '../components/AppHeader';
6
6
  import SocialHub from '../social/SocialHub';
7
7
  import { setNotchTabs } from '../../lib/island';
8
8
  import { loadNetwork } from '../../lib/network';
@@ -18,8 +18,8 @@ import { keyFor } from '../../lib/separation';
18
18
  import { localPhoto } from '../../lib/photos';
19
19
  import { reachIndex } from '../../lib/reach';
20
20
  import { loadScanNotes } from '../../lib/scraper-client';
21
+ import { TIER_COLORS } from '../../lib/themes';
21
22
 
22
- const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
23
23
 
24
24
  // Who to reach first: power, +1 when the company their score is built on is a
25
25
  // top one by the model's own scores (TOP_COMPANY: the curated list's major
@@ -159,8 +159,8 @@ function QueueInner() {
159
159
  if (IS_DEMO) {
160
160
  return (
161
161
  <div style={{
162
- minHeight: '100vh', background: '#0a0a1a', color: 'rgba(255,255,255,0.7)',
163
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
162
+ minHeight: '100vh', background: 'var(--sd-page)', color: 'rgba(255,255,255,0.7)',
163
+ fontFamily: 'var(--sd-font)',
164
164
  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 10,
165
165
  textAlign: 'center', padding: 24,
166
166
  }}>
@@ -254,31 +254,18 @@ function QueueInner() {
254
254
  }
255
255
 
256
256
  if (loading) {
257
- return <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>Loading...</div>;
257
+ return <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--sd-page)', color: '#fff' }}>Loading...</div>;
258
258
  }
259
259
 
260
260
  return (
261
261
  <div style={{
262
- height: '100vh', background: '#0a0a1a', color: '#fff',
263
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
262
+ height: '100vh', background: 'var(--sd-page)', color: '#fff',
263
+ fontFamily: 'var(--sd-font)',
264
264
  display: 'flex', flexDirection: 'column', overflow: 'hidden',
265
265
  }}>
266
266
  {/* The same header as every page: the app's tabs, Outlink lit. Circles, To add,
267
267
  Pending and Messages & follow-ups sit in the notch under it. */}
268
- <header style={{ padding: '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)', flexShrink: 0 }}>
269
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
270
- <Link href="/" style={{ textDecoration: 'none' }}>
271
- <h1 style={{ fontSize: 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
272
- Six Degrees
273
- </h1>
274
- </Link>
275
- <AppTabs active="outlink" />
276
- <Link href="/settings" title="Settings" aria-label="Settings" style={{
277
- width: 32, height: 32, borderRadius: '50%', flexShrink: 0, background: 'rgba(255,255,255,0.06)', color: '#888',
278
- fontSize: 16, textDecoration: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center',
279
- }}>⚙</Link>
280
- </div>
281
- </header>
268
+ <AppHeader active="outlink" />
282
269
  {(view === 'recs' || view === 'pending') && (
283
270
  <div style={{ padding: '10px 24px', borderBottom: '1px solid rgba(255,255,255,0.08)', flexShrink: 0 }}>
284
271
  <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
@@ -18,7 +18,7 @@ export default function ScoresMoved() {
18
18
  router.replace(at === '#health' ? '/profile?view=insights' : `/settings${at || '#scoring'}`);
19
19
  }, [router]);
20
20
  return (
21
- <div style={{ minHeight: '100vh', background: '#0a0a1a', color: '#aab', fontFamily: FONT, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>
21
+ <div style={{ minHeight: '100vh', background: 'var(--sd-page)', color: '#aab', fontFamily: FONT, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>
22
22
  Scores are in <Link href="/settings#scoring" style={{ color: '#3498DB', marginLeft: 4 }}>Settings</Link> now.
23
23
  </div>
24
24
  );