six-degrees 0.4.0 → 0.4.2

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 (209) 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/page.js.nft.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
  7. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  9. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +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.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/ingest/route.js +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/unlock/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/favicon.ico.body +0 -0
  47. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/import.html +1 -1
  49. package/.next/standalone/.next/server/app/import.rsc +2 -2
  50. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  52. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +1 -1
  53. package/.next/standalone/.next/server/app/index.html +1 -1
  54. package/.next/standalone/.next/server/app/index.rsc +3 -3
  55. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  56. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  57. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  58. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/launch.html +2 -2
  60. package/.next/standalone/.next/server/app/launch.rsc +2 -2
  61. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  63. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +1 -1
  64. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/paths.html +1 -1
  69. package/.next/standalone/.next/server/app/paths.rsc +3 -3
  70. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +3 -3
  71. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  72. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +2 -2
  73. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/profile.html +1 -1
  76. package/.next/standalone/.next/server/app/profile.rsc +3 -3
  77. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +3 -3
  78. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  79. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +2 -2
  80. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/app/queue.html +1 -1
  83. package/.next/standalone/.next/server/app/queue.rsc +3 -3
  84. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +3 -3
  85. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  86. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +2 -2
  87. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/scores.html +1 -1
  89. package/.next/standalone/.next/server/app/scores.rsc +2 -2
  90. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +2 -2
  91. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
  92. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +1 -1
  93. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/settings.html +1 -1
  95. package/.next/standalone/.next/server/app/settings.rsc +2 -2
  96. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  97. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  98. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
  99. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/setup.html +1 -1
  102. package/.next/standalone/.next/server/app/setup.rsc +3 -3
  103. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +3 -3
  104. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  105. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +2 -2
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__03ydxxh._.js → [root-of-the-server]__20tmem7._.js} +1 -1
  108. package/.next/standalone/.next/server/chunks/_1_fynia._.js +6 -6
  109. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/_1mxn_lb._.js +1 -1
  112. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +21 -7
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00d8snt._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00g61b8._.js +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08lf3gy._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hg5gib._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ho-u0w._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ze473p._.js +1 -1
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1cnxxc9._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ll97ry._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m0255u._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1w9z0zv._.js +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
  128. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +6 -2
  130. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  131. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  132. package/.next/standalone/.next/server/pages/404.html +1 -1
  133. package/.next/standalone/.next/server/pages/500.html +1 -1
  134. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  135. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  136. package/.next/standalone/.next/static/chunks/{3e8zg6ctvvb8p.js → 049vavjsmqgmn.js} +3 -3
  137. package/.next/standalone/.next/static/chunks/0ljxa-wk_iu__.js +18 -0
  138. package/.next/standalone/.next/static/chunks/0mp9b72fm-jp6.js +6 -0
  139. package/.next/standalone/.next/static/chunks/1pre1cotntk6-.js +1 -0
  140. package/.next/standalone/.next/static/chunks/{18xu0fbgo_qz-.js → 22x2_r768-u5i.js} +1 -1
  141. package/.next/standalone/.next/static/chunks/2p5yt1o9tnxjn.js +2 -0
  142. package/.next/standalone/.next/static/chunks/3zs_6-nn0dapb.js +1 -0
  143. package/.next/standalone/.next/static/media/favicon.0zdzw5kurn8m-.ico +0 -0
  144. package/.next/standalone/CHANGELOG.md +86 -0
  145. package/.next/standalone/app/api/admin-delete/route.js +3 -0
  146. package/.next/standalone/app/api/ingest/route.js +13 -0
  147. package/.next/standalone/app/api/scraper/route.js +10 -2
  148. package/.next/standalone/app/components/ChainView.js +76 -15
  149. package/.next/standalone/app/components/FilterPanel.js +37 -1
  150. package/.next/standalone/app/components/ForceGraph.js +144 -12
  151. package/.next/standalone/app/components/LinkedInLimits.js +20 -3
  152. package/.next/standalone/app/components/OrbitGraph.js +92 -30
  153. package/.next/standalone/app/components/Sidebar.js +37 -0
  154. package/.next/standalone/app/components/views.js +7 -5
  155. package/.next/standalone/app/favicon.ico +0 -0
  156. package/.next/standalone/app/page.js +24 -7
  157. package/.next/standalone/app/paths/page.js +8 -1
  158. package/.next/standalone/db/schema.js +14 -0
  159. package/.next/standalone/lib/brokerage.js +94 -0
  160. package/.next/standalone/lib/chain-layout.js +89 -0
  161. package/.next/standalone/lib/circle.js +23 -4
  162. package/.next/standalone/lib/degrees.js +30 -0
  163. package/.next/standalone/lib/dot-rings.js +21 -0
  164. package/.next/standalone/lib/ingest.js +3 -3
  165. package/.next/standalone/lib/linkedin-limits.js +9 -4
  166. package/.next/standalone/lib/reach.js +22 -1
  167. package/.next/standalone/lib/search-risk.js +38 -0
  168. package/.next/standalone/lib/ties.js +23 -0
  169. package/.next/standalone/package-lock.json +2 -2
  170. package/.next/standalone/package.json +1 -1
  171. package/.next/standalone/scripts/scrape.py +50 -12
  172. package/.next/static/chunks/{3e8zg6ctvvb8p.js → 049vavjsmqgmn.js} +3 -3
  173. package/.next/static/chunks/0ljxa-wk_iu__.js +18 -0
  174. package/.next/static/chunks/0mp9b72fm-jp6.js +6 -0
  175. package/.next/static/chunks/1pre1cotntk6-.js +1 -0
  176. package/.next/static/chunks/{18xu0fbgo_qz-.js → 22x2_r768-u5i.js} +1 -1
  177. package/.next/static/chunks/2p5yt1o9tnxjn.js +2 -0
  178. package/.next/static/chunks/3zs_6-nn0dapb.js +1 -0
  179. package/.next/static/media/favicon.0zdzw5kurn8m-.ico +0 -0
  180. package/CHANGELOG.md +86 -0
  181. package/db/schema.js +14 -0
  182. package/lib/brokerage.js +94 -0
  183. package/lib/chain-layout.js +89 -0
  184. package/lib/circle.js +23 -4
  185. package/lib/degrees.js +30 -0
  186. package/lib/dot-rings.js +21 -0
  187. package/lib/ingest.js +3 -3
  188. package/lib/linkedin-limits.js +9 -4
  189. package/lib/reach.js +22 -1
  190. package/lib/search-risk.js +38 -0
  191. package/lib/ties.js +23 -0
  192. package/package.json +1 -1
  193. package/scripts/scrape.py +50 -12
  194. package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +0 -18
  195. package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +0 -2
  196. package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
  197. package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +0 -2
  198. package/.next/standalone/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  199. package/.next/static/chunks/0w76iraw7ij3k.js +0 -18
  200. package/.next/static/chunks/135n87ydxjrra.js +0 -2
  201. package/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
  202. package/.next/static/chunks/3qh78g2aqhprd.js +0 -2
  203. package/.next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  204. /package/.next/standalone/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_buildManifest.js +0 -0
  205. /package/.next/standalone/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_clientMiddlewareManifest.js +0 -0
  206. /package/.next/standalone/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_ssgManifest.js +0 -0
  207. /package/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_buildManifest.js +0 -0
  208. /package/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_clientMiddlewareManifest.js +0 -0
  209. /package/.next/static/{WseE_5HuU-1q0ywC9CP3U → wGVrXBkFLmEB4snfhtOcO}/_ssgManifest.js +0 -0
