six-degrees 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +32 -4
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/backup/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/csv/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restore/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +4 -4
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +18 -18
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/insights/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/insights/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/insights/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/insights/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/insights/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/insights/page.js +13 -0
- package/.next/standalone/.next/server/app/insights/page.js.map +5 -0
- package/.next/standalone/.next/server/app/insights/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/insights/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/insights.html +1 -0
- package/.next/standalone/.next/server/app/insights.meta +26 -0
- package/.next/standalone/.next/server/app/insights.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_full.segment.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/insights.segments/insights/__PAGE__.segment.rsc +33 -0
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +21 -20
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +18 -18
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__063s0x7._.js → [root-of-the-server]__0r-0t-9._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ra6xyg._.js +15 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20rwfdw._.js → [root-of-the-server]__0rcpjad._.js} +5 -2
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_02iz_9q._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_05agvk3._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_0v84u66._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_db-client_0_dch35.js +9 -9
- package/.next/standalone/.next/server/chunks/{lib_rpc_0jnfp2e.js → lib_rpc_00nndai.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0b17r6v.js → lib_rpc_03ibb-x.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0doq0t-.js → lib_rpc_03ri3ji.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0fd2rlw.js → lib_rpc_078ce48.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0a69mgr.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_005abye.js → lib_rpc_0cej3t8.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0cmh4g6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0nx2_pc.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0r79-w1.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_0is4qoc.js → lib_rpc_0t552z6.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0yx8ynd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_12p-o9-.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1466dnb.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1sryxv6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1trn-x5.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1ygu_mv.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egamyq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04am_h8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_07zb7lu._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0_79idq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0gds903._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0klqt9z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1k9qfsk._.js → _14jak5s._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_14nxd3q._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1cj2yog._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1hdqw0-._.js +7 -0
- package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0gy11ar._.js → _1r62six._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_insights_page_actions_0-0tf75.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_01o3i5y._.js → app_components_settings_SectorPicker_1c-29qw.js} +2 -6
- package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_insights_page_1d_chja.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +14 -14
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +3 -3
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +4 -0
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/prefetch-hints.json +14 -0
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/02wnf8dkxegxg.js +34 -0
- package/.next/standalone/.next/static/chunks/0838_s6w-i_ik.js +1 -0
- package/.next/{static/chunks/2eo9pqq9gay3r.js → standalone/.next/static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/standalone/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/standalone/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/standalone/.next/static/chunks/112mv-com-mqa.js +21 -0
- package/.next/{static/chunks/44yn0tcd9dfk3.js → standalone/.next/static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/standalone/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{static/chunks/1qbjermbf68xj.js → standalone/.next/static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/standalone/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/standalone/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/standalone/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{static/chunks/2zf4jmz9l3li2.js → standalone/.next/static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/standalone/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/standalone/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/standalone/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/standalone/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{static/chunks/0an_b6zi1r_qq.js → standalone/.next/static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/standalone/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/.next/standalone/CHANGELOG.md +60 -0
- package/.next/standalone/README.md +25 -1
- package/.next/standalone/SECURITY.md +8 -0
- package/.next/standalone/app/api/outreach/route.js +13 -73
- package/.next/standalone/app/api/scraper/route.js +97 -4
- package/.next/standalone/app/components/AppHeader.js +22 -6
- package/.next/standalone/app/components/AppTabs.js +4 -0
- package/.next/standalone/app/components/AutoConnect.js +286 -0
- package/.next/standalone/app/components/ChainView.js +75 -2
- package/.next/standalone/app/components/FilterPanel.js +8 -1
- package/.next/standalone/app/components/LinkedInLimits.js +38 -13
- package/.next/standalone/app/components/OutlinkQuest.js +32 -11
- package/.next/standalone/app/components/ScanStatusBar.js +18 -3
- package/.next/standalone/app/components/ScanTrail.js +13 -3
- package/.next/standalone/app/components/SeparationView.js +6 -22
- package/.next/standalone/app/components/Sidebar.js +147 -38
- package/.next/standalone/app/components/insights/Boards.js +275 -0
- package/.next/standalone/app/components/insights/PeopleView.js +266 -0
- package/.next/standalone/app/components/insights/Rail.js +156 -0
- package/.next/standalone/app/components/insights/Report.js +382 -0
- package/.next/standalone/app/components/insights/parts.js +239 -0
- package/.next/standalone/app/components/settings/UsageSection.js +50 -3
- package/.next/standalone/app/globals.css +2 -3
- package/.next/standalone/app/insights/page.js +220 -0
- package/.next/standalone/app/page.js +1 -17
- package/.next/standalone/app/queue/page.js +30 -18
- package/.next/standalone/app/setup/page.js +16 -9
- package/.next/standalone/lib/auto-connect.js +145 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/in-progress.js +46 -0
- package/.next/standalone/lib/insights-board.js +613 -0
- package/.next/standalone/lib/linkedin-limits.js +36 -11
- package/.next/standalone/lib/requests-client.js +10 -0
- package/.next/standalone/lib/requests.js +124 -0
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scraper-client.js +18 -5
- package/.next/standalone/lib/settings.js +3 -0
- package/.next/standalone/lib/usage.js +5 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +569 -9
- package/.next/static/chunks/02wnf8dkxegxg.js +34 -0
- package/.next/static/chunks/0838_s6w-i_ik.js +1 -0
- package/.next/{standalone/.next/static/chunks/2eo9pqq9gay3r.js → static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/static/chunks/112mv-com-mqa.js +21 -0
- package/.next/{standalone/.next/static/chunks/44yn0tcd9dfk3.js → static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{standalone/.next/static/chunks/1qbjermbf68xj.js → static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{standalone/.next/static/chunks/2zf4jmz9l3li2.js → static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{standalone/.next/static/chunks/0an_b6zi1r_qq.js → static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/CHANGELOG.md +60 -0
- package/README.md +25 -1
- package/SECURITY.md +8 -0
- package/lib/auto-connect.js +145 -0
- package/lib/companies.js +1 -1
- package/lib/in-progress.js +46 -0
- package/lib/insights-board.js +613 -0
- package/lib/linkedin-limits.js +36 -11
- package/lib/requests-client.js +10 -0
- package/lib/requests.js +124 -0
- package/lib/scan-state.js +1 -0
- package/lib/scraper-client.js +18 -5
- package/lib/settings.js +3 -0
- package/lib/usage.js +5 -0
- package/package.json +1 -1
- package/scripts/scrape.py +569 -9
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_4y8z7._.js +0 -8
- package/.next/standalone/.next/server/chunks/_02twlca._.js +0 -10
- package/.next/standalone/.next/server/chunks/lib_rpc_0nef_n7.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0plrzra.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_13a4m0c.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1if17c-.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1j-zw7v.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1sngt-q.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1v2sgme.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vh-tgx.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1zaubz7.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_02sband._.js +0 -7
- package/.next/standalone/.next/server/chunks/ssr/_189g8lg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +0 -3
- package/.next/standalone/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/standalone/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/standalone/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/standalone/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/standalone/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/standalone/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/standalone/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/standalone/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/standalone/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/standalone/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/standalone/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- package/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// The cards beside the Power Index: Untapped, Kingmakers, Gatekeepers,
|
|
4
|
+
// Hidden giants, Company power, How sure, and Richest?, the honest answer to
|
|
5
|
+
// a question the app can't answer (it never sees money). Each card's top five,
|
|
6
|
+
// and a link to its board.
|
|
7
|
+
|
|
8
|
+
import { shortName } from '../../../lib/separation';
|
|
9
|
+
import Avatar from '../Avatar';
|
|
10
|
+
import { TIER_COLORS } from '../../../lib/themes';
|
|
11
|
+
import { fmt, one, pc, Card, CardHead, MiniRow, TierChip, Src, NeedsCircles, ELLIPSIS } from './parts';
|
|
12
|
+
|
|
13
|
+
export default function Rail({ data, heavy, open, source, onView, onHidden }) {
|
|
14
|
+
const { kings, gates, hidden, sure } = data;
|
|
15
|
+
const circles = kings.scanned > 0;
|
|
16
|
+
return (
|
|
17
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
|
|
18
|
+
{/* The four boards that read circles; with none scanned, one card says so once. */}
|
|
19
|
+
{!circles ? (
|
|
20
|
+
<Card>
|
|
21
|
+
<CardHead title="Kingmakers, Gatekeepers, Hidden giants, Untapped" sub="Who stands behind your connections, read from their circles" />
|
|
22
|
+
<NeedsCircles source={source} compact />
|
|
23
|
+
</Card>
|
|
24
|
+
) : (
|
|
25
|
+
<>
|
|
26
|
+
<Untapped open={open} />
|
|
27
|
+
|
|
28
|
+
<Card>
|
|
29
|
+
<CardHead title="Kingmakers" sub="Most S and A people behind them" more="Board →" onMore={() => onView('kingmakers')} />
|
|
30
|
+
{kings.list.slice(0, 5).map((k, i) => (
|
|
31
|
+
<MiniRow key={k.row.id} first={i === 0} rank={i + 1} value={fmt(k.SA)} label="S+A behind"
|
|
32
|
+
left={<Who row={k.row} sub={<><TierChip tier={k.row.tier} size={15} /><span style={ELLIPSIS}>circle of {fmt(k.size)}</span></>} />} />
|
|
33
|
+
))}
|
|
34
|
+
</Card>
|
|
35
|
+
|
|
36
|
+
<Card>
|
|
37
|
+
<CardHead title="Gatekeepers" sub={`S and A who reach you through one connection only. Top three: ${pc(gates.top3Share)} of your S and A reach.`}
|
|
38
|
+
more="Board →" onMore={() => onView('gatekeepers')} />
|
|
39
|
+
{gates.list.slice(0, 5).map((g, i) => (
|
|
40
|
+
<MiniRow key={g.row.id} first={i === 0} rank={i + 1} value={fmt(g.onlySA)} valueColor="var(--sd-green, #00ff88)" label="S+A only via them"
|
|
41
|
+
left={<Who row={g.row} sub={<><TierChip tier={g.row.tier} size={15} /><span style={ELLIPSIS}>circle of {fmt(g.size)}</span></>} />} />
|
|
42
|
+
))}
|
|
43
|
+
</Card>
|
|
44
|
+
|
|
45
|
+
<Card>
|
|
46
|
+
<CardHead title={`Hidden giants · ${fmt(hidden.count)}`} sub="S-tier with one way in: the connection who knows them is the only door you have."
|
|
47
|
+
more={hidden.count ? 'All →' : null} onMore={onHidden} />
|
|
48
|
+
{hidden.count === 0 ? <div style={{ fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)', padding: '2px 0 6px' }}>None yet: every S-tier person you can reach has two or more ways in.</div>
|
|
49
|
+
: hidden.list.slice(0, 4).map((h, i) => (
|
|
50
|
+
<MiniRow key={h.key} first={i === 0} value={one(h.score)} label={h.from === 'linkedin' ? '1 mutual (LinkedIn)' : '1 way in (scans)'}
|
|
51
|
+
left={<Who row={h.row} sub={<span style={ELLIPSIS}>{[h.row.company, h.via ? `only via ${shortName(h.via.name)}` : 'only via a connection the app can’t name'].filter(Boolean).join(' · ')}</span>} />} />
|
|
52
|
+
))}
|
|
53
|
+
{hidden.count > 0 && <Src>{fmt(hidden.fromLinkedIn)} from LinkedIn’s own mutual count, {fmt(hidden.fromScans)} from the ways in your scans saw, which can only go up.</Src>}
|
|
54
|
+
</Card>
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<Card>
|
|
59
|
+
<CardHead title="Company power" sub="Everyone there now, power added up" more={heavy ? 'Board →' : null} onMore={() => onView('companies')} />
|
|
60
|
+
{!heavy ? <Counting /> : heavy.companies.list.slice(0, 5).map((c, i) => (
|
|
61
|
+
<MiniRow key={c.name} first={i === 0} rank={i + 1} value={fmt(Math.round(c.total))} label="total power"
|
|
62
|
+
left={<span style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
|
|
63
|
+
<Logo name={c.name} />
|
|
64
|
+
<span style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
|
|
65
|
+
<b style={{ fontSize: 12.5, fontWeight: 650, ...ELLIPSIS }}>{c.name}</b>
|
|
66
|
+
<span style={{ fontSize: 11.5, color: 'var(--sd-fg-3, #8b9a9a)', ...ELLIPSIS }}>
|
|
67
|
+
<span style={{ color: TIER_COLORS.S }}>{fmt(c.S)} S</span> · <span style={{ color: TIER_COLORS.A }}>{fmt(c.A)} A</span> · {fmt(c.people)} people
|
|
68
|
+
</span>
|
|
69
|
+
</span>
|
|
70
|
+
</span>} />
|
|
71
|
+
))}
|
|
72
|
+
</Card>
|
|
73
|
+
|
|
74
|
+
<Card>
|
|
75
|
+
<CardHead title="How sure" sub="How much of this ranking rests on a guess" />
|
|
76
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
|
|
77
|
+
<b style={{ fontSize: 26, fontWeight: 850, color: sure.share >= 50 ? 'var(--sd-orange, #FF6B35)' : 'var(--sd-fg-1, #fff)' }}>{pc(sure.share)}</b>
|
|
78
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.4 }}>
|
|
79
|
+
of these scores lean on an estimate{sure.tie ? <>{sure.share >= 50 ? ', which is why' : '. At the top,'} <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{fmt(sure.tie.count)}</b> people share {one(sure.tie.score)}</> : null}.
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
<Src>{fmt(sure.company + sure.both)} with a company no list knows, {fmt(sure.title + sure.both)} with a title the app couldn’t read. Score the companies you know on the Scores tab and the top of the list separates.</Src>
|
|
83
|
+
</Card>
|
|
84
|
+
|
|
85
|
+
<Richest heavy={heavy} />
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function Untapped({ open }) {
|
|
91
|
+
return (
|
|
92
|
+
<Card style={{ padding: '14px 16px' }}>
|
|
93
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
|
|
94
|
+
<b style={{ fontSize: 30, fontWeight: 850, color: 'var(--sd-orange, #FF6B35)', fontVariantNumeric: 'tabular-nums' }}>{fmt(open?.total)}</b>
|
|
95
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.45 }}>
|
|
96
|
+
<b style={{ color: 'var(--sd-fg-1, #fff)' }}>Untapped:</b> S and A people in your 2nd degree with no request out. {fmt(open?.S)} of them S. Requests marked sent so far: {fmt(open?.asked)}.
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
</Card>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** "Richest?": the app never sees money, and says so; power at big companies is what it can count. */
|
|
104
|
+
function Richest({ heavy }) {
|
|
105
|
+
return (
|
|
106
|
+
<Card dashed>
|
|
107
|
+
<CardHead title="Richest?" />
|
|
108
|
+
<p style={{ fontSize: 12.5, color: 'var(--sd-fg-1, #e9edf3)', margin: '6px 0 8px', lineHeight: 1.5 }}>
|
|
109
|
+
Six Degrees doesn’t know anyone’s money. LinkedIn shows titles and companies, not wealth. The closest honest measure is
|
|
110
|
+
power: who runs the biggest companies you can reach.
|
|
111
|
+
</p>
|
|
112
|
+
{!heavy ? <Counting /> : (
|
|
113
|
+
<div style={{ display: 'flex', gap: 10 }}>
|
|
114
|
+
<Stat n={heavy.companies.big.people} text={`people whose score rests on a company scored 8 or more (Fortune 500 scale)${heavy.companies.big.people ? `, at ${fmt(heavy.companies.big.companies)} compan${heavy.companies.big.companies === 1 ? 'y' : 'ies'}` : ''}`} />
|
|
115
|
+
<Stat n={heavy.ladder.rungs.find((r) => r.key === 'csuite').total} text="C-suite and founders within two steps, by their headline" />
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
<Src>{heavy ? heavy.companies.bigSource : null}</Src>
|
|
119
|
+
</Card>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function Stat({ n, text }) {
|
|
124
|
+
return (
|
|
125
|
+
<div style={{ flex: 1, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.05)', borderRadius: 9, padding: '8px 10px', minWidth: 0 }}>
|
|
126
|
+
<b style={{ fontSize: 20, fontWeight: 800, display: 'block', fontVariantNumeric: 'tabular-nums' }}>{fmt(n)}</b>
|
|
127
|
+
<span style={{ fontSize: 11, color: 'var(--sd-fg-3, #8b9a9a)', lineHeight: 1.35, display: 'block' }}>{text}</span>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function Who({ row, sub }) {
|
|
133
|
+
return (
|
|
134
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
|
|
135
|
+
<Avatar person={row} size={26} tierColors={TIER_COLORS} />
|
|
136
|
+
<span style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
|
|
137
|
+
<b style={{ fontSize: 12.5, fontWeight: 650, ...ELLIPSIS }}>{row.name}</b>
|
|
138
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 11.5, color: 'var(--sd-fg-3, #8b9a9a)', minWidth: 0 }}>{sub}</span>
|
|
139
|
+
</span>
|
|
140
|
+
</span>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function Logo({ name, size = 26 }) {
|
|
145
|
+
const letters = String(name || '').split(/\s+/).filter(Boolean).map((w) => w[0]).join('').slice(0, 2).toUpperCase();
|
|
146
|
+
return (
|
|
147
|
+
<span style={{
|
|
148
|
+
width: size, height: size, borderRadius: 7, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.08)', flexShrink: 0,
|
|
149
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 800, color: 'var(--sd-fg-2, #b8c4c4)',
|
|
150
|
+
}}>{letters}</span>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function Counting() {
|
|
155
|
+
return <div style={{ fontSize: 12, color: 'var(--sd-fg-4, #6f7a88)', padding: '4px 0 6px' }}>Counting…</div>;
|
|
156
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// Insights → Report (mock-up C): the same numbers as the boards, as a page you
|
|
4
|
+
// scroll, one section each: a big number, one line that says what it means,
|
|
5
|
+
// a picture of it, and where it came from. Nothing here is computed apart
|
|
6
|
+
// from lib/insights-board.js.
|
|
7
|
+
|
|
8
|
+
import Link from 'next/link';
|
|
9
|
+
import Avatar from '../Avatar';
|
|
10
|
+
import { TIER_COLORS } from '../../../lib/themes';
|
|
11
|
+
import { shortName } from '../../../lib/separation';
|
|
12
|
+
import { RARITY } from '../../../lib/rarity';
|
|
13
|
+
import { DECIDES } from '../../../lib/insights-board';
|
|
14
|
+
import { fmt, one, pc, TierChip, Bars, NeedsCircles, LINE, CARD_BG, ELLIPSIS } from './parts';
|
|
15
|
+
|
|
16
|
+
const TIERS = ['S', 'A', 'B', 'C', 'D'];
|
|
17
|
+
const today = () => new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' });
|
|
18
|
+
|
|
19
|
+
function Section({ id, no, big, bigStyle, head, lede, children, src, isMobile, last }) {
|
|
20
|
+
return (
|
|
21
|
+
<section id={id} style={{ padding: isMobile ? '34px 0 30px' : '44px 0 40px', borderBottom: last ? 'none' : `1px solid ${LINE}`, scrollMarginTop: 80 }}>
|
|
22
|
+
<div style={{ fontSize: 12, fontWeight: 800, color: 'var(--sd-fg-4, #6f7a88)', letterSpacing: 1.5, marginBottom: 8 }}>{String(no).padStart(2, '0')}</div>
|
|
23
|
+
<div style={{ fontSize: isMobile ? 56 : 84, lineHeight: 0.95, fontWeight: 850, letterSpacing: isMobile ? -2 : -3, fontVariantNumeric: 'tabular-nums', overflowWrap: 'anywhere', ...bigStyle }}>{big}</div>
|
|
24
|
+
<h2 style={{ fontSize: isMobile ? 21 : 26, lineHeight: 1.2, margin: '12px 0 8px', fontWeight: 750, letterSpacing: -0.4, maxWidth: 720, color: 'var(--sd-fg-1, #fff)' }}>{head}</h2>
|
|
25
|
+
{lede && <p style={{ fontSize: 15, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.55, margin: '0 0 20px', maxWidth: 700 }}>{lede}</p>}
|
|
26
|
+
{children}
|
|
27
|
+
{src && <div style={{ fontSize: 11.5, color: 'var(--sd-fg-4, #6f7a88)', marginTop: 16, lineHeight: 1.5, maxWidth: 760 }}>{src}</div>}
|
|
28
|
+
</section>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const W = ({ children }) => <b style={{ color: 'var(--sd-fg-1, #fff)' }}>{children}</b>;
|
|
33
|
+
const Key = ({ color, children }) => (
|
|
34
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
|
|
35
|
+
<i style={{ width: 9, height: 9, borderRadius: 3, background: color, display: 'inline-block' }} />{children}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** A row of a bar chart: label, bar, number. */
|
|
40
|
+
function BarRow({ label, parts, max, value, cols = '170px 1fr 64px', bold, isMobile }) {
|
|
41
|
+
return (
|
|
42
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '118px 1fr 54px' : cols, gap: 12, alignItems: 'center', margin: '7px 0' }}>
|
|
43
|
+
<span style={{ fontSize: 13, color: bold ? 'var(--sd-fg-1, #fff)' : 'var(--sd-fg-2, #b8c4c4)', fontWeight: bold ? 650 : 400, display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, ...ELLIPSIS }}>{label}</span>
|
|
44
|
+
<Bars parts={parts} max={max} />
|
|
45
|
+
<b style={{ fontSize: 13.5, fontWeight: 750, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{value}</b>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const tierParts = (counts) => TIERS.map((t) => ({ value: counts[t] || 0, color: TIER_COLORS[t], ink: TIER_COLORS[t], label: t, title: `${fmt(counts[t])} ${t}` }));
|
|
51
|
+
|
|
52
|
+
export default function Report({ data, heavy, open, net, source, isMobile }) {
|
|
53
|
+
const { reach, kings, gates, hidden, grid, cover, sure } = data;
|
|
54
|
+
const circles = kings.scanned > 0;
|
|
55
|
+
const own = source === 'own';
|
|
56
|
+
const S = reach.tiers.d1.S + reach.tiers.d2.S;
|
|
57
|
+
const A = reach.tiers.d1.A + reach.tiers.d2.A;
|
|
58
|
+
const k0 = kings.list[0];
|
|
59
|
+
const g0 = gates.list[0];
|
|
60
|
+
const toc = [
|
|
61
|
+
['reach', 'Reach'], ['power', 'Power'], ['titles', 'Titles'],
|
|
62
|
+
...(circles ? [['kingmakers', 'Kingmakers'], ['gatekeepers', 'Gatekeepers'], ['hidden', 'Hidden giants'], ['untapped', 'Untapped']] : [['kingmakers', 'Circles']]),
|
|
63
|
+
['companies', 'Company power'], ['richest', 'Richest?'], ['sure', 'How sure'], ['left', 'What’s left'],
|
|
64
|
+
];
|
|
65
|
+
const sec = { isMobile };
|
|
66
|
+
// Sections are numbered as they're shown: with no circles, one section stands in for the four that read them.
|
|
67
|
+
let count = 0;
|
|
68
|
+
const no = () => ++count;
|
|
69
|
+
return (
|
|
70
|
+
<div>
|
|
71
|
+
{/* ── Cover ── */}
|
|
72
|
+
<div style={{ padding: isMobile ? '24px 0 24px' : '30px 0 30px', borderBottom: `1px solid ${LINE}` }}>
|
|
73
|
+
<div style={{ fontSize: 12, letterSpacing: 1.6, textTransform: 'uppercase', color: 'var(--sd-fg-3, #8b9a9a)', fontWeight: 700 }}>Network report · {today()}</div>
|
|
74
|
+
<h1 style={{ fontSize: isMobile ? 40 : 58, lineHeight: 1.02, margin: '12px 0', fontWeight: 850, letterSpacing: isMobile ? -1 : -1.5, color: 'var(--sd-fg-1, #fff)' }}>
|
|
75
|
+
Your network,<br />
|
|
76
|
+
<em style={{ fontStyle: 'normal', background: 'linear-gradient(135deg, var(--sd-gold, #FFD700), var(--sd-orange, #FF6B35) 55%, var(--sd-purple, #9B59B6))', WebkitBackgroundClip: 'text', backgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>by power.</em>
|
|
77
|
+
</h1>
|
|
78
|
+
<p style={{ fontSize: 16, color: 'var(--sd-fg-2, #b8c4c4)', margin: 0, lineHeight: 1.5, maxWidth: 620 }}>
|
|
79
|
+
{source === 'csv'
|
|
80
|
+
? <>What your {fmt(reach.d1)} connections add up to: where the power is, and what a scan of their circles would open.</>
|
|
81
|
+
: <>What your {fmt(reach.d1)} connections and the {fmt(reach.circles)} circle{reach.circles === 1 ? '' : 's'} you’ve scanned add up to: where the power is, who holds the doors, and what’s still closed.</>}
|
|
82
|
+
</p>
|
|
83
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(4, 1fr)', gap: 12, marginTop: 28 }}>
|
|
84
|
+
<CoverStat n={reach.total} label={circles ? 'people within two steps' : 'connections'} />
|
|
85
|
+
<CoverStat n={S} label="S-tier in reach" color={TIER_COLORS.S} />
|
|
86
|
+
{circles
|
|
87
|
+
? <CoverStat n={open?.total} label="S and A not asked" color="var(--sd-orange, #FF6B35)" />
|
|
88
|
+
: <CoverStat n={heavy ? heavy.companies.count : null} label="companies they work at" />}
|
|
89
|
+
<CoverStat n={reach.circles} label={`circle${reach.circles === 1 ? '' : 's'} scanned of ${fmt(reach.d1)}`} />
|
|
90
|
+
</div>
|
|
91
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 24 }}>
|
|
92
|
+
{toc.map(([id, label]) => (
|
|
93
|
+
<a key={id} href={`#${id}`} style={{ fontSize: 12, color: 'var(--sd-fg-2, #b8c4c4)', padding: '5px 10px', borderRadius: 16, border: `1px solid ${LINE}`, background: CARD_BG, textDecoration: 'none' }}>{label}</a>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{/* ── 01 Reach ── */}
|
|
99
|
+
<Section id="reach" no={no()} {...sec} big={fmt(reach.total)} head={circles ? 'people within two steps of you.' : 'connections, and no circles yet.'}
|
|
100
|
+
lede={circles
|
|
101
|
+
? <>Your {fmt(reach.d1)} connections, and everyone in the {fmt(reach.circles)} circle{reach.circles === 1 ? '' : 's'} you’ve scanned so far. Each circle added about <W>{fmt(cover.perCircle)}</W> people you didn’t already reach.</>
|
|
102
|
+
: source === 'csv'
|
|
103
|
+
? 'A CSV import is your connections only: LinkedIn’s export has no circles. A scan of their circles adds the people one step further out.'
|
|
104
|
+
: <>Your {fmt(reach.d1)} connections. Their circles add the people one step further out, once they’re scanned.</>}
|
|
105
|
+
src={`${reach.source}${reach.sharedRows ? ` ${fmt(reach.sharedRows)} times, someone turned up in another circle as well; they count once here.` : ''}`}>
|
|
106
|
+
<div style={{ display: 'flex', gap: 2, height: 22, borderRadius: 6, overflow: 'hidden' }}>
|
|
107
|
+
<span style={{ width: `${(100 * reach.d1) / Math.max(1, reach.total)}%`, background: 'var(--sd-green, #00ff88)' }} />
|
|
108
|
+
<span style={{ width: `${(100 * reach.d2) / Math.max(1, reach.total)}%`, background: 'var(--sd-orange, #FF6B35)' }} />
|
|
109
|
+
</div>
|
|
110
|
+
<div style={{ display: 'flex', gap: 22, marginTop: 10, fontSize: 13, color: 'var(--sd-fg-2, #b8c4c4)', flexWrap: 'wrap' }}>
|
|
111
|
+
<Key color="var(--sd-green, #00ff88)"><W>{fmt(reach.d1)}</W> your connections</Key>
|
|
112
|
+
{circles && <Key color="var(--sd-orange, #FF6B35)"><W>{fmt(reach.d2)}</W> in the circles you’ve scanned</Key>}
|
|
113
|
+
{reach.d3 > 0 && <Key color="var(--sd-fg-5, #556)"><W>{fmt(reach.d3)}</W> more from company scans, not counted here</Key>}
|
|
114
|
+
</div>
|
|
115
|
+
</Section>
|
|
116
|
+
|
|
117
|
+
{/* ── 02 Power ── */}
|
|
118
|
+
<Section id="power" no={no()} {...sec} big={fmt(S)} bigStyle={{ color: TIER_COLORS.S }}
|
|
119
|
+
head={reach.tiers.d2.S > reach.tiers.d1.S ? 'of them are S-tier, and most sit one step out.' : 'of them are S-tier.'}
|
|
120
|
+
lede={circles
|
|
121
|
+
? <><W>{fmt(reach.tiers.d2.S)}</W> S-tier people are in your 2nd degree against <W>{fmt(reach.tiers.d1.S)}</W> among your own connections. Another {fmt(A)} are A.</>
|
|
122
|
+
: <>That’s {pc((100 * reach.tiers.d1.S) / Math.max(1, reach.d1))} of your {fmt(reach.d1)} connections. Another {fmt(A)} are A.</>}
|
|
123
|
+
src="Tiers as Scores → Tiers sets them: on your network’s curve unless you chose the fixed scale. A tier is a statement about network position, not human worth.">
|
|
124
|
+
<BarRow {...sec} label="Your connections" parts={tierParts(reach.tiers.d1)} value={`${fmt(reach.tiers.d1.S)} S`} cols="150px 1fr 80px" />
|
|
125
|
+
{circles && <BarRow {...sec} label="Your 2nd degree" parts={tierParts(reach.tiers.d2)} value={`${fmt(reach.tiers.d2.S)} S`} cols="150px 1fr 80px" />}
|
|
126
|
+
</Section>
|
|
127
|
+
|
|
128
|
+
{/* ── 03 Titles ── */}
|
|
129
|
+
<Section id="titles" no={no()} {...sec} big={heavy ? fmt(heavy.ladder.decide) : '…'}
|
|
130
|
+
head={heavy && heavy.ladder.rungs.find((r) => r.key === 'owner').total ? 'run something: C-suite, founders, owners and VPs.' : 'run something: C-suite, founders and VPs.'}
|
|
131
|
+
lede={heavy ? (() => {
|
|
132
|
+
const r = Object.fromEntries(heavy.ladder.rungs.map((x) => [x.key, x]));
|
|
133
|
+
return <>{fmt(r.csuite.total)} C-suite and founders, {fmt(r.vp.total)} VPs, partners and GMs{r.owner.total ? `, ${fmt(r.owner.total)} owners` : ''}. {fmt(heavy.ladder.decideYours)} of them are already your connections.</>;
|
|
134
|
+
})() : 'Reading every headline…'}
|
|
135
|
+
src={heavy?.ladder.source}>
|
|
136
|
+
{heavy && (() => {
|
|
137
|
+
const rungs = heavy.ladder.rungs.filter((r) => r.total);
|
|
138
|
+
const max = Math.max(1, ...rungs.map((r) => r.total));
|
|
139
|
+
return rungs.map((r) => {
|
|
140
|
+
const top = DECIDES.includes(r.key);
|
|
141
|
+
return <BarRow key={r.key} {...sec} label={r.label} bold={top} max={max} value={fmt(r.total)}
|
|
142
|
+
parts={[{ value: r.total, color: top ? 'var(--sd-fg-1, #e8ecf2)' : 'rgba(var(--sd-ink, 255, 255, 255), 0.4)' }]} />;
|
|
143
|
+
});
|
|
144
|
+
})()}
|
|
145
|
+
</Section>
|
|
146
|
+
|
|
147
|
+
{/* ── Kingmakers, Gatekeepers, Hidden giants and Untapped read circles; with none, one section says so ── */}
|
|
148
|
+
{!circles ? (
|
|
149
|
+
<Section id="kingmakers" no={no()} {...sec} big="No circles" bigStyle={{ fontSize: isMobile ? 40 : 56, letterSpacing: -1.5, color: 'var(--sd-fg-3, #8b9a9a)' }}
|
|
150
|
+
head="Kingmakers, Gatekeepers, Hidden giants and Untapped read your connections’ circles.">
|
|
151
|
+
<NeedsCircles source={source} />
|
|
152
|
+
</Section>
|
|
153
|
+
) : (<>
|
|
154
|
+
{/* ── Kingmakers ── */}
|
|
155
|
+
<Section id="kingmakers" no={no()} {...sec} big={fmt(k0.SA)} bigStyle={{ color: TIER_COLORS.S }}
|
|
156
|
+
head={<>S and A people sit behind {k0.row.name}. Your biggest door is {k0.row.tier}-tier.</>}
|
|
157
|
+
lede={<>{k0.row.name}’s circle holds {fmt(k0.S)} S and {fmt(k0.A)} A, of {fmt(k0.size)} people. A title says what someone holds; a circle says what they can open.</>}
|
|
158
|
+
src={`${kings.source} A strong circle adds up to 2 to someone’s own score.`}>
|
|
159
|
+
<>
|
|
160
|
+
{kings.list.slice(0, 6).map((k) => (
|
|
161
|
+
<BarRow key={k.row.id} {...sec} cols="230px 1fr 50px" max={k0.SA} value={fmt(k.SA)}
|
|
162
|
+
label={<><Avatar person={k.row} size={22} tierColors={TIER_COLORS} /><span style={ELLIPSIS}>{k.row.name}</span><TierChip tier={k.row.tier} size={16} /></>}
|
|
163
|
+
parts={[{ value: k.S, color: TIER_COLORS.S, ink: TIER_COLORS.S, label: 'S' }, { value: k.A, color: TIER_COLORS.A, ink: TIER_COLORS.A, label: 'A' }]} />
|
|
164
|
+
))}
|
|
165
|
+
<div style={{ display: 'flex', gap: 16, fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)', marginTop: 10 }}>
|
|
166
|
+
<Key color={TIER_COLORS.S}>S behind them</Key><Key color={TIER_COLORS.A}>A behind them</Key>
|
|
167
|
+
</div>
|
|
168
|
+
</>
|
|
169
|
+
</Section>
|
|
170
|
+
|
|
171
|
+
{/* ── Gatekeepers ── */}
|
|
172
|
+
<Section id="gatekeepers" no={no()} {...sec} big={pc(gates.onlyShare)} bigStyle={{ color: 'var(--sd-green, #00ff88)' }}
|
|
173
|
+
head="of the S and A people you can reach have one way in."
|
|
174
|
+
lede={g0 ? <>Your reach hangs on a few people. Three connections carry <W>{pc(gates.top3Share)}</W> of it, and if {g0.row.name} disappeared from LinkedIn tomorrow, <W>{fmt(g0.onlySA)}</W> S and A people would drop off your map.</> : null}
|
|
175
|
+
src={gates.source}>
|
|
176
|
+
<>
|
|
177
|
+
<div style={{ display: 'flex', gap: 2, height: 34, borderRadius: 8, overflow: 'hidden' }}>
|
|
178
|
+
{gates.top3.map((g, i) => (
|
|
179
|
+
<span key={g.row.id} style={{ width: `${g.share}%`, background: ['#00ff88', '#00c46a', '#00984f'][i], display: 'flex', alignItems: 'center', paddingLeft: 10, overflow: 'hidden' }}>
|
|
180
|
+
<em style={{ fontStyle: 'normal', fontSize: 12, fontWeight: 750, color: '#00170b', whiteSpace: 'nowrap' }}>{shortName(g.row.name)}</em>
|
|
181
|
+
</span>
|
|
182
|
+
))}
|
|
183
|
+
<span style={{ flex: 1, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.1)', display: 'flex', alignItems: 'center', paddingLeft: 10 }}>
|
|
184
|
+
<em style={{ fontStyle: 'normal', fontSize: 12, color: 'var(--sd-fg-2, #b8c4c4)', whiteSpace: 'nowrap' }}>{fmt(Math.max(0, gates.list.length - 3))} others</em>
|
|
185
|
+
</span>
|
|
186
|
+
</div>
|
|
187
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)', gap: 10, marginTop: 14 }}>
|
|
188
|
+
{gates.list.slice(0, 3).map((g) => (
|
|
189
|
+
<div key={g.row.id} style={{ display: 'flex', gap: 10, alignItems: 'center', padding: '10px 12px', border: `1px solid ${LINE}`, borderRadius: 10, background: CARD_BG, fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.35 }}>
|
|
190
|
+
<Avatar person={g.row} size={30} tierColors={TIER_COLORS} />
|
|
191
|
+
<span><b style={{ color: 'var(--sd-green, #00ff88)', fontSize: 16 }}>{fmt(g.onlySA)}</b> S and A only through <W>{g.row.name}</W></span>
|
|
192
|
+
</div>
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
</>
|
|
196
|
+
</Section>
|
|
197
|
+
|
|
198
|
+
{/* ── Hidden giants ── */}
|
|
199
|
+
<Section id="hidden" no={no()} {...sec} big={fmt(hidden.count)} bigStyle={{ color: 'var(--sd-cyan, #00E5FF)' }}
|
|
200
|
+
head="hidden giants: S-tier, one way in." lede="The connection who knows them is the only door you have."
|
|
201
|
+
src={hidden.source}>
|
|
202
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 10 }}>
|
|
203
|
+
{hidden.list.slice(0, 6).map((h) => (
|
|
204
|
+
<div key={h.key} style={{ display: 'flex', gap: 12, alignItems: 'center', padding: '12px 14px', border: `1px solid ${LINE}`, borderRadius: 12, background: CARD_BG, minWidth: 0 }}>
|
|
205
|
+
<Avatar person={h.row} size={36} tierColors={TIER_COLORS} />
|
|
206
|
+
<div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
207
|
+
<b style={{ fontSize: 14, ...ELLIPSIS }}>{h.row.name}</b>
|
|
208
|
+
<span style={{ fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)', ...ELLIPSIS }}>{h.row.company || h.row.headline}</span>
|
|
209
|
+
<span style={{ fontSize: 11.5, color: 'var(--sd-fg-2, #b8c4c4)', ...ELLIPSIS }}>
|
|
210
|
+
only via {h.via ? h.via.name : 'a connection the app can’t name'} · {h.from === 'linkedin' ? '1 mutual connection (LinkedIn)' : '1 way in (your scans)'}
|
|
211
|
+
</span>
|
|
212
|
+
</div>
|
|
213
|
+
<em style={{ fontStyle: 'normal', fontWeight: 800, fontSize: 17, color: TIER_COLORS.S }}>{one(h.score)}</em>
|
|
214
|
+
</div>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
217
|
+
</Section>
|
|
218
|
+
|
|
219
|
+
{/* ── Untapped ── */}
|
|
220
|
+
<Section id="untapped" no={no()} {...sec} big={fmt(open?.total)} bigStyle={{ color: 'var(--sd-orange, #FF6B35)' }}
|
|
221
|
+
head={open?.askedSA === 0 ? 'S and A people you haven’t asked. That’s all of them.' : 'S and A people you haven’t asked.'}
|
|
222
|
+
lede={<><W>{fmt(grid.grid.S.warm)}</W> of the S-tier are warm, with 31 or more mutual connections: the easiest asks you have. <W>{fmt(grid.grid.S.only)}</W> are the hidden giants above.</>}
|
|
223
|
+
src={`${open?.source} ${grid.source}`}>
|
|
224
|
+
<RarityGrid grid={grid.grid} isMobile={isMobile} />
|
|
225
|
+
</Section>
|
|
226
|
+
</>)}
|
|
227
|
+
|
|
228
|
+
{/* ── 08 Company power ── */}
|
|
229
|
+
<Section id="companies" no={no()} {...sec} big={heavy?.companies.list[0]?.name || '…'} bigStyle={{ fontSize: isMobile ? 38 : 56, letterSpacing: -1.5, color: 'var(--sd-fg-1, #fff)' }}
|
|
230
|
+
head="holds the most power you can reach."
|
|
231
|
+
lede={heavy?.companies.list[0] ? (() => {
|
|
232
|
+
const c = heavy.companies.list[0];
|
|
233
|
+
const ind = heavy.industries.list[0];
|
|
234
|
+
return <>{fmt(c.people)} people work there, {fmt(c.S)} of them S-tier, with <W>{fmt(c.waysIn)}</W> way{c.waysIn === 1 ? '' : 's'} in.{ind ? <> By industry, <W>{ind.label}</W> holds {pc(ind.share)} of your S and A.</> : null}</>;
|
|
235
|
+
})() : null}
|
|
236
|
+
src={heavy ? `${heavy.companies.source} ${heavy.industries.source}` : null}>
|
|
237
|
+
{heavy && (() => {
|
|
238
|
+
const cos = heavy.companies.list.slice(0, 5);
|
|
239
|
+
const max = cos[0]?.total || 1;
|
|
240
|
+
return (
|
|
241
|
+
<>
|
|
242
|
+
{cos.map((c) => (
|
|
243
|
+
<BarRow key={c.name} {...sec} cols="190px 1fr 64px" max={max} value={fmt(Math.round(c.total))} label={c.name}
|
|
244
|
+
parts={[{ value: c.total, color: 'var(--sd-gold, #FFD700)', title: `${fmt(c.S)} S, ${fmt(c.people)} people` }]} />
|
|
245
|
+
))}
|
|
246
|
+
<div style={{ display: 'flex', gap: 18, flexWrap: 'wrap', marginTop: 14, fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)' }}>
|
|
247
|
+
{heavy.industries.list.slice(0, 4).map((e) => <Key key={e.key} color={e.color}>{e.label} <W>{pc(e.share)}</W></Key>)}
|
|
248
|
+
</div>
|
|
249
|
+
</>
|
|
250
|
+
);
|
|
251
|
+
})()}
|
|
252
|
+
</Section>
|
|
253
|
+
|
|
254
|
+
{/* ── 09 Richest? ── */}
|
|
255
|
+
<Section id="richest" no={no()} {...sec} big="Richest?" bigStyle={{ fontSize: isMobile ? 40 : 56, letterSpacing: -1.5, color: 'var(--sd-gold, #FFD700)' }}
|
|
256
|
+
head="The app can’t know, and won’t guess."
|
|
257
|
+
lede="Six Degrees doesn’t know anyone’s money. LinkedIn shows titles and companies, not wealth. The closest honest measure is power: who runs the biggest companies you can reach."
|
|
258
|
+
src={heavy?.companies.bigSource}>
|
|
259
|
+
<ul style={{ listStyle: 'none', padding: 0, margin: '0 0 18px', display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
260
|
+
{['Net worth', 'Pay', 'Company revenue', 'Funding raised'].map((x) => (
|
|
261
|
+
<li key={x} style={{ fontSize: 12.5, color: 'var(--sd-fg-3, #8b9a9a)', padding: '5px 10px', borderRadius: 16, border: '1px solid rgba(255,80,80,0.3)', background: 'rgba(255,80,80,0.05)', textDecoration: 'line-through' }}>{x}</li>
|
|
262
|
+
))}
|
|
263
|
+
</ul>
|
|
264
|
+
{heavy && (
|
|
265
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)', gap: 10 }}>
|
|
266
|
+
<Dashed n={heavy.companies.big.people} text="people whose score rests on a company scored 8 or more, Fortune 500 scale" />
|
|
267
|
+
<Dashed n={heavy.ladder.rungs.find((r) => r.key === 'csuite').total} text="C-suite and founders within two steps, by their headline" />
|
|
268
|
+
<Dashed n={data.index.people.filter((p) => p.score >= 8).length} text="people at 8 power or more" />
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
</Section>
|
|
272
|
+
|
|
273
|
+
{/* ── 10 How sure ── */}
|
|
274
|
+
<Section id="sure" no={no()} {...sec} big={pc(sure.share)} bigStyle={{ color: sure.share >= 50 ? 'var(--sd-orange, #FF6B35)' : 'var(--sd-fg-1, #fff)' }}
|
|
275
|
+
head="of these scores lean on an estimate."
|
|
276
|
+
lede={heavy ? <>{fmt(heavy.companies.estimated)} of {fmt(heavy.companies.count)} companies here are ones no list knows and you haven’t scored, so they score from how many of your people work there.{sure.tie ? <> {sure.share >= 50 ? 'That’s why' : 'At the top,'} <W>{fmt(sure.tie.count)}</W> people tie at {one(sure.tie.score)}.</> : null} Score the companies you know on the Scores tab and the top of every list separates.</> : null}
|
|
277
|
+
src={sure.source}>
|
|
278
|
+
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)', gap: 10 }}>
|
|
279
|
+
<Plain n={heavy ? heavy.companies.estimated : null} text={`compan${heavy?.companies.estimated === 1 ? 'y' : 'ies'} estimated`} />
|
|
280
|
+
<Plain n={sure.title + sure.both} text="titles the app couldn’t read" />
|
|
281
|
+
<Plain n={sure.tie?.count ?? 0} text={sure.tie ? `people tied at ${one(sure.tie.score)}, the top tie` : 'no big tie at the top'} />
|
|
282
|
+
</div>
|
|
283
|
+
</Section>
|
|
284
|
+
|
|
285
|
+
{/* ── 11 What's left ── */}
|
|
286
|
+
<Section id="left" no={no()} {...sec} last big={fmt(cover.todoS)} bigStyle={{ color: TIER_COLORS.S }}
|
|
287
|
+
head={`S-tier circle${cover.todoS === 1 ? ' is' : 's are'} still closed.`}
|
|
288
|
+
lede={source === 'csv'
|
|
289
|
+
? 'A CSV import is your connections only. Scanning your own network opens their circles.'
|
|
290
|
+
: <>You’ve scanned {fmt(cover.scanned)} of {fmt(cover.total)} circles, {fmt(cover.byTier.S.scanned)} of them S-tier{cover.hidden ? `, and ${fmt(cover.hidden)} keep their list hidden` : ''}. {cover.strongest.length ? <>The next {cover.strongest.length === 1 ? 'strongest is' : `${cover.strongest.length} strongest are`} below: up to {fmt(cover.nextSearches)} searches, about {fmt(cover.nextDays)} day{cover.nextDays === 1 ? '' : 's'} at {fmt(cover.daily)} a day.</> : null}</>}
|
|
291
|
+
src={source === 'csv' ? null : <>{cover.source} Lists read to the end: {fmt(cover.lists.full)}; stopped partway: {fmt(cover.lists.partial)}; not recorded: {fmt(cover.lists.unknown)}.</>}>
|
|
292
|
+
{TIERS.map((t) => {
|
|
293
|
+
const c = cover.byTier[t];
|
|
294
|
+
return (
|
|
295
|
+
<div key={t} style={{ display: 'grid', gridTemplateColumns: '30px 1fr 90px', gap: 12, alignItems: 'center', margin: '5px 0' }}>
|
|
296
|
+
<TierChip tier={t} />
|
|
297
|
+
<Bars max={c.total || 1} height={12} parts={[{ value: c.scanned, color: TIER_COLORS[t] }, { value: c.hidden, color: 'rgba(var(--sd-ink, 255, 255, 255), 0.25)' }]} />
|
|
298
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-3, #8b9a9a)', textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}><W>{fmt(c.scanned)}</W> of {fmt(c.total)}</span>
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
})}
|
|
302
|
+
{cover.strongest.length > 0 && source !== 'csv' && (
|
|
303
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 16 }}>
|
|
304
|
+
{cover.strongest.map((p) => {
|
|
305
|
+
const chip = <><Avatar person={p} size={22} tierColors={TIER_COLORS} />{p.name}<span style={{ color: 'var(--sd-fg-4, #6f7a88)' }}>{one(p.power_score)}</span></>;
|
|
306
|
+
const style = { display: 'inline-flex', gap: 7, alignItems: 'center', fontSize: 12.5, padding: '5px 11px 5px 5px', borderRadius: 18, border: `1px solid ${LINE}`, background: CARD_BG, color: 'var(--sd-fg-1, #fff)', textDecoration: 'none' };
|
|
307
|
+
return own
|
|
308
|
+
? <Link key={p.id} href={`/setup?scan=${encodeURIComponent(p.id)}`} title={`Scan ${p.name}’s circle: opens the Scan page to confirm`} style={style}>{chip}</Link>
|
|
309
|
+
: <span key={p.id} style={style}>{chip}</span>;
|
|
310
|
+
})}
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
{own && (
|
|
314
|
+
<Link href="/setup" style={{ display: 'inline-flex', fontSize: 13, fontWeight: 750, color: '#000', background: 'linear-gradient(135deg, #00ff88, #1abc9c)', padding: '8px 14px', borderRadius: 8, marginTop: 16, textDecoration: 'none' }}>
|
|
315
|
+
Open Scan →
|
|
316
|
+
</Link>
|
|
317
|
+
)}
|
|
318
|
+
</Section>
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function CoverStat({ n, label, color }) {
|
|
324
|
+
return (
|
|
325
|
+
<div style={{ borderLeft: `2px solid ${LINE}`, padding: '2px 0 2px 14px' }}>
|
|
326
|
+
<b style={{ display: 'block', fontSize: 30, fontWeight: 800, color: color || 'var(--sd-fg-1, #fff)', fontVariantNumeric: 'tabular-nums' }}>{n == null ? 'None' : fmt(n)}</b>
|
|
327
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-3, #8b9a9a)' }}>{label}</span>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function Dashed({ n, text }) {
|
|
333
|
+
return (
|
|
334
|
+
<div style={{ padding: 14, border: '1px dashed rgba(255,215,0,0.35)', borderRadius: 12, background: 'rgba(255,215,0,0.035)' }}>
|
|
335
|
+
<b style={{ fontSize: 28, fontWeight: 800, display: 'block', fontVariantNumeric: 'tabular-nums' }}>{fmt(n)}</b>
|
|
336
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.4, display: 'block', marginTop: 2 }}>{text}</span>
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function Plain({ n, text }) {
|
|
342
|
+
return (
|
|
343
|
+
<div style={{ padding: 14, border: `1px solid ${LINE}`, borderRadius: 12, background: CARD_BG }}>
|
|
344
|
+
<b style={{ fontSize: 26, fontWeight: 800, display: 'block', fontVariantNumeric: 'tabular-nums' }}>{n == null ? '…' : fmt(n)}</b>
|
|
345
|
+
<span style={{ fontSize: 12.5, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.4 }}>{text}</span>
|
|
346
|
+
</div>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** S and A by rarity: the cells shaded by how many, the hidden giants and the warm S marked. */
|
|
351
|
+
function RarityGrid({ grid, isMobile }) {
|
|
352
|
+
const max = Math.max(1, ...['S', 'A'].flatMap((t) => RARITY.map((r) => grid[t][r.key])));
|
|
353
|
+
return (
|
|
354
|
+
<div style={{ display: 'grid', gridTemplateColumns: `34px repeat(5, minmax(0, 1fr))`, gap: 4 }}>
|
|
355
|
+
<div />
|
|
356
|
+
{RARITY.map((r) => (
|
|
357
|
+
<div key={r.key} style={{ fontSize: isMobile ? 10 : 11.5, color: 'var(--sd-fg-3, #8b9a9a)', textAlign: 'center', paddingBottom: 4, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 5 }}>
|
|
358
|
+
<i className="sd-dot-html" style={{ width: 8, height: 8, borderRadius: '50%', background: r.color, flexShrink: 0 }} />{isMobile ? r.range : r.label}
|
|
359
|
+
</div>
|
|
360
|
+
))}
|
|
361
|
+
{['S', 'A'].map((t) => (
|
|
362
|
+
<div key={t} style={{ display: 'contents' }}>
|
|
363
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}><TierChip tier={t} /></div>
|
|
364
|
+
{RARITY.map((r) => {
|
|
365
|
+
const v = grid[t][r.key];
|
|
366
|
+
const hot = t === 'S' && r.key === 'only';
|
|
367
|
+
const warm = t === 'S' && r.key === 'warm';
|
|
368
|
+
return (
|
|
369
|
+
<div key={r.key} title={`${fmt(v)} ${t}-tier, ${r.label.toLowerCase()} (${r.range} mutual)`} style={{
|
|
370
|
+
height: 44, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 15, fontWeight: 700, fontVariantNumeric: 'tabular-nums',
|
|
371
|
+
background: `rgba(0, 229, 255, ${(0.06 + 0.45 * Math.sqrt(v / max)).toFixed(3)})`, color: 'var(--sd-fg-1, #eaf6f8)',
|
|
372
|
+
// An outline, not a shadow: Analyst takes every shadow off, and these mark the hidden giants and the warm S.
|
|
373
|
+
outline: hot ? `2px solid ${TIER_COLORS.S}` : warm ? '2px solid #FF7043' : 'none', outlineOffset: -2,
|
|
374
|
+
}}>{fmt(v)}</div>
|
|
375
|
+
);
|
|
376
|
+
})}
|
|
377
|
+
</div>
|
|
378
|
+
))}
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|