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
@@ -8,6 +8,7 @@ import DataSection from '../components/settings/DataSection';
8
8
  import SectorSection from '../components/settings/SectorSection';
9
9
  import TierSection from '../components/settings/TierSection';
10
10
  import TitleSection from '../components/settings/TitleSection';
11
+ import AppearanceSection from '../components/settings/AppearanceSection';
11
12
  import CompanyScores from '../components/CompanyScores';
12
13
  import { Section, Body, Mono, LINE, FONT } from '../components/ui';
13
14
  import { IS_DEMO } from '../../lib/demo';
@@ -28,6 +29,8 @@ export default function SettingsPage() {
28
29
 
29
30
  const KIND_LABEL = {
30
31
  'mac-app': 'the Mac app',
32
+ 'windows-app': 'the Windows app',
33
+ 'linux-app': 'the Linux app',
31
34
  npm: 'the npm package (npx six-degrees)',
32
35
  source: 'a copy built from the source code',
33
36
  git: 'a git checkout of the source code',
@@ -50,7 +53,7 @@ function SettingsInner() {
50
53
  if (IS_DEMO) {
51
54
  return (
52
55
  <div style={{
53
- minHeight: '100vh', background: '#0a0a1a', color: 'rgba(255,255,255,0.7)',
56
+ minHeight: '100vh', background: 'var(--sd-page)', color: 'rgba(255,255,255,0.7)',
54
57
  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
55
58
  gap: 12, textAlign: 'center', padding: 24, fontFamily: FONT,
56
59
  }}>
@@ -64,7 +67,7 @@ function SettingsInner() {
64
67
  const about = info?.about;
65
68
 
66
69
  return (
67
- <div style={{ minHeight: '100vh', background: '#0a0a1a', color: '#fff', fontFamily: FONT }}>
70
+ <div style={{ minHeight: '100vh', background: 'var(--sd-page)', color: '#fff', fontFamily: FONT }}>
68
71
  <header style={{ padding: '16px 24px', borderBottom: LINE, display: 'flex', alignItems: 'center', gap: 16 }}>
69
72
  <Link href="/" style={{
70
73
  display: 'flex', alignItems: 'center', gap: 6, color: '#888', textDecoration: 'none',
@@ -82,6 +85,7 @@ function SettingsInner() {
82
85
  {error && <Body style={{ color: '#ff7676', marginTop: 16 }}>{error}</Body>}
83
86
 
84
87
  <UpdatePanel />
88
+ <AppearanceSection />
85
89
  <Section id="scoring" title="Scores">
86
90
  <Body>
87
91
  How someone&rsquo;s power score is worked out, and the four things you can change about it:{' '}
@@ -108,7 +112,7 @@ function SettingsInner() {
108
112
  <Body>
109
113
  Your network is kept in <Mono>{about.dataDir}</Mono>
110
114
  {about.customDataDir ? ' (a folder you chose).' : '.'}
111
- {about.kind === 'mac-app' && ' Help → Show the Data Folder opens it.'}
115
+ {['mac-app', 'windows-app', 'linux-app'].includes(about.kind) && ' Help → Show the Data Folder opens it.'}
112
116
  </Body>
113
117
  <Body style={{ fontSize: 12, color: '#667', marginTop: 10 }}>
114
118
  Settings are saved with your network, on this computer. Nothing here is sent anywhere.
@@ -9,16 +9,17 @@ import { setupStep, askForField } from '../../lib/scanner-setup';
9
9
  import { IS_DEMO } from '../../lib/demo';
10
10
  import { circleScanCost } from '../../lib/reach';
11
11
  import ScanRadar from '../components/ScanRadar';
12
- import AppTabs from '../components/AppTabs';
12
+ import AppHeader from '../components/AppHeader';
13
13
  import { paceOf } from '../../lib/scan-pace';
14
14
  import { BudgetBox, CooldownBanner, PausedList } from '../components/LinkedInLimits';
15
15
  import FieldStep, { FieldAnswer } from '../components/FieldStep';
16
+ import { TIER_COLORS as THEME_TIERS } from '../../lib/themes';
16
17
 
17
18
  // Everything here runs through /api/scraper. There is deliberately no second
18
19
  // server and no command to copy: the step where people gave up was starting a
19
20
  // Python server in a terminal they had not been told they needed.
20
21
 
21
- const BG = '#0a0a1a';
22
+ const BG = 'var(--sd-page)'; // clear: the theme shows through (lib/themes.js)
22
23
  const LINE = '1px solid rgba(255,255,255,0.1)';
23
24
 
24
25
  const ACTION_LABELS = {
@@ -73,8 +74,6 @@ function SetupInner() {
73
74
  // Scanning with no Chrome window (scripts/scrape.py --headless); lib/scraper-client.js reads it for every scan.
74
75
  const [hideChrome, setHideChrome] = useRemembered(HIDE_CHROME_KEY, false);
75
76
  const [error, setError] = useState(null);
76
- // The step open on the rail: null follows the one you're on.
77
- const [view, setView] = useState(null);
78
77
  const logRef = useRef(null);
79
78
  // What's saved, for the question about your field: undefined while it
80
79
  // loads, null when it couldn't be read. And the answer given here, if any.
@@ -182,7 +181,8 @@ function SetupInner() {
182
181
  const mapped = net.first;
183
182
  // The step you're on: the first not done. Any other opens from the rail.
184
183
  const current = !s || !step1.done ? 1 : !c.signedIn ? 2 : mapped === 0 ? 3 : 4;
185
- const shown = view ?? current;
184
+ // Each step: done, the one you're on (now), or still to come (next).
185
+ const stateOf = (n, done) => (done && n !== current ? 'done' : n === current ? 'now' : n < current ? 'done' : 'next');
186
186
  // Your field, asked once before the first scan (lib/scanner-setup.js
187
187
  // askForField). The steps wait while that isn't known yet (null).
188
188
  const askField = IS_DEMO || field ? false : askForField(s, settings);
@@ -199,23 +199,10 @@ function SetupInner() {
199
199
  return (
200
200
  <div style={{
201
201
  minHeight: '100vh', background: BG, color: '#fff',
202
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
202
+ fontFamily: 'var(--sd-font)',
203
203
  }}>
204
204
  {/* The same header as every page, Scan lit (Blake, 2026-10-02: continuity) */}
205
- <header style={{ padding: '20px 30px', borderBottom: LINE }}>
206
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
207
- <Link href="/" style={{ textDecoration: 'none' }}>
208
- <span style={{ fontSize: 28, fontWeight: 700, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
209
- Six Degrees
210
- </span>
211
- </Link>
212
- <AppTabs active="scan" />
213
- <Link href="/settings" title="Settings" aria-label="Settings" style={{
214
- width: 32, height: 32, borderRadius: '50%', flexShrink: 0, background: 'rgba(255,255,255,0.06)', color: '#888',
215
- fontSize: 16, textDecoration: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center',
216
- }}>⚙</Link>
217
- </div>
218
- </header>
205
+ <AppHeader active="scan" brand="span" />
219
206
 
220
207
  <div style={{ maxWidth: 720, margin: '0 auto', padding: '32px 24px 64px' }}>
221
208
 
@@ -341,50 +328,49 @@ function SetupInner() {
341
328
  <Box tone={step1.done ? undefined : 'bad'}>{step1.note}</Box>
342
329
  )}
343
330
 
344
- {/* ---- the journey (Blake, 2026-10-02: "simple and easy to understand … like they are
345
- launching an agent"): four steps on a rail, the one you're on open, one button to
346
- launch it. Everything else waits in Fine-tune. ---- */}
347
- <Journey current={current} view={shown} onView={setView} steps={[
348
- { key: 1, label: 'Get ready', done: step1.done },
349
- { key: 2, label: 'Sign in', done: !!c.signedIn },
350
- { key: 3, label: 'Who you know', done: mapped > 0 },
351
- { key: 4, label: 'Who they know', done: net.second > 0 },
352
- ]} />
353
-
354
- {shown === 1 && (
355
- <Mission step={1} title="Get your scanner ready" time="A minute or two, once" body={s ? step1.text : 'Checking…'}
331
+ {/* ---- the steps (Blake, 2026-10-02: "simple and easy to understand … like they are
332
+ launching an agent"; then "the label and them to be displayed … the dots vertically
333
+ displayed with the line following"): every step shown at once, down a line that turns
334
+ green as each is done, the one you're on lit, one button each. Everything else waits
335
+ in Fine-tune. ---- */}
336
+ <div role="list" aria-label="Scanning, step by step" style={{ margin: '4px 0 8px' }}>
337
+ <style>{JOURNEY_CSS}</style>
338
+ <StepRow n={1} label="Get ready" state={stateOf(1, step1.done)}>
339
+ <Mission title="Get your scanner ready" time="A minute or two, once" done={step1.done} body={s ? step1.text : 'Checking…'}
356
340
  launch={s && step1.button && (
357
341
  <Launch onClick={() => run(step1.button.action)} disabled={busy || running}>{step1.button.label}</Launch>
358
342
  )} />
359
- )}
360
- {shown === 2 && (
361
- <Mission step={2} title="Sign in to LinkedIn, once" time="You do this part"
343
+ </StepRow>
344
+ <StepRow n={2} label="Sign in" state={stateOf(2, !!c.signedIn)}>
345
+ <Mission title="Sign in to LinkedIn, once" time="You do this part" done={!!c.signedIn}
362
346
  body={c.signedIn
363
347
  ? 'Signed in on this Mac. If LinkedIn ever asks for a security check, or a scan says you were signed out, open LinkedIn here and finish it by hand.'
364
348
  : 'A Chrome window opens. Sign in with your email and password there: the scanner never sees them. (“Continue with Google” can’t work in it, because Google blocks its sign-in in automated browsers.)'}
365
349
  launch={c.dependencies && (
366
350
  // Shown after sign-in too: a security check survives the session cookie (TRAPS §35).
367
- <Launch onClick={() => run('login')} disabled={busy || running} quiet={!!c.signedIn}>
351
+ <Launch onClick={() => run('login')} disabled={busy || running || current < 2} quiet={!!c.signedIn}>
368
352
  {running && s.action === 'login' ? 'Waiting for you…' : c.signedIn ? 'Open LinkedIn again' : 'Open LinkedIn'}
369
353
  </Launch>
370
354
  )} />
371
- )}
372
- {shown === 3 && (
373
- <Mission step={3} title="Map the people you know" time="About a minute and a half for 750 people"
374
- body="It reads your connections list and saves everyone here. After the first time, Check for new only looks at who you've added since."
355
+ </StepRow>
356
+ <StepRow n={3} label="Who you know" state={stateOf(3, mapped > 0)}>
357
+ <Mission title="Map the people you know" time="About a minute and a half for 750 people" done={mapped > 0}
358
+ body={mapped > 0
359
+ ? `${mapped.toLocaleString()} connections saved. Check for new picks up anyone you've added since; Scan it all again reads the whole list.`
360
+ : 'It reads your connections list and saves everyone here. After the first time, Check for new only looks at who you\'ve added since.'}
375
361
  launch={(
376
- <Launch onClick={() => run('full')} disabled={!canSearch} quiet={mapped > 0}>
362
+ <Launch onClick={() => run('full')} disabled={!canSearch || current < 3} quiet={mapped > 0}>
377
363
  {running && s.action === 'full' ? 'Scanning…' : mapped > 0 ? 'Scan it all again' : 'Scan my network'}
378
364
  </Launch>
379
365
  )}
380
- more={(
381
- <Btn onClick={() => run('refresh')} disabled={!canSearch} primary={mapped > 0}>
382
- {running && s.action === 'refresh' ? 'Checking…' : 'Check for new'}
366
+ more={mapped > 0 && (
367
+ <Btn onClick={() => run('refresh')} disabled={!canSearch || current < 3} primary>
368
+ {running && s.action === 'refresh' ? 'Checking…' : '↻ Check for new'}
383
369
  </Btn>
384
370
  )} />
385
- )}
386
- {shown === 4 && (
387
- <Mission step={4} title="Map who they know" time="A small batch a day, slowly"
371
+ </StepRow>
372
+ <StepRow n={4} label="Who they know" state={stateOf(4, false)} last>
373
+ <Mission title="Map who they know" time="A small batch a day, slowly"
388
374
  body={<>It opens your connections one at a time and reads who <i>they</i> know: that fills Degrees, Separation and Outlink. Every round picks up where the last one stopped.</>}>
389
375
  <CooldownBanner
390
376
  cooldown={li?.cooldown}
@@ -452,7 +438,8 @@ function SetupInner() {
452
438
  </div>
453
439
  </div>
454
440
  </Mission>
455
- )}
441
+ </StepRow>
442
+ </div>
456
443
 
457
444
  {/* ---- the scanner at work: what it's doing, and Stop, right under the step ---- */}
458
445
  {running && (
@@ -535,7 +522,7 @@ function SetupInner() {
535
522
  <Btn onClick={() => run('auto-bridge-retry', { maxBridges: batch, tiers: order === 'score' ? tiers : [], order, maxPages: pages, deeper: finish })} disabled={!canSearch || (order === 'score' && !tiers.length)}>
536
523
  Retry hidden ones
537
524
  </Btn>
538
- <span style={{ fontSize: 12, color: '#8b9a9a', marginLeft: 10 }}>People whose list was hidden last time, in case they've opened it.</span>
525
+ <span style={{ fontSize: 12, color: '#8b9a9a', marginLeft: 10 }}>People whose list was hidden last time, in case they&rsquo;ve opened it.</span>
539
526
  </div>
540
527
  <PausedList
541
528
  paused={s?.paused || []}
@@ -658,7 +645,7 @@ function ScanOne({ pick, pages, setPages, li, running, s, canSearch, busy, onSta
658
645
  );
659
646
  }
660
647
 
661
- const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
648
+ const TIER = THEME_TIERS; // the theme's dot colours (lib/themes.js)
662
649
 
663
650
  // A setting this browser remembers between visits: the order and depth someone
664
651
  // picked should not reset every time the page opens. localStorage can be missing
@@ -693,54 +680,62 @@ const JOURNEY_CSS = `
693
680
  @media (prefers-reduced-motion: reduce) { .journey-now { animation: none; } }
694
681
  `;
695
682
 
696
- /** The four steps on a rail: done ones ticked, the one you're on pulsing, any of them opens on a tap. */
697
- function Journey({ steps, current, view, onView }) {
683
+ /**
684
+ * One step down the line: its dot (a tick once done, pulsing while it's the one
685
+ * you're on), the line on to the next step (green once this one is done), its
686
+ * label, and what it does.
687
+ */
688
+ function StepRow({ n, label, state, last = false, children }) {
689
+ const done = state === 'done';
690
+ const now = state === 'now';
698
691
  return (
699
- <div role="tablist" aria-label="Scanning, step by step" style={{ display: 'flex', alignItems: 'flex-start', margin: '4px 0 14px' }}>
700
- <style>{JOURNEY_CSS}</style>
701
- {steps.map((st, i) => {
702
- const now = st.key === current && !st.done;
703
- const open = st.key === view;
704
- const color = st.done ? '#00ff88' : now ? '#fff' : '#667';
705
- return (
706
- <div key={st.key} style={{ flex: 1, display: 'flex', alignItems: 'flex-start', minWidth: 0 }}>
707
- <button type="button" role="tab" aria-selected={open} onClick={() => onView(st.key === current ? null : st.key)}
708
- style={{ flex: '0 0 auto', width: 76, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>
709
- <span className={now ? 'journey-now' : undefined} style={{
710
- width: 38, height: 38, borderRadius: '50%', display: 'grid', placeItems: 'center', fontSize: 15, fontWeight: 800,
711
- background: st.done ? 'rgba(0,255,136,0.16)' : now ? 'linear-gradient(135deg, #00ff88, #1abc9c)' : 'rgba(255,255,255,0.05)',
712
- color: st.done ? '#00ff88' : now ? '#04140c' : '#778',
713
- border: `2px solid ${open ? '#fff' : st.done ? 'rgba(0,255,136,0.55)' : now ? 'transparent' : 'rgba(255,255,255,0.12)'}`,
714
- }}>{st.done ? '✓' : st.key}</span>
715
- {/* Wraps to two lines rather than running into the next on a phone */}
716
- <span style={{ fontSize: 12, fontWeight: open ? 800 : 650, color, textAlign: 'center', lineHeight: 1.25, maxWidth: 76, overflowWrap: 'break-word' }}>{st.label}</span>
717
- </button>
718
- {i < steps.length - 1 && (
719
- <span aria-hidden="true" style={{
720
- flex: 1, height: 2, marginTop: 19, borderRadius: 1, minWidth: 8,
721
- background: st.done ? 'linear-gradient(90deg, rgba(0,255,136,0.7), rgba(0,255,136,0.25))' : 'rgba(255,255,255,0.1)',
722
- }} />
723
- )}
724
- </div>
725
- );
726
- })}
692
+ <div role="listitem" aria-label={`Step ${n}, ${label}: ${done ? 'done' : now ? 'the one to do now' : 'still to come'}`}
693
+ style={{ display: 'grid', gridTemplateColumns: '40px 1fr', columnGap: 14 }}>
694
+ <div style={{ position: 'relative', display: 'flex', justifyContent: 'center' }}>
695
+ <span className={now ? 'journey-now' : undefined} aria-hidden="true" style={{
696
+ position: 'relative', zIndex: 1, width: 38, height: 38, borderRadius: '50%', display: 'grid', placeItems: 'center',
697
+ fontSize: 15, fontWeight: 800,
698
+ background: done ? 'rgba(0,255,136,0.16)' : now ? 'linear-gradient(135deg, #00ff88, #1abc9c)' : '#12142a',
699
+ color: done ? '#00ff88' : now ? '#04140c' : '#778',
700
+ border: `2px solid ${done ? 'rgba(0,255,136,0.55)' : now ? 'transparent' : 'rgba(255,255,255,0.14)'}`,
701
+ }}>{done ? '✓' : n}</span>
702
+ {!last && (
703
+ <span aria-hidden="true" style={{
704
+ position: 'absolute', top: 40, bottom: 0, left: '50%', width: 2, marginLeft: -1, borderRadius: 1,
705
+ background: done ? 'linear-gradient(rgba(0,255,136,0.75), rgba(0,255,136,0.3))' : 'rgba(255,255,255,0.1)',
706
+ }} />
707
+ )}
708
+ </div>
709
+ <div style={{ minWidth: 0, paddingBottom: last ? 0 : 16, opacity: state === 'next' ? 0.75 : 1 }}>
710
+ <div style={{ display: 'flex', alignItems: 'center', gap: 10, minHeight: 38, marginBottom: 6 }}>
711
+ <span style={{ fontSize: 12, fontWeight: 800, letterSpacing: 1.2, textTransform: 'uppercase', color: done ? '#00ff88' : now ? '#fff' : '#889' }}>
712
+ Step {n} · {label}
713
+ </span>
714
+ <span style={{
715
+ fontSize: 10.5, fontWeight: 800, padding: '2px 8px', borderRadius: 10,
716
+ color: done ? '#00ff88' : now ? '#04140c' : '#889',
717
+ background: done ? 'rgba(0,255,136,0.12)' : now ? '#00ff88' : 'rgba(255,255,255,0.06)',
718
+ }}>{done ? 'Done' : now ? 'Now' : 'Next'}</span>
719
+ </div>
720
+ {children}
721
+ </div>
727
722
  </div>
728
723
  );
729
724
  }
730
725
 
731
- /** The step that's open: what it does, how long, and the one button that does it. */
732
- function Mission({ step, title, time, body, launch, more, children }) {
726
+ /** A step's card: what it does, how long, and the one button that does it. Once done, a quieter card. */
727
+ function Mission({ title, time, body, launch, more, done = false, children }) {
733
728
  return (
734
729
  <div style={{
735
- padding: '20px 22px', borderRadius: 14, marginBottom: 8,
736
- background: 'rgba(255,255,255,0.035)', border: '1px solid rgba(255,255,255,0.1)',
730
+ padding: done ? '14px 18px' : '18px 20px', borderRadius: 14,
731
+ background: done ? 'rgba(0,255,136,0.03)' : 'rgba(255,255,255,0.035)',
732
+ border: `1px solid ${done ? 'rgba(0,255,136,0.18)' : 'rgba(255,255,255,0.1)'}`,
737
733
  }}>
738
734
  <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
739
- <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: 1.2, color: '#778' }}>STEP {step}</span>
740
- <span style={{ fontSize: 19, fontWeight: 800 }}>{title}</span>
741
- {time && <span style={{ fontSize: 12, color: '#8fd9b6', marginLeft: 'auto' }}>⏱ {time}</span>}
735
+ <span style={{ fontSize: done ? 16 : 19, fontWeight: 800 }}>{title}</span>
736
+ {time && !done && <span style={{ fontSize: 12, color: '#8fd9b6', marginLeft: 'auto' }}>⏱ {time}</span>}
742
737
  </div>
743
- <div style={{ fontSize: 14, color: '#9fb0bb', lineHeight: 1.6, margin: '8px 0 14px', maxWidth: 600 }}>{body}</div>
738
+ <div style={{ fontSize: done ? 13 : 14, color: '#9fb0bb', lineHeight: 1.6, margin: done ? '6px 0 10px' : '8px 0 14px', maxWidth: 600 }}>{body}</div>
744
739
  {children}
745
740
  {(launch || more) && <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>{launch}{more}</div>}
746
741
  </div>
@@ -19,8 +19,9 @@ import {
19
19
  import { keyFor } from '../../lib/separation';
20
20
  import { Body, LINE } from '../components/ui';
21
21
  import Thread from './Thread';
22
+ import { TIER_COLORS as THEME_TIERS } from '../../lib/themes';
22
23
 
23
- const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
24
+ const TIER = THEME_TIERS; // the theme's dot colours (lib/themes.js)
24
25
  const STAGE_COLOUR = { new: '#8b9a9a', contacted: '#3498DB', replied: '#00d4aa', meeting: '#ff9f43', won: '#FFD700', 'not-now': '#667' };
25
26
  const LIST_PAGE = 100;
26
27
  const DAY = 86400000;
@@ -237,13 +237,13 @@ export default function SocialHub({ embedded = false }) {
237
237
  return counts;
238
238
  }, [net, merged, asOf]);
239
239
 
240
- if (IS_DEMO) return <div style={{ minHeight: '100vh', background: '#0a0a1a', color: '#aaa', padding: 40, fontFamily: FONT }}>Not part of the demo.</div>;
240
+ if (IS_DEMO) return <div style={{ minHeight: '100vh', background: 'var(--sd-page)', color: '#aaa', padding: 40, fontFamily: FONT }}>Not part of the demo.</div>;
241
241
  const running = job?.running;
242
242
  const box = { padding: '12px 14px', borderRadius: 8, border: LINE, background: 'rgba(255,255,255,0.03)', marginTop: 10 };
243
243
  const h2 = { fontSize: 16, margin: '24px 0 6px' };
244
244
 
245
245
  return (
246
- <div style={embedded ? { color: '#fff', fontFamily: FONT } : { minHeight: '100vh', background: '#0a0a1a', color: '#fff', fontFamily: FONT }}>
246
+ <div style={embedded ? { color: '#fff', fontFamily: FONT } : { minHeight: '100vh', background: 'var(--sd-page)', color: '#fff', fontFamily: FONT }}>
247
247
  {!embedded && (
248
248
  <header style={{ padding: '16px 24px', borderBottom: LINE, display: 'flex', alignItems: 'center', gap: 16 }}>
249
249
  <Link href="/" style={{ color: '#888', textDecoration: 'none', fontSize: 13, fontWeight: 600, padding: '6px 14px', borderRadius: 6, background: 'rgba(255,255,255,0.06)', border: LINE }}>← Back to Map</Link>
@@ -12,7 +12,7 @@ export default function SocialMoved() {
12
12
  const router = useRouter();
13
13
  useEffect(() => { router.replace('/queue?view=messages'); }, [router]);
14
14
  return (
15
- <div style={{ minHeight: '100vh', background: '#0a0a1a', color: '#aab', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>
15
+ <div style={{ minHeight: '100vh', background: 'var(--sd-page)', color: '#aab', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>
16
16
  Social is part of <Link href="/queue?view=messages" style={{ color: '#FF6B35', marginLeft: 4 }}>Outlink</Link> now.
17
17
  </div>
18
18
  );
@@ -869,6 +869,7 @@ export function restartAdvice({ kind, code, dataDir = null }) {
869
869
  return { canRestart: true, how: 'Click Restart now to finish it: Six Degrees restarts, which takes a few seconds, and finishes the import as it starts.' };
870
870
  }
871
871
  if (kind === 'mac-app') return { canRestart: false, how: `Quit Six Degrees (⌘Q) and open it again. ${finish}` };
872
+ if (kind === 'windows-app' || kind === 'linux-app') return { canRestart: false, how: `Quit Six Degrees (File → Exit) and open it again. ${finish}` };
872
873
  if (kind === 'npm') {
873
874
  const flag = dataDir ? ` --data-dir '${String(dataDir).replace(/'/g, `'\\''`)}'` : '';
874
875
  return {
@@ -4,6 +4,7 @@
4
4
  // like any other download.
5
5
 
6
6
  import { clockNow, setClock, play, pause } from './galaxy-lab.js';
7
+ import { MAP_LOOK } from './themes.js';
7
8
 
8
9
  let current = null; // { svg, stamp: () => text or null, legend: () => [[label, colour]] }
9
10
 
@@ -41,7 +42,7 @@ function svgImage(svg) {
41
42
  // One frame: the Galaxy, its date while a replay runs, the legend, and a small credit.
42
43
  async function drawFrame(ctx, w, h) {
43
44
  const img = await svgImage(current.svg);
44
- ctx.fillStyle = '#0a0a1a';
45
+ ctx.fillStyle = MAP_LOOK.bg; // the theme's colour (lib/themes.js)
45
46
  ctx.fillRect(0, 0, w, h);
46
47
  ctx.drawImage(img, 0, 0, w, h);
47
48
  const scale = w / current.svg.clientWidth;
@@ -23,7 +23,8 @@ export const LAB_DEFAULTS = Object.freeze({
23
23
  distance: 1, // how far out those people sit
24
24
  dotSize: 1,
25
25
  lines: 1,
26
- sizeBy: 'power', // 'power' (power score) or 'reach' (how many hang off them)
26
+ sizeBy: 'power', // 'power' (power score), 'links' (how many lines meet at them, as Obsidian sizes a note) or 'reach' (how many hang off them)
27
+ orbit: 0, // the whole map turning round you, in tenths of a radian a second; 0 holds still
27
28
  names: 'key', // 'key' (you, S tier, catalysts) or 'all' (all your connections)
28
29
  branch: false, // hovering a dot lights up its branch and dims the rest: off to start (Blake, 2026-10-02: it glitched)
29
30
  colourBy: 'tier', // 'tier', 'heat' (power as a thermal map), 'degree', 'company' or 'warmth' (from the Social tab)
@@ -34,10 +35,24 @@ export const LAB_DEFAULTS = Object.freeze({
34
35
  });
35
36
 
36
37
  /** The slider settings a saved layout keeps (not whether the lab is on, nor the names switch). */
37
- const LAYOUT_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance', 'dotSize', 'lines', 'sizeBy', 'names', 'branch', 'colourBy', 'speed', 'loop'];
38
+ const LAYOUT_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance', 'orbit', 'dotSize', 'lines', 'sizeBy', 'names', 'branch', 'colourBy', 'speed', 'loop'];
38
39
 
39
- /** Let the layout find its own shape: no rings, each connection's circle bunched round them. */
40
- export const CLUSTERS = Object.freeze({ gravity: 0.25, rings: 0, push: 25, pull: 2.5, distance: 0.35, sizeBy: 'reach' });
40
+ /** The forces a layout sets, and the ones the Galaxy re-settles for when they change. */
41
+ export const FORCE_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance'];
42
+
43
+ /**
44
+ * Clusters, the way Obsidian draws a vault (Blake, 2026-10-03: "like obsidian
45
+ * where it showing the nodes of the clusters all connecting back but retaining
46
+ * a circular look but the nodes need to be a big thing"): no rings, a center
47
+ * force strong enough to keep the whole network a round disc, each connection's
48
+ * circle held round them by its links, everyone sized by how many lines meet at
49
+ * them, so a connection with a scanned circle is a big hub. Someone in several
50
+ * circles is linked to each, so they sit between those hubs and tie them together.
51
+ */
52
+ export const CLUSTERS = Object.freeze({ gravity: 0.8, rings: 0, push: 6, pull: 6, distance: 0.15, sizeBy: 'links' });
53
+
54
+ /** What picking a layout does to the look, without being part of what makes it that layout. */
55
+ export const LAYOUT_LOOKS = Object.freeze({ rings: { lines: 1 }, orbit: { lines: 1 }, clusters: { lines: 3 } });
41
56
 
42
57
  /**
43
58
  * Orbit, the old view, as a layout of the Galaxy (Blake, 2026-10-02: "add that as a
@@ -50,7 +65,7 @@ export const ORBIT = Object.freeze({ gravity: 0, rings: 3, push: 6, pull: 4, dis
50
65
  /** Which layout the sliders are set to: 'rings', 'clusters', 'orbit', or null for your own. */
51
66
  export function layoutOf(lab) {
52
67
  const is = (preset) => Object.entries(preset).every(([k, v]) => lab[k] === v);
53
- if (['gravity', 'rings', 'push', 'pull', 'distance'].every((k) => lab[k] === LAB_DEFAULTS[k])) return 'rings';
68
+ if (FORCE_KEYS.every((k) => lab[k] === LAB_DEFAULTS[k])) return 'rings';
54
69
  if (is(CLUSTERS)) return 'clusters';
55
70
  if (is(ORBIT)) return 'orbit';
56
71
  return null;
@@ -123,10 +138,11 @@ export function forgetLayout(name) { writeLayouts(layoutsNow().filter((l) => l.n
123
138
  // `max` are the first and last dates in the network on screen, set by the Galaxy
124
139
  // (both null when it has no dates, as in the sample network).
125
140
  // The same store holds the lab's other passing state: what Find is looking
126
- // for (and a count that asks the Galaxy to fly to the first match), and the
127
- // Social tab's data once it's loaded.
141
+ // for (and a count that asks the Galaxy to fly to the first match), a count
142
+ // that asks it to fit everyone on screen once it settles, and the Social tab's
143
+ // data once it's loaded.
128
144
  const clockListeners = new Set();
129
- let clock = { at: null, playing: false, min: null, max: null, of: 0, find: '', fly: 0, found: 0, social: undefined };
145
+ let clock = { at: null, playing: false, min: null, max: null, of: 0, find: '', fly: 0, found: 0, fit: 0, social: undefined };
130
146
  let raf = null;
131
147
 
132
148
  export const clockNow = () => clock;
@@ -0,0 +1,51 @@
1
+ // Your network's level: the number in the round button at the top right, and
2
+ // the Profile page's Network Power. One formula for both: the header used to
3
+ // leave out C and D tiers and catalysts, so it showed a lower level than the
4
+ // Profile page it opens.
5
+
6
+ /** Network Power, as the Profile page explains it. */
7
+ export function networkPower(degree1 = [], degree2 = []) {
8
+ const tiers = { S: 0, A: 0, B: 0, C: 0, D: 0 };
9
+ for (const c of degree1) tiers[c.tier] = (tiers[c.tier] || 0) + 1;
10
+ let d2S = 0;
11
+ for (const c of degree2) if (c.tier === 'S') d2S += 1;
12
+ const clusters = new Set(degree2.map((c) => c.source_connection_id).filter(Boolean)).size;
13
+ const catalysts = degree1.filter((c) => c.is_catalyst).length;
14
+ return tiers.S * 100 + tiers.A * 40 + tiers.B * 15 + tiers.C * 5 + tiers.D * 1
15
+ + clusters * 200 + catalysts * 150 + d2S * 50 + degree1.length;
16
+ }
17
+
18
+ /** The level a Network Power reaches: level n takes 10·n² power. */
19
+ export const levelOf = (power) => Math.floor(Math.sqrt(Math.max(0, power) / 10));
20
+
21
+ export const networkLevel = (degree1, degree2) => levelOf(networkPower(degree1, degree2));
22
+
23
+ // Remembered for this tab, so a page that doesn't load the network (Scan, say)
24
+ // can show the same number without loading it. One per network: your own, the
25
+ // sample, or a CSV. A small store, for useSyncExternalStore.
26
+ const KEY = 'six-degrees-level';
27
+ const known = {};
28
+ const listeners = new Set();
29
+
30
+ export function watchLevel(f) {
31
+ listeners.add(f);
32
+ return () => listeners.delete(f);
33
+ }
34
+
35
+ export function levelNow(source = 'own') {
36
+ if (!(source in known)) {
37
+ try {
38
+ const raw = sessionStorage.getItem(`${KEY}:${source}`);
39
+ const n = raw == null ? NaN : Number(raw);
40
+ known[source] = Number.isFinite(n) ? n : null;
41
+ } catch { known[source] = null; }
42
+ }
43
+ return known[source];
44
+ }
45
+
46
+ export function rememberLevel(level, source = 'own') {
47
+ if (levelNow(source) === level) return;
48
+ known[source] = level;
49
+ try { sessionStorage.setItem(`${KEY}:${source}`, String(level)); } catch { /* private window: it's loaded again instead */ }
50
+ listeners.forEach((f) => f());
51
+ }
@@ -26,20 +26,26 @@ export function compareVersions(a, b) {
26
26
  return 0;
27
27
  }
28
28
 
29
+ /** The desktop apps that update by running the new installer, not by replacing themselves (DESKTOP.md D3). */
30
+ export const INSTALLER_KINDS = Object.freeze(['windows-app', 'linux-app']);
31
+
29
32
  /**
30
- * 'mac-app', 'source' (a git checkout run with `npm run start:packaged`) or
31
- * 'npm'. The launchers say which (SIX_DEGREES_INSTALL); an older Mac app that
32
- * predates that is recognised by where it is running from.
33
+ * 'mac-app', 'windows-app', 'linux-app', 'source' (a git checkout run with
34
+ * `npm run start:packaged`) or 'npm'. The launchers say which
35
+ * (SIX_DEGREES_INSTALL); an older Mac app that predates that is recognised by
36
+ * where it is running from, on a Mac only.
33
37
  */
34
- export function installKind(env = process.env, root = '') {
35
- if (['mac-app', 'source', 'npm'].includes(env.SIX_DEGREES_INSTALL)) {
38
+ export function installKind(env = process.env, root = '', platform = process.platform) {
39
+ if (['mac-app', 'source', 'npm', ...INSTALLER_KINDS].includes(env.SIX_DEGREES_INSTALL)) {
36
40
  return env.SIX_DEGREES_INSTALL;
37
41
  }
38
- return /\.app\/Contents\/Resources/.test(root || '') ? 'mac-app' : 'npm';
42
+ return platform === 'darwin' && /\.app\/Contents\/Resources/.test(root || '') ? 'mac-app' : 'npm';
39
43
  }
40
44
 
41
- /** What to run to update, for each way of installing. */
45
+ /** What to run to update, for each way of installing (null: download the installer instead). */
42
46
  export function updateCommand(kind, slug, { dataDir } = {}) {
47
+ // Windows and Linux: no Terminal line; the release page's installer replaces the app.
48
+ if (INSTALLER_KINDS.includes(kind)) return null;
43
49
  if (kind === 'mac-app') {
44
50
  return `curl -fsSL https://raw.githubusercontent.com/${slug}/main/install.sh | bash`;
45
51
  }