@@ -0,0 +1,2 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,85957,e=>{"use strict";var t=e.i(43476),n=e.i(71645),i=e.i(18566),o=e.i(40996),r=e.i(19821),l=e.i(64745),s=e.i(7837),a=e.i(2776);e.i(47716);var d=e.i(36534),c=e.i(77899),p=e.i(17983),h=e.i(43079),f=e.i(23731),u=e.i(94657),g=e.i(89154);let y={S:"#FFD700",A:"#9B59B6",B:"#3498DB",C:"#95A5A6",D:"#BDC3C7"},x="1px solid rgba(255,255,255,0.1)",m=[...g.INDUSTRIES,g.UNKNOWN_INDUSTRY];function b(e){let t=0x811c9dc5;for(let n=0;n<e.length;n++)t=Math.imul(t^e.charCodeAt(n),0x1000193);return(t>>>0)%1e3/1e3}function k({value:e,onChange:n,options:i}){return(0,t.jsx)("div",{style:{display:"flex",borderRadius:7,overflow:"hidden",border:x},children:i.map(([i,o])=>(0,t.jsx)("button",{onClick:()=>n(i),style:{padding:"5px 10px",fontSize:11.5,fontWeight:600,border:"none",cursor:"pointer",background:e===i?"rgba(52,152,219,0.25)":"transparent",color:e===i?"#cfe6f7":"#8b9a9a"},children:o},i))})}function v(e,t){return("all"===t.degree||String(e.degree)===t.degree)&&("senior"!==t.seniority||!!(0,g.isSenior)(e.headline))&&("csuite"!==t.seniority||!((0,g.getSeniority)(e.headline).level<6))&&("SA"!==t.tier||"S"===e.tier||"A"===e.tier)}function j({d1:e=[],d2:i=[],d3:o=[],tab:r="map",onOpenCompany:l}){let[s,a]=(0,n.useState)({degree:"all",seniority:"all",tier:"all"}),[d,c]=(0,n.useState)(()=>new Set),[p,h]=(0,n.useState)(""),[f,u]=(0,n.useState)(null),y=(0,n.useMemo)(()=>[...e,...i,...o],[e,i,o]),x=(0,n.useMemo)(()=>(0,g.buildCompanyIndex)(y),[y]),b=(0,n.useMemo)(()=>(0,g.companyLinks)(e,i),[e,i]),k=(0,n.useMemo)(()=>{let e=p.trim().toLowerCase(),t=[];for(let n of x.values()){if(d.has(n.industry.key)||e&&!n.name.toLowerCase().includes(e))continue;let i=n.people.filter(e=>v(e,s));i.length&&t.push({...n,shown:i})}return t.sort((e,t)=>t.shown.length-e.shown.length)},[x,s,d,p]),C=(0,n.useMemo)(()=>{let e=new Map(m.map(e=>[e.key,{...e,people:[],companies:new Map}])),t=new Set;for(let n of y){let i=n.profile_url||`id:${n.id}`;if(t.has(i)||(t.add(i),!v(n,s)))continue;let o=(0,g.companyOf)(n),r=o?x.get(o)?.industry||(0,g.industryOf)(o,n.headline):(0,g.industryOf)(null,n.headline),l=e.get(r.key);l.people.push(n),o&&l.companies.set(o,(l.companies.get(o)||0)+1)}return[...e.values()].filter(e=>e.people.length).sort((e,t)=>t.people.length-e.people.length)},[y,x,s]),z=f?.kind==="company"?x.get(f.key):f?.kind==="industry"?C.find(e=>e.key===f.key):null;return(0,t.jsxs)("div",{style:{display:"flex",flex:1,minHeight:0},children:[(0,t.jsxs)("div",{style:{flex:1,minWidth:0,display:"flex",flexDirection:"column"},children:[(0,t.jsx)(S,{filters:s,setFilters:a,query:p,setQuery:h,industries:C,hidden:d,setHidden:c,onIndustry:e=>u({kind:"industry",key:e})}),"map"===r?(0,t.jsx)(w,{companies:k,links:b,focus:f,onCompany:e=>u({kind:"company",key:e}),onIndustry:e=>u({kind:"industry",key:e}),onClear:()=>u(null)}):(0,t.jsx)(M,{industries:C,index:x,d1:e,d2:i,onIndustry:e=>u({kind:"industry",key:e}),onCompany:e=>u({kind:"company",key:e})})]}),z&&(0,t.jsx)(B,{focus:f,data:z,index:x,links:b,d1:e,d2:i,filters:s,onClose:()=>u(null),onCompany:e=>u({kind:"company",key:e}),onOpenCompany:l})]})}function S({filters:e,setFilters:n,query:i,setQuery:o,industries:r,hidden:l,setHidden:s,onIndustry:a}){let d=(e,t)=>n(n=>({...n,[e]:t}));return(0,t.jsxs)("div",{style:{padding:"10px 16px",borderBottom:x,display:"flex",flexDirection:"column",gap:8},children:[(0,t.jsxs)("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[(0,t.jsx)("input",{value:i,onChange:e=>o(e.target.value),placeholder:"Find a company",style:{padding:"6px 10px",borderRadius:7,border:x,background:"rgba(255,255,255,0.05)",color:"#fff",fontSize:13,minWidth:170}}),(0,t.jsx)(k,{value:e.degree,onChange:e=>d("degree",e),options:[["all","All degrees"],["1","1st"],["2","2nd"],["3","3rd"]]}),(0,t.jsx)(k,{value:e.seniority,onChange:e=>d("seniority",e),options:[["all","Any level"],["senior","Director+"],["csuite","C-suite"]]}),(0,t.jsx)(k,{value:e.tier,onChange:e=>d("tier",e),options:[["all","All tiers"],["SA","S & A only"]]})]}),(0,t.jsxs)("div",{style:{display:"flex",gap:6,flexWrap:"wrap"},children:[r.map(e=>{let n=l.has(e.key);return(0,t.jsxs)("span",{style:{display:"inline-flex",borderRadius:14,border:`1px solid ${e.color}55`,overflow:"hidden",opacity:n?.4:1},children:[(0,t.jsx)("button",{title:n?"Show on the map":"Hide from the map",onClick:()=>s(t=>{let n=new Set(t);return n.has(e.key)?n.delete(e.key):n.add(e.key),n}),style:{padding:"3px 7px",border:"none",background:`${e.color}22`,cursor:"pointer",color:e.color,fontSize:11},children:"●"}),(0,t.jsxs)("button",{onClick:()=>a(e.key),title:"Analyse this industry",style:{padding:"3px 9px 3px 5px",border:"none",background:"transparent",cursor:"pointer",color:"#cfd8d8",fontSize:11.5},children:[e.label," ",(0,t.jsx)("span",{style:{color:"#778"},children:e.people.length})]})]},e.key)}),(0,t.jsx)("span",{style:{fontSize:11,color:"#667",alignSelf:"center"},children:"industries inferred from companies and headlines"})]})]})}function w({companies:e,links:i,focus:o,onCompany:r,onIndustry:l,onClear:s}){let a=(0,n.useRef)(null),{w:u,h:x}=function(e){let[t,i]=(0,n.useState)({w:0,h:0});return(0,n.useEffect)(()=>{let t=e.current;if(!t)return;let n=()=>{let e=t.getBoundingClientRect();i(t=>t.w===Math.round(e.width)&&t.h===Math.round(e.height)?t:{w:Math.round(e.width),h:Math.round(e.height)})};n();let o=new ResizeObserver(n);return o.observe(t),()=>o.disconnect()},[e]),t}(a),[m,k]=(0,n.useState)(null),v=(0,n.useMemo)(()=>{let t=e.slice(0,140),n=new Set(t.map(e=>e.name)),o=[...new Set(t.map(e=>e.industry.key))],r=new Map(o.map((e,t)=>{let n=t/Math.max(1,o.length)*Math.PI*2-Math.PI/2;return[e,{x:260*Math.cos(n),y:260*Math.sin(n)}]})),l=t.map(e=>({id:e.name,co:e,r:4+3.4*Math.sqrt(e.shown.length),x:r.get(e.industry.key).x+(b(e.name)-.5)*40,y:r.get(e.industry.key).y+(b(e.name+"#")-.5)*40})),s=i.filter(e=>n.has(e.a)&&n.has(e.b)).map(e=>({source:e.a,target:e.b,weight:e.weight,via:e.via.size})),a=(0,h.forceSimulation)(l).force("x",(function(e){var t,n,i,o=(0,f.default)(.1);function r(e){for(var o,r=0,l=t.length;r<l;++r)o=t[r],o.vx+=(i[r]-o.x)*n[r]*e}function l(){if(t){var r,l=t.length;for(n=Array(l),i=Array(l),r=0;r<l;++r)n[r]=isNaN(i[r]=+e(t[r],r,t))?0:+o(t[r],r,t)}}return"function"!=typeof e&&(e=(0,f.default)(null==e?0:+e)),r.initialize=function(e){t=e,l()},r.strength=function(e){return arguments.length?(o="function"==typeof e?e:(0,f.default)(+e),l(),r):o},r.x=function(t){return arguments.length?(e="function"==typeof t?t:(0,f.default)(+t),l(),r):e},r})(e=>r.get(e.co.industry.key).x).strength(.12)).force("y",(function(e){var t,n,i,o=(0,f.default)(.1);function r(e){for(var o,r=0,l=t.length;r<l;++r)o=t[r],o.vy+=(i[r]-o.y)*n[r]*e}function l(){if(t){var r,l=t.length;for(n=Array(l),i=Array(l),r=0;r<l;++r)n[r]=isNaN(i[r]=+e(t[r],r,t))?0:+o(t[r],r,t)}}return"function"!=typeof e&&(e=(0,f.default)(null==e?0:+e)),r.initialize=function(e){t=e,l()},r.strength=function(e){return arguments.length?(o="function"==typeof e?e:(0,f.default)(+e),l(),r):o},r.y=function(t){return arguments.length?(e="function"==typeof t?t:(0,f.default)(+t),l(),r):e},r})(e=>r.get(e.co.industry.key).y).strength(.12)).force("collide",(0,d.forceCollide)(e=>e.r+3)).force("charge",(0,p.forceManyBody)().strength(-25)).force("link",(0,c.forceLink)(s).id(e=>e.id).strength(e=>Math.min(.25,.03*e.weight)).distance(60)).stop();for(let e=0;e<320;e++)a.tick();let u=l.flatMap(e=>[e.x-e.r,e.x+e.r]),y=l.flatMap(e=>[e.y-e.r,e.y+e.r]),x=l.length?{x:Math.min(...u)-60,y:Math.min(...y)-40,w:Math.max(...u)-Math.min(...u)+120,h:Math.max(...y)-Math.min(...y)+80}:{x:-300,y:-300,w:600,h:600},m=o.map(e=>{let t=l.filter(t=>t.co.industry.key===e),n=t.reduce((e,t)=>e+t.x,0)/t.length,i=t.reduce((e,t)=>e+t.y,0)/t.length,o=Math.hypot(n,i)||1,r=n/o,s=i/o,a=Math.max(...t.map(e=>(e.x-n)*r+(e.y-i)*s+e.r));return{key:e,x:n+r*(a+16),y:i+s*(a+16)+4,anchor:r>.35?"start":r<-.35?"end":"middle",ind:(0,g.industryByKey)(e)}});for(let e of m){let t=7.5*e.ind.label.length,n="start"===e.anchor?e.x:"end"===e.anchor?e.x-t:e.x-t/2,i=Math.max(x.x+x.w,n+t+10);x.x=Math.min(x.x,n-10),x.w=i-x.x;let o=Math.max(x.y+x.h,e.y+12);x.y=Math.min(x.y,e.y-18),x.h=o-x.y}return x.h+=Math.max(60,.1*x.h),{nodes:l,edges:s,box:x,labels:m,cut:Math.max(0,e.length-t.length)}},[e,i]),j=m||(o?.kind==="company"?o.key:null),S=o?.kind==="industry"?o.key:null,C=(0,n.useMemo)(()=>{if(!j)return null;let e=new Set([j]);for(let t of v.edges)t.source.id===j&&e.add(t.target.id),t.target.id===j&&e.add(t.source.id);return e},[j,v]),M=(0,n.useMemo)(()=>new Set(v.nodes.slice(0,28).map(e=>e.id)),[v]);return(0,t.jsxs)("div",{ref:a,style:{flex:1,minHeight:360,position:"relative"},onClick:s,children:[u>0&&(0,t.jsxs)("svg",{width:u,height:x,viewBox:`${v.box.x} ${v.box.y} ${v.box.w} ${v.box.h}`,style:{display:"block"},children:[(0,t.jsx)("g",{children:v.edges.map((e,n)=>{let i=j&&(e.source.id===j||e.target.id===j);return(0,t.jsx)("line",{x1:e.source.x,y1:e.source.y,x2:e.target.x,y2:e.target.y,stroke:i?"#FF6B35":"#8fa6c0",strokeOpacity:i?.8:j?.04:.14,strokeWidth:i?1+Math.log2(1+e.weight):.4+.5*Math.log2(1+e.weight)},n)})}),v.labels.map(e=>(0,t.jsx)("text",{x:e.x,y:e.y,textAnchor:e.anchor,fontSize:13,fontWeight:800,fill:e.ind.color,opacity:S&&S!==e.key?.25:.85,style:{cursor:"pointer"},onClick:t=>{t.stopPropagation(),l(e.key)},children:e.ind.label},e.key)),v.nodes.map(e=>{let n=C&&!C.has(e.id)||S&&e.co.industry.key!==S,i=o?.kind==="company"&&o.key===e.id;return(0,t.jsxs)("g",{transform:`translate(${e.x},${e.y})`,style:{cursor:"pointer"},opacity:n?.18:1,onMouseEnter:()=>k(e.id),onMouseLeave:()=>k(null),onClick:t=>{t.stopPropagation(),r(e.id)},children:[(0,t.jsx)("title",{children:`${e.co.name} \xb7 ${e.co.industry.label} (inferred)
2
+ ${e.co.d1} you know \xb7 ${e.co.d2} reachable${e.co.d3?` \xb7 ${e.co.d3} further`:""}`}),(0,t.jsx)("circle",{r:e.r,fill:e.co.industry.color,fillOpacity:.75,stroke:i?"#fff":e.co.S?y.S:"rgba(0,0,0,0.35)",strokeWidth:i?2.5:e.co.S?1.5:.6}),e.co.d1>0&&(0,t.jsx)("circle",{r:e.r*Math.sqrt(e.co.d1/Math.max(1,e.co.people.length)),fill:"#fff",fillOpacity:.35}),(M.has(e.id)||e.id===j||i)&&(0,t.jsx)("text",{y:e.r+11,textAnchor:"middle",fontSize:10,fill:"#e6edf0",style:{pointerEvents:"none"},children:e.co.name})]},e.id)})]}),(0,t.jsxs)("div",{style:{position:"absolute",left:12,bottom:10,fontSize:11,color:"#778",lineHeight:1.6,pointerEvents:"none"},children:["Bubble = a company, sized by your people there; white centre = the share you already know; gold ring = an S-tier person inside.",(0,t.jsx)("br",{}),"A line = one of your connections at one company knows people at the other.",v.cut?` Showing the 140 largest; ${v.cut} smaller companies are in Industries.`:""]})]})}function C({parts:e,total:n}){return(0,t.jsx)("div",{style:{display:"flex",height:6,borderRadius:3,overflow:"hidden",background:"rgba(255,255,255,0.06)"},children:e.map(([e,i],o)=>(0,t.jsx)("div",{style:{width:`${e/Math.max(1,n)*100}%`,background:i}},o))})}function M({industries:e,index:i,d1:o,d2:r,onIndustry:l,onCompany:s}){let a=(0,n.useMemo)(()=>{let e=new Map(o.map(e=>[e.id,e])),t=new Map;for(let e of r){let n=(0,g.companyOf)(e),o=n?i.get(n)?.industry.key:(0,g.industryOf)(null,e.headline).key;if(!o)continue;let r=t.get(o)||new Map;r.set(e.source_connection_id,(r.get(e.source_connection_id)||0)+1),t.set(o,r)}let n=new Map;for(let[i,o]of t){let[t,r]=[...o.entries()].sort((e,t)=>t[1]-e[1])[0]||[];e.get(t)&&n.set(i,{bridge:e.get(t),n:r})}return n},[i,o,r]);return(0,t.jsx)("div",{style:{flex:1,overflow:"auto",padding:16},children:(0,t.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(280px, 1fr))",gap:12},children:e.map(e=>{let n=e.people.filter(e=>1===e.degree).length,i=e.people.filter(e=>2===e.degree).length,o=e.people.filter(e=>(0,g.isSenior)(e.headline)).length,r=e.people.filter(e=>"S"===e.tier).length,d=[...e.companies.entries()].sort((e,t)=>t[1]-e[1]).slice(0,3),c=a.get(e.key);return(0,t.jsxs)("div",{onClick:()=>l(e.key),style:{padding:14,borderRadius:10,border:x,borderTop:`3px solid ${e.color}`,background:"rgba(255,255,255,0.03)",cursor:"pointer"},children:[(0,t.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"baseline"},children:[(0,t.jsx)("b",{style:{fontSize:14,color:e.color},children:e.label}),(0,t.jsx)("span",{style:{fontSize:20,fontWeight:800},children:e.people.length})]}),(0,t.jsxs)("div",{style:{fontSize:11.5,color:"#8b9a9a",margin:"4px 0 6px"},children:[n," you know · ",i," reachable · ",e.companies.size," companies · ",o," director+ · ",r," S-tier"]}),(0,t.jsx)(C,{total:e.people.length,parts:[[n,"#00ff88"],[i,"#FF6B35"]]}),d.length>0&&(0,t.jsx)("div",{style:{marginTop:8,display:"flex",gap:6,flexWrap:"wrap"},children:d.map(([e,n])=>(0,t.jsxs)("button",{onClick:t=>{t.stopPropagation(),s(e)},style:{padding:"2px 8px",borderRadius:10,border:x,background:"rgba(255,255,255,0.05)",color:"#dfe6e9",fontSize:11,cursor:"pointer"},children:[e," ",(0,t.jsx)("span",{style:{color:"#778"},children:n})]},e))}),c&&(0,t.jsxs)("div",{style:{marginTop:8,fontSize:11.5,color:"#aab7b7"},children:["Best way in: ",(0,t.jsx)("b",{style:{color:y[c.bridge.tier]||"#ccc"},children:c.bridge.name})," knows ",c.n," here"]})]},e.key)})})})}function z({p:e,note:n}){return(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"5px 0",fontSize:12.5},children:[(0,t.jsx)("span",{style:{width:7,height:7,borderRadius:"50%",background:y[e.tier]||"#667",flexShrink:0}}),(0,t.jsxs)("div",{style:{minWidth:0,flex:1},children:[(0,t.jsx)("div",{style:{fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:11,color:"#8b9a9a",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:n||e.headline})]})]})}function B({focus:e,data:i,index:o,links:r,d1:l,d2:s,filters:a,onClose:d,onCompany:c,onOpenCompany:p}){let h,f,u,y="company"===e.kind,m=i.people.filter(e=>v(e,a)),b=(0,n.useMemo)(()=>{let e=new Map;for(let t of m){let n=(0,g.getSeniority)(t.headline);e.set(n.label,{n:(e.get(n.label)?.n||0)+1,level:n.level})}return[...e.entries()].sort((e,t)=>t[1].level-e[1].level)},[m]),k=(0,n.useMemo)(()=>y?(0,g.waysInto)(i.name,l,s):null,[y,i,l,s]),j=(0,n.useMemo)(()=>y?r.filter(e=>e.a===i.name||e.b===i.name).map(e=>({name:e.a===i.name?e.b:e.a,weight:e.weight})).sort((e,t)=>t.weight-e.weight).slice(0,8):[],[y,r,i]),S=[...m].sort((e,t)=>(Number(t.power_score)||0)-(Number(e.power_score)||0)).slice(0,6),w=y?i.industry:i,M=m.filter(e=>1===e.degree).length,W=m.filter(e=>2===e.degree).length,I=y?[]:[...i.companies.entries()].sort((e,t)=>t[1]-e[1]).slice(0,10);return(0,t.jsxs)("aside",{style:{width:360,flexShrink:0,borderLeft:x,overflow:"auto",padding:16,background:"rgba(255,255,255,0.02)"},children:[(0,t.jsx)("button",{onClick:d,style:{background:"none",border:"none",color:"#888",cursor:"pointer",fontSize:12,padding:0},children:"✕ Close"}),(0,t.jsxs)("div",{style:{fontSize:10,letterSpacing:1,color:"#8b9a9a",marginTop:8},children:[y?"COMPANY":"INDUSTRY"," ANALYZER"]}),(0,t.jsx)("h2",{style:{margin:"4px 0 6px",fontSize:20},children:y?i.name:i.label}),(0,t.jsxs)("span",{style:{fontSize:11,padding:"2px 8px",borderRadius:10,background:`${w.color}22`,color:w.color},children:[w.label,y?" · inferred":""]}),y&&(0,t.jsx)(R,{name:i.name}),(0,t.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(3, 1fr)",gap:8,margin:"14px 0"},children:[[M,"you know","#00ff88"],[W,"reachable","#FF6B35"],[m.filter(e=>(0,g.isSenior)(e.headline)).length,"director+","#FFD700"]].map(([e,n,i])=>(0,t.jsxs)("div",{style:{padding:8,borderRadius:8,background:"rgba(255,255,255,0.04)",textAlign:"center"},children:[(0,t.jsx)("div",{style:{fontSize:20,fontWeight:800,color:i},children:e}),(0,t.jsx)("div",{style:{fontSize:10.5,color:"#8b9a9a"},children:n})]},n))}),(0,t.jsx)(F,{title:"By level",children:b.map(([e,{n}])=>(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"110px 1fr 28px",gap:8,alignItems:"center",fontSize:11.5,marginBottom:4},children:[(0,t.jsx)("span",{style:{color:"#aab7b7"},children:e}),(0,t.jsx)(C,{total:m.length,parts:[[n,"#3498DB"]]}),(0,t.jsx)("span",{style:{color:"#778",textAlign:"right"},children:n})]},e))}),y&&k&&(0,t.jsxs)(F,{title:"Ways in",children:[k.direct.length>0&&(0,t.jsxs)("div",{style:{fontSize:11,color:"#00ff88",margin:"2px 0"},children:["You already know ",k.direct.length," here"]}),(h=new Map(k.bridges.map(({bridge:e,n:t})=>[e.id,t])),f=[...k.direct].sort((e,t)=>(h.get(t.id)||0)-(h.get(e.id)||0)).slice(0,4),u=new Set(k.direct.map(e=>e.id)),(0,t.jsxs)(t.Fragment,{children:[f.map(e=>(0,t.jsx)(z,{p:e,note:h.get(e.id)?`works here \xb7 knows ${h.get(e.id)} here`:`works here \xb7 ${e.headline||""}`},e.id)),k.bridges.filter(({bridge:e})=>!u.has(e.id)).slice(0,5).map(({bridge:e,n})=>(0,t.jsx)(z,{p:e,note:`knows ${n} ${1===n?"person":"people"} here`},e.id))]})),!k.direct.length&&!k.bridges.length&&(0,t.jsx)("div",{style:{fontSize:12,color:"#778"},children:"No one you know links here yet."})]}),(0,t.jsx)(F,{title:y?"Most powerful people here":"Most powerful people in this industry",children:S.map(e=>(0,t.jsx)(z,{p:e,note:`${1===e.degree?"you know them":2===e.degree?"2nd degree":"3rd+"} \xb7 ${e.headline||""}`},e.id))}),y&&j.length>0&&(0,t.jsx)(F,{title:"Connected companies",children:(0,t.jsx)("div",{style:{display:"flex",gap:6,flexWrap:"wrap"},children:j.map(e=>(0,t.jsxs)("button",{onClick:()=>c(e.name),style:{padding:"3px 9px",borderRadius:10,border:x,background:`${o.get(e.name)?.industry.color||"#666"}22`,color:"#dfe6e9",fontSize:11.5,cursor:"pointer"},children:[e.name," ",(0,t.jsx)("span",{style:{color:"#778"},children:e.weight})]},e.name))})}),!y&&(0,t.jsx)(F,{title:"Companies",children:I.map(([e,n])=>(0,t.jsxs)("div",{onClick:()=>c(e),style:{display:"grid",gridTemplateColumns:"1fr 60px 24px",gap:8,alignItems:"center",fontSize:12,padding:"3px 0",cursor:"pointer"},children:[(0,t.jsx)("span",{style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e}),(0,t.jsx)(C,{total:I[0][1],parts:[[n,w.color]]}),(0,t.jsx)("span",{style:{color:"#778",textAlign:"right"},children:n})]},e))}),y&&p&&(0,t.jsxs)("button",{onClick:()=>p(i),style:{marginTop:14,width:"100%",padding:"10px",borderRadius:8,border:"none",cursor:"pointer",fontWeight:700,background:"linear-gradient(135deg, #00ff88, #3498DB)",color:"#000"},children:["Path to the top of ",i.name," →"]})]})}function F({title:e,children:n}){return(0,t.jsxs)("div",{style:{marginTop:14},children:[(0,t.jsx)("div",{style:{fontSize:10.5,letterSpacing:.8,color:"#8b9a9a",fontWeight:700,marginBottom:6},children:e.toUpperCase()}),n]})}function R({name:e}){let{companies:n,setScore:i}=(0,u.useCompanyScores)(),o=n?.find(t=>t.name===e);if(!o)return null;let r={yours:"your score",known:"known list",network:"estimated",default:"unknown company"}[o.source];return(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,marginTop:10,fontSize:12,color:"#aab7b7"},children:["Company score ",(0,t.jsx)("b",{style:{color:"#fff",fontSize:15},children:o.score}),"/10 · ",r,(0,t.jsx)("span",{style:{marginLeft:"auto"},children:(0,t.jsx)(u.ScorePicker,{company:o,onSet:i,compact:!0})})]})}var W=e.i(15812),I=e.i(30215),$=e.i(22016),D=e.i(36774);let A=[["map","Map"],["industries","Industries"],["companies","Companies"]];function T(){let{userId:e}=(0,W.useUser)(),[a,d]=(0,n.useState)([]),[c,p]=(0,n.useState)(null),[h,f]=(0,n.useState)([]),[u,y]=(0,n.useState)(!0),[x,m]=(0,n.useState)([]),[b,k]=(0,n.useState)([]),[v,S]=(0,n.useState)([]),w=(0,i.useSearchParams)().get("tab"),[C,M]=(0,n.useState)(()=>A.some(([e])=>e===w)?w:"map"),z=(0,i.useRouter)();(0,n.useEffect)(()=>{"scores"===w&&z.replace("/scores#companies")},[w,z]);let[B,F]=(0,n.useState)(0),[R,D]=(0,n.useState)(!1);(0,n.useEffect)(()=>{!l.IS_DEMO&&e&&t();async function t(){let t=(0,s.hasCsvNetwork)()?(0,s.loadCsvNetwork)():null;D(!!t);let n=[],i=[],o=[];if(t)n=t.degree1,i=t.degree2||[];else{let t=await (0,r.loadNetwork)(e);n=t.degree1,i=t.degree2,o=t.degree3}m(n),k(i),S(o||[]);let l={},a=new Set;[...n,...i,...o].forEach(e=>{if(a.has(e.profile_url))return;a.add(e.profile_url);let t=(0,g.companyOf)(e);t&&(l[t]||(l[t]={name:t,d1:0,d2:0,d3:0,sCount:0,aCount:0,people:[]}),1===e.degree?l[t].d1++:2===e.degree?l[t].d2++:l[t].d3++,"S"===e.tier&&l[t].sCount++,"A"===e.tier&&l[t].aCount++,l[t].people.push(e))}),d(Object.values(l).filter(e=>e.d1+e.d2+e.d3>=3).sort((e,t)=>3*t.sCount+2*t.aCount+t.d1+t.d2+t.d3-(3*e.sCount+2*e.aCount+e.d1+e.d2+e.d3))),y(!1)}},[e,B]);let[T,O]=(0,n.useState)(!1),[N,L]=(0,n.useState)([]),E=(0,I.default)(),U=!T&&E.running?(0,o.busyReason)(E):null;function P(e){let t=new Set(x.map(e=>e.profile_url)),n=new Set([...x,...b].map(e=>e.profile_url));return e.map(i=>({...i,connected:t.has(i.profile_url||i.profileUrl),known:n.has(i.profile_url||i.profileUrl),seniority:(0,g.getSeniority)(i.headline),mutualCount:e.filter(e=>t.has(e.profile_url||e.profileUrl)&&e.id!==i.id).length}))}function H(e){p(e),f(P(e.people))}async function Y(){if(l.IS_DEMO||R||!c)return;let e=!1;try{e="1"===localStorage.getItem("six-degrees-company-scan-ok")}catch{}if(!e){if(!window.confirm("Company scans are experimental: they have not been tested against live LinkedIn yet, so the results may be incomplete. Like any scan, it opens a Chrome window and counts toward the profile views LinkedIn watches.\n\nRun it anyway?"))return;try{localStorage.setItem("six-degrees-company-scan-ok","1")}catch{}}O(!0),L(["Checking the scanner..."]);let t=(0,o.notReadyMessage)(await (0,o.scraperStatus)().catch(()=>null));if(t){L([t]),O(!1);return}try{let e=await (0,o.runScrape)("company",{name:c.name,onLog:L});if(0===e.exitCode){L(e=>[...e,"Loading the new people..."]);let e=await fetch(`/api/connections?scanned_company=${encodeURIComponent(c.name)}&limit=500`),t=(await e.json()).connections||[];t.length?(f(P(t)),L(e=>[...e,`${t.length} people at ${c.name}.`])):L(e=>[...e,"Nothing new was found."])}}catch(e){L([e.message||"Failed to start the scan"])}finally{O(!1)}}let K={},q=0;h.forEach(e=>{let t=e.seniority.key;K[t]||(K[t]={...e.seniority,people:[]}),e.connected&&q++;let n=Math.min(90,12*e.mutualCount);e.seniority.level<=1?n+=20:e.seniority.level<=2?n+=10:e.seniority.level>=5&&(n-=10),n=Math.max(5,Math.min(95,n)),e.connected&&(n=100);let i=null;if(2===e.degree&&e.source_connection_id){let t=x.find(t=>t.id===e.source_connection_id);t&&(i=t.name)}let o={0:5,1:10,2:20,3:35,4:50,5:75,6:100}[e.seniority.level]||10;K[t].people.push({...e,likelihood:n,bridgeName:i,xp:o})}),Object.values(K).forEach(e=>e.people.sort((e,t)=>t.likelihood-e.likelihood));let X=Object.values(K).sort((e,t)=>t.level-e.level);return l.IS_DEMO?(0,t.jsxs)("div",{style:{minHeight:"100vh",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:12,background:"#0a0a1a",color:"rgba(255,255,255,0.7)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'},children:[(0,t.jsx)("h1",{style:{fontSize:20,fontWeight:700,margin:0,color:"#fff"},children:"Not part of the demo"}),(0,t.jsx)("p",{style:{fontSize:13,margin:0},children:"The public demo includes the Network Circle and Degrees views only."}),(0,t.jsx)($.default,{href:"/",style:{fontSize:13,color:"#888",textDecoration:"none"},children:"← Back to the network"})]}):u?(0,t.jsx)("div",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#0a0a1a",color:"#fff"},children:"Loading..."}):(0,t.jsxs)("div",{style:{height:"100vh",background:"#0a0a1a",color:"#fff",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',display:"flex",flexDirection:"column",overflow:"hidden"},children:[(0,t.jsx)("header",{style:{padding:"14px 24px",borderBottom:"1px solid rgba(255,255,255,0.1)",flexShrink:0},children:(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:12},children:[(0,t.jsxs)($.default,{href:"/",style:{display:"flex",alignItems:"center",gap:6,color:"#888",textDecoration:"none",fontSize:13,fontWeight:600,padding:"6px 14px",borderRadius:6,background:"rgba(255,255,255,0.06)",border:"1px solid rgba(255,255,255,0.1)"},children:[(0,t.jsx)("span",{style:{fontSize:16},children:"←"})," Map"]}),(0,t.jsx)("h1",{style:{fontSize:20,fontWeight:700,margin:0,background:"linear-gradient(135deg, #00ff88, #3498DB)",WebkitBackgroundClip:"text",WebkitTextFillColor:"transparent"},children:"Paths"}),!c&&(0,t.jsx)("div",{style:{display:"flex",gap:4,marginLeft:8,padding:3,borderRadius:8,background:"rgba(255,255,255,0.05)"},children:A.map(([e,n])=>(0,t.jsx)("button",{onClick:()=>M(e),style:{padding:"5px 12px",borderRadius:6,border:"none",cursor:"pointer",fontSize:12.5,fontWeight:700,background:C===e?"linear-gradient(135deg, #00ff88, #3498DB)":"transparent",color:C===e?"#000":"#aab"},children:n},e))}),c&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{onClick:()=>{p(null),f([])},style:{background:"none",border:"none",color:"#888",cursor:"pointer",fontSize:12},children:"← Companies"}),(0,t.jsx)("span",{style:{fontSize:13,fontWeight:600},children:c.name}),(0,t.jsxs)("span",{style:{fontSize:11,color:"#888"},children:[q,"/",h.length," connected"]}),R?(0,t.jsx)("span",{style:{marginLeft:"auto",fontSize:11,color:"#888"},children:"Scanning needs your own network"}):(0,t.jsx)("button",{onClick:Y,disabled:T||!!U,title:U?`${U}. One scan at a time.`:void 0,style:{marginLeft:"auto",padding:"5px 12px",borderRadius:6,border:"none",cursor:T||U?"not-allowed":"pointer",background:T||U?"#333":"rgba(0,255,136,0.15)",color:T||U?"#555":"#00ff88",fontSize:11,fontWeight:600},children:T?"Scanning...":U?"Scanner busy":"+ Scan Full Company"}),(0,t.jsx)("span",{title:"Not yet tested against live LinkedIn. Results may be incomplete.",style:{fontSize:9.5,fontWeight:800,letterSpacing:.6,padding:"3px 7px",borderRadius:20,border:"1px solid rgba(255,215,0,0.45)",color:"#FFD700"},children:"EXPERIMENTAL"})]})]})}),c||"companies"===C?(0,t.jsx)("div",{style:{flex:1,overflow:"auto",padding:"16px 24px"},children:(0,t.jsxs)("div",{style:{maxWidth:700,margin:"0 auto"},children:[!c&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{style:{fontSize:13,color:"#888",marginBottom:16},children:["Pick a company to see your path to the top. Based on ",x.length+b.length," people in your network."]}),(0,t.jsx)("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:10},children:a.slice(0,60).map(e=>(0,t.jsxs)("button",{onClick:()=>H(e),style:{padding:"14px",borderRadius:10,border:"none",cursor:"pointer",textAlign:"left",background:"rgba(255,255,255,0.04)",transition:"background 0.15s",borderLeft:`3px solid ${e.sCount>0?"#FFD700":e.aCount>0?"#9B59B6":"#3498DB"}`},onMouseEnter:e=>e.currentTarget.style.background="rgba(255,255,255,0.08)",onMouseLeave:e=>e.currentTarget.style.background="rgba(255,255,255,0.04)",children:[(0,t.jsx)("div",{style:{fontSize:14,fontWeight:700,color:"#fff",marginBottom:4},children:e.name}),(0,t.jsxs)("div",{style:{display:"flex",gap:8,fontSize:10,color:"#888"},children:[(0,t.jsxs)("span",{style:{color:"#00ff88"},children:[e.d1," connected"]}),(0,t.jsxs)("span",{children:[e.d2+e.d3," locked"]})]}),(0,t.jsxs)("div",{style:{display:"flex",gap:6,marginTop:4,fontSize:9},children:[e.sCount>0&&(0,t.jsxs)("span",{style:{color:"#FFD700",fontWeight:600},children:[e.sCount,"S"]}),e.aCount>0&&(0,t.jsxs)("span",{style:{color:"#9B59B6",fontWeight:600},children:[e.aCount,"A"]}),(0,t.jsxs)("span",{style:{color:"#555"},children:[e.d1+e.d2," total"]})]}),(0,t.jsx)("div",{style:{height:3,background:"rgba(255,255,255,0.08)",borderRadius:2,marginTop:6},children:(0,t.jsx)("div",{style:{height:"100%",borderRadius:2,width:`${e.d1/Math.max(e.d1+e.d2+e.d3,1)*100}%`,background:e.sCount>0?"#FFD700":"#00ff88"}})})]},e.name))})]}),T&&(0,t.jsxs)("div",{style:{padding:"10px",borderRadius:8,background:"rgba(0,255,136,0.06)",border:"1px solid rgba(0,255,136,0.2)",marginBottom:12},children:[(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:4},children:[(0,t.jsx)("div",{style:{width:6,height:6,borderRadius:"50%",background:"#00ff88",animation:"pulse 1s infinite"}}),(0,t.jsx)("span",{style:{fontSize:10,fontWeight:600,color:"#00ff88"},children:"Scanning LinkedIn..."})]}),(0,t.jsx)("div",{style:{fontFamily:"monospace",fontSize:9,color:"#777",maxHeight:60,overflow:"auto"},children:N.map((e,n)=>(0,t.jsx)("div",{children:e},n))})]}),c&&X.map(e=>(0,t.jsxs)("div",{style:{marginBottom:16},children:[(0,t.jsxs)("div",{style:{padding:"8px 12px",borderRadius:6,marginBottom:4,background:e.level>=5?"rgba(255,215,0,0.08)":e.level>=3?"rgba(155,89,182,0.08)":"rgba(52,152,219,0.08)",borderLeft:`3px solid ${e.level>=5?"#FFD700":e.level>=3?"#9B59B6":"#3498DB"}`,display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{style:{fontSize:12,fontWeight:700,color:e.level>=5?"#FFD700":e.level>=3?"#9B59B6":"#3498DB"},children:e.label}),e.level>=5&&(0,t.jsx)("span",{style:{fontSize:9,color:"#FF6B35",marginLeft:8,fontWeight:700},children:"TARGET"})]}),(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,t.jsxs)("span",{style:{fontSize:9,color:"#FF6B35",fontWeight:600},children:["+",e.people.filter(e=>!e.connected).reduce((e,t)=>e+(t.xp||0),0)," XP"]}),(0,t.jsxs)("span",{style:{fontSize:10,color:"#666"},children:[e.people.filter(e=>e.connected).length,"/",e.people.length]})]})]}),e.people.map((n,i)=>(0,t.jsx)(_,{p:n,level:e,companyPeople:h,d1Data:x,selectedCompany:c},n.id||i))]},e.key))]})}):(0,t.jsx)(j,{d1:x,d2:b,d3:v,tab:C,onOpenCompany:e=>H({...e,sCount:e.S,aCount:e.A})})]})}function _({p:e,level:i,companyPeople:o,d1Data:r,selectedCompany:l}){let[s,a]=(0,n.useState)(!1),d=new Set(r.map(e=>e.profile_url));o.filter(e=>d.has(e.profile_url||e.profileUrl));let c=o.filter(t=>(0,g.getSeniority)(t.headline).level>e.seniority.level&&!d.has(t.profile_url||t.profileUrl)),p=o.filter(t=>(0,g.getSeniority)(t.headline).level===e.seniority.level&&t.id!==e.id),h=[];if(!e.connected){let t=c.length;t>0&&h.push({icon:"⬆️",text:`Adding them gives you a mutual with ${t} people above in the hierarchy`,color:"#00ff88"});let n=p.filter(e=>d.has(e.profile_url||e.profileUrl)).length;n>0?h.push({icon:"🤝",text:`${n} people at their level already connected — high social proof`,color:"#FFD700"}):h.push({icon:"🎯",text:"First connection at this level — opens a new layer",color:"#3498DB"}),e.bridgeName&&h.push({icon:"🔗",text:`Reachable through ${e.bridgeName} — warm intro possible`,color:"#FF6B35"}),e.seniority.level>=5?h.push({icon:"👑",text:`${e.seniority.label} — decision maker, high strategic value but low accept rate`,color:"#FFD700"}):e.seniority.level>=3?h.push({icon:"📊",text:`${e.seniority.label} — middle management, good balance of access and accept rate`,color:"#9B59B6"}):h.push({icon:"✅",text:`${e.seniority.label} — highest accept rate, builds mutual foundation for people above`,color:"#00ff88"}),e.likelihood>=60?h.push({icon:"🔥",text:`${e.likelihood}% likely to accept — strong mutual overlap + approachable role`,color:"#00ff88"}):e.likelihood>=30?h.push({icon:"⚡",text:`${e.likelihood}% likely — moderate chance, add more people at their level first`,color:"#FFD700"}):h.push({icon:"🔒",text:`${e.likelihood}% likely — low chance right now, build more connections below them first`,color:"#ff5050"})}return(0,t.jsxs)("div",{style:{borderBottom:"1px solid rgba(255,255,255,0.03)"},children:[(0,t.jsxs)("div",{onClick:()=>a(!s),style:{display:"flex",alignItems:"center",gap:8,padding:"6px 8px",cursor:"pointer",opacity:e.connected?.7:1},onMouseEnter:e=>e.currentTarget.style.background="rgba(255,255,255,0.04)",onMouseLeave:e=>e.currentTarget.style.background="transparent",children:[(0,t.jsx)("span",{style:{fontSize:8,color:"#555",flexShrink:0,width:10},children:s?"▾":"▸"}),(0,t.jsx)("div",{style:{width:18,height:18,borderRadius:"50%",flexShrink:0,fontSize:8,display:"flex",alignItems:"center",justifyContent:"center",background:e.connected?"rgba(0,255,136,0.2)":"rgba(255,255,255,0.05)",color:e.connected?"#00ff88":"#555",border:`1px solid ${e.connected?"rgba(0,255,136,0.3)":"rgba(255,255,255,0.1)"}`},children:e.connected?"✓":"🔒"}),(0,D.localPhoto)(e.profile_image_url||e.imageUrl)?(0,t.jsx)("img",{src:(0,D.localPhoto)(e.profile_image_url||e.imageUrl),alt:"",style:{width:26,height:26,borderRadius:"50%",objectFit:"cover",flexShrink:0},onError:e=>e.target.style.display="none"}):(0,t.jsx)("div",{style:{width:26,height:26,borderRadius:"50%",flexShrink:0,fontSize:10,fontWeight:700,background:"#333",color:"#888",display:"flex",alignItems:"center",justifyContent:"center"},children:e.name?.charAt(0)}),(0,t.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,t.jsx)("div",{style:{fontSize:11,fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:8,color:"#666",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.headline?.substring(0,45)})]}),!e.connected&&e.bridgeName&&(0,t.jsxs)("span",{style:{fontSize:8,color:"#FF6B35",flexShrink:0,maxWidth:55,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:["via ",e.bridgeName.split(" ")[0]]}),!e.connected&&(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:6,flexShrink:0},children:[(0,t.jsxs)("span",{style:{fontSize:8,fontWeight:700,color:"#FF6B35"},children:["+",e.xp]}),(0,t.jsxs)("div",{style:{fontSize:9,fontWeight:700,minWidth:30,textAlign:"right",color:e.likelihood>=60?"#00ff88":e.likelihood>=30?"#FFD700":"#ff5050"},children:[e.likelihood,"%"]})]}),!e.connected&&(0,t.jsx)("a",{href:e.profile_url||e.profileUrl,target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),style:{padding:"3px 7px",borderRadius:4,fontSize:8,fontWeight:700,background:"#0077B5",color:"#fff",textDecoration:"none",flexShrink:0},children:"Add"})]}),s&&(0,t.jsxs)("div",{style:{padding:"8px 12px 10px 44px",background:"rgba(255,255,255,0.02)",borderTop:"1px solid rgba(255,255,255,0.04)"},children:[(0,t.jsx)("div",{style:{fontSize:11,color:"#aaa",marginBottom:8},children:e.headline||"No headline available"}),(0,t.jsxs)("div",{style:{display:"flex",gap:6,alignItems:"center",marginBottom:8,flexWrap:"wrap"},children:[(0,t.jsxs)("div",{style:{display:"inline-block",padding:"2px 8px",borderRadius:4,fontSize:9,fontWeight:600,background:e.seniority.level>=5?"rgba(255,215,0,0.1)":e.seniority.level>=3?"rgba(155,89,182,0.1)":"rgba(52,152,219,0.1)",color:e.seniority.level>=5?"#FFD700":e.seniority.level>=3?"#9B59B6":"#3498DB",border:`1px solid ${e.seniority.level>=5?"rgba(255,215,0,0.2)":e.seniority.level>=3?"rgba(155,89,182,0.2)":"rgba(52,152,219,0.2)"}`},children:[e.seniority.label," · Level ",e.seniority.level]}),!e.connected&&(0,t.jsxs)("div",{style:{display:"inline-block",padding:"2px 8px",borderRadius:4,fontSize:9,fontWeight:700,background:"rgba(255,107,53,0.1)",color:"#FF6B35",border:"1px solid rgba(255,107,53,0.2)"},children:["+",e.xp," XP"]})]}),!e.connected&&h.length>0&&(0,t.jsxs)("div",{style:{marginTop:4},children:[(0,t.jsx)("div",{style:{fontSize:9,fontWeight:700,color:"#888",letterSpacing:.5,marginBottom:4},children:"STRATEGIC VALUE"}),h.map((e,n)=>(0,t.jsxs)("div",{style:{display:"flex",gap:6,alignItems:"flex-start",padding:"2px 0",fontSize:10,color:"#bbb"},children:[(0,t.jsx)("span",{style:{fontSize:11,lineHeight:1},children:e.icon}),(0,t.jsx)("span",{style:{color:e.color},children:e.text})]},n))]}),e.connected&&(0,t.jsxs)("div",{style:{fontSize:10,color:"#00ff88",fontWeight:600},children:["✓ Already connected — contributes as mutual for others at ",l?.name]})]})]})}e.s(["default",0,function(){return(0,t.jsx)(a.default,{children:(0,t.jsx)(n.Suspense,{children:(0,t.jsx)(T,{})})})}],85957)}]);
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,94657,e=>{"use strict";var t=e.i(43476),n=e.i(71645),r=e.i(7837);let o=e=>`${e.name} ${e.was} → ${e.estimated?"estimated":e.now}`,a=e=>({padding:"6px 12px",borderRadius:7,cursor:"pointer",fontSize:12,fontWeight:700,border:e?"none":"1px solid rgba(255,255,255,0.15)",background:e?"linear-gradient(135deg, #00ff88, #3498DB)":"transparent",color:e?"#000":"#cfd8d8"});function i({onAnswered:e}){var s;let l,c,[u,d]=(0,n.useState)(null),[p,f]=(0,n.useState)(!1),[h,g]=(0,n.useState)(()=>new Set),[y,m]=(0,n.useState)(!1),[b,w]=(0,n.useState)(null);if((0,n.useEffect)(()=>{if((0,r.csvNetworkSource)())return;let e=!1;return fetch("/api/company-scores/legacy").then(e=>e.json()).then(t=>{!e&&t.offer?.companies?.length&&(d(t.offer),g(new Set(t.offer.companies.map(e=>e.name))))}).catch(()=>{}),()=>{e=!0}},[]),!u)return null;let x=async t=>{m(!0),w(null);try{let n=await fetch("/api/company-scores/legacy",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({keep:t})}),r=await n.json();if(!n.ok&&!r.kept)throw Error(r.error||"Could not save your answer.");d(null),e?.({kept:r.kept||[],scored:r.scored||0,error:n.ok?null:r.error})}catch(e){w(e.message)}finally{m(!1)}};return(0,t.jsxs)("section",{"aria-label":"Built-in scores changed",style:{marginBottom:14,padding:14,borderRadius:10,border:"1px solid rgba(255,215,0,0.35)",background:"rgba(255,215,0,0.06)",fontSize:12.5,color:"#cfd8d8",lineHeight:1.6},children:[(0,t.jsx)("b",{style:{color:"#fff"},children:"Built-in scores changed in this version:"})," ",(l=(s=u.companies).slice(0,3).map(o).join(", "),(c=s.length-3)>0?`${l} and ${c} more`:l),"."," ","Keep any of the old ones as your own?",(0,t.jsx)("div",{style:{marginTop:4,color:"#8b9a9a"},children:"The built-in list now holds only companies most professionals would recognise, scored the same for everyone; anything else is estimated from your network. A score you keep is yours, like one you set below, and Auto hands it back."}),p&&(0,t.jsx)("ul",{style:{listStyle:"none",padding:0,margin:"10px 0 0",display:"grid",gap:4},children:u.companies.map(e=>(0,t.jsx)("li",{children:(0,t.jsxs)("label",{style:{display:"flex",gap:8,alignItems:"baseline",cursor:"pointer"},children:[(0,t.jsx)("input",{type:"checkbox",checked:h.has(e.name),disabled:y,onChange:()=>{let t;return t=e.name,g(e=>{let n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n})}}),(0,t.jsxs)("span",{children:[(0,t.jsx)("b",{style:{color:"#fff"},children:e.name})," ",e.was," → ",e.estimated?`estimated (${e.now})`:e.now,(0,t.jsxs)("span",{style:{color:"#778"},children:[` \xb7 ${e.people} ${1===e.people?"person":"people"} there now`,e.names.length>1&&` \xb7 for ${e.names.join(", ")}`]})]})]})},e.name))}),(0,t.jsxs)("div",{style:{display:"flex",gap:8,marginTop:10,flexWrap:"wrap"},children:[p?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("button",{disabled:y||!h.size,onClick:()=>x([...h]),style:{...a(!0),opacity:y||!h.size?.5:1},children:["Keep ",h.size," ",1===h.size?"score":"scores"]}),(0,t.jsx)("button",{disabled:y,onClick:()=>f(!1),style:a(!1),children:"Back"})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("button",{disabled:y,onClick:()=>x(u.companies.map(e=>e.name)),style:a(!0),children:"Keep all"}),(0,t.jsx)("button",{disabled:y,onClick:()=>f(!0),style:a(!1),children:"Choose…"})]}),(0,t.jsx)("button",{disabled:y,onClick:()=>x([]),style:a(!1),children:"No thanks"})]}),b&&(0,t.jsx)("div",{role:"alert",style:{marginTop:8,color:"#e74c3c"},children:b})]})}let s="1px solid rgba(255,255,255,0.1)",l={yours:{label:"your score",color:"#00ff88"},known:{label:"known list",color:"#3498DB"},data:{label:"public data",color:"#1abc9c"},network:{label:"estimated: many of your people",color:"#FF6B35"},default:{label:"unknown company",color:"#778"}};async function c(){let e=await fetch("/api/company-scores"),t=await e.json();if(!e.ok)throw Error(t.error||"Could not load company scores");return t.companies}function u(){let[e,t]=(0,n.useState)(null),[r,o]=(0,n.useState)(null);return(0,n.useEffect)(()=>{let e=!1;return c().then(n=>{e||t(n)}).catch(t=>{e||o(t.message)}),()=>{e=!0}},[]),{companies:e,error:r,setScore:(0,n.useCallback)(async(e,n)=>{let r=await fetch("/api/company-scores",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,score:n})}),o=await r.json();if(!r.ok)throw Error(o.error||"Could not save");return t(await c()),o},[]),reload:(0,n.useCallback)(async()=>t(await c()),[])}}function d({company:e,onSet:r,compact:o}){let[a,i]=(0,n.useState)(!1),l=async t=>{i(!0);try{await r(e.name,"auto"===t?null:Number(t))}finally{i(!1)}};return(0,t.jsxs)("select",{disabled:a,value:"yours"===e.source?String(e.score):"auto",onChange:e=>l(e.target.value),title:"Set this company's score. Auto uses the known list or an estimate.",style:{padding:o?"2px 4px":"4px 6px",borderRadius:6,border:s,background:"#11131c",color:"#dfe6e9",fontSize:12},children:[(0,t.jsxs)("option",{value:"auto",children:["Auto (","yours"===e.source?e.suggested??"est.":e.score,")"]}),[10,9,8,7,6,5,4,3,2,1].map(e=>(0,t.jsx)("option",{value:e,children:e},e))]})}e.s(["ScorePicker",0,d,"default",0,function({onRescored:e,version:r=0}){let{companies:o,error:a,setScore:c,reload:p}=u();(0,n.useEffect)(()=>{r&&p().catch(()=>{})},[r,p]);let[f,h]=(0,n.useState)(""),[g,y]=(0,n.useState)("all"),[m,b]=(0,n.useState)(150),[w,x]=(0,n.useState)(null),k=e=>{x(e),setTimeout(()=>x(null),3500)},v=async(t,n)=>{let r=await c(t,n);k(`${t}: ${null==n?"back to auto":`set to ${n}`} \xb7 rescored ${r.scored} people`),e?.()},S=async({kept:t,scored:n,error:r})=>{if(!t.length)return k("Keeping the new built-in scores.");k(r||`Kept ${t.length} old ${1===t.length?"score":"scores"} as your own \xb7 rescored ${n} people`),await p().catch(()=>{}),e?.()},j=(0,n.useMemo)(()=>{if(!o)return[];let e=f.trim().toLowerCase();return o.filter(t=>(!e||t.name.toLowerCase().includes(e))&&("all"===g||t.source===g))},[o,f,g]);if(a)return(0,t.jsx)("div",{style:{padding:24,color:"#e74c3c"},children:a});if(!o)return(0,t.jsx)("div",{style:{padding:24,color:"#778"},children:"Loading companies…"});let C=e=>o.filter(t=>t.source===e).length;return(0,t.jsxs)("div",{style:{flex:1,overflow:"auto",padding:16},children:[(0,t.jsxs)("div",{style:{maxWidth:980,margin:"0 auto"},children:[(0,t.jsx)(i,{onAnswered:S}),(0,t.jsxs)("div",{style:{padding:14,borderRadius:10,border:s,background:"rgba(255,255,255,0.03)",fontSize:12.5,color:"#cfd8d8",lineHeight:1.6},children:[(0,t.jsx)("b",{style:{color:"#fff"},children:"How a power score works."})," power = ",(0,t.jsx)("b",{children:"title"})," × ",(0,t.jsx)("b",{children:"company weight"})," + bonus. The title (student 1 … founder or C-suite 10) comes from someone’s roles, with former roles at 70%; an audience of their own counts like a title (1M+ followers like a director). The company weight runs from 0.725 for a company we don’t know (or none found), the middle of the scale, to 1.0 (a company scored 10), so seniority counts for more at a bigger company. The bonus is at most +1.5, for investor, YC, an audience in the millions or the top award of any field, and counts half at an unknown company. A 1st-degree person whose circle is unusually strong gets up to +2 more: up to +1 for a high share of it at A or S, or +1 for every 25 people there. Tiers are graded on your network’s curve unless you choose the fixed scale (",(0,t.jsx)("a",{href:"#tiers",style:{color:"#3498DB"},children:"Tiers"}),", above): your top 3% are S, the next 12% A, the next 25% B (it only lifts, and never lifts anyone under 4 into S or A). On the fixed scale, S ≥ 7.5 · A ≥ 5.5 · B ≥ 4 · C ≥ 2.5.",(0,t.jsxs)("div",{style:{marginTop:6,color:"#8b9a9a"},children:["So a VP at a 10 scores 9.0, a founder at an unknown company 7.3, a director at an unknown company 5.4, and an intern at a 10 scores 2.0. Set a company’s score below and everyone there is rescored. Companies in the sectors you pick (",(0,t.jsx)("a",{href:"#sector",style:{color:"#3498DB"},children:"Your sector"}),", above) get +1 or +2; a score you set is never changed."]})]}),(0,t.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap",margin:"14px 0 10px"},children:[(0,t.jsx)("input",{value:f,onChange:e=>h(e.target.value),placeholder:"Find a company",style:{padding:"6px 10px",borderRadius:7,border:s,background:"rgba(255,255,255,0.05)",color:"#fff",fontSize:13,minWidth:180}}),[["all",`All ${o.length}`],["known",`Known list ${C("known")}`],["data",`Public data ${C("data")}`],["yours",`Yours ${C("yours")}`],["network",`Estimated ${C("network")}`],["default",`Unknown ${C("default")}`]].map(([e,n])=>(0,t.jsx)("button",{onClick:()=>y(e),style:{padding:"5px 10px",borderRadius:7,border:s,cursor:"pointer",fontSize:11.5,fontWeight:600,background:g===e?"rgba(52,152,219,0.25)":"transparent",color:g===e?"#cfe6f7":"#8b9a9a"},children:n},e))]}),(0,t.jsxs)("div",{style:{borderRadius:10,border:s,overflow:"hidden"},children:[(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"1fr 70px 70px 70px 60px 180px 110px",gap:8,padding:"8px 12px",fontSize:10.5,letterSpacing:.6,color:"#8b9a9a",background:"rgba(255,255,255,0.04)"},children:[(0,t.jsx)("span",{children:"COMPANY"}),(0,t.jsx)("span",{style:{textAlign:"right"},children:"PEOPLE"}),(0,t.jsx)("span",{style:{textAlign:"right"},children:"YOU KNOW"}),(0,t.jsx)("span",{style:{textAlign:"right"},children:"DIRECTOR+"}),(0,t.jsx)("span",{style:{textAlign:"right"},children:"SCORE"}),(0,t.jsx)("span",{children:"SOURCE"}),(0,t.jsx)("span",{children:"SET"})]}),j.slice(0,m).map(e=>(0,t.jsxs)("div",{style:{display:"grid",gridTemplateColumns:"1fr 70px 70px 70px 60px 180px 110px",gap:8,padding:"7px 12px",alignItems:"center",borderTop:"1px solid rgba(255,255,255,0.05)",fontSize:12.5},children:[(0,t.jsxs)("span",{style:{display:"flex",alignItems:"center",gap:8,minWidth:0},children:[(0,t.jsx)("span",{title:`${e.industry.label} (inferred)`,style:{width:8,height:8,borderRadius:"50%",background:e.industry.color,flexShrink:0}}),(0,t.jsx)("span",{style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name})]}),(0,t.jsx)("span",{style:{textAlign:"right"},children:e.people}),(0,t.jsx)("span",{style:{textAlign:"right",color:e.d1?"#00ff88":"#556"},children:e.d1}),(0,t.jsx)("span",{style:{textAlign:"right",color:e.senior?"#FFD700":"#556"},children:e.senior}),(0,t.jsx)("b",{style:{textAlign:"right",fontSize:15},children:e.score}),(0,t.jsxs)("span",{style:{fontSize:11,color:l[e.source]?.color},children:[l[e.source]?.label,e.sectorBonus>0&&(0,t.jsxs)("span",{style:{color:"#00ff88"},children:[" + ",e.sectorBonus," your sector"]})]}),(0,t.jsx)(d,{company:e,onSet:v,compact:!0})]},e.name))]}),j.length>m&&(0,t.jsxs)("button",{onClick:()=>b(e=>e+300),style:{marginTop:10,padding:"7px 14px",borderRadius:8,border:s,background:"rgba(255,255,255,0.05)",color:"#dfe6e9",cursor:"pointer",fontSize:12},children:["Show more (",j.length-m," left)"]})]}),w&&(0,t.jsx)("div",{role:"status",style:{position:"fixed",bottom:24,left:"50%",transform:"translateX(-50%)",padding:"10px 18px",borderRadius:24,background:"linear-gradient(135deg, #00ff88, #3498DB)",color:"#000",fontWeight:800,fontSize:13,zIndex:50},children:w})]})},"useCompanyScores",0,u],94657)},2776,e=>{"use strict";var t=e.i(43476),n=e.i(15812);e.s(["default",0,function({children:e}){let{userId:r,ready:o}=(0,n.useUser)();return o&&r?e:(0,t.jsx)("div",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#0a0a1a",color:"#fff",padding:24,textAlign:"center",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'},children:o?(0,t.jsxs)("div",{style:{maxWidth:420},children:[(0,t.jsx)("div",{style:{fontSize:18,fontWeight:700,marginBottom:8},children:"Can’t reach Six Degrees’ local server"}),(0,t.jsx)("div",{style:{color:"#888",fontSize:14,lineHeight:1.6},children:"The page loaded but the app behind it is not answering. If you started it from a terminal, check that window for an error, then reload this page."})]}):(0,t.jsx)("div",{style:{fontSize:18,fontWeight:600},children:"Loading…"})})}])},89154,e=>{"use strict";var t=e.i(90179);function n(e){let n=(0,t.readTitle)(e);return{key:n.key,label:n.label,level:n.level}}function r(e){return(0,t.currentCompany)(e)}let o=[{key:"defense",label:"Government, Defense & Aerospace",color:"#7f8c8d",words:/\b(government|federal|state of|county|city of|defen[cs]e|army|navy|air force|marine corps|military|dod|nasa|aerospace|lockheed|northrop|boeing|raytheon|l3harris|leidos|blue origin|spacex|public sector)\b/i},{key:"finance",label:"Finance & Investing",color:"#2ecc71",words:/\b(bank|banking|capital|ventures?|vc|investments?|investor|fund|private equity|growth equity|asset management|wealth|trading|trader|hedge|crypto|blockchain|web3|defi|fintech|stripe|coinbase|blackrock|robinhood|goldman|morgan stanley|jpmorgan|chase|bny|mellon|wells fargo|citi|citigroup|capital one|fidelity|vanguard|schwab|visa|mastercard|amex|american express|paypal|insurance|accounting|cpa|financial)\b/i},{key:"health",label:"Healthcare & Biotech",color:"#e74c3c",words:/\b(health|healthcare|medical|medicine|hospital|clinic|pharma|pharmaceutical|biotech|nurse|nursing|physician|doctor|dental|therapy|therapist|wellness)\b/i},{key:"education",label:"Education",color:"#f1c40f",words:/\b(university|college|school|education|student|professor|teacher|academy|phd|faculty|alumni)\b/i},{key:"media",label:"Marketing & Media",color:"#e67e22",words:/\b(marketing|brand|branding|advertising|agency|media|content|social media|creative|creator|influencer|pr|public relations|communications|tiktok|pinterest|youtube|growth marketing)\b/i},{key:"entertainment",label:"Entertainment & Gaming",color:"#9b59b6",words:/\b(entertainment|film|movie|music|records|gaming|games?|esports|studio|studios|netflix|disney|spotify|hollywood|theater|theatre|sports)\b/i},{key:"tech",label:"Tech, Software & AI",color:"#3498db",words:/\b(software|saas|engineer|engineering|developer|tech|technology|technologies|ai|artificial intelligence|machine learning|ml|data|cloud|cyber|security|devops|product manager|google|microsoft|apple|meta|amazon|nvidia|openai|anthropic|palantir|salesforce|oracle|ibm|intel|app|apps|startup|platform|labs?)\b/i},{key:"consulting",label:"Consulting, Legal & Services",color:"#1abc9c",words:/\b(consulting|consultant|consultancy|deloitte|accenture|mckinsey|kpmg|pwc|ey|bain|bcg|legal|law|lawyer|attorney|paralegal|recruiting|recruiter|staffing|talent)\b/i},{key:"realestate",label:"Real Estate & Construction",color:"#d35400",words:/\b(real estate|realtor|realty|property|properties|construction|architecture|architect|mortgage|homes|housing|builders?)\b/i},{key:"consumer",label:"Retail, Consumer & Hospitality",color:"#ff79c6",words:/\b(retail|e-?commerce|shopify|consumer|cpg|fashion|apparel|beauty|cosmetics|food|beverage|restaurant|hospitality|hotel|travel|tourism|coca-cola|nike|store|shop)\b/i},{key:"industry",label:"Manufacturing, Energy & Logistics",color:"#95a5a6",words:/\b(manufacturing|energy|oil|gas|solar|utilities|automotive|tesla|siemens|ge|general electric|honeywell|3m|logistics|supply chain|shipping|freight|fedex|ups|industrial|mining|chemicals?)\b/i},{key:"nonprofit",label:"Nonprofit & Community",color:"#bdc3c7",words:/\b(nonprofit|non-profit|foundation|charity|ngo|church|ministry|volunteer)\b/i}],a={key:"unknown",label:"Unclear",color:"#555c66"},i=Object.fromEntries([...o,a].map(e=>[e.key,e])),s=e=>i[e]||a;function l(e,t){for(let n of[e,t]){if(!n)continue;let e=o.find(e=>e.words.test(n));if(e)return e}return a}let c=(e,t)=>l(e,t).key,u=e=>n(e).level>=4;e.s(["INDUSTRIES",0,o,"UNKNOWN_INDUSTRY",0,a,"buildCompanyIndex",0,function(e=[]){let n=new Map,r=e=>(n.has(e)||n.set(e,(0,t.rolesWithCompanies)(e)),n.get(e)),{industries:o}=(0,t.networkCompanies)(e,{industryOf:c,rolesOf:r}),a=new Map;for(let t of e){let e=t.profile_url||`id:${t.id}`,n=a.get(e);(!n||(t.degree||9)<(n.degree||9))&&a.set(e,t)}let i=new Map;for(let e of a.values()){let t=r(e).find(e=>!e.former)?.company;if(!t)continue;let n=i.get(t);n||(n={name:t,people:[],d1:0,d2:0,d3:0,S:0,A:0,senior:0},i.set(t,n)),n.people.push(e),1===e.degree?n.d1++:2===e.degree?n.d2++:n.d3++,"S"===e.tier&&n.S++,"A"===e.tier&&n.A++,u(e.headline)&&n.senior++}for(let e of i.values())e.industry=s(o.get(e.name));return i},"companyLinks",0,function(e=[],t=[]){let n=new Map;for(let t of e){let e=r(t);e&&n.set(t.id,e)}let o=new Map;for(let e of t){let t=n.get(e.source_connection_id),a=r(e);if(!t||!a||t===a)continue;let[i,s]=t<a?[t,a]:[a,t],l=`${i}\u0000${s}`,c=o.get(l);c||(c={a:i,b:s,weight:0,via:new Set},o.set(l,c)),c.weight++,c.via.add(e.source_connection_id)}return[...o.values()]},"companyOf",0,r,"getSeniority",0,n,"industryByKey",0,s,"industryOf",0,l,"isSenior",0,u,"waysInto",0,function(e,t=[],n=[]){let o=t.filter(t=>r(t)===e),a=new Map;for(let t of n)r(t)===e&&a.set(t.source_connection_id,(a.get(t.source_connection_id)||0)+1);let i=new Map(t.map(e=>[e.id,e]));return{direct:o,bridges:[...a.entries()].map(([e,t])=>({bridge:i.get(e),n:t})).filter(e=>e.bridge).sort((e,t)=>t.n-e.n||(Number(t.bridge.power_score)||0)-(Number(e.bridge.power_score)||0))}}])},19821,e=>{"use strict";async function t(e){let t=e?`/api/network?userId=${encodeURIComponent(e)}`:"/api/network",n=await fetch(t);if(!n.ok)throw Error(`Failed to load network (${n.status})`);let r=await n.json();return{degree1:r.degree1||[],degree2:r.degree2||[],degree3:r.degree3||[]}}e.s(["loadNetwork",0,t])},36774,e=>{"use strict";let t=/^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/,n="/avatars/";e.s(["localPhoto",0,function(e){return"string"==typeof e&&e.startsWith(n)&&t.test(e.slice(n.length))?e:null}])},40996,e=>{"use strict";async function t(){let e=await fetch("/api/scraper");if(!e.ok)throw Error("Could not reach the app.");return e.json()}async function n(e,t,r={}){let o=await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:e,...t?{name:t}:{},...r})}),a=await o.json().catch(()=>({}));if(!o.ok)throw Error(a.error||"Could not start the scanner.");return a}async function r(e){let t=await fetch(`/api/scraper?resume=${encodeURIComponent(e)}`);return t.ok?(await t.json()).resume??null:null}Symbol.for("six-degrees.scan-state");let o=Object.freeze({skips:[],read:[]});async function a(){try{let e=await fetch("/api/scraper?reach=1");if(!e.ok)return o;let t=await e.json();return{skips:Array.isArray(t.skips)?t.skips:[],read:Array.isArray(t.read)?t.read:[]}}catch{return o}}async function i(e){let t=await fetch(`/api/scraper?person=${encodeURIComponent(e)}`);return t.ok?t.json():{person:null}}async function s(){await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"cancel"})})}let l=Object.freeze({known:!1,running:!1,pending:!1,action:null,target:null,startedAt:null,progress:null,log:[],finished:[]}),c=new Set,u=l,d=JSON.stringify(u),p=null,f=null,h=0,g=!1,y=new Set;function m(e){let t=JSON.stringify(e);if(t!==d)for(let n of(d=t,u=Object.freeze(e),[...c]))n()}async function b(){(!("u">typeof document&&document.hidden)||u.running)&&await function(){if(f&&f.epoch===h)return f.promise;let e=h,t=fetch("/api/scraper?job=1").then(e=>e.ok?e.json():null).then(t=>{e===h&&function(e){if(!e||"object"!=typeof e)return;let t=!0===e.running;t&&null!=e.startedAt&&y.add(e.startedAt);let n=(Array.isArray(e.recent)?e.recent:[]).filter(e=>y.has(e.startedAt)).map(n=>({...n,log:!t&&n.startedAt===e.startedAt&&Array.isArray(e.log)?e.log:[]}));m({known:!0,running:t||g,pending:g&&!t,action:t?e.action??null:g?u.action:null,target:t?e.target??null:g?u.target:null,startedAt:t?e.startedAt??null:null,progress:t?e.progress??null:null,log:t&&Array.isArray(e.log)?e.log:[],finished:n})}(t)}).catch(()=>{}).finally(()=>{f?.promise===t&&(f=null)});return f={epoch:e,promise:t},t}(),clearTimeout(p),p=c.size?setTimeout(b,u.running?1500:5e3):null}function w(e){return c.add(e),1===c.size&&b(),()=>{c.delete(e),c.size||(clearTimeout(p),p=null)}}async function x(e,{name:t,id:r,onLog:o,...a}={}){let i;h++,g=!0,m({...u,known:!0,running:!0,pending:!0,action:e,target:t||r?{id:r??null,name:t??null}:null,startedAt:null,progress:null,log:[]});try{i=await n(e,t,{...r?{id:r}:{},...a})}catch(e){throw g=!1,h++,u.pending&&m({...u,running:!1,pending:!1,action:null,target:null,log:[]}),b(),e}g=!1,h++;let s=i?.startedAt??null;null!=s&&y.add(s);let l=new Promise(e=>{let t=w(()=>{u.running&&u.startedAt===s&&o?.(u.log);let n=null==s?u.running?null:{exitCode:null,failure:null,log:[]}:u.finished.find(e=>e.startedAt===s);n&&(t(),n.log.length&&o?.(n.log),e(n))});b()});return{startedAt:s,ended:l}}function k(e){return!!e?.target&&["bridge","rescrape","resume"].includes(e.action)}e.s(["NO_SCAN_NOTES",0,o,"SCANNER_UNKNOWN",0,l,"beginScrape",0,x,"busyReason",0,function(e=u){if(!e?.running)return null;let t=e.target?.name;switch(e.action){case"bridge":case"rescrape":case"resume":return t?`${t}’s circle is being scanned`:"A circle is being scanned";case"company":return t?`${t} is being scanned`:"A company is being scanned";case"auto-bridge":case"auto-bridge-retry":case"resume-all":return"Your bridges are being mapped, one by one";case"full":case"refresh":return"Your own connections are being scanned";default:switch(e.action){case"install":return"The scanner's packages are being installed";case"setup":return"The scanner is being set up";case"login":return"The LinkedIn sign-in window is open";case"photos":return"Profile photos are being saved";default:return"A scan is running"}}},"isCircleScan",0,k,"loadScanNotes",0,a,"notReadyMessage",0,function(e){if(!e)return"Could not reach the app.";let t=e.checks||{};return t.scriptsFound?t.dependencies?t.chrome?null:"Google Chrome is not installed.":t.python||t.download?"The scanner is not set up yet — open Scan to set it up.":"No Python 3.10 to 3.14 is installed on this machine.":"The scanner files are missing from this install."},"pickedPerson",0,i,"resumePoint",0,r,"runScrape",0,function(e,t={}){return x(e,t).then(e=>e.ended)},"scannerNow",0,function(){return u},"scansCircleOf",0,function(e,t){if(!k(e)||!t)return!1;let{id:n,name:r}=e.target;return n&&t.id?n===t.id:!!r&&r===t.name},"scraperStatus",0,t,"stopScrape",0,s,"watchScanner",0,w],40996)},22016,(e,t,n)=>{"use strict";e.i(47167),Object.defineProperty(n,"__esModule",{value:!0});var r={default:function(){return m},useLinkStatus:function(){return w}};for(var o in r)Object.defineProperty(n,o,{enumerable:!0,get:r[o]});let a=e.r(90809),i=e.r(43476),s=a._(e.r(71645)),l=e.r(95057),c=e.r(8372),u=e.r(18581),d=e.r(18967),p=e.r(5550),f=e.r(88540),h=e.r(91949),g=e.r(73668),y=e.r(9396);function m(t){var n;let r,o,a,[m,w]=(0,s.useOptimistic)(h.IDLE_LINK_STATUS),x=(0,s.useRef)(null),{href:k,as:v,children:S,prefetch:j=null,passHref:C,replace:A,shallow:T,scroll:O,onClick:E,onMouseEnter:P,onTouchStart:N,legacyBehavior:_=!1,onNavigate:R,transitionTypes:$,ref:I,unstable_dynamicOnHover:z,...M}=t;r=S,_&&("string"==typeof r||"number"==typeof r)&&(r=(0,i.jsx)("a",{children:r}));let U=s.default.useContext(c.AppRouterContext),B=!1!==j,L=!1===j?"none":!0===j?"full":"auto",F="none"!==L?"auto"===L?y.FetchStrategy.PPR:y.FetchStrategy.Full:y.FetchStrategy.PPR,D="string"==typeof(n=v||k)?n:(0,l.formatUrl)(n);if(_){if(r?.$$typeof===Symbol.for("react.lazy"))throw Object.defineProperty(Error("`<Link legacyBehavior>` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's `<a>` tag."),"__NEXT_ERROR_CODE",{value:"E863",enumerable:!1,configurable:!0});o=s.default.Children.only(r)}let K=_?o&&"object"==typeof o&&o.ref:I,W,J=s.default.useCallback(e=>(null!==U&&(x.current=(0,h.mountLinkInstance)(e,D,U,F,B,w,W)),()=>{x.current&&((0,h.unmountLinkForCurrentNavigation)(x.current),x.current=null),(0,h.unmountPrefetchableInstance)(e)}),[B,D,U,F,w,W]),Y={ref:(0,u.useMergedRef)(J,K),onClick(t){_||"function"!=typeof E||E(t),_&&o.props&&"function"==typeof o.props.onClick&&o.props.onClick(t),!U||t.defaultPrevented||function(t,n,r,o,a,i,l,c="none"){if("u">typeof window){let u,{nodeName:d}=t.currentTarget;if("A"===d.toUpperCase()&&((u=t.currentTarget.getAttribute("target"))&&"_self"!==u||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.nativeEvent&&2===t.nativeEvent.which)||t.currentTarget.hasAttribute("download"))return;if(!(0,g.isLocalURL)(n)){o&&(t.preventDefault(),location.replace(n));return}if(t.preventDefault(),i){let e=!1;if(i({preventDefault:()=>{e=!0}}),e)return}let{dispatchNavigateAction:p}=e.r(99781);s.default.startTransition(()=>{p(n,o?"replace":"push",!1===a?f.ScrollBehavior.NoScroll:f.ScrollBehavior.Default,r.current,l,c)})}}(t,D,x,A,O,R,$,L)},onMouseEnter(e){_||"function"!=typeof P||P(e),_&&o.props&&"function"==typeof o.props.onMouseEnter&&o.props.onMouseEnter(e),U&&B&&(0,h.onNavigationIntent)(e.currentTarget,!0===z)},onTouchStart:function(e){_||"function"!=typeof N||N(e),_&&o.props&&"function"==typeof o.props.onTouchStart&&o.props.onTouchStart(e),U&&B&&(0,h.onNavigationIntent)(e.currentTarget,!0===z)}};return(0,d.isAbsoluteUrl)(D)?Y.href=D:_&&!C&&("a"!==o.type||"href"in o.props)||(Y.href=(0,p.addBasePath)(D)),a=_?s.default.cloneElement(o,Y):(0,i.jsx)("a",{...M,...Y,children:r}),(0,i.jsx)(b.Provider,{value:m,children:a})}let b=(0,s.createContext)(h.IDLE_LINK_STATUS),w=()=>(0,s.useContext)(b);("function"==typeof n.default||"object"==typeof n.default&&null!==n.default)&&void 0===n.default.__esModule&&(Object.defineProperty(n.default,"__esModule",{value:!0}),Object.assign(n.default,n),t.exports=n.default)},18581,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"useMergedRef",{enumerable:!0,get:function(){return o}});let r=e.r(71645);function o(e,t){let n=(0,r.useRef)(null),o=(0,r.useRef)(null);return(0,r.useCallback)(r=>{if(null===r){let e=n.current;e&&(n.current=null,e());let t=o.current;t&&(o.current=null,t())}else e&&(n.current=a(e,r)),t&&(o.current=a(t,r))},[e,t])}function a(e,t){if("function"!=typeof e)return e.current=t,()=>{e.current=null};{let n=e(t);return"function"==typeof n?n:()=>e(null)}}("function"==typeof n.default||"object"==typeof n.default&&null!==n.default)&&void 0===n.default.__esModule&&(Object.defineProperty(n.default,"__esModule",{value:!0}),Object.assign(n.default,n),t.exports=n.default)},18967,(e,t,n)=>{"use strict";e.i(47167),Object.defineProperty(n,"__esModule",{value:!0});var r={DecodeError:function(){return m},MiddlewareNotFoundError:function(){return k},MissingStaticPage:function(){return x},NormalizeError:function(){return b},PageNotFoundError:function(){return w},SP:function(){return g},ST:function(){return y},WEB_VITALS:function(){return a},execOnce:function(){return i},getDisplayName:function(){return d},getLocationOrigin:function(){return c},getURL:function(){return u},isAbsoluteUrl:function(){return l},isResSent:function(){return p},loadGetInitialProps:function(){return h},normalizeRepeatedSlashes:function(){return f},stringifyError:function(){return v}};for(var o in r)Object.defineProperty(n,o,{enumerable:!0,get:r[o]});let a=["CLS","FCP","FID","INP","LCP","TTFB"];function i(e){let t,n=!1;return(...r)=>(n||(n=!0,t=e(...r)),t)}let s=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,l=e=>{let t=e.charCodeAt(0);return!!(t>=65&&t<=90||t>=97&&t<=122)&&s.test(e)};function c(){let{protocol:e,hostname:t,port:n}=window.location;return`${e}//${t}${n?":"+n:""}`}function u(){let{href:e}=window.location,t=c();return e.substring(t.length)}function d(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function p(e){return e.finished||e.headersSent}function f(e){let t=e.split("?");return t[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(t[1]?`?${t.slice(1).join("?")}`:"")}async function h(e,t){let n=t.res||t.ctx&&t.ctx.res;if(!e.getInitialProps)return t.ctx&&t.Component?{pageProps:await h(t.Component,t.ctx)}:{};let r=await e.getInitialProps(t);if(n&&p(n))return r;if(!r)throw Object.defineProperty(Error(`"${d(e)}.getInitialProps()" should resolve to an object. But found "${r}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return r}let g="u">typeof performance,y=g&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class m extends Error{}class b extends Error{}class w extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class x extends Error{constructor(e,t){super(),this.message=`Failed to load static file for page: ${e} ${t}`}}class k extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function v(e){return JSON.stringify({message:e.message,stack:e.stack})}},73668,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"isLocalURL",{enumerable:!0,get:function(){return a}});let r=e.r(18967),o=e.r(52817);function a(e){if(!(0,r.isAbsoluteUrl)(e))return!0;try{let t=(0,r.getLocationOrigin)(),n=new URL(e,t);return n.origin===t&&(0,o.hasBasePath)(n.pathname)}catch(e){return!1}}},98183,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0});var r={assign:function(){return l},searchParamsToUrlQuery:function(){return a},urlQueryToSearchParams:function(){return s}};for(var o in r)Object.defineProperty(n,o,{enumerable:!0,get:r[o]});function a(e){let t={};for(let[n,r]of e.entries()){let e=t[n];void 0===e?t[n]=r:Array.isArray(e)?e.push(r):t[n]=[e,r]}return t}function i(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function s(e){let t=new URLSearchParams;for(let[n,r]of Object.entries(e))if(Array.isArray(r))for(let e of r)t.append(n,i(e));else t.set(n,i(r));return t}function l(e,...t){for(let n of t){for(let t of n.keys())e.delete(t);for(let[t,r]of n.entries())e.append(t,r)}return e}},95057,(e,t,n)=>{"use strict";e.i(47167),Object.defineProperty(n,"__esModule",{value:!0});var r={formatUrl:function(){return s},formatWithValidation:function(){return c},urlObjectKeys:function(){return l}};for(var o in r)Object.defineProperty(n,o,{enumerable:!0,get:r[o]});let a=e.r(90809)._(e.r(98183)),i=/https?|ftp|gopher|file/;function s(e){let{auth:t,hostname:n}=e,r=e.protocol||"",o=e.pathname||"",s=e.hash||"",l=e.query||"",c=!1;t=t?encodeURIComponent(t).replace(/%3A/i,":")+"@":"",e.host?c=t+e.host:n&&(c=t+(~n.indexOf(":")?`[${n}]`:n),e.port&&(c+=":"+e.port)),l&&"object"==typeof l&&(l=String(a.urlQueryToSearchParams(l)));let u=e.search||l&&`?${l}`||"";return r&&!r.endsWith(":")&&(r+=":"),e.slashes||(!r||i.test(r))&&!1!==c?(c="//"+(c||""),o&&"/"!==o[0]&&(o="/"+o)):c||(c=""),s&&"#"!==s[0]&&(s="#"+s),u&&"?"!==u[0]&&(u="?"+u),o=o.replace(/[?#]/g,encodeURIComponent),u=u.replace("#","%23"),`${r}${c}${o}${u}${s}`}let l=["auth","hash","host","hostname","href","path","pathname","port","protocol","query","search","slashes"];function c(e){return s(e)}},18566,(e,t,n)=>{t.exports=e.r(76562)}]);
@@ -6,6 +6,92 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.2] - 2026-09-29
10
+
11
+ ### Added
12
+ - **Orbit goes past 2nd degree.** Someone you added through a circle, whose own circle is
13
+ scanned, now sits in the fan of the circle they came from, green-ringed, and their people fan
14
+ out behind them a band further out, fainter, with a faint line to each. Their added people's
15
+ circles go further out again, to 6th degree. Before, they sat on the ring as one more bridge,
16
+ and Orbit showed only 2nd degree.
17
+ - **A ring round each connection's dot.** Five thin bars show how much of their circle is
18
+ scanned: all five once their list is read to the end, and part-way the pages read against
19
+ LinkedIn's own count of the list, which scans now keep (no extra traffic). Lists read before
20
+ scans kept a count show two. A small badge counts the people in their circle ready to scan,
21
+ and a catalyst is the green outline on the dot. It's on the Galaxy and on Bridge Chains'
22
+ bridges.
23
+
24
+ ### Changed
25
+ - **The Galaxy in bands when more than your connections are drawn.** With 2nd (or 3rd) degree
26
+ on, your connections sit on a tighter inner ring, their circles in a band outside it, and past
27
+ someone you added through a circle, their people further out again. Each band is sorted by
28
+ tier, S nearest, and has a faint label. Lines follow the chain: from you to your connections,
29
+ from each connection to their circle, and from someone you added to theirs. Your connections
30
+ alone keep the roomier tier rings.
31
+
32
+ ### Removed
33
+ - **The "N only here" line under each bridge in Bridge Chains.** Their card still says who is
34
+ reached only through them.
35
+
36
+ ## [0.4.1] - 2026-09-29
37
+
38
+ ### Added
39
+ - **Which connections open the same doors.** A card says whose circle overlaps theirs most, when
40
+ it's real (15% or more, and at least 3 people): "Opens the same doors as Tom: 62% of the
41
+ people either reaches, both do". Bridge Chains' Degrees box adds how much of your 2nd degree
42
+ you reach two or more ways.
43
+ - **Circle scans keep who among your connections knows whom.** Your own connections turn up in
44
+ other people's lists, and were set aside there and forgotten. Each is now kept as a tie
45
+ between the two, in a new table, with no extra LinkedIn traffic, since the scan read them
46
+ anyway. Nothing shows them yet: they're what clusters and communities will be built from.
47
+ Deleting someone deletes their ties too.
48
+ - **Who only one connection reaches.** In Bridge Chains, each connection with a scanned circle
49
+ shows how many of its people none of your other connections reach ("746 only here"), and
50
+ their card says it in a line: those people are reached through them alone. It's their
51
+ exclusive reach, counted across every circle you've scanned (`lib/brokerage.js`).
52
+ - **Network Circle's Filter picks degrees.** *1st*, *2nd* and *3rd* chips choose who the
53
+ Galaxy draws: your connections, anyone in a scanned circle, and anyone only a company scan
54
+ found. Any mix works, and 2nd without 1st shows who's valuable outside your own
55
+ connections. Each person counts once, at the nearest degree. Further out is drawn a little
56
+ smaller and fainter, gathered near the connection whose circle they're in, and the tier
57
+ counts follow what's drawn. It opens on your connections alone, as before.
58
+
59
+ ### Changed
60
+ - **The Galaxy marks its tier bands.** A faint dashed ring, labelled S to D, sits where each
61
+ tier's dots settle, so the bands read at a glance, most of all with a filter on. Not on a
62
+ phone, whose layout has no rings.
63
+ - **Degrees opens on Bridge Chains,** then Separation, then Orbit, in the menu too. Orbit
64
+ used to come first.
65
+ - **Orbit is in Degrees only.** Network Circle's Orbit was slow on a big network and showed
66
+ nothing the Galaxy doesn't; a Network Circle left on it opens the Galaxy.
67
+ - **A new app icon: Orbits.** Your tier rings round you, with one bridge reaching out through
68
+ them. It's the Mac app's icon, the website's tab icon and its home-screen icon.
69
+ - **The Scan page asks before a risky search budget.** Picking more than 100 searches a
70
+ day, or no monthly cap, now asks first, because a real account was restricted after 373
71
+ searches in 24 hours, run back to back (it had opened only 7 profiles that day). While
72
+ the budget stays that high, a note under it says so, with a button back to 50 a day and
73
+ 250 a month. Lowering a budget, or anything up to 100 a day, is never asked about.
74
+
75
+ ### Fixed
76
+ - **A card's circle map never draws a degree as a solid line.** A big circle, or the people
77
+ behind someone you added packed into their slice, used to run together into one line or
78
+ band. When a slice holds more than fit, its dots now take a few rows, neighbours
79
+ alternating between them, each still behind whoever they hang off, and the dots are sized
80
+ for the most crowded slice.
81
+ - **Bridge Chains: a hover preview no longer draws over the bridges.** With many bridges the
82
+ overview grows extra rings, and a bridge's circle, previewed on hover, landed on top of
83
+ them. It now starts beyond the outermost ring of bridges.
84
+ - **Stop during a re-map's wait keeps the circle.** Re-mapping someone deleted their circle
85
+ first and then waited out the minute between profile views, so a Stop in that minute left
86
+ it deleted and unread. It now waits first and deletes only once the read can start.
87
+ - **A profile-view cap typed into `scan-limits.json` by hand stays within the choices.** It
88
+ counts as the largest offered choice under it (10, 25, 50 or 100), in the scanner and on
89
+ the Scan page alike; before, 1,000 was honoured and the picker showed a different number.
90
+ - **The Filter menu highlights the view that's showing.** After *View Bridge →* from the
91
+ Galaxy, Orbit showed but no view was highlighted.
92
+ - **No *Scan Full Company* on the sample or a CSV import.** Paths offered it there, and it
93
+ searched LinkedIn for a network that isn't saved. A line says scanning needs your own network.
94
+
9
95
  ## [0.4.0] - 2026-09-28
10
96
 
11
97
  ### Added
@@ -11,6 +11,9 @@ export async function POST(request) {
11
11
  .eq('profile_url', profileUrl);
12
12
 
13
13
  if (error) return Response.json({ error: error.message }, { status: 500 });
14
+ // And every tie they're in (lib/ties.js).
15
+ await supabase.from('connection_ties').delete().eq('a_url', profileUrl);
16
+ await supabase.from('connection_ties').delete().eq('b_url', profileUrl);
14
17
  return Response.json({ success: true });
15
18
  } catch (err) {
16
19
  return Response.json({ error: err.message }, { status: 500 });
@@ -1,5 +1,6 @@
1
1
  import { db as supabase } from '../../../lib/db';
2
2
  import { uniqueByProfile, splitAlreadyConnected, toIsoDate, refreshNotifications, mutualCountOf } from '../../../lib/ingest';
3
+ import { tiePairs } from '../../../lib/ties';
3
4
  import { promoteToFirstDegree } from '../../../lib/promote';
4
5
  import { localPhoto } from '../../../lib/photos';
5
6
 
@@ -74,6 +75,18 @@ export async function POST(request) {
74
75
  const split = splitAlreadyConnected(records, firstDegree);
75
76
  records = split.keep;
76
77
  alreadyConnected = split.alreadyConnected;
78
+ // Each of your connections in someone's circle is a tie between the two
79
+ // (lib/ties.js). Kept quietly: a failure here never costs the scan's save.
80
+ if (degree === 2 && bridgeId && split.connected.length) {
81
+ try {
82
+ const { data: owner } = await supabase.from('linkedin_connections')
83
+ .select('profile_url').eq('id', bridgeId).eq('user_id', userId).limit(1);
84
+ const pairs = tiePairs(owner?.[0]?.profile_url, split.connected).map((p) => ({ user_id: userId, ...p }));
85
+ if (pairs.length) {
86
+ await supabase.from('connection_ties').upsert(pairs, { onConflict: 'user_id,a_url,b_url', ignoreDuplicates: true });
87
+ }
88
+ } catch { /* ties are extra; the circle is what matters */ }
89
+ }
77
90
  }
78
91
 
79
92
  // Pre-filter: find which profile_urls already exist for this specific context
@@ -139,11 +139,19 @@ function bridgeSkips() {
139
139
  */
140
140
  function scanNotes() {
141
141
  let read = [];
142
+ const lists = {};
142
143
  try {
143
144
  const me = resolveProfile({ create: false });
144
- if (me) read = Object.keys(readProgress(dataDir(), me.id));
145
+ if (me) {
146
+ const progress = readProgress(dataDir(), me.id);
147
+ read = Object.keys(progress);
148
+ // How far each list was read, for the rings round a dot (lib/reach.js scanBars).
149
+ for (const [url, e] of Object.entries(progress)) {
150
+ lists[url] = { pages: Number(e?.pages) || 0, more: !!e?.more, total: Number(e?.total) || null };
151
+ }
152
+ }
145
153
  } catch {}
146
- return { skips: bridgeSkips(), read };
154
+ return { skips: bridgeSkips(), read, lists };
147
155
  }
148
156
 
149
157
  /**
@@ -31,8 +31,10 @@ import Link from 'next/link';
31
31
  import { useRouter } from 'next/navigation';
32
32
  import { circleIndex } from '../../lib/circle';
33
33
  import { localPhoto } from '../../lib/photos';
34
- import { reachIndex, reachState, circleState, readyByCircle, circleScanCost } from '../../lib/reach';
35
- import { ringLayout, dotRadius } from '../../lib/chain-layout';
34
+ import { reachIndex, reachState, circleState, readyByCircle, circleScanCost, scanBars } from '../../lib/reach';
35
+ import { ringSegments, RING } from '../../lib/dot-rings';
36
+ import { ringLayout, dotRadius, previewBand, tierBandLayout, outerFans } from '../../lib/chain-layout';
37
+ import { redundancy } from '../../lib/brokerage';
36
38
  import { keyFor, score } from '../../lib/separation';
37
39
  import { hasRequest } from '../../lib/requests-client';
38
40
  import { watchScanner, scannerNow, isCircleScan } from '../../lib/scraper-client';
@@ -138,6 +140,7 @@ export default function ChainView({ connections, degree2 = [], onSelect, userNam
138
140
  const index = useMemo(() => circleIndex(all1, all2), [all1, all2]);
139
141
  const reach = useMemo(() => reachIndex(all1, all2, scanNotes), [all1, all2, scanNotes]);
140
142
  const readyCount = useMemo(() => readyByCircle(reach), [reach]);
143
+ const twoWays = useMemo(() => redundancy(fullDegree2 || degree2, fullDegree1 || connections), [fullDegree2, degree2, fullDegree1, connections]);
141
144
  const rowById = useMemo(() => {
142
145
  const m = new Map();
143
146
  for (const r of all2) m.set(r.id, r);
@@ -271,7 +274,7 @@ export default function ChainView({ connections, degree2 = [], onSelect, userNam
271
274
  {/* Their circle, previewed on hover, in a wedge that grows rows as it fills */}
272
275
  {hovBridge && (
273
276
  <CirclePreview bridge={hovBridge} members={membersOf(hovBridge, index)} reach={reach}
274
- cx={cx} cy={cy} maxR={maxR} still={still} />
277
+ cx={cx} cy={cy} maxR={maxR} still={still} band={previewBand(maxR, bridgeLayout.rings)} />
275
278
  )}
276
279
 
277
280
  {/* Bridge nodes — hover to preview their circle, click to open it */}
@@ -290,6 +293,20 @@ export default function ChainView({ connections, degree2 = [], onSelect, userNam
290
293
  <circle cx={b.x} cy={b.y} r={isHov ? 20 : 16}
291
294
  fill={`${TIER_COLORS[b.tier]}08`}
292
295
  stroke={`${TIER_COLORS[b.tier]}${isHov ? '60' : '25'}`} strokeWidth={1} />
296
+ {/* How much of their circle is scanned: five thin bars (design C) */}
297
+ {(() => {
298
+ const bars = scanBars(b, reach);
299
+ if (bars == null) return null;
300
+ return (
301
+ <g transform={`translate(${b.x} ${b.y})`} pointerEvents="none">
302
+ {ringSegments((isHov ? 20 : 16) + 3.5).map((seg) => (
303
+ <path key={seg.i} d={seg.d} fill="none" strokeLinecap="round" strokeWidth={1.6}
304
+ stroke={seg.i < bars ? RING.filled : RING.empty} />
305
+ ))}
306
+ <title>{bars === 5 ? 'Their whole list is scanned' : `About ${bars * 20}% of their list is scanned`}</title>
307
+ </g>
308
+ );
309
+ })()}
293
310
  {/* Node */}
294
311
  <circle cx={b.x} cy={b.y} r={isHov ? 13 : 11}
295
312
  fill={localPhoto(b.profile_image_url) ? '#1a1a2e' : TIER_COLORS[b.tier]}
@@ -372,6 +389,12 @@ export default function ChainView({ connections, degree2 = [], onSelect, userNam
372
389
  <div style={{ fontSize: 9, color: '#555', marginTop: 8 }}>
373
390
  {bridges.length} bridges · {d2S} S + {d2A} A at 2nd degree
374
391
  </div>
392
+ {twoWays > 0 && (
393
+ <div style={{ fontSize: 9, color: '#555', marginTop: 4 }}
394
+ title="The share of everyone you reach through a circle that more than one of your connections reaches">
395
+ {Math.round(twoWays * 100)}% of your 2nd degree you reach two or more ways
396
+ </div>
397
+ )}
375
398
  <div style={{ fontSize: 8, color: '#444', marginTop: 4 }}>Click a bridge to open their circle, then anyone in it to open theirs</div>
376
399
  </div>
377
400
  </div>
@@ -379,13 +402,13 @@ export default function ChainView({ connections, degree2 = [], onSelect, userNam
379
402
  }
380
403
 
381
404
  /** A bridge's circle on hover: the people you reached through it first, then their S, A and B. */
382
- function CirclePreview({ bridge, members, reach, cx, cy, maxR, still }) {
405
+ function CirclePreview({ bridge, members, reach, cx, cy, maxR, still, band }) {
383
406
  const shown = members.filter((m) => m.degree === 1 || m.tier === 'S' || m.tier === 'A' || m.tier === 'B');
384
- // Beyond the ring of bridges, inside the window at the overview's zoom.
385
- const inner = maxR * 0.66;
386
- const sweep = Math.min(Math.PI * 0.9, Math.max(Math.PI * 0.2, (shown.length * 11) / (maxR * 0.75)));
407
+ // Beyond every ring of bridges (lib/chain-layout.js previewBand).
408
+ const { inner, outer } = band;
409
+ const sweep = Math.min(Math.PI * 0.9, Math.max(Math.PI * 0.2, (shown.length * 11) / inner));
387
410
  const layout = ringLayout(shown.length, {
388
- inner, outer: maxR * 0.8, spacing: 11, minSpacing: 3.5, start: bridge.angle - sweep / 2, sweep,
411
+ inner, outer, spacing: 11, minSpacing: 3.5, start: bridge.angle - sweep / 2, sweep,
389
412
  });
390
413
  return (
391
414
  <g>
@@ -473,10 +496,25 @@ function CircleFocus({ trail, index, reach, dims, requests, scanningId, still, c
473
496
  const cx = dims.w / 2;
474
497
  const cy = dims.h / 2;
475
498
  const maxR = Math.min(cx, cy) - 30;
476
- const layout = useMemo(() => ringLayout(members.length, {
477
- inner: maxR * 0.55, innerMin: 62, outer: maxR * 0.94,
478
- spacing: Math.max(14, Math.min(24, maxR * 0.085)), minSpacing: 4,
479
- }), [members.length, maxR]);
499
+ // Whose own circle is known, drawn behind them (their people, one degree
500
+ // further out): anyone you're connected to whose circle was scanned, or who
501
+ // introduced you to someone.
502
+ const behind = useMemo(() => members.map((row) => (row.degree === 1 ? membersOf(row, index) : [])), [members, index]);
503
+ const hasFans = behind.some((people) => people.length > 0);
504
+ // Tier bands round them, S nearest, like Network Circle's orbits; with fans
505
+ // to draw, the bands leave the outside of the circle for them.
506
+ const layout = useMemo(() => tierBandLayout(members, {
507
+ inner: Math.max(62, maxR * 0.3), outer: maxR * (hasFans ? 0.72 : 0.94),
508
+ spacing: Math.max(14, Math.min(24, maxR * 0.085)), minSpacing: 4, gap: Math.max(10, maxR * 0.04),
509
+ }), [members, maxR, hasFans]);
510
+ const fans = useMemo(() => {
511
+ if (!hasFans) return [];
512
+ const placed = outerFans(
513
+ members.map((row, i) => ({ angle: layout.points[i].angle, slot: layout.points[i].slot, count: behind[i].length })),
514
+ { from: layout.edge + layout.spacing * 1.5 },
515
+ );
516
+ return placed.map((f, i) => f && { ...f, i, people: behind[i].slice(0, f.points.length) }).filter(Boolean);
517
+ }, [members, behind, hasFans, layout]);
480
518
 
481
519
  // What each dot is: someone you reached (ready, hidden, scanned) or a
482
520
  // 2nd-degree row, maybe with a request out; and how big their own circle is.
@@ -608,10 +646,33 @@ function CircleFocus({ trail, index, reach, dims, requests, scanningId, still, c
608
646
  <filter id="focusGlow"><feGaussianBlur stdDeviation="4" result="blur" /><feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge></filter>
609
647
  </defs>
610
648
  <g transform={`translate(${cx + view.x} ${cy + view.y}) scale(${k})`}>
611
- {/* Ring guides, one per ring of dots */}
612
- {layout.rings.map((ring) => (
613
- <circle key={ring.radius} r={ring.radius} fill="none" stroke={`${DEGREE_COLORS[2]}10`} strokeWidth={1 / k} />
649
+ {/* A faint band behind each tier's dots, in its colour */}
650
+ {layout.bands.map((band) => (
651
+ <circle key={band.tier} r={(band.inner + band.outer) / 2} fill="none"
652
+ stroke={TIER_COLORS[band.tier] || '#555'} strokeOpacity={0.07}
653
+ strokeWidth={Math.max(1 / k, band.outer - band.inner + layout.spacing)} pointerEvents="none" />
614
654
  ))}
655
+ {/* Behind anyone whose circle is known: their people, a faint line to each */}
656
+ {fans.map((fan) => {
657
+ const a = layout.points[fan.i];
658
+ const last = fan.points[fan.points.length - 1];
659
+ return (
660
+ <g key={'fan-' + members[fan.i].id} pointerEvents="none">
661
+ {fan.points.map((p, j) => (
662
+ <line key={'l' + j} x1={a.x} y1={a.y} x2={p.x} y2={p.y}
663
+ stroke={GREEN} strokeOpacity={0.12} strokeWidth={0.5 / k} />
664
+ ))}
665
+ {fan.points.map((p, j) => (
666
+ <circle key={'d' + j} cx={p.x} cy={p.y} r={2.2}
667
+ fill={TIER_COLORS[fan.people[j]?.tier] || '#555'} fillOpacity={0.75} />
668
+ ))}
669
+ {fan.more > 0 && last && (
670
+ <text x={last.x * 1.06} y={last.y * 1.06} textAnchor="middle" fontSize={8 / k} fill="#8b9a9a"
671
+ stroke="#0a0a1a" strokeWidth={2.4 / k} paintOrder="stroke">+{fan.more.toLocaleString()}</text>
672
+ )}
673
+ </g>
674
+ );
675
+ })}
615
676
  {dots}
616
677
  {/* The one under the pointer, on top */}
617
678
  {hp && (
@@ -12,7 +12,7 @@ function useIsMobile() {
12
12
  const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
13
13
 
14
14
 
15
- export default function FilterPanel({ collapsed, onToggle, mode, filter, onFilterChange, visualMode, onVisualModeChange, tierCounts, bridgeTierCounts }) {
15
+ export default function FilterPanel({ collapsed, onToggle, mode, filter, onFilterChange, visualMode, onVisualModeChange, tierCounts, bridgeTierCounts, degrees = [1], onDegreesChange, degreeCounts = {} }) {
16
16
  const isMobile = useIsMobile();
17
17
  const isDegreesMode = mode === 'degrees';
18
18
  // Same source of truth the renderer uses, so the menu can never offer a
@@ -105,6 +105,42 @@ export default function FilterPanel({ collapsed, onToggle, mode, filter, onFilte
105
105
  </div>
106
106
 
107
107
  {/* Tier Filters */}
108
+ {/* Network Circle: which degrees to draw. Any mix, never none: 2nd and
109
+ 3rd without your own connections shows who's valuable outside them. */}
110
+ {!isDegreesMode && onDegreesChange && (
111
+ <div style={{ marginBottom: 18 }}>
112
+ <div style={{ fontSize: 9, fontWeight: 700, color: '#555', letterSpacing: 1, marginBottom: 8, textTransform: 'uppercase' }}>
113
+ Degree
114
+ </div>
115
+ <div style={{ display: 'flex', gap: 6 }}>
116
+ {[[1, '1st', 'Your connections'], [2, '2nd', 'In a scanned circle'], [3, '3rd', 'Found by a company scan']].map(([d, label, what]) => {
117
+ const on = degrees.includes(d);
118
+ const count = degreeCounts[d] || 0;
119
+ const last = on && degrees.length === 1;
120
+ return (
121
+ <button
122
+ key={d}
123
+ title={`${what}: ${count.toLocaleString()}${last ? ' (keep at least one)' : ''}`}
124
+ aria-pressed={on}
125
+ disabled={last || (!on && !count)}
126
+ onClick={() => onDegreesChange(on ? degrees.filter((x) => x !== d) : [...degrees, d].sort())}
127
+ style={{
128
+ flex: 1, padding: '7px 4px', borderRadius: 8, fontSize: 11.5, fontWeight: 700,
129
+ cursor: last || (!on && !count) ? 'default' : 'pointer',
130
+ border: on ? '1px solid rgba(52,152,219,0.6)' : '1px solid rgba(255,255,255,0.08)',
131
+ background: on ? 'rgba(52,152,219,0.18)' : 'rgba(255,255,255,0.03)',
132
+ color: on ? '#cfe6f7' : count ? '#888' : '#444',
133
+ }}
134
+ >
135
+ {label}
136
+ <div style={{ fontSize: 9.5, fontWeight: 500, opacity: 0.8, marginTop: 2 }}>{count.toLocaleString()}</div>
137
+ </button>
138
+ );
139
+ })}
140
+ </div>
141
+ </div>
142
+ )}
143
+
108
144
  <div style={{ marginBottom: 18 }}>
109
145
  {/* In Degrees these chips filter by the tier of the connection who
110
146
  introduces you, not the person reached — Separation has its own