SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
151.9 KB · 1,737 lines javascript
Raw Blame History
1#!/usr/bin/env node2/**3 * Company Atlas — mock API for development and QA (NOT used in production).4 * Dependency-free Node HTTP server on :8371 serving contract-shaped sample data for every endpoint in docs/API.md:5 * ~40 companies across countries/industries, events of every type with careful wording, metrics, rankings, industries,6 * countries, signals, trends, map buckets, sensors/snapshots/changes/diff, in-memory watchlists/alerts, SSE live stream7 * (one event every ~4 s) and admin payloads. Data is generated from a fixed seed so SSR and client agree.8 *9 * Run: node apps/web/qa/mock-api.mjs [port]10 */11import { createServer } from 'node:http';1213const PORT = Number(process.argv[2] ?? process.env.PORT ?? 8371);14const NOW = Date.now();15const DAY = 86_400_000;1617// ------------------------------------------------------------------------------------------------ deterministic random18let seed = 20260912;19function rnd() {20  seed |= 0;21  seed = (seed + 0x6d2b79f5) | 0;22  let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);23  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;24  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;25}26const ri = (a, b) => a + Math.floor(rnd() * (b - a + 1));27const pick = (arr) => arr[Math.floor(rnd() * arr.length)];28const chance = (p) => rnd() < p;29const B32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';30let idc = 1000;31function id(prefix) {32  idc += 7;33  let n = idc * 9973 + 12345;34  let s = '';35  for (let i = 0; i < 16; i++) {36    s = B32[(n + i * 7) % 32] + s;37    n = Math.floor(n / 3) + i * 131;38  }39  return `${prefix}_${s}`;40}41const iso = (t) => new Date(t).toISOString();42const day = (t) => new Date(t).toISOString().slice(0, 10);43const slugify = (s) =>44  s45    .toLowerCase()46    .replace(/&/g, ' and ')47    .replace(/[^a-z0-9]+/g, '-')48    .replace(/^-|-$/g, '');49const r1 = (x) => Math.round(x * 10) / 10;50const clamp = (x, a, b) => Math.max(a, Math.min(b, x));5152// ------------------------------------------------------------------------------------------------ reference data53const INDUSTRIES = [54  ['fintech', 'Fintech', 'financial-services'],55  ['financial-services', 'Financial services', null],56  ['banking', 'Banking', 'financial-services'],57  ['software', 'Software', 'technology'],58  ['technology', 'Technology', null],59  ['artificial-intelligence', 'Artificial intelligence', 'technology'],60  ['semiconductors', 'Semiconductors', 'technology'],61  ['cloud-infrastructure', 'Cloud infrastructure', 'technology'],62  ['e-commerce', 'E-commerce', 'retail'],63  ['retail', 'Retail', null],64  ['automotive', 'Automotive', 'manufacturing'],65  ['manufacturing', 'Manufacturing', null],66  ['aerospace', 'Aerospace', 'manufacturing'],67  ['energy', 'Energy', null],68  ['renewable-energy', 'Renewable energy', 'energy'],69  ['healthcare', 'Healthcare', null],70  ['biotech', 'Biotech', 'healthcare'],71  ['pharmaceuticals', 'Pharmaceuticals', 'healthcare'],72  ['telecom', 'Telecom', null],73  ['media', 'Media', null],74  ['logistics', 'Logistics', null],75  ['insurance', 'Insurance', 'financial-services'],76  ['real-estate', 'Real estate', null],77  ['consumer', 'Consumer goods', null],78  ['cybersecurity', 'Cybersecurity', 'technology'],79];80const IND = Object.fromEntries(INDUSTRIES.map(([slug, name, parent]) => [slug, { slug, name, parent_slug: parent }]));8182const COUNTRY_META = {83  US: ['United States', 'North America', 39.8, -98.6],84  CA: ['Canada', 'North America', 56.1, -106.3],85  GB: ['United Kingdom', 'Europe', 55.4, -3.4],86  DE: ['Germany', 'Europe', 51.2, 10.4],87  FR: ['France', 'Europe', 46.2, 2.2],88  NL: ['Netherlands', 'Europe', 52.1, 5.3],89  SE: ['Sweden', 'Europe', 60.1, 18.6],90  CH: ['Switzerland', 'Europe', 46.8, 8.2],91  IE: ['Ireland', 'Europe', 53.4, -8.2],92  ES: ['Spain', 'Europe', 40.5, -3.7],93  JP: ['Japan', 'Asia', 36.2, 138.3],94  KR: ['South Korea', 'Asia', 35.9, 127.8],95  IN: ['India', 'Asia', 20.6, 79.0],96  SG: ['Singapore', 'Asia', 1.35, 103.8],97  AU: ['Australia', 'Oceania', -25.3, 133.8],98  BR: ['Brazil', 'South America', -14.2, -51.9],99  MX: ['Mexico', 'North America', 23.6, -102.6],100  AE: ['United Arab Emirates', 'Middle East', 23.4, 53.8],101  IL: ['Israel', 'Middle East', 31.0, 34.9],102  ZA: ['South Africa', 'Africa', -30.6, 22.9],103  NG: ['Nigeria', 'Africa', 9.1, 8.7],104  TW: ['Taiwan', 'Asia', 23.7, 121.0],105  CN: ['China', 'Asia', 35.9, 104.2],106  FI: ['Finland', 'Europe', 61.9, 25.7],107};108109// [name, domain, country, city, lat, lon, industries, public, ticker, exchange, founded, employees_band, importance, description]110const SEED = [111  ['Stripe', 'stripe.com', 'US', 'South San Francisco', 37.65, -122.4, ['fintech', 'software'], false, null, null, 2010, '5,001–10,000', 92, 'Payments infrastructure for the internet.'],112  ['Adyen', 'adyen.com', 'NL', 'Amsterdam', 52.37, 4.9, ['fintech'], true, 'ADYEN', 'Euronext', 2006, '1,001–5,000', 84, 'Global payments platform for enterprises.'],113  ['Block', 'block.xyz', 'US', 'Oakland', 37.8, -122.27, ['fintech'], true, 'XYZ', 'NYSE', 2009, '10,001+', 83, 'Economic empowerment tools: Square, Cash App, TIDAL.'],114  ['Shopify', 'shopify.com', 'CA', 'Ottawa', 45.42, -75.7, ['e-commerce', 'software'], true, 'SHOP', 'TSX', 2006, '5,001–10,000', 90, 'Commerce platform for merchants of every size.'],115  ['Lightspeed', 'lightspeedhq.com', 'CA', 'Montréal', 45.5, -73.57, ['software', 'retail'], true, 'LSPD', 'TSX', 2005, '1,001–5,000', 62, 'Point-of-sale and commerce platform.'],116  ['Wealthsimple', 'wealthsimple.com', 'CA', 'Toronto', 43.65, -79.38, ['fintech'], false, null, null, 2014, '1,001–5,000', 58, 'Investing, spending and saving app.'],117  ['Cohere', 'cohere.com', 'CA', 'Toronto', 43.65, -79.38, ['artificial-intelligence'], false, null, null, 2019, '201–500', 74, 'Enterprise AI models and retrieval.'],118  ['Anthropic', 'anthropic.com', 'US', 'San Francisco', 37.77, -122.42, ['artificial-intelligence'], false, null, null, 2021, '1,001–5,000', 91, 'AI safety and research company building Claude.'],119  ['NVIDIA', 'nvidia.com', 'US', 'Santa Clara', 37.35, -121.95, ['semiconductors', 'artificial-intelligence'], true, 'NVDA', 'NASDAQ', 1993, '10,001+', 98, 'Accelerated computing and AI platforms.'],120  ['Apple', 'apple.com', 'US', 'Cupertino', 37.32, -122.03, ['technology', 'consumer'], true, 'AAPL', 'NASDAQ', 1976, '10,001+', 99, 'Consumer hardware, software and services.'],121  ['Snowflake', 'snowflake.com', 'US', 'Bozeman', 45.68, -111.04, ['cloud-infrastructure', 'software'], true, 'SNOW', 'NYSE', 2012, '5,001–10,000', 80, 'AI Data Cloud.'],122  ['Datadog', 'datadoghq.com', 'US', 'New York', 40.71, -74.0, ['software', 'cloud-infrastructure'], true, 'DDOG', 'NASDAQ', 2010, '5,001–10,000', 79, 'Observability and security platform.'],123  ['Cloudflare', 'cloudflare.com', 'US', 'San Francisco', 37.77, -122.42, ['cloud-infrastructure', 'cybersecurity'], true, 'NET', 'NYSE', 2009, '1,001–5,000', 85, 'Connectivity cloud.'],124  ['Revolut', 'revolut.com', 'GB', 'London', 51.5, -0.12, ['fintech', 'banking'], false, null, null, 2015, '5,001–10,000', 78, 'Global financial super-app.'],125  ['Monzo', 'monzo.com', 'GB', 'London', 51.5, -0.12, ['banking', 'fintech'], false, null, null, 2015, '1,001–5,000', 60, 'Digital bank.'],126  ['Arm', 'arm.com', 'GB', 'Cambridge', 52.2, 0.12, ['semiconductors'], true, 'ARM', 'NASDAQ', 1990, '5,001–10,000', 82, 'CPU architecture and IP.'],127  ['SAP', 'sap.com', 'DE', 'Walldorf', 49.3, 8.64, ['software'], true, 'SAP', 'XETRA', 1972, '10,001+', 88, 'Enterprise application software.'],128  ['Siemens', 'siemens.com', 'DE', 'Munich', 48.14, 11.58, ['manufacturing', 'technology'], true, 'SIE', 'XETRA', 1847, '10,001+', 90, 'Industrial technology.'],129  ['Zalando', 'zalando.com', 'DE', 'Berlin', 52.52, 13.4, ['e-commerce', 'retail'], true, 'ZAL', 'XETRA', 2008, '10,001+', 70, 'Online fashion platform.'],130  ['Mistral AI', 'mistral.ai', 'FR', 'Paris', 48.86, 2.35, ['artificial-intelligence'], false, null, null, 2023, '201–500', 76, 'Open and portable generative AI.'],131  ['Dassault Systèmes', '3ds.com', 'FR', 'Vélizy-Villacoublay', 48.78, 2.19, ['software', 'manufacturing'], true, 'DSY', 'Euronext', 1981, '10,001+', 77, '3D design and PLM software.'],132  ['Spotify', 'spotify.com', 'SE', 'Stockholm', 59.33, 18.07, ['media', 'technology'], true, 'SPOT', 'NYSE', 2006, '5,001–10,000', 83, 'Audio streaming.'],133  ['Klarna', 'klarna.com', 'SE', 'Stockholm', 59.33, 18.07, ['fintech'], false, null, null, 2005, '1,001–5,000', 72, 'Payments and shopping.'],134  ['Roche', 'roche.com', 'CH', 'Basel', 47.56, 7.59, ['pharmaceuticals', 'healthcare'], true, 'ROG', 'SIX', 1896, '10,001+', 89, 'Pharmaceuticals and diagnostics.'],135  ['Intercom', 'intercom.com', 'IE', 'Dublin', 53.35, -6.26, ['software'], false, null, null, 2011, '1,001–5,000', 61, 'AI-first customer service.'],136  ['Cabify', 'cabify.com', 'ES', 'Madrid', 40.42, -3.7, ['logistics', 'technology'], false, null, null, 2011, '1,001–5,000', 48, 'Mobility platform.'],137  ['Toyota', 'toyota-global.com', 'JP', 'Toyota City', 35.08, 137.16, ['automotive', 'manufacturing'], true, '7203', 'TSE', 1937, '10,001+', 95, 'Automobiles and mobility.'],138  ['Sony', 'sony.com', 'JP', 'Tokyo', 35.68, 139.69, ['consumer', 'media', 'technology'], true, '6758', 'TSE', 1946, '10,001+', 91, 'Electronics, entertainment and financial services.'],139  ['Samsung Electronics', 'samsung.com', 'KR', 'Suwon', 37.26, 127.03, ['semiconductors', 'consumer'], true, '005930', 'KRX', 1969, '10,001+', 96, 'Consumer electronics and semiconductors.'],140  ['Infosys', 'infosys.com', 'IN', 'Bengaluru', 12.97, 77.59, ['software', 'technology'], true, 'INFY', 'NSE', 1981, '10,001+', 80, 'Digital services and consulting.'],141  ['Zerodha', 'zerodha.com', 'IN', 'Bengaluru', 12.97, 77.59, ['fintech'], false, null, null, 2010, '1,001–5,000', 55, 'Discount brokerage.'],142  ['Grab', 'grab.com', 'SG', 'Singapore', 1.29, 103.85, ['logistics', 'fintech'], true, 'GRAB', 'NASDAQ', 2012, '5,001–10,000', 71, 'Superapp for deliveries, mobility and finance.'],143  ['Atlassian', 'atlassian.com', 'AU', 'Sydney', -33.87, 151.21, ['software'], true, 'TEAM', 'NASDAQ', 2002, '10,001+', 84, 'Team collaboration software.'],144  ['Canva', 'canva.com', 'AU', 'Sydney', -33.87, 151.21, ['software', 'media'], false, null, null, 2013, '1,001–5,000', 73, 'Visual communication platform.'],145  ['Nubank', 'nubank.com.br', 'BR', 'São Paulo', -23.55, -46.63, ['banking', 'fintech'], true, 'NU', 'NYSE', 2013, '5,001–10,000', 81, 'Digital banking platform.'],146  ['Bitso', 'bitso.com', 'MX', 'Mexico City', 19.43, -99.13, ['fintech'], false, null, null, 2014, '501–1,000', 45, 'Crypto financial services.'],147  ['Careem', 'careem.com', 'AE', 'Dubai', 25.2, 55.27, ['logistics', 'technology'], false, null, null, 2012, '1,001–5,000', 52, 'Everything app for the Middle East.'],148  ['Wiz', 'wiz.io', 'IL', 'Tel Aviv', 32.08, 34.78, ['cybersecurity', 'cloud-infrastructure'], false, null, null, 2020, '1,001–5,000', 69, 'Cloud security platform.'],149  ['Discovery', 'discovery.co.za', 'ZA', 'Sandton', -26.1, 28.05, ['insurance', 'financial-services'], true, 'DSY', 'JSE', 1992, '10,001+', 57, 'Shared-value insurance.'],150  ['Flutterwave', 'flutterwave.com', 'NG', 'Lagos', 6.52, 3.38, ['fintech'], false, null, null, 2016, '501–1,000', 50, 'Payments technology for Africa.'],151  ['TSMC', 'tsmc.com', 'TW', 'Hsinchu', 24.8, 120.97, ['semiconductors', 'manufacturing'], true, '2330', 'TWSE', 1987, '10,001+', 97, 'Dedicated semiconductor foundry.'],152  ['Ørsted', 'orsted.com', 'DE', 'Hamburg', 53.55, 9.99, ['renewable-energy', 'energy'], true, 'ORSTED', 'Nasdaq Copenhagen', 1972, '5,001–10,000', 66, 'Offshore wind developer.'],153  ['Nokia', 'nokia.com', 'FI', 'Espoo', 60.2, 24.66, ['telecom', 'technology'], true, 'NOKIA', 'Nasdaq Helsinki', 1865, '10,001+', 78, 'Network infrastructure and technology.'],154  ['Moderna', 'modernatx.com', 'US', 'Cambridge', 42.37, -71.11, ['biotech', 'pharmaceuticals'], true, 'MRNA', 'NASDAQ', 2010, '5,001–10,000', 75, 'mRNA medicines.'],155  ['Rocket Lab', 'rocketlabusa.com', 'US', 'Long Beach', 33.77, -118.19, ['aerospace'], true, 'RKLB', 'NASDAQ', 2006, '1,001–5,000', 64, 'Launch and space systems.'],156  ['Compass', 'compass.com', 'US', 'New York', 40.71, -74.0, ['real-estate', 'technology'], true, 'COMP', 'NYSE', 2012, '1,001–5,000', 47, 'Real estate technology and brokerage.'],157];158159const SURFACES = ['homepage', 'about', 'careers', 'newsroom', 'blog', 'products', 'pricing', 'leadership', 'locations', 'investor_relations', 'documentation', 'changelog', 'legal', 'security', 'developer', 'partners', 'customers', 'sitemap', 'feed'];160const SURFACE_PATH = { homepage: '/', about: '/about', careers: '/careers', newsroom: '/newsroom', blog: '/blog', products: '/products', pricing: '/pricing', leadership: '/about/leadership', locations: '/about/locations', investor_relations: '/investors', documentation: '/docs', changelog: '/changelog', legal: '/legal/terms', security: '/security', developer: '/developers', partners: '/partners', customers: '/customers', sitemap: '/sitemap.xml', feed: '/blog/rss.xml' };161const CONNECTOR_FOR = { careers: 'generic_careers', newsroom: 'generic_news', blog: 'rss_connector', pricing: 'generic_pricing', leadership: 'generic_leadership', locations: 'generic_locations', products: 'generic_products', documentation: 'generic_docs', changelog: 'generic_changelog', legal: 'generic_legal', sitemap: 'sitemap_connector', feed: 'rss_connector', investor_relations: 'generic_ir' };162const CONNECTORS = [163  ['generic_html', 'Generic HTML page', '1.4.0', 'core'],164  ['generic_careers', 'Generic careers page', '2.1.0', 'jobs'],165  ['greenhouse_connector', 'Greenhouse board', '1.2.0', 'jobs'],166  ['lever_connector', 'Lever board', '1.1.0', 'jobs'],167  ['generic_news', 'Generic newsroom', '1.3.0', 'news'],168  ['rss_connector', 'RSS / Atom feed', '1.0.2', 'news'],169  ['generic_pricing', 'Generic pricing page', '2.0.0', 'pricing'],170  ['generic_leadership', 'Generic leadership page', '1.1.0', 'people'],171  ['generic_locations', 'Generic locations page', '1.0.0', 'locations'],172  ['generic_products', 'Generic product catalogue', '1.2.0', 'products'],173  ['generic_docs', 'Documentation', '1.0.1', 'developer'],174  ['generic_changelog', 'Changelog', '1.0.0', 'developer'],175  ['generic_legal', 'Legal / terms', '1.0.0', 'legal'],176  ['generic_ir', 'Investor relations', '1.0.0', 'ir'],177  ['sitemap_connector', 'Sitemap discovery', '1.5.0', 'discovery'],178];179const TIERS = ['A', 'B', 'C', 'D', 'E'];180const TIER_INTERVAL = { A: 900, B: 3600, C: 21600, D: 86400, E: 432000 };181const FAILURE_CLASSES = ['TIMEOUT', 'HTTP_4XX', 'HTTP_5XX', 'BOT_CHALLENGE', 'PARSING', 'REDIRECT', 'PAGE_REMOVED', 'RATE_LIMIT', 'DNS'];182183const DEPARTMENTS = ['Engineering', 'Product', 'Sales', 'Marketing', 'Customer Success', 'Finance', 'Legal', 'Operations', 'Data', 'Design', 'Security', 'People'];184const JOB_TITLES = ['Software Engineer', 'Senior Software Engineer', 'Staff Engineer', 'Machine Learning Engineer', 'Applied AI Engineer', 'Data Scientist', 'Product Manager', 'Account Executive', 'Solutions Engineer', 'Security Engineer', 'Site Reliability Engineer', 'Technical Writer', 'Designer', 'Recruiter', 'Finance Analyst', 'Legal Counsel', 'Research Scientist, LLMs', 'Developer Advocate', 'Platform Engineer', 'Support Specialist'];185const PRODUCT_NAMES = ['Terminal', 'Connect', 'Radar', 'Atlas', 'Issuing', 'Billing', 'Sigma', 'Vault', 'Insights', 'Studio', 'Workflows', 'Assistant', 'Guard', 'Ledger', 'Pulse', 'Edge', 'Core API', 'Marketplace', 'Analytics', 'Identity'];186const PEOPLE = ['Ava Martin', 'Noah Chen', 'Léa Dubois', 'Mateo Rossi', 'Priya Nair', 'Kenji Watanabe', 'Sofia Alvarez', 'Liam O’Connor', 'Amara Okafor', 'Hugo Lindqvist', 'Yuna Park', 'Daniel Cohen', 'Fatima Al-Sayed', 'Elena Petrova', 'Tomás Silva', 'Grace Kim', 'Arjun Mehta', 'Mia Fischer', 'Oliver Brown', 'Chloé Bernard'];187const TITLES = ['Chief Executive Officer', 'Chief Financial Officer', 'Chief Technology Officer', 'Chief Operating Officer', 'Chief Product Officer', 'Chief Revenue Officer', 'General Counsel', 'Chief People Officer', 'VP Engineering', 'VP Sales', 'Head of AI', 'Chief Information Security Officer'];188const CITIES = [['New York', 'US', 40.71, -74.0], ['London', 'GB', 51.5, -0.12], ['Berlin', 'DE', 52.52, 13.4], ['Paris', 'FR', 48.86, 2.35], ['Toronto', 'CA', 43.65, -79.38], ['Singapore', 'SG', 1.29, 103.85], ['Sydney', 'AU', -33.87, 151.21], ['Tokyo', 'JP', 35.68, 139.69], ['Dublin', 'IE', 53.35, -6.26], ['Bengaluru', 'IN', 12.97, 77.59], ['São Paulo', 'BR', -23.55, -46.63], ['Austin', 'US', 30.27, -97.74], ['Amsterdam', 'NL', 52.37, 4.9], ['Dubai', 'AE', 25.2, 55.27], ['Seoul', 'KR', 37.57, 126.98], ['Mexico City', 'MX', 19.43, -99.13], ['Warsaw', 'PL', 52.23, 21.01], ['Lisbon', 'PT', 38.72, -9.14]];189const TREND_TERMS = ['agentic AI', 'AI engineer', 'usage-based pricing', 'data residency', 'FedRAMP', 'EU AI Act', 'sovereign cloud', 'stablecoin', 'on-device inference', 'enterprise tier', 'SOC 2', 'MCP server', 'embedded finance', 'carbon accounting', 'returns policy', 'API deprecation', 'self-serve', 'hybrid work', 'Bengaluru hub', 'German expansion'];190191// ------------------------------------------------------------------------------------------------ event templates192// [event_type, event_subtype, importanceRange, surfaces, titleFn, summaryFn, old/new fn]193const EVENT_TEMPLATES = [194  ['PRODUCT', 'PRODUCT_LAUNCH', [0.55, 0.92], ['products', 'homepage', 'newsroom', 'blog'], (c, p) => `${c.display_name} lists a new product: ${p.name}`, (c, p) => `A product page for ${p.name} was detected on the monitored catalog. It did not appear in the previous snapshot.`, (c, p) => [null, p.name]],195  ['PRODUCT', 'PRODUCT_REMOVAL', [0.5, 0.8], ['products'], (c, p) => `${p.name} no longer listed on ${c.display_name}'s product catalog`, (c, p) => `The product entry was present in the previous version of the page and is no longer visible. This does not by itself confirm discontinuation.`, (c, p) => [p.name, null]],196  ['PRODUCT', 'PRODUCT_RENAME', [0.35, 0.6], ['products'], (c, p) => `${c.display_name} appears to have renamed ${p.name}`, (c, p) => `The product card kept its position and description but its name changed between snapshots.`, (c, p) => [p.name, `${p.name} Pro`]],197  ['PRICING', 'PRICE_INCREASE', [0.6, 0.95], ['pricing'], (c, p) => `${c.display_name} ${p.plan} plan price increased`, (c, p) => `The listed monthly price of the ${p.plan} plan changed on the public pricing page.`, (c, p) => [`$${p.oldPrice}/mo`, `$${p.newPrice}/mo`]],198  ['PRICING', 'PRICE_DECREASE', [0.5, 0.85], ['pricing'], (c, p) => `${c.display_name} ${p.plan} plan price decreased`, (c, p) => `The listed monthly price of the ${p.plan} plan is lower than in the previous snapshot.`, (c, p) => [`$${p.newPrice}/mo`, `$${p.oldPrice}/mo`]],199  ['PRICING', 'NEW_PRICING_TIER', [0.55, 0.85], ['pricing'], (c, p) => `${c.display_name} adds a new pricing tier: ${p.plan}`, (c) => `A pricing column that did not exist in the previous snapshot is now listed.`, (c, p) => [null, p.plan]],200  ['HIRING', 'JOB_COUNT_INCREASE', [0.3, 0.75], ['careers'], (c, p) => `${c.display_name} open listings up ${p.pct} % over 7 days`, (c, p) => `${p.n} listings were added on the monitored careers surface, ${p.ai} of them mentioning AI or machine learning.`, (c, p) => [`${p.from} open listings`, `${p.to} open listings`]],201  ['HIRING', 'JOB_COUNT_DECREASE', [0.4, 0.85], ['careers'], (c, p) => `${p.n} ${c.display_name} listings are no longer visible`, (c, p) => `${p.n} monitored job listings are no longer visible on the careers page (${p.pct} % of the previous count). This reflects public listings only and is not evidence of layoffs.`, (c, p) => [`${p.from} open listings`, `${p.to} open listings`]],202  ['LEADERSHIP', 'NEW_EXECUTIVE', [0.6, 0.9], ['leadership', 'newsroom'], (c, p) => `${p.name} listed as ${p.title} at ${c.display_name}`, (c, p) => `A new profile appeared on the monitored leadership page.`, (c, p) => [null, `${p.name} — ${p.title}`]],203  ['LEADERSHIP', 'EXECUTIVE_REMOVED', [0.6, 0.9], ['leadership'], (c, p) => `${p.name} no longer listed on ${c.display_name}'s leadership page`, (c, p) => `The profile for ${p.name} (${p.title}) is no longer visible on the monitored leadership page. The reason is not stated on the page.`, (c, p) => [`${p.name} — ${p.title}`, null]],204  ['LOCATION', 'NEW_OFFICE', [0.5, 0.8], ['locations', 'careers'], (c, p) => `${c.display_name} lists a new office in ${p.city}`, (c, p) => `A ${p.city}, ${p.country} location was added to the monitored locations page.`, (c, p) => [null, `${p.city}, ${p.country}`]],205  ['LOCATION', 'COUNTRY_EXPANSION', [0.65, 0.92], ['locations', 'careers', 'newsroom'], (c, p) => `${c.display_name} appears to expand into ${p.country}`, (c, p) => `First monitored presence in ${p.country}: a location entry and ${p.jobs} job listings referencing ${p.city}.`, (c, p) => [null, p.country]],206  ['LOCATION', 'OFFICE_REMOVED', [0.45, 0.75], ['locations'], (c, p) => `${c.display_name}'s ${p.city} office no longer listed`, (c, p) => `The ${p.city} entry was present in the previous snapshot of the locations page and is no longer visible.`, (c, p) => [`${p.city}, ${p.country}`, null]],207  ['DEVELOPER', 'API_LAUNCH', [0.55, 0.85], ['developer', 'documentation', 'changelog'], (c, p) => `${c.display_name} publishes a new API: ${p.name} API`, (c, p) => `A new reference section for the ${p.name} API appeared in the developer documentation.`, (c, p) => [null, `${p.name} API v1`]],208  ['DEVELOPER', 'DOCUMENTATION_CHANGE', [0.25, 0.55], ['documentation', 'changelog'], (c, p) => `${c.display_name} documentation updated: ${p.section}`, (c, p) => `${p.blocks} blocks changed in the ${p.section} section of the monitored documentation.`, () => [null, null]],209  ['LEGAL', 'TERMS_CHANGE', [0.45, 0.8], ['legal'], (c, p) => `${c.display_name} terms of service updated (section ${p.section})`, (c, p) => `Section ${p.section} of the terms was materially updated; ${p.pct} % of the text changed. The previous version is preserved.`, (c, p) => [`Terms v${p.v}`, `Terms v${p.v + 1}`]],210  ['COMMUNICATION', 'NEWS_RELEASE', [0.3, 0.7], ['newsroom', 'blog', 'feed'], (c, p) => `${c.display_name}: ${p.headline}`, (c, p) => `A first-party newsroom item was published.`, () => [null, null]],211  ['PARTNERSHIP', 'NEW_PARTNERSHIP', [0.5, 0.8], ['newsroom', 'partners'], (c, p) => `${c.display_name} announces a partnership with ${p.partner}`, (c, p) => `A partnership was described in a first-party release and ${p.partner} now appears on the partners page.`, (c, p) => [null, p.partner]],212  ['M&A', 'ACQUISITION', [0.75, 0.98], ['newsroom', 'investor_relations'], (c, p) => `${c.display_name} announces the acquisition of ${p.target}`, (c, p) => `Stated in a first-party release on the newsroom; corroborated by an investor relations notice ${p.delay} minutes later.`, (c, p) => [null, p.target]],213  ['FINANCING', 'FUNDING_ROUND', [0.7, 0.95], ['newsroom', 'blog'], (c, p) => `${c.display_name} states a ${p.amount} funding round`, (c, p) => `Amount as stated by the company in its own release; not independently verified.`, (c, p) => [null, p.amount]],214  ['SECURITY', 'SECURITY_NOTICE', [0.6, 0.9], ['security', 'blog'], (c, p) => `${c.display_name} publishes a security notice`, (c, p) => `A new advisory appeared on the monitored security page (${p.kind}).`, () => [null, null]],215  ['STRATEGY', 'BRAND_REPOSITIONING', [0.45, 0.75], ['homepage', 'about'], (c, p) => `${c.display_name} homepage headline changed`, (c, p) => `The hero headline changed from an SMB-oriented message to enterprise language. Signal, not a confirmed strategy change.`, (c, p) => [p.oldHeadline, p.newHeadline]],216  ['TECHNOLOGY', 'TECHNOLOGY_SIGNAL', [0.3, 0.6], ['developer', 'blog', 'careers'], (c, p) => `${c.display_name} references ${p.tech} across ${p.n} surfaces`, (c, p) => `${p.tech} appeared in new documentation and ${p.jobs} job listings within the same week.`, () => [null, null]],217  ['INVESTOR_RELATIONS', 'IR_UPDATE', [0.4, 0.7], ['investor_relations'], (c, p) => `${c.display_name} posts ${p.item} on investor relations`, () => `Published on the company's investor relations page; not a substitute for regulated filings.`, () => [null, null]],218  ['SUSTAINABILITY', 'SUSTAINABILITY_UPDATE', [0.3, 0.6], ['about', 'newsroom'], (c, p) => `${c.display_name} publishes its ${p.year} sustainability report`, () => `A new report link appeared on the monitored page.`, () => [null, null]],219  ['MARKETING', 'CAMPAIGN_CHANGE', [0.2, 0.45], ['homepage'], (c) => `${c.display_name} homepage hero campaign changed`, () => `Hero imagery and call-to-action blocks were replaced; classified as marketing noise above the significance floor.`, () => [null, null]],220  ['OPERATIONS', 'STATUS_INCIDENT', [0.4, 0.7], ['status', 'security'], (c, p) => `${c.display_name} status page reports ${p.kind}`, () => `Observed on the public status page.`, () => [null, null]],221];222const HEADLINES = ['expands enterprise offering to the EU', 'reports record quarter for developer sign-ups', 'opens applications for its startup program', 'introduces new sustainability commitments', 'launches regional data residency', 'names new advisory board members', 'publishes annual developer survey', 'partners with universities on AI research'];223const PARTNERS = ['Microsoft', 'AWS', 'Google Cloud', 'Accenture', 'Deloitte', 'Salesforce', 'Visa', 'Mastercard', 'Snowflake', 'NVIDIA'];224const TECHS = ['Rust', 'Kubernetes', 'MCP', 'vector search', 'WebAssembly', 'PostgreSQL', 'Kafka', 'Terraform', 'Claude', 'LLM evaluation'];225226// ------------------------------------------------------------------------------------------------ enrichment profiles227// Sample `CompanyCard.profile` payloads (mock only — figures are approximate public values used to exercise the UI, not228// a dataset). Mix: complete (nvidia, apple, toyota, sony, roche, sap, spotify, adyen, arm, tsmc, siemens, samsung),229// partial (stripe, cohere, anthropic, mistral-ai, shopify, nubank, revolut, klarna), null-filled (cabify, careem,230// flutterwave, zerodha, discovery), absent key (compass, bitso, wiz), and an auto-derived minimal profile for the rest.231const MOCK_ASSET_BASE = `http://127.0.0.1:${PORT}/api/v1/_mock`;232const WD = (q) => `https://www.wikidata.org/wiki/${q}`;233const WP = (t) => `https://en.wikipedia.org/wiki/${t}`;234const money = (value, currency, year) => ({ value, currency, year });235const RETRIEVED = iso(NOW - 3 * DAY);236const NO_PROFILE_KEY = new Set(['compass', 'bitso', 'wiz']);237const NULL_PROFILE = new Set(['cabify', 'careem', 'flutterwave', 'zerodha', 'discovery']);238const PROFILES = {239  nvidia: {240    description: 'Nvidia Corporation is an American technology company headquartered in Santa Clara, California. It designs graphics processing units, system-on-a-chip units and application programming interfaces for data science, high-performance computing and mobile and automotive markets.',241    description_source: 'wikipedia', description_url: WP('Nvidia'), description_license: 'CC BY-SA 4.0',242    logo: true, icon: true, founded_year: 1993, legal_form: 'Public company (Delaware corporation)', employees: 36000, employees_year: 2025,243    revenue: money(130497000000, 'USD', 2025), net_income: money(72880000000, 'USD', 2025), total_assets: money(111601000000, 'USD', 2025),244    hq: { city: 'Santa Clara', region: 'California', country: 'US', address: '2788 San Tomas Expressway', lat: 37.3706, lon: -121.9636 },245    ticker: 'NVDA', exchange: 'NASDAQ', isin: 'US67066G1040', lei: '549300S4KLFTLO7GSQ80', sec_cik: '0001045810', public_company: true,246    wikipedia_url: WP('Nvidia'), wikidata_url: WD('Q182477'), official_website: 'https://www.nvidia.com', phone: '+1 408-486-2000',247    products: ['GeForce', 'Quadro', 'CUDA', 'DGX', 'Jetson', 'Tegra', 'NVIDIA DRIVE', 'Omniverse', 'Nvidia RTX'], industry_labels: ['semiconductor industry', 'artificial intelligence', 'computer hardware'],248    socials: { linkedin: 'https://www.linkedin.com/company/nvidia', x: 'https://x.com/nvidia', youtube: 'https://www.youtube.com/@NVIDIA', facebook: 'https://www.facebook.com/NVIDIA', instagram: 'https://www.instagram.com/nvidia', github: 'https://github.com/NVIDIA' },249    extra_facts: [{ key: 'index_membership', label: 'Index membership', value: 'S&P 500 · Nasdaq-100 · Dow Jones Industrial Average', source: 'wikidata' }],250    financial_source: 'sec_edgar',251  },252  apple: {253    description: 'Apple Inc. is an American multinational technology company headquartered in Cupertino, California. It designs, develops and sells consumer electronics, software and online services, including the iPhone, Mac, iPad, Apple Watch and the App Store.',254    description_source: 'wikipedia', description_url: WP('Apple_Inc.'), description_license: 'CC BY-SA 4.0',255    logo: true, icon: true, founded_year: 1976, legal_form: 'Public company (California corporation)', employees: 164000, employees_year: 2024,256    revenue: money(391035000000, 'USD', 2024), net_income: money(93736000000, 'USD', 2024), total_assets: money(364980000000, 'USD', 2024),257    hq: { city: 'Cupertino', region: 'California', country: 'US', address: 'One Apple Park Way', lat: 37.3349, lon: -122.009 },258    ticker: 'AAPL', exchange: 'NASDAQ', isin: 'US0378331005', lei: 'HWUPKR0MPOU8FGXBT394', sec_cik: '0000320193', public_company: true,259    wikipedia_url: WP('Apple_Inc.'), wikidata_url: WD('Q312'), official_website: 'https://www.apple.com', phone: '+1 408-996-1010',260    products: ['iPhone', 'iPad', 'Mac', 'Apple Watch', 'AirPods', 'Apple TV', 'Apple Vision Pro', 'iOS', 'macOS', 'App Store', 'Apple Music', 'iCloud'], industry_labels: ['consumer electronics', 'software industry', 'online services'],261    socials: { linkedin: 'https://www.linkedin.com/company/apple', x: 'https://x.com/Apple', youtube: 'https://www.youtube.com/@Apple', instagram: 'https://www.instagram.com/apple', facebook: 'https://www.facebook.com/apple' },262    extra_facts: [{ key: 'index_membership', label: 'Index membership', value: 'S&P 500 · Nasdaq-100 · Dow Jones Industrial Average', source: 'wikidata' }, { key: 'auditor', label: 'Auditor', value: 'Ernst & Young', source: 'sec_edgar' }],263    financial_source: 'sec_edgar',264  },265  toyota: {266    description: 'Toyota Motor Corporation is a Japanese multinational automotive manufacturer headquartered in Toyota City, Aichi. It is one of the largest automobile manufacturers in the world by production volume.',267    description_source: 'wikipedia', description_url: WP('Toyota'), description_license: 'CC BY-SA 4.0',268    logo: true, icon: true, founded_year: 1937, legal_form: 'Kabushiki gaisha (public)', employees: 380793, employees_year: 2024,269    revenue: money(48036704000000, 'JPY', 2025), net_income: money(4765086000000, 'JPY', 2025), total_assets: money(93601350000000, 'JPY', 2025),270    hq: { city: 'Toyota City', region: 'Aichi', country: 'JP', address: '1 Toyota-cho', lat: 35.0826, lon: 137.1562 },271    ticker: '7203', exchange: 'TSE', isin: 'JP3633400001', lei: '5493006W3QUS5LMH6R84', sec_cik: '0001094517', public_company: true,272    wikipedia_url: WP('Toyota'), wikidata_url: WD('Q53268'), official_website: 'https://global.toyota', phone: null,273    products: ['Toyota Corolla', 'Toyota Camry', 'Toyota RAV4', 'Toyota Prius', 'Toyota Hilux', 'Toyota Land Cruiser', 'Lexus'], industry_labels: ['automotive industry'],274    socials: { linkedin: 'https://www.linkedin.com/company/toyota', x: 'https://x.com/Toyota', youtube: 'https://www.youtube.com/@toyotaglobal' },275  },276  sony: {277    description: 'Sony Group Corporation is a Japanese multinational conglomerate headquartered in Minato, Tokyo, active in electronics, gaming, entertainment (pictures and music), imaging sensors and financial services.',278    description_source: 'homepage', description_url: 'https://www.sony.com/en/SonyInfo/CorporateInfo/', description_license: null,279    logo: true, icon: false, founded_year: 1946, legal_form: 'Kabushiki gaisha (public)', employees: 113000, employees_year: 2024,280    revenue: money(12957000000000, 'JPY', 2025), net_income: money(1141600000000, 'JPY', 2025), total_assets: money(35300000000000, 'JPY', 2025),281    hq: { city: 'Minato', region: 'Tokyo', country: 'JP', address: '1-7-1 Konan', lat: 35.6299, lon: 139.7402 },282    ticker: '6758', exchange: 'TSE', isin: 'JP3435000009', lei: '353800A2DP3ZMC4LR436', sec_cik: '0000313838', public_company: true,283    wikipedia_url: WP('Sony'), wikidata_url: WD('Q41187'), official_website: 'https://www.sony.com', phone: null,284    products: ['PlayStation 5', 'Sony Alpha', 'Bravia', 'WH-1000XM5', 'Xperia', 'Sony Pictures', 'Sony Music'], industry_labels: ['conglomerate', 'consumer electronics', 'entertainment industry'],285    socials: { linkedin: 'https://www.linkedin.com/company/sony', x: 'https://x.com/Sony', youtube: 'https://www.youtube.com/@Sony', instagram: 'https://www.instagram.com/sony', facebook: 'https://www.facebook.com/Sony', tiktok: 'https://www.tiktok.com/@sony' },286  },287  roche: {288    description: 'F. Hoffmann-La Roche AG is a Swiss multinational healthcare company operating worldwide under two divisions: Pharmaceuticals and Diagnostics. It is headquartered in Basel.',289    description_source: 'wikipedia', description_url: WP('Hoffmann-La_Roche'), description_license: 'CC BY-SA 4.0',290    logo: true, icon: true, founded_year: 1896, legal_form: 'Aktiengesellschaft (public)', employees: 103613, employees_year: 2024,291    revenue: money(60500000000, 'CHF', 2024), net_income: money(9186000000, 'CHF', 2024), total_assets: money(93700000000, 'CHF', 2024),292    hq: { city: 'Basel', region: 'Basel-Stadt', country: 'CH', address: 'Grenzacherstrasse 124', lat: 47.5615, lon: 7.6086 },293    ticker: 'ROG', exchange: 'SIX', isin: 'CH0012032048', lei: '549300U41AUUVOAZRV96', sec_cik: null, public_company: true,294    wikipedia_url: WP('Hoffmann-La_Roche'), wikidata_url: WD('Q212646'), official_website: 'https://www.roche.com', phone: '+41 61 688 11 11',295    products: ['Ocrevus', 'Hemlibra', 'Tecentriq', 'Vabysmo', 'Perjeta', 'cobas', 'Accu-Chek'], industry_labels: ['pharmaceutical industry', 'in vitro diagnostics'],296    socials: { linkedin: 'https://www.linkedin.com/company/roche', x: 'https://x.com/Roche', youtube: 'https://www.youtube.com/@roche' },297  },298  sap: {299    description: 'SAP SE is a German multinational software company based in Walldorf, Baden-Württemberg, that develops enterprise software to manage business operations and customer relations.',300    description_source: 'wikipedia', description_url: WP('SAP'), description_license: 'CC BY-SA 4.0',301    logo: true, icon: true, founded_year: 1972, legal_form: 'Societas Europaea (SE)', employees: 109121, employees_year: 2024,302    revenue: money(34176000000, 'EUR', 2024), net_income: money(3096000000, 'EUR', 2024), total_assets: money(69700000000, 'EUR', 2024),303    hq: { city: 'Walldorf', region: 'Baden-Württemberg', country: 'DE', address: 'Dietmar-Hopp-Allee 16', lat: 49.2933, lon: 8.6414 },304    ticker: 'SAP', exchange: 'XETRA', isin: 'DE0007164600', lei: '529900D6BF99LW9R2E68', sec_cik: '0001000184', public_company: true,305    wikipedia_url: WP('SAP'), wikidata_url: WD('Q166262'), official_website: 'https://www.sap.com', phone: '+49 6227 7-47474',306    products: ['SAP S/4HANA', 'SAP HANA', 'SAP Business Technology Platform', 'SAP SuccessFactors', 'SAP Ariba', 'SAP Concur', 'Joule'], industry_labels: ['software industry', 'enterprise software'],307    socials: { linkedin: 'https://www.linkedin.com/company/sap', x: 'https://x.com/SAP', youtube: 'https://www.youtube.com/@SAP', github: 'https://github.com/SAP' },308  },309  spotify: {310    description: 'Spotify Technology S.A. is a Swedish audio streaming and media services provider founded in 2006 and headquartered in Stockholm, with its legal seat in Luxembourg.',311    description_source: 'wikipedia', description_url: WP('Spotify'), description_license: 'CC BY-SA 4.0',312    logo: true, icon: true, founded_year: 2006, legal_form: 'Société anonyme (Luxembourg)', employees: 7359, employees_year: 2024,313    revenue: money(15673000000, 'EUR', 2024), net_income: money(1138000000, 'EUR', 2024), total_assets: money(11100000000, 'EUR', 2024),314    hq: { city: 'Stockholm', region: null, country: 'SE', address: 'Regeringsgatan 19', lat: 59.3326, lon: 18.0649 },315    ticker: 'SPOT', exchange: 'NYSE', isin: 'LU1778762911', lei: '549300I8UDPDOMRCNP86', sec_cik: '0001639920', public_company: true,316    wikipedia_url: WP('Spotify'), wikidata_url: WD('Q689141'), official_website: 'https://www.spotify.com', phone: null,317    products: ['Spotify', 'Spotify Premium', 'Spotify for Artists', 'Spotify for Podcasters', 'Anchor'], industry_labels: ['music streaming', 'podcasting'],318    socials: { linkedin: 'https://www.linkedin.com/company/spotify', x: 'https://x.com/Spotify', youtube: 'https://www.youtube.com/@Spotify', instagram: 'https://www.instagram.com/spotify', github: 'https://github.com/spotify' },319  },320  adyen: {321    description: 'Adyen N.V. is a Dutch payment company headquartered in Amsterdam that provides a single platform for accepting payments across online, mobile and in-store channels.',322    description_source: 'wikipedia', description_url: WP('Adyen'), description_license: 'CC BY-SA 4.0',323    logo: true, icon: true, founded_year: 2006, legal_form: 'Naamloze vennootschap (public)', employees: 4322, employees_year: 2024,324    revenue: money(1996000000, 'EUR', 2024), net_income: money(925000000, 'EUR', 2024), total_assets: null,325    hq: { city: 'Amsterdam', region: 'North Holland', country: 'NL', address: 'Simon Carmiggeltstraat 6-50', lat: 52.3765, lon: 4.9016 },326    ticker: 'ADYEN', exchange: 'Euronext Amsterdam', isin: 'NL0012969182', lei: '724500PSWKAY73WSLD26', sec_cik: null, public_company: true,327    wikipedia_url: WP('Adyen'), wikidata_url: WD('Q19833716'), official_website: 'https://www.adyen.com', phone: null,328    products: ['Adyen Platform', 'Adyen for Platforms', 'Adyen Issuing', 'Adyen Terminal'], industry_labels: ['payment service provider', 'financial technology'],329    socials: { linkedin: 'https://www.linkedin.com/company/adyen', x: 'https://x.com/Adyen', youtube: 'https://www.youtube.com/@adyen', github: 'https://github.com/Adyen' },330  },331  arm: {332    description: 'Arm Holdings plc is a British semiconductor and software design company based in Cambridge that licenses processor architectures and IP cores; majority-owned by SoftBank Group.',333    description_source: 'wikipedia', description_url: WP('Arm_Holdings'), description_license: 'CC BY-SA 4.0',334    logo: true, icon: true, founded_year: 1990, legal_form: 'Public limited company', employees: 8300, employees_year: 2025,335    revenue: money(4007000000, 'USD', 2025), net_income: money(792000000, 'USD', 2025), total_assets: money(8700000000, 'USD', 2025),336    hq: { city: 'Cambridge', region: 'Cambridgeshire', country: 'GB', address: '110 Fulbourn Road', lat: 52.1839, lon: 0.1791 },337    ticker: 'ARM', exchange: 'NASDAQ', isin: 'US0420682058', lei: '213800ND9OV4ZZKK7O47', sec_cik: '0001973239', public_company: true,338    wikipedia_url: WP('Arm_Holdings'), wikidata_url: WD('Q1063165'), official_website: 'https://www.arm.com', phone: null,339    products: ['Cortex-A', 'Cortex-M', 'Neoverse', 'Mali', 'Armv9', 'Arm Compute Subsystems'], industry_labels: ['semiconductor industry', 'intellectual property licensing'],340    socials: { linkedin: 'https://www.linkedin.com/company/arm', x: 'https://x.com/Arm', youtube: 'https://www.youtube.com/@Arm', github: 'https://github.com/ARM-software' },341    financial_source: 'sec_edgar',342  },343  tsmc: {344    description: 'Taiwan Semiconductor Manufacturing Company Limited is a Taiwanese multinational semiconductor contract manufacturing and design company headquartered in Hsinchu Science Park; it is the world’s largest dedicated independent semiconductor foundry.',345    description_source: 'wikipedia', description_url: WP('TSMC'), description_license: 'CC BY-SA 4.0',346    logo: true, icon: true, founded_year: 1987, legal_form: 'Public company', employees: 83825, employees_year: 2024,347    revenue: money(2894307000000, 'TWD', 2024), net_income: money(1173268000000, 'TWD', 2024), total_assets: money(6691000000000, 'TWD', 2024),348    hq: { city: 'Hsinchu', region: null, country: 'TW', address: '8 Li-Hsin Road 6, Hsinchu Science Park', lat: 24.7739, lon: 121.0107 },349    ticker: '2330', exchange: 'TWSE', isin: 'TW0002330008', lei: '549300YSKHMGWXOR9E51', sec_cik: '0001046179', public_company: true,350    wikipedia_url: WP('TSMC'), wikidata_url: WD('Q713418'), official_website: 'https://www.tsmc.com', phone: null,351    products: ['3 nm process', '5 nm process', 'CoWoS', 'InFO'], industry_labels: ['semiconductor industry', 'semiconductor fabrication'],352    socials: { linkedin: 'https://www.linkedin.com/company/tsmc', youtube: 'https://www.youtube.com/@tsmc' },353  },354  siemens: {355    description: 'Siemens AG is a German multinational technology conglomerate headquartered in Munich, focused on industrial automation, smart infrastructure, rail transport and, through Siemens Healthineers, medical technology.',356    description_source: 'wikipedia', description_url: WP('Siemens'), description_license: 'CC BY-SA 4.0',357    logo: true, icon: true, founded_year: 1847, legal_form: 'Aktiengesellschaft (public)', employees: 327000, employees_year: 2024,358    revenue: money(75930000000, 'EUR', 2024), net_income: money(9000000000, 'EUR', 2024), total_assets: money(146000000000, 'EUR', 2024),359    hq: { city: 'Munich', region: 'Bavaria', country: 'DE', address: 'Werner-von-Siemens-Straße 1', lat: 48.1396, lon: 11.5744 },360    ticker: 'SIE', exchange: 'XETRA', isin: 'DE0007236101', lei: 'W38RGI023J3WT1HWRP32', sec_cik: null, public_company: true,361    wikipedia_url: WP('Siemens'), wikidata_url: WD('Q81230'), official_website: 'https://www.siemens.com', phone: '+49 89 636-00',362    products: ['SIMATIC', 'Siemens Xcelerator', 'TIA Portal', 'Desigo', 'Velaro', 'Mobility Vectron'], industry_labels: ['industrial automation', 'electrical engineering', 'rail transport'],363    socials: { linkedin: 'https://www.linkedin.com/company/siemens', x: 'https://x.com/Siemens', youtube: 'https://www.youtube.com/@Siemens', instagram: 'https://www.instagram.com/siemens' },364  },365  'samsung-electronics': {366    description: 'Samsung Electronics Co., Ltd. is a South Korean multinational electronics company headquartered in Suwon, and the flagship affiliate of the Samsung Group, producing memory chips, displays, smartphones and home appliances.',367    description_source: 'wikipedia', description_url: WP('Samsung_Electronics'), description_license: 'CC BY-SA 4.0',368    logo: true, icon: true, founded_year: 1969, legal_form: 'Chusik hoesa (public)', employees: 267860, employees_year: 2023,369    revenue: money(300870900000000, 'KRW', 2024), net_income: money(34451000000000, 'KRW', 2024), total_assets: money(514531900000000, 'KRW', 2024),370    hq: { city: 'Suwon', region: 'Gyeonggi', country: 'KR', address: '129 Samsung-ro, Yeongtong-gu', lat: 37.2599, lon: 127.0303 },371    ticker: '005930', exchange: 'KRX', isin: 'KR7005930003', lei: '988400E5HRVX81AYLM04', sec_cik: null, public_company: true,372    wikipedia_url: WP('Samsung_Electronics'), wikidata_url: WD('Q20718'), official_website: 'https://www.samsung.com', phone: null,373    products: ['Galaxy S', 'Galaxy Z', 'Galaxy Tab', 'Neo QLED', 'Bespoke', 'Exynos', 'HBM3E'], industry_labels: ['consumer electronics', 'semiconductor industry', 'display technology'],374    socials: { linkedin: 'https://www.linkedin.com/company/samsung-electronics', x: 'https://x.com/Samsung', youtube: 'https://www.youtube.com/@Samsung', instagram: 'https://www.instagram.com/samsung', facebook: 'https://www.facebook.com/SamsungGlobal', tiktok: 'https://www.tiktok.com/@samsung' },375  },376  // ---- partial profiles377  stripe: {378    description: 'Stripe, Inc. is an Irish-American multinational financial services and software-as-a-service company dual-headquartered in South San Francisco, California, and Dublin, Ireland. It offers payment-processing software and APIs for e-commerce websites and mobile applications.',379    description_source: 'wikipedia', description_url: WP('Stripe,_Inc.'), description_license: 'CC BY-SA 4.0',380    logo: true, icon: true, founded_year: 2010, legal_form: 'Private company', employees: 8550, employees_year: 2024,381    hq: { city: 'South San Francisco', region: 'California', country: 'US', address: '354 Oyster Point Blvd', lat: 37.6547, lon: -122.3894 },382    public_company: false, wikipedia_url: WP('Stripe,_Inc.'), wikidata_url: WD('Q10318979'), official_website: 'https://stripe.com',383    products: ['Stripe Payments', 'Stripe Connect', 'Stripe Billing', 'Stripe Terminal', 'Stripe Radar', 'Stripe Atlas', 'Stripe Issuing', 'Stripe Treasury'], industry_labels: ['payment service provider', 'financial technology'],384    socials: { linkedin: 'https://www.linkedin.com/company/stripe', x: 'https://x.com/stripe', youtube: 'https://www.youtube.com/@StripeDevelopers', github: 'https://github.com/stripe', crunchbase: 'https://www.crunchbase.com/organization/stripe' },385  },386  shopify: {387    description: 'Shopify Inc. is a Canadian multinational e-commerce company headquartered in Ottawa, Ontario, that provides a proprietary platform for online stores and retail point-of-sale systems.',388    description_source: 'wikipedia', description_url: WP('Shopify'), description_license: 'CC BY-SA 4.0',389    logo: true, icon: true, founded_year: 2006, legal_form: 'Public company (Canada Business Corporations Act)', employees: 8100, employees_year: 2024,390    revenue: money(8880000000, 'USD', 2024), net_income: money(2020000000, 'USD', 2024),391    hq: { city: 'Ottawa', region: 'Ontario', country: 'CA', address: '151 O’Connor Street', lat: 45.4215, lon: -75.6972 },392    ticker: 'SHOP', exchange: 'TSX · NASDAQ', isin: 'CA82509L1076', lei: '549300HPKKP5AVXRM893', sec_cik: '0001594805', public_company: true,393    wikipedia_url: WP('Shopify'), wikidata_url: WD('Q3963870'), official_website: 'https://www.shopify.com',394    products: ['Shopify', 'Shopify Plus', 'Shopify POS', 'Shopify Payments', 'Shop Pay', 'Shop app', 'Shopify Magic'], industry_labels: ['e-commerce', 'software as a service'],395    socials: { linkedin: 'https://www.linkedin.com/company/shopify', x: 'https://x.com/Shopify', youtube: 'https://www.youtube.com/@Shopify', github: 'https://github.com/Shopify' },396    financial_source: 'sec_edgar',397  },398  cohere: {399    description: 'Cohere is a Toronto-based AI company that builds large language models and retrieval systems for enterprises, offered through an API and deployed in private clouds. Its public pages emphasise data privacy and multilingual models.',400    description_source: 'llm', description_url: null, description_license: null,401    logo: false, icon: true, founded_year: 2019, legal_form: 'Private company', employees: null, employees_year: null,402    hq: { city: 'Toronto', region: 'Ontario', country: 'CA', address: null, lat: 43.6532, lon: -79.3832 },403    public_company: false, wikipedia_url: WP('Cohere'), wikidata_url: WD('Q108024780'), official_website: 'https://cohere.com',404    products: ['Command', 'Embed', 'Rerank', 'Aya', 'North'], industry_labels: ['artificial intelligence'],405    socials: { linkedin: 'https://www.linkedin.com/company/cohere-ai', x: 'https://x.com/cohere', github: 'https://github.com/cohere-ai', youtube: 'https://www.youtube.com/@cohere-ai' },406  },407  anthropic: {408    description: 'American artificial intelligence company founded in 2021, developer of the Claude family of large language models.',409    description_source: 'wikidata', description_url: WD('Q109832790'), description_license: 'CC0',410    logo: false, icon: true, founded_year: 2021, legal_form: 'Public-benefit corporation', employees: null, employees_year: null,411    hq: { city: 'San Francisco', region: 'California', country: 'US', address: null, lat: 37.7749, lon: -122.4194 },412    public_company: false, wikipedia_url: WP('Anthropic'), wikidata_url: WD('Q109832790'), official_website: 'https://www.anthropic.com',413    products: ['Claude'], industry_labels: ['artificial intelligence'],414    socials: { linkedin: 'https://www.linkedin.com/company/anthropicresearch', x: 'https://x.com/AnthropicAI', youtube: 'https://www.youtube.com/@anthropic-ai', github: 'https://github.com/anthropics' },415  },416  'mistral-ai': {417    description: 'Mistral AI is a French artificial intelligence company headquartered in Paris that develops open-weight and commercial large language models.',418    description_source: 'wikipedia', description_url: WP('Mistral_AI'), description_license: 'CC BY-SA 4.0',419    logo: true, icon: false, founded_year: 2023, legal_form: 'Société par actions simplifiée', employees: null, employees_year: null,420    hq: { city: 'Paris', region: 'Île-de-France', country: 'FR', address: null, lat: 48.8566, lon: 2.3522 },421    public_company: false, wikipedia_url: WP('Mistral_AI'), wikidata_url: WD('Q119711183'), official_website: 'https://mistral.ai',422    products: ['Mistral Large', 'Mistral Small', 'Codestral', 'Le Chat', 'Pixtral'], industry_labels: ['artificial intelligence'],423    socials: { linkedin: 'https://www.linkedin.com/company/mistralai', x: 'https://x.com/MistralAI', github: 'https://github.com/mistralai' },424  },425  nubank: {426    description: 'Nu Holdings Ltd. is a Brazilian neobank headquartered in São Paulo, operating in Brazil, Mexico and Colombia; it is one of the largest digital banking platforms in the world by customer count.',427    description_source: 'wikipedia', description_url: WP('Nubank'), description_license: 'CC BY-SA 4.0',428    logo: true, icon: true, founded_year: 2013, legal_form: 'Cayman Islands holding company', employees: null, employees_year: null,429    revenue: money(11500000000, 'USD', 2024), net_income: money(1970000000, 'USD', 2024),430    hq: { city: 'São Paulo', region: 'São Paulo', country: 'BR', address: null, lat: -23.5505, lon: -46.6333 },431    ticker: 'NU', exchange: 'NYSE', isin: 'KYG6683N1034', sec_cik: '0001691493', public_company: true,432    wikipedia_url: WP('Nubank'), wikidata_url: WD('Q28129905'), official_website: 'https://nubank.com.br',433    products: ['Nu conta', 'Nu cartão', 'NuInvest', 'Nu Pagamentos'], industry_labels: ['neobank', 'financial technology'],434    socials: { linkedin: 'https://www.linkedin.com/company/nubank', x: 'https://x.com/nubank', instagram: 'https://www.instagram.com/nubank', youtube: 'https://www.youtube.com/@nubank' },435    financial_source: 'sec_edgar',436  },437  revolut: {438    description: 'Revolut Group Holdings Ltd is a British fintech company headquartered in London offering banking services, including multi-currency accounts, cards, trading and crypto, to retail and business customers.',439    description_source: 'wikipedia', description_url: WP('Revolut'), description_license: 'CC BY-SA 4.0',440    logo: true, icon: true, founded_year: 2015, legal_form: 'Private limited company', employees: 10000, employees_year: 2024,441    hq: { city: 'London', region: 'England', country: 'GB', address: null, lat: 51.5074, lon: -0.1278 },442    public_company: false, wikipedia_url: WP('Revolut'), wikidata_url: WD('Q21179207'), official_website: 'https://www.revolut.com',443    products: ['Revolut', 'Revolut Business', 'Revolut X', 'Revolut <18'], industry_labels: ['neobank', 'financial technology'],444    socials: { linkedin: 'https://www.linkedin.com/company/revolut', x: 'https://x.com/RevolutApp', instagram: 'https://www.instagram.com/revolutapp', youtube: 'https://www.youtube.com/@Revolut' },445  },446  klarna: {447    description: 'Klarna Group plc is a Swedish fintech company that provides online financial services such as payments for online storefronts, direct payments and post-purchase payments.',448    description_source: 'wikipedia', description_url: WP('Klarna'), description_license: 'CC BY-SA 4.0',449    logo: true, icon: false, founded_year: 2005, legal_form: 'Public limited company', employees: 3422, employees_year: 2024,450    revenue: money(2810000000, 'USD', 2024),451    hq: { city: 'Stockholm', region: null, country: 'SE', address: 'Sveavägen 46', lat: 59.3376, lon: 18.0603 },452    ticker: 'KLAR', exchange: 'NYSE', public_company: true, wikipedia_url: WP('Klarna'), wikidata_url: WD('Q1747210'), official_website: 'https://www.klarna.com',453    products: ['Klarna', 'Pay in 4', 'Klarna Card'], industry_labels: ['financial technology', 'buy now, pay later'],454    socials: { linkedin: 'https://www.linkedin.com/company/klarna', x: 'https://x.com/Klarna', instagram: 'https://www.instagram.com/klarna' },455  },456};457458/** Build a full-shape profile from a sample; `null` fills everything the sample omits (the UI must hide those). */459function makeProfile(c, sample) {460  const base = {461    description: null, description_source: null, description_url: null, description_license: null,462    logo_url: null, icon_url: null, founded_year: null, legal_form: null, employees: null, employees_year: null, revenue: null, net_income: null, total_assets: null,463    hq: { city: null, region: null, country: null, address: null, lat: null, lon: null },464    ticker: null, exchange: null, isin: null, lei: null, sec_cik: null, public_company: false,465    wikipedia_url: null, wikidata_url: null, official_website: null, phone: null, products: [], industries: [], industry_labels: [], socials: {}, enriched_at: null, sources: [],466  };467  if (!sample) return base;468  const { logo, icon, extra_facts, financial_source, ...rest } = sample;469  const p = { ...base, ...rest, hq: { ...base.hq, ...(rest.hq ?? {}) } };470  if (logo) p.logo_url = `${MOCK_ASSET_BASE}/logo/${c.slug}.svg`;471  if (icon) p.icon_url = `${MOCK_ASSET_BASE}/icon/${c.slug}.svg`;472  p.enriched_at = RETRIEVED;473  const src = (field, source, url) => p.sources.push({ field, source, url: url ?? null, retrieved_at: RETRIEVED });474  if (p.description) src('description', p.description_source ?? 'wikidata', p.description_url);475  if (p.logo_url || p.icon_url) src('logo_url', 'wikidata', p.wikidata_url);476  if (p.founded_year) src('founded_year', 'wikidata', p.wikidata_url);477  if (p.legal_form) src('legal_form', 'wikidata', p.wikidata_url);478  if (p.hq.city || p.hq.country) src('hq', 'wikidata', p.wikidata_url);479  if (typeof p.employees === 'number') src('employees', financial_source === 'sec_edgar' ? 'sec_edgar' : 'wikidata', financial_source === 'sec_edgar' && p.sec_cik ? `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}` : p.wikidata_url);480  for (const k of ['revenue', 'net_income', 'total_assets']) if (p[k]) src(k, financial_source === 'sec_edgar' ? 'sec_edgar' : 'wikidata', financial_source === 'sec_edgar' && p.sec_cik ? `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}` : p.wikidata_url);481  if (p.ticker) src('ticker', 'wikidata', p.wikidata_url);482  if (p.isin) src('isin', 'wikidata', p.wikidata_url);483  if (p.lei) src('lei', 'gleif', `https://search.gleif.org/#/record/${p.lei}`);484  if (p.sec_cik) src('sec_cik', 'sec_edgar', `https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=${p.sec_cik}`);485  if (p.products.length) src('products', 'wikidata', p.wikidata_url);486  if (p.industry_labels.length) src('industry_labels', 'wikidata', p.wikidata_url);487  if (p.phone) src('phone', 'homepage', p.official_website);488  if (Object.keys(p.socials).length) src('socials', 'homepage', p.official_website);489  if (p.wikipedia_url) src('wikipedia_url', 'wikidata', p.wikidata_url);490  return p;491}492/** Minimal auto profile for companies without a hand-written sample: registry description, HQ, listing; no logo, no numbers. */493function autoProfile(c) {494  return makeProfile(c, { description: c.description, description_source: 'homepage', description_url: c.website, founded_year: c.founded_year, hq: { city: c.hq_city, country: c.country, lat: c._lat, lon: c._lon }, ticker: c.ticker, exchange: c.exchange, public_company: c.public_company, official_website: c.website, logo: false, icon: chance(0.6) });495}496/** Flattened, pre-formatted facts (`CompanyDetail.facts`) — the structured profile + registry extras. */497function factsOf(c) {498  const p = c.profile;499  if (!p) return [];500  const out = [];501  const bySrc = Object.fromEntries(p.sources.map((s) => [s.field, s]));502  const add = (key, label, value, field = key) => {503    const s = bySrc[field];504    if (value === null || value === undefined || value === '') return;505    out.push({ key, label, value: String(value), source: s?.source ?? 'wikidata', url: s?.url ?? p.wikidata_url, retrieved_at: s?.retrieved_at ?? RETRIEVED });506  };507  add('founded_year', 'Founded', p.founded_year);508  add('hq', 'Headquarters', [p.hq.city, p.hq.region, p.hq.country].filter(Boolean).join(', ') || null);509  add('employees', 'Employees', typeof p.employees === 'number' ? `${p.employees}${p.employees_year ? ` (${p.employees_year})` : ''}` : null);510  for (const [k, label] of [['revenue', 'Revenue'], ['net_income', 'Net income'], ['total_assets', 'Total assets']]) if (p[k]) add(k, label, `${p[k].value} ${p[k].currency} (${p[k].year})`);511  add('legal_form', 'Legal form', p.legal_form);512  add('isin', 'ISIN', p.isin);513  add('lei', 'LEI', p.lei);514  add('sec_cik', 'SEC CIK', p.sec_cik);515  for (const f of PROFILES[c.slug]?.extra_facts ?? []) out.push({ key: f.key, label: f.label, value: f.value, source: f.source, url: f.source === 'wikidata' ? p.wikidata_url : null, retrieved_at: RETRIEVED });516  return out;517}518519// relationships in the v1.1 shape: [kind, counterpart (atlas slug or plain name), valid_from, valid_to, confidence, source, property]520const WDP = (prop) => ({ source: 'wikidata', property: prop });521const RELATIONS = {522  nvidia: [['PARENT_OF', 'Mellanox Technologies', '2020-04-27', null, 0.97, WDP('P355')], ['PARENT_OF', 'Cumulus Networks', '2020-05-04', null, 0.9, WDP('P355')], ['PARENT_OF', 'NVIDIA GmbH', null, null, 0.85, WDP('P355')], ['ACQUIRED', 'Run:ai', '2024-12-30', null, 0.9, WDP('P1830')], ['ACQUIRED', 'OctoAI', '2024-09-30', null, 0.8, WDP('P1830')], ['PARTNER', 'tsmc', null, null, 0.75, { source: 'registry' }], ['COMPETITOR', 'arm', null, null, 0.6, { source: 'registry' }]],523  apple: [['PARENT_OF', 'Beats Electronics', '2014-08-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Shazam', '2018-09-24', null, 0.95, WDP('P355')], ['PARENT_OF', 'Claris International', '1998-01-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Apple Sales International', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Braeburn Capital', '2005-01-01', null, 0.85, WDP('P355')], ['ACQUIRED', 'NeXT', '1997-02-07', null, 0.99, WDP('P1830')], ['PARTNER', 'tsmc', null, null, 0.7, { source: 'registry' }], ['COMPETITOR', 'samsung-electronics', null, null, 0.7, { source: 'registry' }]],524  sony: [['PARENT_OF', 'Sony Interactive Entertainment', '2016-04-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Pictures Entertainment', '1991-08-07', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Music Entertainment', '1991-01-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Sony Semiconductor Solutions', '2016-04-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Sony Music Publishing', '2012-06-29', null, 0.9, WDP('P355')], ['PARENT_OF', 'Crunchyroll', '2021-08-09', null, 0.95, WDP('P355')], ['PARENT_OF', 'Bungie', '2022-07-15', null, 0.95, WDP('P355')], ['PARENT_OF', 'Insomniac Games', '2019-08-19', null, 0.95, WDP('P355')], ['PARENT_OF', 'Aniplex', '1995-09-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Sony Honda Mobility', '2022-09-28', null, 0.8, WDP('P355')], ['PARENT_OF', 'Sony Financial Group', '2004-04-01', '2025-10-01', 0.85, WDP('P355')], ['OWNER_OF', 'Olympus Corporation', '2012-09-28', '2019-08-30', 0.8, WDP('P1830')], ['COMPETITOR', 'samsung-electronics', null, null, 0.6, { source: 'registry' }]],525  toyota: [['PARENT_OF', 'Daihatsu', '2016-08-01', null, 0.98, WDP('P355')], ['PARENT_OF', 'Hino Motors', '2001-01-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Toyota Financial Services', '2000-07-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Woven by Toyota', '2021-01-01', null, 0.85, WDP('P355')], ['PARENT_OF', 'Toyota Motor Europe', null, null, 0.85, WDP('P355')], ['OWNER_OF', 'Subaru Corporation', '2019-12-27', null, 0.8, WDP('P1830')], ['OWNER_OF', 'Mazda Motor Corporation', '2017-08-04', null, 0.7, WDP('P1830')]],526  roche: [['PARENT_OF', 'Genentech', '2009-03-26', null, 0.99, WDP('P355')], ['PARENT_OF', 'Chugai Pharmaceutical', '2002-10-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Foundation Medicine', '2018-07-31', null, 0.95, WDP('P355')], ['PARENT_OF', 'Spark Therapeutics', '2019-12-17', null, 0.95, WDP('P355')], ['PARENT_OF', 'Flatiron Health', '2018-04-06', null, 0.9, WDP('P355')], ['PARENT_OF', 'Ventana Medical Systems', '2008-02-01', null, 0.9, WDP('P355')]],527  sap: [['PARENT_OF', 'Qualtrics', '2019-01-23', '2023-06-28', 0.95, WDP('P355')], ['PARENT_OF', 'SAP Concur', '2014-12-04', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP Ariba', '2012-10-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP SuccessFactors', '2012-02-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'SAP Fieldglass', '2014-05-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Signavio', '2021-03-05', null, 0.9, WDP('P355')], ['PARENT_OF', 'LeanIX', '2023-11-08', null, 0.85, WDP('P355')], ['PARENT_OF', 'WalkMe', '2024-09-12', null, 0.85, WDP('P355')], ['PARTNER', 'siemens', null, null, 0.5, { source: 'registry' }]],528  spotify: [['PARENT_OF', 'Anchor', '2019-02-06', null, 0.9, WDP('P355')], ['PARENT_OF', 'Megaphone', '2020-12-15', null, 0.9, WDP('P355')], ['PARENT_OF', 'Podsights', '2022-02-16', null, 0.8, WDP('P355')], ['PARENT_OF', 'Findaway', '2022-06-01', null, 0.8, WDP('P355')]],529  adyen: [['PARENT_OF', 'Adyen Bank N.V.', null, null, 0.8, WDP('P355')], ['COMPETITOR', 'stripe', null, null, 0.7, { source: 'registry' }]],530  arm: [['SUBSIDIARY_OF', 'SoftBank Group', '2016-09-05', null, 0.98, WDP('P749')], ['OWNED_BY', 'SoftBank Group', '2016-09-05', null, 0.95, WDP('P127')], ['PARENT_OF', 'Arm China', '2018-04-01', null, 0.7, WDP('P355')], ['COMPETITOR', 'nvidia', null, null, 0.5, { source: 'registry' }]],531  tsmc: [['PARENT_OF', 'TSMC Arizona', '2020-05-15', null, 0.95, WDP('P355')], ['PARENT_OF', 'WaferTech', '1996-06-01', null, 0.9, WDP('P355')], ['OWNER_OF', 'ESMC (European Semiconductor Manufacturing Company)', '2023-08-08', null, 0.85, WDP('P1830')], ['OWNER_OF', 'Vanguard International Semiconductor', '1994-12-01', null, 0.8, WDP('P1830')], ['PARTNER', 'nvidia', null, null, 0.7, { source: 'registry' }], ['PARTNER', 'apple', null, null, 0.7, { source: 'registry' }]],532  siemens: [['PARENT_OF', 'Siemens Healthineers', '2018-03-16', null, 0.95, WDP('P355')], ['PARENT_OF', 'Siemens Mobility', '2018-08-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Siemens Financial Services', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Siemens Energy', '2020-04-01', '2020-09-28', 0.9, WDP('P355')], ['OWNER_OF', 'Siemens Energy', '2020-09-28', null, 0.8, WDP('P1830')], ['ACQUIRED', 'Altair Engineering', '2025-03-26', null, 0.9, WDP('P1830')], ['ACQUIRED', 'Brightly Software', '2022-06-27', null, 0.85, WDP('P1830')]],533  'samsung-electronics': [['PARENT_OF', 'Harman International', '2017-03-10', null, 0.98, WDP('P355')], ['PARENT_OF', 'Samsung Display', '2012-04-01', null, 0.95, WDP('P355')], ['PARENT_OF', 'Samsung Electronics America', null, null, 0.9, WDP('P355')], ['PARENT_OF', 'Samsung Medison', '2011-04-01', null, 0.85, WDP('P355')], ['OWNED_BY', 'Samsung Life Insurance', null, null, 0.7, WDP('P127')], ['COMPETITOR', 'apple', null, null, 0.7, { source: 'registry' }]],534  stripe: [['PARENT_OF', 'Stripe Payments Europe, Ltd.', null, null, 0.85, WDP('P355')], ['PARENT_OF', 'Stripe Payments UK, Ltd.', null, null, 0.8, WDP('P355')], ['ACQUIRED', 'Paystack', '2020-10-15', null, 0.95, WDP('P1830')], ['ACQUIRED', 'Lemon Squeezy', '2024-07-26', null, 0.85, WDP('P1830')], ['ACQUIRED', 'Bridge', '2025-02-04', null, 0.85, WDP('P1830')], ['COMPETITOR', 'adyen', null, null, 0.7, { source: 'registry' }], ['COMPETITOR', 'block', null, null, 0.6, { source: 'registry' }]],535  shopify: [['ACQUIRED', 'Deliverr', '2022-07-08', '2023-05-04', 0.9, WDP('P1830')], ['ACQUIRED', 'Vantage Discovery', '2025-03-10', null, 0.7, WDP('P1830')], ['PARTNER', 'stripe', null, null, 0.6, { source: 'registry' }]],536  cohere: [['OWNED_BY', 'Nvidia (minority investor)', '2023-06-08', null, 0.5, { source: 'registry' }]],537  anthropic: [['OWNED_BY', 'Amazon (minority investor)', '2023-09-25', null, 0.6, { source: 'registry' }], ['OWNED_BY', 'Alphabet (minority investor)', '2023-02-03', null, 0.6, { source: 'registry' }]],538  nubank: [['PARENT_OF', 'Nu México', '2019-05-01', null, 0.9, WDP('P355')], ['PARENT_OF', 'Nu Colombia', '2020-09-01', null, 0.9, WDP('P355')], ['ACQUIRED', 'Easynvest', '2020-09-01', null, 0.85, WDP('P1830')]],539};540function relationsFor(c) {541  const rows = RELATIONS[c.slug];542  if (rows) return rows.map(([kind, who, from, to, confidence, provenance]) => {543    const other = companiesBySlug.get(who);544    return { kind, company: other ? { slug: other.slug, display_name: other.display_name, logo_url: other.profile?.logo_url ?? other.profile?.icon_url ?? null } : null, to_name: other ? null : who, valid_from: from, valid_to: to, confidence, provenance };545  });546  const peer = companies.find((o) => o !== c && o.industries[0] === c.industries[0]);547  return [{ kind: 'COMPETITOR', company: peer ? { slug: peer.slug, display_name: peer.display_name, logo_url: peer.profile?.logo_url ?? peer.profile?.icon_url ?? null } : null, to_name: peer ? null : 'Unnamed peer', valid_from: null, valid_to: null, confidence: 0.6, provenance: { source: 'registry' } }, { kind: 'PARTNER', company: null, to_name: pick(PARTNERS), valid_from: iso(NOW - 200 * DAY), valid_to: null, confidence: 0.8, provenance: { source: 'registry' } }];548}549550// Wikidata-sourced executives (`source: 'wikidata'`), some overlapping the page-observed rows to exercise the merge.551// [name, title, status, valid_from]552const WD_PEOPLE = {553  nvidia: [['Jensen Huang', 'Chief Executive Officer, President and co-founder', 'listed', '1993-04-05'], ['Colette Kress', 'Chief Financial Officer', 'listed', '2013-09-01'], ['Chris Malachowsky', 'Co-founder', 'listed', '1993-04-05']],554  apple: [['Tim Cook', 'Chief Executive Officer', 'listed', '2011-08-24'], ['Arthur D. Levinson', 'Chairman of the board', 'listed', '2011-11-15'], ['Kevan Parekh', 'Chief Financial Officer', 'listed', '2025-01-01'], ['Luca Maestri', 'Chief Financial Officer', 'no_longer_listed', '2014-05-29'], ['Steve Jobs', 'Co-founder', 'no_longer_listed', '1976-04-01']],555  sony: [['Hiroki Totoki', 'President and Chief Executive Officer', 'listed', '2025-04-01'], ['Kenichiro Yoshida', 'Chairman', 'listed', '2025-04-01'], ['Lin Tao', 'Chief Financial Officer', 'listed', '2025-04-01'], ['Kazuo Hirai', 'Chief Executive Officer', 'no_longer_listed', '2012-04-01']],556  toyota: [['Koji Sato', 'President and Chief Executive Officer', 'listed', '2023-04-01'], ['Akio Toyoda', 'Chairman of the board', 'listed', '2023-04-01']],557  roche: [['Thomas Schinecker', 'Chief Executive Officer', 'listed', '2023-03-15'], ['Severin Schwan', 'Chairman of the board', 'listed', '2023-03-15']],558  sap: [['Christian Klein', 'Chief Executive Officer', 'listed', '2019-10-11'], ['Dominik Asam', 'Chief Financial Officer', 'listed', '2023-03-07'], ['Hasso Plattner', 'Co-founder and Chairman of the supervisory board', 'no_longer_listed', '2003-05-01']],559  spotify: [['Daniel Ek', 'Chief Executive Officer and co-founder', 'listed', '2006-04-23'], ['Martin Lorentzon', 'Co-founder and Chairman', 'listed', '2006-04-23']],560  adyen: [['Pieter van der Does', 'Co-founder and co-CEO', 'listed', '2006-01-01'], ['Ingo Uytdehaage', 'Co-CEO', 'listed', '2023-05-01']],561  arm: [['Rene Haas', 'Chief Executive Officer', 'listed', '2022-02-08'], ['Masayoshi Son', 'Chairman of the board', 'listed', '2016-09-05']],562  tsmc: [['C. C. Wei', 'Chairman and Chief Executive Officer', 'listed', '2024-06-04'], ['Morris Chang', 'Founder', 'no_longer_listed', '1987-02-21']],563  siemens: [['Roland Busch', 'President and Chief Executive Officer', 'listed', '2021-02-03'], ['Ralf P. Thomas', 'Chief Financial Officer', 'listed', '2013-09-18']],564  'samsung-electronics': [['Jun Young-hyun', 'Vice Chairman and co-CEO', 'listed', '2024-11-27'], ['Roh Tae-moon', 'Acting head, Device eXperience division', 'listed', '2025-03-01']],565  stripe: [['Patrick Collison', 'Chief Executive Officer and co-founder', 'listed', '2010-01-01'], ['John Collison', 'President and co-founder', 'listed', '2010-01-01']],566  shopify: [['Tobias Lütke', 'Chief Executive Officer and founder', 'listed', '2008-01-01'], ['Harley Finkelstein', 'President', 'listed', '2020-09-01']],567  cohere: [['Aidan Gomez', 'Chief Executive Officer and co-founder', 'listed', '2019-01-01'], ['Nick Frosst', 'Co-founder', 'listed', '2019-01-01'], ['Ivan Zhang', 'Co-founder', 'listed', '2019-01-01']],568  anthropic: [['Dario Amodei', 'Chief Executive Officer and co-founder', 'listed', '2021-01-01'], ['Daniela Amodei', 'President and co-founder', 'listed', '2021-01-01']],569  'mistral-ai': [['Arthur Mensch', 'Chief Executive Officer and co-founder', 'listed', '2023-04-28'], ['Guillaume Lample', 'Chief Scientist and co-founder', 'listed', '2023-04-28'], ['Timothée Lacroix', 'Chief Technology Officer and co-founder', 'listed', '2023-04-28']],570  nubank: [['David Vélez', 'Chief Executive Officer and founder', 'listed', '2013-05-06'], ['Cristina Junqueira', 'Co-founder', 'listed', '2013-05-06']],571};572function wikidataPeople(c) {573  const rows = WD_PEOPLE[c.slug] ?? [];574  const url = c.profile?.wikidata_url ?? `https://www.wikidata.org/wiki/Special:Search?search=${encodeURIComponent(c.display_name)}`;575  return rows.map(([name, title, status, from]) => ({ id: id('per'), name, title, role_category: /chair/i.test(title) ? 'board' : 'c_suite', is_executive: true, first_seen_at: iso(new Date(from).getTime()), last_seen_at: RETRIEVED, removed_at: status === 'listed' ? null : iso(NOW - ri(200, 2000) * DAY), status, source_url: url, source: 'wikidata' }));576}577578/** Self-hosted sample logos (plate + monogram) so QA never depends on the network; `icon` is the round variant. */579function logoSvg(slug, round) {580  const name = companiesBySlug.get(slug)?.display_name ?? slug;581  const words = name.split(/\s+/).filter(Boolean);582  const mono = (words.length > 1 ? words[0][0] + words[1][0] : name.slice(0, 1)).toUpperCase();583  let h = 0;584  for (const ch of slug) h = (h * 31 + ch.charCodeAt(0)) >>> 0;585  const hue = h % 360;586  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="${round ? 32 : 10}" fill="hsl(${hue} 55% 42%)"/><text x="32" y="40" font-family="ui-monospace, Menlo, monospace" font-size="${mono.length > 1 ? 24 : 30}" font-weight="700" fill="#fff" text-anchor="middle">${mono}</text></svg>`;587}588589// ------------------------------------------------------------------------------------------------ build dataset590const companies = [];591const companiesBySlug = new Map();592const sensors = new Map();593const snapshots = new Map();594const changes = new Map();595const events = [];596const eventsById = new Map();597const perCompany = new Map();598599function series(days, base, vol, drift = 0) {600  const out = [];601  let v = base;602  for (let i = days - 1; i >= 0; i--) {603    v = clamp(v + (rnd() - 0.5) * vol + drift, 0, 100);604    out.push({ day: day(NOW - i * DAY), value: r1(v), confidence: r1(0.6 + rnd() * 0.35) });605  }606  return out;607}608609const blockKinds = ['heading', 'paragraph', 'list', 'card', 'table', 'nav'];610function makeBlocks(surface, n) {611  const blocks = [];612  for (let i = 0; i < n; i++) {613    const kind = pick(blockKinds);614    blocks.push({ key: `${surface}:${kind}:${i}`, kind, path: `main > section:nth-of-type(${1 + Math.floor(i / 3)}) > ${kind === 'heading' ? 'h2' : kind === 'list' ? 'ul' : 'div'}:nth-child(${1 + (i % 3)})`, text: sampleText(surface, i) });615  }616  return blocks;617}618function sampleText(surface, i) {619  const map = {620    pricing: ['Starter — $29 per month. Up to 3 seats, community support.', 'Growth — $99 per month. Unlimited seats, SSO, priority support.', 'Enterprise — Contact sales. Custom SLAs, dedicated support, audit logs.', 'All plans include 14-day trial. Prices in USD, billed annually.'],621    careers: ['Senior Software Engineer — Toronto, Canada · Engineering', 'Machine Learning Engineer — Remote (EU) · AI Platform', 'Account Executive — London, UK · Sales', 'We are hiring across 12 offices. See all 148 open roles.'],622    leadership: ['Ava Martin — Chief Executive Officer', 'Noah Chen — Chief Technology Officer', 'Léa Dubois — Chief Financial Officer', 'Our leadership team brings decades of experience across payments and infrastructure.'],623    locations: ['Headquarters — 354 Oyster Point Blvd, South San Francisco', 'Dublin — Grand Canal Dock', 'Singapore — Raffles Place', 'Bengaluru — Indiranagar'],624    legal: ['7. Limitation of liability. To the maximum extent permitted by law…', '8. Governing law. These terms are governed by the laws of Ireland.', '12. Changes to these terms. We may update these terms; material changes will be notified 30 days in advance.'],625    products: ['Terminal — In-person payments hardware and SDKs.', 'Radar — Fraud prevention powered by network data.', 'Atlas — Company incorporation in days.', 'Billing — Subscriptions, invoices and revenue recovery.'],626  };627  const arr = map[surface] ?? ['Welcome to our company. We build tools that help teams move faster.', 'Trusted by thousands of businesses worldwide.', 'Read our latest news and product updates.', 'Contact us to learn more about enterprise plans.'];628  return arr[i % arr.length];629}630631function buildCompany(row, idx) {632  const [name, domain, country, city, lat, lon, inds, pub, ticker, exchange, founded, band, importance, desc] = row;633  const slug = slugify(name);634  const cid = id('co');635  const tier = importance >= 90 ? 1 : importance >= 75 ? 2 : importance >= 55 ? 3 : 4;636  const created = NOW - ri(120, 420) * DAY;637  // sensors638  const nSensors = tier === 1 ? ri(28, 63) : tier === 2 ? ri(16, 32) : tier === 3 ? ri(8, 18) : ri(4, 10);639  const compSensors = [];640  const surfacesUsed = [];641  for (let i = 0; i < nSensors; i++) {642    const surface = i < SURFACES.length ? SURFACES[i] : pick(SURFACES);643    surfacesUsed.push(surface);644    const sid = id('sen');645    const stier = surface === 'homepage' || surface === 'newsroom' || surface === 'careers' ? pick(['A', 'B']) : surface === 'legal' || surface === 'sitemap' ? pick(['D', 'E']) : pick(['B', 'C', 'C', 'D']);646    const statusRoll = rnd();647    const status = statusRoll < 0.86 ? 'active' : statusRoll < 0.93 ? 'failing' : statusRoll < 0.97 ? 'paused' : 'retired';648    const obs = ri(40, 2400);649    const snaps = ri(3, 12);650    const chg = Math.floor(snaps * (0.4 + rnd()));651    const path = SURFACE_PATH[surface] ?? `/${surface}`;652    const url = `https://${domain}${path}${i >= SURFACES.length ? `?p=${i}` : ''}`;653    const lastRun = NOW - ri(1, 300) * 60_000;654    const failing = status === 'failing';655    const sensor = {656      id: sid,657      company_id: cid,658      surface,659      connector_id: CONNECTOR_FOR[surface] ?? 'generic_html',660      url,661      canonical_url: url,662      domain,663      status,664      tier: stier,665      quality_score: r1(45 + rnd() * 55),666      discovery_confidence: r1(0.55 + rnd() * 0.45),667      discovery_method: pick(['navigation', 'sitemap', 'url_pattern', 'json_ld', 'manual']),668      current_interval_s: TIER_INTERVAL[stier] * (failing ? 4 : 1),669      next_run_at: iso(lastRun + TIER_INTERVAL[stier] * 1000),670      last_run_at: iso(lastRun),671      last_success_at: failing ? iso(NOW - ri(2, 6) * DAY) : iso(lastRun),672      last_change_at: iso(NOW - ri(1, 30) * DAY),673      last_status: failing ? pick([403, 429, 503, 0]) : 200,674      last_failure_class: failing ? pick(FAILURE_CLASSES) : null,675      consecutive_failures: failing ? ri(3, 40) : 0,676      observation_count: obs,677      snapshot_count: snaps,678      change_count: chg,679      meaningful_change_count: Math.floor(chg * 0.35),680      event_count: 0,681      created_at: iso(created + ri(0, 10) * DAY),682      _versions: [],683      _changes: [],684    };685    // snapshots (versions) and changes686    let prev = null;687    let t = created + ri(1, 20) * DAY;688    for (let v = 1; v <= snaps; v++) {689      const snapId = id('snap');690      t += ri(2, 20) * DAY;691      if (t > NOW) t = NOW - ri(0, 3) * 3600_000;692      const blocks = makeBlocks(surface, ri(8, 20));693      const snap = {694        id: snapId,695        sensor_id: sid,696        version_no: v,697        fetched_at: iso(t),698        title: `${name} — ${surface.replace('_', ' ')}`,699        language: pick(['en', 'en', 'en', 'fr', 'de', 'ja']),700        text_length: blocks.reduce((a, b) => a + b.text.length, 0) * ri(8, 20),701        block_count: blocks.length,702        extracted_summary: surface === 'careers' ? { jobs: ri(5, 200) } : surface === 'pricing' ? { plans: ri(2, 5) } : surface === 'leadership' ? { people: ri(4, 14) } : surface === 'locations' ? { locations: ri(1, 12) } : { blocks: blocks.length },703        content_hash: `sha256:${Array.from({ length: 16 }, () => B32[Math.floor(rnd() * 32)].toLowerCase()).join('')}`,704        previous_snapshot_id: prev ? prev.id : null,705        _blocks: blocks,706        _text: blocks.map((b) => b.text).join('\n\n'),707      };708      snapshots.set(snapId, snap);709      sensor._versions.push(snap);710      if (prev) {711        const chgId = id('chg');712        const added = blocks.slice(0, ri(0, 3)).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: null, after: b.text, weight: r1(0.3 + rnd() * 0.7), similarity: null }));713        const removed = prev._blocks.slice(-ri(0, 2)).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: null, weight: r1(0.3 + rnd() * 0.7), similarity: null }));714        const modified = prev._blocks.slice(1, 1 + ri(0, 3)).map((b, k) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: (blocks[k + 1] ?? blocks[0]).text, weight: r1(0.3 + rnd() * 0.7), similarity: r1(0.4 + rnd() * 0.5) }));715        const sig = clamp(r1(0.1 + (added.length + removed.length + modified.length) * 0.12 + rnd() * 0.25), 0.02, 0.98);716        const reasons = [];717        if (added.length) reasons.push(`${added.length} new block${added.length > 1 ? 's' : ''} (${added.map((a) => a.kind).join(', ')})`);718        if (removed.length) reasons.push(`${removed.length} block${removed.length > 1 ? 's' : ''} no longer present`);719        if (modified.length) reasons.push(`${modified.length} block${modified.length > 1 ? 's' : ''} modified with low similarity`);720        if (surface === 'pricing') reasons.push('structured pricing fields changed');721        if (surface === 'careers') reasons.push('job count changed');722        if (!reasons.length) reasons.push('text delta below noise threshold');723        const change = {724          id: chgId,725          sensor_id: sid,726          surface,727          company_id: cid,728          detected_at: snap.fetched_at,729          significance: sig,730          kind: sig < 0.2 ? 'noise' : sig < 0.4 ? 'minor' : sig < 0.65 ? 'meaningful' : sig < 0.85 ? 'major' : 'critical',731          blocks_added: added.length,732          blocks_removed: removed.length,733          blocks_modified: modified.length,734          text_delta_ratio: r1(sig * 0.6),735          similarity: r1(1 - sig * 0.7),736          snapshot_before: prev.id,737          snapshot_after: snapId,738          diff: { added, removed, modified, moved: [], counts: { added: added.length, removed: removed.length, modified: modified.length, unchanged: Math.max(0, blocks.length - added.length - modified.length) }, text_delta_ratio: r1(sig * 0.6), similarity: r1(1 - sig * 0.7), reasons },739          structured_delta: surface === 'careers' ? { jobs_added: added.length + ri(0, 4), jobs_removed: removed.length } : surface === 'pricing' ? { plans_changed: modified.length } : {},740          _events: [],741        };742        changes.set(chgId, change);743        sensor._changes.push(change);744      }745      prev = snap;746    }747    sensors.set(sid, sensor);748    compSensors.push(sensor);749  }750751  // entities752  const jobs = [];753  const nJobs = tier === 1 ? ri(60, 240) : tier === 2 ? ri(20, 90) : ri(3, 30);754  for (let i = 0; i < nJobs; i++) {755    const title = pick(JOB_TITLES);756    const cityRow = chance(0.5) ? [city, country] : pick(CITIES);757    const first = NOW - ri(1, 120) * DAY;758    const removed = chance(0.22);759    const removedAt = removed ? first + ri(5, 60) * DAY : null;760    jobs.push({761      id: id('job'),762      title,763      department: pick(DEPARTMENTS),764      location_text: `${cityRow[0]}, ${cityRow[1]}`,765      city: cityRow[0],766      country: cityRow[1],767      remote: chance(0.35),768      employment_type: pick(['full_time', 'full_time', 'contract', 'internship']),769      seniority: pick(['junior', 'mid', 'senior', 'staff', 'lead']),770      url: `https://${domain}/careers/${slugify(title)}-${i}`,771      posted_at: iso(first - ri(0, 3) * DAY),772      first_seen_at: iso(first),773      last_seen_at: iso(removed ? Math.min(NOW, removedAt) : NOW - ri(0, 3) * 3600_000),774      removed_at: removed && removedAt < NOW ? iso(removedAt) : null,775      status: removed && removedAt < NOW ? 'no_longer_listed' : 'open',776      is_ai: /AI|Machine Learning|LLM|Research/.test(title) || chance(0.1),777    });778  }779  const people = [];780  for (let i = 0; i < ri(5, 12); i++) {781    const removed = chance(0.2);782    const first = NOW - ri(30, 400) * DAY;783    people.push({ id: id('per'), name: PEOPLE[(idx * 3 + i) % PEOPLE.length], title: TITLES[i % TITLES.length], role_category: i < 6 ? 'c_suite' : 'vp', is_executive: i < 8, first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - ri(0, 2) * DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'no_longer_listed' : 'listed', source_url: `https://${domain}/about/leadership`, source: 'page' });784  }785  // a couple of Wikidata executives also appear on the monitored page (exercise the name merge → both chips)786  for (const [pname, ptitle] of (WD_PEOPLE[slug] ?? []).slice(0, 1)) people.unshift({ id: id('per'), name: pname, title: ptitle.split(' and ')[0], role_category: 'c_suite', is_executive: true, first_seen_at: iso(created + 5 * DAY), last_seen_at: iso(NOW - DAY), removed_at: null, status: 'listed', source_url: `https://${domain}/about/leadership`, source: 'page' });787  const products = [];788  for (let i = 0; i < ri(3, 10); i++) {789    const removed = chance(0.15);790    const first = NOW - ri(30, 400) * DAY;791    const pname = PRODUCT_NAMES[(idx * 5 + i) % PRODUCT_NAMES.length];792    products.push({ id: id('prd'), name: pname, category: pick(['platform', 'api', 'app', 'hardware', 'service']), description: `${pname} by ${name}.`, url: `https://${domain}/products/${slugify(pname)}`, first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - ri(0, 2) * DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'removed' : 'listed' });793  }794  const plans = [];795  const planNames = ['Starter', 'Growth', 'Scale', 'Enterprise'];796  let pv = 1;797  for (let i = 0; i < ri(2, 4); i++) {798    const pname = planNames[i];799    const enterprise = pname === 'Enterprise';800    let price = enterprise ? null : [29, 99, 299][i] ?? 49;801    const versions = ri(1, 3);802    let from = NOW - ri(200, 400) * DAY;803    for (let v = 1; v <= versions; v++) {804      const last = v === versions;805      const to = last ? null : from + ri(40, 120) * DAY;806      plans.push({ id: id('pln'), plan_name: pname, price, price_text: enterprise ? 'Contact sales' : `$${price}/mo`, currency: enterprise ? null : 'USD', billing_period: enterprise ? null : 'month', unit: enterprise ? null : 'seat', features: enterprise ? ['Custom SLAs', 'Dedicated support', 'Audit logs', 'SSO'] : [`${[3, 10, 50][i] ?? 5} seats`, 'API access', v > 1 ? 'Priority support' : 'Community support'], contact_sales: enterprise, version_no: pv++, valid_from: iso(from), valid_to: to ? iso(to) : null, status: last ? 'current' : 'superseded', source_url: `https://${domain}/pricing` });807      if (to) from = to;808      if (price !== null) price = Math.round(price * (1 + (rnd() * 0.3 - 0.05)));809    }810  }811  const locations = [{ id: id('loc'), kind: 'headquarters', name: `${name} HQ`, city, region: null, country, lat, lon, first_seen_at: iso(created), last_seen_at: iso(NOW - DAY), removed_at: null, status: 'listed', source_url: `https://${domain}/about/locations` }];812  for (let i = 0; i < (tier <= 2 ? ri(3, 9) : ri(0, 3)); i++) {813    const c = pick(CITIES);814    const removed = chance(0.15);815    const first = NOW - ri(30, 400) * DAY;816    locations.push({ id: id('loc'), kind: pick(['office', 'office', 'office', 'lab', 'warehouse', 'store']), name: `${c[0]} office`, city: c[0], region: null, country: c[1], lat: c[2], lon: c[3], first_seen_at: iso(first), last_seen_at: iso(removed ? first + ri(10, 200) * DAY : NOW - DAY), removed_at: removed ? iso(first + ri(10, 200) * DAY) : null, status: removed ? 'no_longer_listed' : 'listed', source_url: `https://${domain}/about/locations` });817  }818  const news = [];819  for (let i = 0; i < ri(5, 18); i++) {820    const t = NOW - ri(0, 120) * DAY;821    news.push({ id: id('nws'), title: `${name} ${pick(HEADLINES)}`, url: `https://${domain}/newsroom/${t}`, summary: 'First-party newsroom item.', category: pick(['press', 'product', 'company', 'research']), published_at: iso(t), first_seen_at: iso(t + ri(5, 240) * 60_000), language: 'en' });822  }823824  // metrics825  const activity = clamp(importance * 0.6 + rnd() * 40 - 10, 5, 99);826  const hiring30 = r1((rnd() - 0.42) * 60);827  const metrics = {828    activity_score: r1(activity),829    hiring_momentum_7d: r1(hiring30 / 3 + (rnd() - 0.5) * 8),830    hiring_momentum_30d: hiring30,831    hiring_momentum_90d: r1(hiring30 * 1.6 + (rnd() - 0.5) * 12),832    open_jobs: jobs.filter((j) => j.status === 'open').length,833    ai_adoption: r1(clamp(inds.includes('artificial-intelligence') ? 70 + rnd() * 30 : rnd() * 70, 0, 100)),834    product_velocity: r1(clamp(rnd() * 90, 0, 100)),835    geo_expansion: r1(clamp(rnd() * 80, 0, 100)),836    developer_momentum: r1(clamp((inds.includes('software') || inds.includes('cloud-infrastructure') ? 40 : 5) + rnd() * 60, 0, 100)),837    communication_activity: r1(rnd() * 100),838    pricing_activity: r1(rnd() * 60),839    leadership_activity: r1(rnd() * 50),840    corporate_change_index: 0,841    anomaly_score: r1(rnd() * 100),842    historical_coverage: r1(60 + rnd() * 40),843  };844  metrics.corporate_change_index = r1(0.25 * clamp(50 + hiring30, 0, 100) + 0.2 * metrics.product_velocity + 0.15 * metrics.geo_expansion + 0.15 * metrics.leadership_activity + 0.1 * metrics.developer_momentum + 0.1 * metrics.communication_activity + 0.05 * metrics.pricing_activity);845  if (chance(0.12)) delete metrics.ai_adoption; // omit when no inputs (never fabricate)846  if (chance(0.1)) delete metrics.developer_momentum;847  const act30 = series(30, metrics.activity_score, 12);848  const hir90 = series(90, 50 + hiring30 / 2, 8);849  const seriesAll = { activity_score: series(90, metrics.activity_score, 10), hiring_momentum_30d: hir90.map((p) => ({ ...p, value: r1((p.value - 50) * 2) })), product_velocity: series(90, metrics.product_velocity, 9), ai_adoption: series(90, metrics.ai_adoption ?? 30, 6, 0.1), corporate_change_index: series(90, metrics.corporate_change_index, 7), open_jobs: series(90, 50, 4).map((p) => ({ ...p, value: Math.round(metrics.open_jobs * (0.7 + p.value / 160)) })) };850  seriesAll.activity_score.splice(-30, 30, ...act30);851852  const company = {853    id: cid,854    slug,855    display_name: name,856    legal_name: `${name}${pub ? ' Inc.' : ', Inc.'}`,857    canonical_domain: domain,858    website: `https://${domain}`,859    description: desc,860    industries: inds,861    industry_primary: inds[0],862    country,863    hq_city: city,864    hq_region: null,865    public_company: pub,866    ticker,867    exchange,868    founded_year: founded,869    employees_band: band,870    logo_url: null,871    profile: null, // filled after all companies exist (relationships reference peers)872    status: chance(0.94) ? 'ACTIVE' : 'POSSIBLY_INACTIVE',873    onboarding_status: 'active',874    importance,875    tier,876    metrics,877    counts: { sensors: compSensors.filter((s) => s.status === 'active').length, observations: compSensors.reduce((a, s) => a + s.observation_count, 0), changes: compSensors.reduce((a, s) => a + s.change_count, 0), events: 0, jobs_open: metrics.open_jobs },878    last_event_at: null,879    last_observed_at: iso(NOW - ri(1, 90) * 60_000),880    sparkline: act30.map((p) => p.value),881    _lat: lat,882    _lon: lon,883    _created: created,884    _sensors: compSensors,885    _jobs: jobs,886    _people: people,887    _products: products,888    _plans: plans,889    _locations: locations,890    _news: news,891    _series: seriesAll,892    _hir90: hir90.map((p) => r1((p.value - 50) * 2)),893    _signals: [],894    _aliases: [name.toUpperCase(), `${name} Inc`, domain.split('.')[0]],895  };896  companies.push(company);897  companiesBySlug.set(slug, company);898  companiesBySlug.set(cid, company);899  return company;900}901902SEED.forEach((row, i) => buildCompany(row, i));903for (const c of companies) c.profile = NULL_PROFILE.has(c.slug) || NO_PROFILE_KEY.has(c.slug) ? makeProfile(c, null) : PROFILES[c.slug] ? makeProfile(c, PROFILES[c.slug]) : autoProfile(c);904const NO_CATALOGUE = new Set(['anthropic', 'mistral-ai']); // no monitored catalogue surface → Products tab falls back to Wikidata products905for (const c of companies) {906  c._relationships = relationsFor(c);907  c._facts = factsOf(c);908  c._people.push(...wikidataPeople(c));909  if (NO_CATALOGUE.has(c.slug)) c._products = [];910}911912// events913function makeEvent(c, template, t, opts = {}) {914  const [type, subtype, [imLo, imHi], surfaces, titleFn, summaryFn, ovFn] = template;915  const p = {916    name: pick(PRODUCT_NAMES),917    plan: pick(['Starter', 'Growth', 'Scale', 'Team']),918    oldPrice: ri(19, 199),919    city: pick(CITIES)[0],920    country: COUNTRY_META[pick(Object.keys(COUNTRY_META))][0],921    jobs: ri(2, 14),922    n: ri(3, 72),923    pct: ri(5, 45),924    ai: ri(0, 9),925    from: ri(40, 300),926    section: pick(['7', '8.2', '12', '3.1', 'Data processing addendum', 'Authentication', 'Webhooks', 'Rate limits']),927    blocks: ri(2, 30),928    v: ri(3, 14),929    headline: pick(HEADLINES),930    partner: pick(PARTNERS),931    target: `${pick(['Nimbus', 'Lattice', 'Parcel', 'Quill', 'Beacon'])} ${pick(['Labs', 'Systems', 'AI', 'Technologies'])}`,932    delay: ri(3, 90),933    amount: `$${pick([40, 75, 120, 250, 500])}M`,934    kind: pick(['advisory', 'partial outage', 'degraded performance', 'disclosure']),935    oldHeadline: 'Payments for small businesses',936    newHeadline: 'The financial infrastructure platform for enterprises',937    tech: pick(TECHS),938    item: pick(['Q3 results', 'annual report', 'investor day materials', 'a shareholder letter']),939    year: 2025,940    name2: pick(PEOPLE),941    title: pick(TITLES),942  };943  p.newPrice = subtype === 'PRICE_INCREASE' ? Math.round(p.oldPrice * (1.05 + rnd() * 0.3)) : Math.round(p.oldPrice * (0.7 + rnd() * 0.25));944  p.to = subtype === 'JOB_COUNT_INCREASE' ? p.from + p.n : Math.max(0, p.from - p.n);945  if (type === 'LEADERSHIP') {946    const person = pick(c._people);947    p.name = person.name;948    p.title = person.title;949  }950  const [oldV, newV] = ovFn(c, p);951  const surface = pick(surfaces);952  const sensor = c._sensors.find((s) => s.surface === surface) ?? c._sensors[0];953  const change = sensor._changes.length ? pick(sensor._changes) : null;954  const origin = pick(['deterministic', 'deterministic', 'deterministic', 'llm', 'hybrid', 'backfill']);955  const confidence = r1(clamp(0.45 + rnd() * 0.55 - (origin === 'llm' ? 0.12 : 0), 0.3, 0.99));956  const label = confidence >= 0.95 ? 'VERIFIED' : confidence >= 0.85 ? 'HIGH_CONFIDENCE' : confidence >= 0.7 ? 'LIKELY' : confidence >= 0.55 ? 'INFERRED' : 'LOW_CONFIDENCE';957  const importance = r1(imLo + rnd() * (imHi - imLo));958  const statusRoll = rnd();959  const eid = id('evt');960  const ev = {961    id: eid,962    company: { id: c.id, slug: c.slug, display_name: c.display_name, canonical_domain: c.canonical_domain, country: c.country, logo_url: null },963    event_type: type,964    event_subtype: subtype,965    importance,966    confidence,967    confidence_label: label,968    title: titleFn(c, p),969    summary: summaryFn(c, p),970    old_value: oldV,971    new_value: newV,972    payload: { template: subtype, ...(type === 'HIRING' ? { count_before: p.from, count_after: p.to } : {}), ...(type === 'PRICING' ? { plan: p.plan, price_before: p.oldPrice, price_after: p.newPrice, currency: 'USD' } : {}) },973    entities: type === 'LEADERSHIP' ? { person: p.name, title: p.title } : type === 'LOCATION' ? { city: p.city, country: p.country } : type === 'PRODUCT' ? { product: p.name } : {},974    tags: [...new Set([type.toLowerCase(), surface, ...(p.ai > 4 && type === 'HIRING' ? ['ai'] : [])])],975    detected_at: iso(t),976    effective_at: chance(0.5) ? iso(t - ri(0, 3) * DAY) : null,977    published_at: type === 'COMMUNICATION' || type === 'M&A' ? iso(t - ri(5, 240) * 60_000) : null,978    source_url: sensor.url,979    surface,980    sensor_id: sensor.id,981    change_id: change ? change.id : null,982    cluster_id: chance(0.3) ? id('cls') : null,983    origin,984    model_name: origin === 'llm' || origin === 'hybrid' ? 'qwen3.6-35b-a3b-4bit' : null,985    prompt_version: origin === 'llm' || origin === 'hybrid' ? 'event-classifier/v3' : null,986    status: opts.live ? 'active' : statusRoll < 0.94 ? 'active' : statusRoll < 0.97 ? 'retracted' : statusRoll < 0.99 ? 'review' : 'duplicate',987    sources: [{ source_url: sensor.url, surface, detected_at: iso(t), kind: 'primary', sensor_id: sensor.id }],988  };989  if (chance(0.4)) {990    const other = pick(c._sensors);991    ev.sources.push({ source_url: other.url, surface: other.surface, detected_at: iso(t + ri(2, 180) * 60_000), kind: 'corroboration', sensor_id: other.id });992  }993  if (change) change._events.push(ev.id);994  sensor.event_count += 1;995  return ev;996}997998for (const c of companies) {999  const n = c.tier === 1 ? ri(90, 260) : c.tier === 2 ? ri(40, 120) : c.tier === 3 ? ri(12, 50) : ri(3, 15);1000  for (let i = 0; i < n; i++) {1001    const t = NOW - Math.floor(Math.pow(rnd(), 1.6) * 365 * DAY) - ri(0, 3600_000);1002    events.push(makeEvent(c, pick(EVENT_TEMPLATES), t));1003  }1004}1005// make sure the most recent hours are populated for the live feed1006for (let i = 0; i < 60; i++) {1007  const c = pick(companies);1008  events.push(makeEvent(c, pick(EVENT_TEMPLATES), NOW - ri(1, 360) * 60_000));1009}1010events.sort((a, b) => (a.detected_at < b.detected_at ? 1 : -1));1011for (const e of events) {1012  eventsById.set(e.id, e);1013  const c = companiesBySlug.get(e.company.slug);1014  if (!perCompany.has(c.slug)) perCompany.set(c.slug, []);1015  perCompany.get(c.slug).push(e);1016}1017for (const c of companies) {1018  const list = perCompany.get(c.slug) ?? [];1019  c.counts.events = list.filter((e) => e.status === 'active').length;1020  c.last_event_at = list[0]?.detected_at ?? null;1021}10221023// signals1024const SIGNAL_KINDS = [['hiring_surge', 'Hiring surge signal'], ['hiring_freeze', 'Hiring slowdown signal'], ['launch_buildup', 'Possible launch preparation signal'], ['international_expansion', 'International expansion signal'], ['pricing_migration', 'Pricing migration signal'], ['developer_push', 'Developer ecosystem push'], ['enterprise_repositioning', 'Enterprise repositioning signal'], ['ai_acceleration', 'AI acceleration signal']];1025const signals = [];1026for (const c of companies) {1027  for (let i = 0; i < ri(0, 3); i++) {1028    const [kind, label] = pick(SIGNAL_KINDS);1029    const s = { id: id('sig'), company_id: c.id, scope: 'company', scope_key: c.slug, kind, strength: r1(0.3 + rnd() * 0.7), confidence: r1(0.4 + rnd() * 0.5), title: `${label} — ${c.display_name}`, explanation: kind === 'launch_buildup' ? 'Documentation, careers and changelog surfaces changed together within 9 days; historically this pattern preceded a product listing in 4 of 7 monitored cases for this company. Presented as a signal, not a fact.' : kind === 'hiring_surge' ? `Open listings are ${ri(20, 80)} % above the 90-day baseline, concentrated in ${pick(DEPARTMENTS)}.` : 'Derived from per-company baselines of monitored surfaces.', evidence: { events: ri(2, 12), surfaces: ri(2, 5), window_days: 30 }, window_days: 30, detected_at: iso(NOW - ri(1, 20) * DAY), status: 'active' };1030    signals.push(s);1031    c._signals.push(s);1032  }1033}1034for (const [slug, ind] of Object.entries(IND).slice(0, 8)) signals.push({ id: id('sig'), company_id: null, scope: 'industry', scope_key: slug, kind: 'ai_acceleration', strength: r1(0.3 + rnd() * 0.6), confidence: r1(0.5 + rnd() * 0.4), title: `AI hiring acceleration in ${ind.name}`, explanation: 'Share of AI-tagged listings rose across monitored companies in this industry.', evidence: { companies: ri(3, 12) }, window_days: 30, detected_at: iso(NOW - ri(1, 10) * DAY), status: 'active' });1035signals.push({ id: id('sig'), company_id: null, scope: 'global', scope_key: null, kind: 'pricing_migration', strength: 0.61, confidence: 0.72, title: 'SaaS pricing pages shifting to usage-based tiers', explanation: `${ri(8, 20)} monitored pricing pages added usage units in 30 days.`, evidence: { companies: 14 }, window_days: 30, detected_at: iso(NOW - 2 * DAY), status: 'active' });10361037// trends1038const trends = TREND_TERMS.map((term) => ({ term, mentions: ri(12, 480), companies: ri(3, 30), momentum: r1((rnd() - 0.3) * 120), series: Array.from({ length: 14 }, () => ri(0, 40)) })).sort((a, b) => b.momentum - a.momentum);10391040// industries and countries1041function industryRow(slug) {1042  const ind = IND[slug];1043  const cs = companies.filter((c) => c.industries.includes(slug));1044  const evs = cs.flatMap((c) => perCompany.get(c.slug) ?? []).filter((e) => e.status === 'active');1045  const avg = (k) => {1046    const v = cs.map((c) => c.metrics[k]).filter((x) => typeof x === 'number');1047    return v.length ? r1(v.reduce((a, b) => a + b, 0) / v.length) : null;1048  };1049  const byType = {};1050  for (const e of evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY))) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1051  return { slug, name: ind.name, parent_slug: ind.parent_slug, companies: cs.length, events_7d: evs.filter((e) => e.detected_at > iso(NOW - 7 * DAY)).length, events_30d: evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length, hiring_momentum_30d: avg('hiring_momentum_30d'), activity_score: avg('activity_score'), ai_adoption: avg('ai_adoption'), top_event_types: Object.entries(byType).sort((a, b) => b[1] - a[1]).slice(0, 3).map((x) => x[0]), _companies: cs, _events: evs };1052}1053function countryRow(code) {1054  const [name, region, lat, lon] = COUNTRY_META[code];1055  const cs = companies.filter((c) => c.country === code);1056  const evs = cs.flatMap((c) => perCompany.get(c.slug) ?? []).filter((e) => e.status === 'active');1057  const avg = (k) => {1058    const v = cs.map((c) => c.metrics[k]).filter((x) => typeof x === 'number');1059    return v.length ? r1(v.reduce((a, b) => a + b, 0) / v.length) : null;1060  };1061  const mix = {};1062  for (const c of cs) for (const i of c.industries) mix[i] = (mix[i] ?? 0) + 1;1063  return { code, name, region, companies: cs.length, events_7d: evs.filter((e) => e.detected_at > iso(NOW - 7 * DAY)).length, events_30d: evs.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length, hiring_momentum_30d: avg('hiring_momentum_30d'), activity_score: avg('activity_score'), industry_mix: Object.entries(mix).map(([industry, companies]) => ({ industry, companies })).sort((a, b) => b.companies - a.companies), lat, lon, _companies: cs, _events: evs };1064}1065const industryRows = () => Object.keys(IND).map(industryRow).filter((r) => r.companies > 0).sort((a, b) => b.events_30d - a.events_30d);1066const countryRows = () => Object.keys(COUNTRY_META).map(countryRow).filter((r) => r.companies > 0).sort((a, b) => b.events_30d - a.events_30d);10671068// global daily history + index1069const history = [];1070{1071  let idx = 100;1072  for (let i = 364; i >= 0; i--) {1073    const t = NOW - i * DAY;1074    idx = clamp(idx + (rnd() - 0.48) * 3, 70, 150);1075    const dayEvents = events.filter((e) => day(new Date(e.detected_at).getTime()) === day(t));1076    const byType = {};1077    for (const e of dayEvents) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1078    const growth = 1 - i / 600;1079    history.push({ day: day(t), companies_active: Math.round(companies.length * growth), sensors_active: Math.round(sensors.size * growth), observations: Math.round((18000 + rnd() * 6000) * growth), changes: Math.round((900 + rnd() * 400) * growth), meaningful_changes: Math.round((260 + rnd() * 120) * growth), events: dayEvents.length, events_by_type: byType, jobs_open: Math.round(12000 * growth + rnd() * 800), jobs_new: ri(80, 400), jobs_removed: ri(60, 380), activity_index: r1(idx) });1080  }1081}10821083function stats() {1084  const obs = [...sensors.values()].reduce((a, s) => a + s.observation_count, 0);1085  const first = Math.min(...companies.map((c) => c._created));1086  return { companies: companies.length, companies_active: companies.filter((c) => c.status === 'ACTIVE').length, sensors: sensors.size, sensors_active: [...sensors.values()].filter((s) => s.status === 'active').length, observations: obs + liveTicks * 37, snapshots: [...sensors.values()].reduce((a, s) => a + s.snapshot_count, 0), changes: changes.size + liveTicks * 3, meaningful_changes: [...changes.values()].filter((c) => c.significance >= 0.4).length + liveTicks, events: events.filter((e) => e.status === 'active').length, jobs_open: companies.reduce((a, c) => a + c.counts.jobs_open, 0), countries: new Set(companies.map((c) => c.country)).size, industries: new Set(companies.flatMap((c) => c.industries)).size, observations_today: 21560 + liveTicks * 37, changes_today: 1130 + liveTicks * 3, events_today: events.filter((e) => e.detected_at.slice(0, 10) === day(NOW) && e.status === 'active').length, dataset_started_at: iso(first), dataset_age_days: Math.round((NOW - first) / DAY), oldest_history_days: Math.round((NOW - first) / DAY), last_observation_at: iso(Date.now() - ri(3, 40) * 1000), archive: { objects: 1_284_310 + liveTicks * 30, bytes: 412_000_000_000 + liveTicks * 800_000 } };1087}10881089function mapBuckets(metric) {1090  const buckets = new Map();1091  for (const c of companies) {1092    const key = `${c.country}:${c.hq_city}`;1093    if (!buckets.has(key)) buckets.set(key, { lat: c._lat, lon: c._lon, country: c.country, city: c.hq_city, companies: 0, events_30d: 0, jobs_open: 0, top: [] });1094    const b = buckets.get(key);1095    b.companies += 1;1096    b.events_30d += (perCompany.get(c.slug) ?? []).filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length;1097    b.jobs_open += c.counts.jobs_open;1098    b.top.push({ slug: c.slug, display_name: c.display_name, importance: c.importance });1099  }1100  for (const b of buckets.values()) b.top = b.top.sort((a, z) => z.importance - a.importance).slice(0, 3).map(({ slug, display_name }) => ({ slug, display_name }));1101  return [...buckets.values()].sort((a, b) => b[metric] - a[metric]);1102}11031104function rankings(kind, window, country, industry, limit) {1105  let cs = companies.filter((c) => (!country || c.country === country.toUpperCase()) && (!industry || c.industries.includes(industry)));1106  const wf = { '24h': 0.25, '7d': 0.6, '30d': 1, '90d': 1.3, '1y': 1.6 }[window] ?? 1;1107  const val = (c) => {1108    switch (kind) {1109      case 'hiring_growth':1110        return c.metrics.hiring_momentum_30d * wf;1111      case 'hiring_decline':1112        return c.metrics.hiring_momentum_30d * wf;1113      case 'product_velocity':1114        return c.metrics.product_velocity;1115      case 'ai_active':1116        return c.metrics.ai_adoption ?? -1;1117      case 'geo_expansion':1118        return c.metrics.geo_expansion;1119      case 'developer_momentum':1120        return c.metrics.developer_momentum ?? -1;1121      case 'pricing_changes':1122        return (perCompany.get(c.slug) ?? []).filter((e) => e.event_type === 'PRICING').length * wf;1123      case 'unusual_activity':1124        return c.metrics.anomaly_score;1125      default:1126        return c.metrics.activity_score;1127    }1128  };1129  cs = cs.filter((c) => val(c) >= 0);1130  cs.sort((a, b) => (kind === 'hiring_decline' ? val(a) - val(b) : val(b) - val(a)));1131  return cs.slice(0, limit).map((c, i) => ({ ...pub(c), rank: i + 1, value: r1(val(c)), delta: chance(0.8) ? r1((rnd() - 0.5) * 20) : null }));1132}11331134const pub = (c) => {1135  const { _lat, _lon, _created, _sensors, _jobs, _people, _products, _plans, _locations, _news, _series, _hir90, _signals, _aliases, _relationships, _facts, ...rest } = c;1136  if (NO_PROFILE_KEY.has(c.slug)) delete rest.profile; // API builds that predate the profile field1137  return rest;1138};1139const pubSensor = (s) => {1140  const { _versions, _changes, ...rest } = s;1141  return rest;1142};1143const pubChange = (c) => {1144  const { _events, ...rest } = c;1145  return rest;1146};1147const pubSnap = (s) => {1148  const { _blocks, _text, ...rest } = s;1149  return rest;1150};1151const ref = (c) => ({ id: c.id, slug: c.slug, display_name: c.display_name, canonical_domain: c.canonical_domain, country: c.country, logo_url: null });11521153// ------------------------------------------------------------------------------------------------ live stream1154let liveTicks = 0;1155const sseClients = new Set();1156setInterval(() => {1157  const c = pick(companies);1158  const ev = makeEvent(c, pick(EVENT_TEMPLATES), Date.now() - ri(0, 2000), { live: true });1159  events.unshift(ev);1160  eventsById.set(ev.id, ev);1161  (perCompany.get(c.slug) ?? perCompany.set(c.slug, []).get(c.slug)).unshift(ev);1162  c.counts.events += 1;1163  c.last_event_at = ev.detected_at;1164  liveTicks += 1;1165  const frame = `event: event\nid: ${ev.id}\ndata: ${JSON.stringify(ev)}\n\n`;1166  for (const res of sseClients) res.write(frame);1167}, 4000);1168setInterval(() => {1169  for (const res of sseClients) res.write(`event: heartbeat\ndata: ${JSON.stringify({ at: new Date().toISOString(), clients: sseClients.size })}\n\n`);1170}, 20000);11711172// ------------------------------------------------------------------------------------------------ owner / admin state1173const watchlists = new Map(); // token -> Set(slug)1174const alerts = new Map(); // token -> Alert[]1175const adminToken = process.env.CA_ADMIN_TOKEN ?? 'dev-admin-token';11761177// ------------------------------------------------------------------------------------------------ helpers1178function paginate(items, q, defaultPer = 25) {1179  const page = Math.max(1, Number(q.get('page') ?? 1));1180  const per = clamp(Number(q.get('per_page') ?? defaultPer), 1, 200);1181  const total = items.length;1182  return { items: items.slice((page - 1) * per, page * per), page, per_page: per, total, pages: Math.max(1, Math.ceil(total / per)) };1183}1184function filterEvents(list, q) {1185  let out = list;1186  const g = (k) => q.get(k);1187  if (g('event_type')) out = out.filter((e) => e.event_type === g('event_type').toUpperCase());1188  if (g('event_subtype')) out = out.filter((e) => e.event_subtype === g('event_subtype').toUpperCase());1189  if (g('country')) out = out.filter((e) => (e.company.country ?? '').toUpperCase() === g('country').toUpperCase());1190  if (g('industry')) out = out.filter((e) => companiesBySlug.get(e.company.slug)?.industries.includes(g('industry')));1191  if (g('since')) out = out.filter((e) => e.detected_at > g('since'));1192  if (g('until')) out = out.filter((e) => e.detected_at < g('until'));1193  if (g('min_importance')) out = out.filter((e) => e.importance >= Number(g('min_importance')));1194  if (g('min_confidence')) out = out.filter((e) => e.confidence >= Number(g('min_confidence')));1195  if (g('surface')) out = out.filter((e) => e.surface === g('surface'));1196  if (g('origin')) out = out.filter((e) => e.origin === g('origin'));1197  if (g('company')) out = out.filter((e) => e.company.slug === g('company'));1198  if (g('status')) out = out.filter((e) => e.status === g('status'));1199  if (g('q')) {1200    const s = g('q').toLowerCase();1201    out = out.filter((e) => e.title.toLowerCase().includes(s) || (e.summary ?? '').toLowerCase().includes(s) || e.company.display_name.toLowerCase().includes(s));1202  }1203  if (g('sort') === 'importance') out = [...out].sort((a, b) => b.importance - a.importance);1204  return out;1205}1206const TIMELINE_MAP = { products: ['PRODUCT'], jobs: ['HIRING'], pricing: ['PRICING'], leadership: ['LEADERSHIP'], locations: ['LOCATION'], legal: ['LEGAL'], news: ['COMMUNICATION', 'PARTNERSHIP', 'M&A', 'FINANCING', 'INVESTOR_RELATIONS'], developer: ['DEVELOPER', 'TECHNOLOGY'] };12071208function json(res, status, body, extra = {}) {1209  const data = JSON.stringify(body);1210  res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'access-control-allow-origin': '*', 'access-control-allow-headers': 'content-type, x-ca-owner-token, x-ca-admin-token', 'access-control-allow-methods': 'GET,POST,DELETE,OPTIONS', 'x-api-version': 'mock-1', ...extra });1211  res.end(data);1212}1213const notFound = (res, what = 'not found') => json(res, 404, { detail: what });1214function readBody(req) {1215  return new Promise((resolve) => {1216    let b = '';1217    req.on('data', (c) => (b += c));1218    req.on('end', () => {1219      try {1220        resolve(b ? JSON.parse(b) : {});1221      } catch {1222        resolve({});1223      }1224    });1225  });1226}1227function csv(rows) {1228  if (!rows.length) return '';1229  const keys = Object.keys(rows[0]).filter((k) => typeof rows[0][k] !== 'object' || rows[0][k] === null);1230  const esc = (v) => (v === null || v === undefined ? '' : /[",\n]/.test(String(v)) ? `"${String(v).replace(/"/g, '""')}"` : String(v));1231  return [keys.join(','), ...rows.map((r) => keys.map((k) => esc(r[k])).join(','))].join('\n');1232}12331234// ------------------------------------------------------------------------------------------------ router1235const server = createServer(async (req, res) => {1236  const url = new URL(req.url, `http://${req.headers.host ?? 'localhost'}`);1237  const q = url.searchParams;1238  let path = url.pathname.replace(/\/$/, '') || '/';1239  if (req.method === 'OPTIONS') return json(res, 204, {});1240  if (path === '/health' || path === '/ready' || path === '/api/v1/health' || path === '/api/v1/ready') return json(res, 200, { status: 'ok', service: 'company-atlas-mock', time: new Date().toISOString() });1241  if (!path.startsWith('/api/v1')) return notFound(res);1242  path = path.slice('/api/v1'.length) || '/';1243  const seg = path.split('/').filter(Boolean);1244  const owner = req.headers['x-ca-owner-token'];1245  const admin = req.headers['x-ca-admin-token'];12461247  try {1248    // ---- mock assets (sample logos; not part of the API contract)1249    if (seg[0] === '_mock' && (seg[1] === 'logo' || seg[1] === 'icon') && seg[2]) {1250      const slug = seg[2].replace(/\.svg$/, '');1251      if (!companiesBySlug.has(slug)) return notFound(res, 'logo not found');1252      res.writeHead(200, { 'content-type': 'image/svg+xml', 'cache-control': 'public, max-age=3600', 'access-control-allow-origin': '*' });1253      return res.end(logoSvg(slug, seg[1] === 'icon'));1254    }12551256    // ---- platform1257    if (path === '/stats') return json(res, 200, stats(), { 'cache-control': 'public, max-age=60' });1258    if (path === '/stats/history') return json(res, 200, { items: history.slice(-clamp(Number(q.get('days') ?? 90), 1, 365)) });1259    if (path === '/system') return json(res, 200, { sensors_online: [...sensors.values()].filter((s) => s.status === 'active').length, sensors_failing: [...sensors.values()].filter((s) => s.status === 'failing').length, observations_today: 21560 + liveTicks * 37, events_today: stats().events_today, countries_covered: new Set(companies.map((c) => c.country)).size, queue_lag_s: ri(2, 40), scheduler_last_tick_at: iso(Date.now() - ri(1, 20) * 1000), fetch_per_min: ri(280, 460), success_rate_24h: r1(96 + rnd() * 3.5) });1260    if (path === '/pulse') {1261      const active = events.filter((e) => e.status === 'active');1262      const idxSeries = history.slice(-30).map((h) => ({ day: h.day, value: h.activity_index, confidence: 0.9 }));1263      const last = idxSeries[idxSeries.length - 1].value;1264      const wk = idxSeries[idxSeries.length - 8].value;1265      return json(res, 200, { stats: stats(), live: active.slice(0, 12), movers: [...companies].sort((a, b) => b.metrics.corporate_change_index - a.metrics.corporate_change_index).slice(0, 10).map(pub), hiring: [...companies].sort((a, b) => b.metrics.hiring_momentum_30d - a.metrics.hiring_momentum_30d).slice(0, 8).map(pub), launches: active.filter((e) => e.event_subtype === 'PRODUCT_LAUNCH').slice(0, 8), pricing: active.filter((e) => e.event_type === 'PRICING').slice(0, 8), ai: [...companies].filter((c) => typeof c.metrics.ai_adoption === 'number').sort((a, b) => b.metrics.ai_adoption - a.metrics.ai_adoption).slice(0, 8).map(pub), industries: industryRows().slice(0, 12).map(({ _companies, _events, ...r }) => r), countries: countryRows().slice(0, 12).map(({ _companies, _events, ...r }) => r), trending: trends.slice(0, 10), activity_index: { value: last, delta_7d: r1(((last - wk) / wk) * 100), series: idxSeries }, map: mapBuckets('events_30d') }, { 'cache-control': 'public, max-age=60' });1266    }1267    if (path === '/live') {1268      const limit = clamp(Number(q.get('limit') ?? 50), 1, 500);1269      let list = events.filter((e) => e.status === 'active');1270      list = filterEvents(list, q);1271      return json(res, 200, { items: list.slice(0, limit) }, { 'cache-control': 'no-store' });1272    }1273    if (path === '/live/stream') {1274      res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store, no-transform', connection: 'keep-alive', 'access-control-allow-origin': '*', 'x-accel-buffering': 'no' });1275      res.write(`: connected\n\n`);1276      const since = q.get('since');1277      if (since) for (const e of filterEvents(events.filter((e) => e.status === 'active' && e.detected_at > since), q).slice(0, 20).reverse()) res.write(`event: event\nid: ${e.id}\ndata: ${JSON.stringify(e)}\n\n`);1278      sseClients.add(res);1279      req.on('close', () => sseClients.delete(res));1280      return;1281    }12821283    // ---- companies1284    if (path === '/companies/compare') {1285      const slugs = (q.get('companies') ?? '').split(',').map((s) => s.trim()).filter(Boolean).slice(0, 6);1286      const cs = slugs.map((s) => companiesBySlug.get(s)).filter(Boolean);1287      if (cs.length < 2) return json(res, 400, { detail: 'compare needs 2–6 known companies' });1288      const metrics = {};1289      for (const k of ['activity_score', 'hiring_momentum_30d', 'product_velocity', 'ai_adoption', 'geo_expansion', 'developer_momentum', 'corporate_change_index', 'open_jobs']) {1290        metrics[k] = {};1291        for (const c of cs) if (typeof c.metrics[k] === 'number') metrics[k][c.slug] = c.metrics[k];1292      }1293      const seriesOut = {};1294      const events_30d = {};1295      const jobs = {};1296      const locations = {};1297      for (const c of cs) {1298        seriesOut[c.slug] = c._series.activity_score;1299        const by = {};1300        for (const e of (perCompany.get(c.slug) ?? []).filter((e) => e.detected_at > iso(NOW - 30 * DAY) && e.status === 'active')) by[e.event_type] = (by[e.event_type] ?? 0) + 1;1301        events_30d[c.slug] = by;1302        jobs[c.slug] = { open: c.counts.jobs_open, ai_open: c._jobs.filter((j) => j.status === 'open' && j.is_ai).length, new_30d: c._jobs.filter((j) => j.first_seen_at > iso(NOW - 30 * DAY)).length };1303        locations[c.slug] = c._locations.filter((l) => l.status === 'listed').length;1304      }1305      return json(res, 200, { companies: cs.map(pub), metrics, series: seriesOut, events_30d, jobs, locations });1306    }1307    if (path === '/companies') {1308      let list = [...companies];1309      const g = (k) => q.get(k);1310      if (g('q')) {1311        const s = g('q').toLowerCase();1312        list = list.filter((c) => c.display_name.toLowerCase().includes(s) || c.canonical_domain.includes(s) || c._aliases.some((a) => a.toLowerCase().includes(s)));1313      }1314      if (g('country')) list = list.filter((c) => c.country === g('country').toUpperCase());1315      if (g('industry')) list = list.filter((c) => c.industries.includes(g('industry')));1316      if (g('tier')) list = list.filter((c) => String(c.tier) === g('tier'));1317      if (g('public')) list = list.filter((c) => c.public_company === (g('public') === '1' || g('public') === 'true'));1318      if (g('status')) list = list.filter((c) => c.status.toLowerCase() === g('status').toLowerCase());1319      if (g('has_events')) list = list.filter((c) => (c.counts.events > 0) === (g('has_events') === '1' || g('has_events') === 'true'));1320      const sort = g('sort') ?? 'activity';1321      const cmp = { activity: (a, b) => b.metrics.activity_score - a.metrics.activity_score, events: (a, b) => b.counts.events - a.counts.events, hiring: (a, b) => b.metrics.hiring_momentum_30d - a.metrics.hiring_momentum_30d, name: (a, b) => a.display_name.localeCompare(b.display_name), importance: (a, b) => b.importance - a.importance, recent: (a, b) => (b.last_event_at ?? '').localeCompare(a.last_event_at ?? '') }[sort];1322      if (cmp) list.sort(cmp);1323      const page = paginate(list, q);1324      const spark = g('sparkline') === '1';1325      page.items = page.items.map((c) => {1326        const p = pub(c);1327        if (!spark) delete p.sparkline;1328        return p;1329      });1330      return json(res, 200, page);1331    }1332    if (seg[0] === 'companies' && seg[1]) {1333      const c = companiesBySlug.get(decodeURIComponent(seg[1]));1334      if (!c) return notFound(res, 'company not found');1335      const sub = seg[2];1336      const list = perCompany.get(c.slug) ?? [];1337      if (!sub) {1338        return json(res, 200, { ...pub(c), aliases: c._aliases, domains: [{ domain: c.canonical_domain, kind: 'canonical' }, { domain: `docs.${c.canonical_domain}`, kind: 'subdomain' }, { domain: `status.${c.canonical_domain}`, kind: 'subdomain' }], relationships: c._relationships, facts: c._facts, metrics_detail: Object.entries(c.metrics).map(([metric, value]) => ({ metric, value, confidence: r1(0.6 + rnd() * 0.35), computed_at: iso(NOW - ri(5, 120) * 60_000), formula_version: 'v1.0', inputs: { sensors: c.counts.sensors, events_30d: list.filter((e) => e.detected_at > iso(NOW - 30 * DAY)).length } })), sensors_by_surface: c._sensors.reduce((a, s) => ((a[s.surface] = (a[s.surface] ?? 0) + 1), a), {}), coverage: { historical_coverage: c.metrics.historical_coverage, first_observed_at: iso(c._created), days_observed: Math.round((NOW - c._created) / DAY), sensor_uptime: r1(90 + rnd() * 9.5) }, signals: c._signals, sparklines: { activity_30d: c.sparkline, hiring_90d: c._hir90 } });1339      }1340      if (sub === 'events') return json(res, 200, paginate(filterEvents(list, q), q));1341      if (sub === 'timeline') {1342        const f = q.get('filter') ?? 'all';1343        const limit = clamp(Number(q.get('limit') ?? 200), 1, 500);1344        const types = TIMELINE_MAP[f];1345        const items = (types ? list.filter((e) => types.includes(e.event_type)) : list).slice(0, limit).map((e) => ({ ...e, day: e.detected_at.slice(0, 10) }));1346        const days = {};1347        for (const e of items) days[e.day] = (days[e.day] ?? 0) + 1;1348        return json(res, 200, { items, days: Object.entries(days).map(([day, count]) => ({ day, count })) });1349      }1350      if (sub === 'metrics') {1351        const days = clamp(Number(q.get('days') ?? 90), 7, 365);1352        const s = {};1353        for (const [k, v] of Object.entries(c._series)) s[k] = v.slice(-days);1354        return json(res, 200, { current: Object.entries(c.metrics).map(([metric, value]) => ({ metric, value, confidence: r1(0.6 + rnd() * 0.35), computed_at: iso(NOW - ri(5, 120) * 60_000), formula_version: 'v1.0', inputs: {} })), series: s });1355      }1356      if (sub === 'jobs') {1357        let jobs = [...c._jobs];1358        const st = q.get('status') ?? 'open';1359        if (st === 'open') jobs = jobs.filter((j) => j.status === 'open');1360        if (st === 'removed') jobs = jobs.filter((j) => j.status === 'no_longer_listed');1361        if (q.get('q')) jobs = jobs.filter((j) => j.title.toLowerCase().includes(q.get('q').toLowerCase()));1362        if (q.get('country')) jobs = jobs.filter((j) => j.country === q.get('country').toUpperCase());1363        if (q.get('ai') === '1') jobs = jobs.filter((j) => j.is_ai);1364        jobs.sort((a, b) => b.first_seen_at.localeCompare(a.first_seen_at));1365        const open = c._jobs.filter((j) => j.status === 'open');1366        const byC = {};1367        const byD = {};1368        for (const j of open) {1369          byC[j.country] = (byC[j.country] ?? 0) + 1;1370          byD[j.department] = (byD[j.department] ?? 0) + 1;1371        }1372        const page = paginate(jobs, q, 50);1373        page.meta = { summary: { open: open.length, new_7d: c._jobs.filter((j) => j.first_seen_at > iso(NOW - 7 * DAY)).length, removed_7d: c._jobs.filter((j) => j.removed_at && j.removed_at > iso(NOW - 7 * DAY)).length, ai_open: open.filter((j) => j.is_ai).length, by_country: Object.entries(byC).map(([country, n]) => ({ country, n })).sort((a, b) => b.n - a.n), by_department: Object.entries(byD).map(([department, n]) => ({ department, n })).sort((a, b) => b.n - a.n), remote_ratio: open.length ? r1(open.filter((j) => j.remote).length / open.length) : null } };1374        return json(res, 200, page);1375      }1376      if (sub === 'people') return json(res, 200, { listed: c._people.filter((p) => p.status === 'listed'), no_longer_listed: c._people.filter((p) => p.status !== 'listed') });1377      if (sub === 'products') return json(res, 200, { listed: c._products.filter((p) => p.status === 'listed'), removed: c._products.filter((p) => p.status !== 'listed') });1378      if (sub === 'pricing') return json(res, 200, { current: c._plans.filter((p) => p.status === 'current'), history: c._plans.filter((p) => p.status !== 'current') });1379      if (sub === 'locations') return json(res, 200, { items: c._locations, countries: [...new Set(c._locations.filter((l) => l.status === 'listed').map((l) => l.country))] });1380      if (sub === 'news') return json(res, 200, { items: [...c._news].sort((a, b) => b.published_at.localeCompare(a.published_at)).slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) });1381      if (sub === 'sensors') return json(res, 200, { items: c._sensors.map(pubSensor) });1382      if (sub === 'history') return json(res, 200, { sensors: c._sensors.filter((s) => s._versions.length).map((s) => ({ ...pubSensor(s), versions: [...s._versions].reverse().slice(0, 20).map(pubSnap) })) });1383      if (sub === 'similar') return json(res, 200, { items: companies.filter((o) => o !== c && (o.industries.some((i) => c.industries.includes(i)) || o.country === c.country)).sort((a, b) => Math.abs(a.importance - c.importance) - Math.abs(b.importance - c.importance)).slice(0, clamp(Number(q.get('limit') ?? 8), 1, 24)).map(pub) });1384      return notFound(res);1385    }13861387    // ---- provenance1388    if (seg[0] === 'sensors' && seg[1]) {1389      const s = sensors.get(seg[1]);1390      if (!s) return notFound(res, 'sensor not found');1391      const c = companies.find((x) => x.id === s.company_id);1392      if (!seg[2]) return json(res, 200, { ...pubSensor(s), company: ref(c), latest_snapshot: s._versions.length ? pubSnap(s._versions[s._versions.length - 1]) : null });1393      if (seg[2] === 'snapshots') return json(res, 200, { items: [...s._versions].reverse().slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)).map(pubSnap) });1394      if (seg[2] === 'changes') return json(res, 200, { items: [...s._changes].reverse().slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)).map(pubChange) });1395    }1396    if (seg[0] === 'snapshots' && seg[1]) {1397      const s = snapshots.get(seg[1]);1398      if (!s) return notFound(res, 'snapshot not found');1399      if (seg[2] === 'diff' && seg[3]) {1400        const o = snapshots.get(seg[3]);1401        if (!o) return notFound(res, 'snapshot not found');1402        const before = s.fetched_at < o.fetched_at ? s : o;1403        const after = before === s ? o : s;1404        const existing = [...changes.values()].find((c) => c.snapshot_before === before.id && c.snapshot_after === after.id);1405        const diff = existing ? existing.diff : { added: after._blocks.slice(0, 2).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: null, after: b.text, weight: 0.5, similarity: null })), removed: before._blocks.slice(-1).map((b) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: null, weight: 0.5, similarity: null })), modified: before._blocks.slice(1, 3).map((b, i) => ({ key: b.key, kind: b.kind, path: b.path, before: b.text, after: after._blocks[i + 1]?.text ?? '', weight: 0.5, similarity: 0.6 })), moved: [], counts: { added: 2, removed: 1, modified: 2 }, text_delta_ratio: 0.18, similarity: 0.81, reasons: ['computed on demand between non-adjacent versions'] };1406        return json(res, 200, { before: pubSnap(before), after: pubSnap(after), diff });1407      }1408      return json(res, 200, { ...pubSnap(s), text: s._text, blocks: s._blocks, extracted: s.extracted_summary });1409    }1410    if (seg[0] === 'changes' && seg[1]) {1411      const ch = changes.get(seg[1]);1412      if (!ch) return notFound(res, 'change not found');1413      const c = companies.find((x) => x.id === ch.company_id);1414      return json(res, 200, { ...pubChange(ch), events: ch._events.map((id) => eventsById.get(id)).filter(Boolean), company: ref(c) });1415    }14161417    // ---- events1418    if (path === '/events/types') {1419      const cutoff = iso(NOW - 30 * DAY);1420      const byType = {};1421      for (const e of events.filter((e) => e.detected_at > cutoff && e.status === 'active')) {1422        byType[e.event_type] ??= { event_type: e.event_type, count_30d: 0, subtypes: {} };1423        byType[e.event_type].count_30d += 1;1424        byType[e.event_type].subtypes[e.event_subtype] = (byType[e.event_type].subtypes[e.event_subtype] ?? 0) + 1;1425      }1426      return json(res, 200, { types: Object.values(byType).map((t) => ({ ...t, subtypes: Object.entries(t.subtypes).map(([event_subtype, count_30d]) => ({ event_subtype, count_30d })) })).sort((a, b) => b.count_30d - a.count_30d) });1427    }1428    if (path === '/events/summary') {1429      const days = clamp(Number(q.get('days') ?? 7), 1, 365);1430      const group = q.get('group') ?? 'type';1431      const cur = events.filter((e) => e.detected_at > iso(NOW - days * DAY) && e.status === 'active');1432      const prev = events.filter((e) => e.detected_at > iso(NOW - 2 * days * DAY) && e.detected_at <= iso(NOW - days * DAY) && e.status === 'active');1433      const keyOf = (e) => (group === 'country' ? e.company.country : group === 'industry' ? companiesBySlug.get(e.company.slug)?.industry_primary : e.event_type);1434      const count = (list) => list.reduce((a, e) => ((a[keyOf(e)] = (a[keyOf(e)] ?? 0) + 1), a), {});1435      const a = count(cur);1436      const b = count(prev);1437      return json(res, 200, { items: Object.entries(a).map(([key, count]) => ({ key, count, delta_pct: b[key] ? r1(((count - b[key]) / b[key]) * 100) : null })).sort((x, y) => y.count - x.count) });1438    }1439    if (path === '/events') return json(res, 200, paginate(filterEvents(events.filter((e) => q.get('status') ? true : e.status !== 'duplicate'), q), q));1440    if (seg[0] === 'events' && seg[1]) {1441      const e = eventsById.get(seg[1]);1442      if (!e) return notFound(res, 'event not found');1443      const ch = e.change_id ? changes.get(e.change_id) : null;1444      return json(res, 200, { ...e, change: ch ? pubChange({ ...ch, diff: undefined, structured_delta: undefined }) : null });1445    }14461447    // ---- rankings, atlases1448    if (path === '/rankings') return json(res, 200, { kind: q.get('kind') ?? 'most_active', window: q.get('window') ?? '30d', items: rankings(q.get('kind') ?? 'most_active', q.get('window') ?? '30d', q.get('country'), q.get('industry'), clamp(Number(q.get('limit') ?? 50), 1, 200)) }, { 'cache-control': 'public, max-age=60' });1449    if (path === '/industries') return json(res, 200, { items: industryRows().map(({ _companies, _events, ...r }) => r) });1450    if (seg[0] === 'industries' && seg[1]) {1451      if (!IND[seg[1]]) return notFound(res, 'industry not found');1452      const r = industryRow(seg[1]);1453      const cs = r._companies;1454      const open = cs.reduce((a, c) => a + c.counts.jobs_open, 0);1455      const { _companies, _events, ...row } = r;1456      const cc = {};1457      for (const c of cs) cc[c.country] = (cc[c.country] ?? 0) + 1;1458      return json(res, 200, { ...row, description: `Monitored companies classified under ${row.name}.`, companies: [...cs].sort((a, b) => b.metrics.activity_score - a.metrics.activity_score).slice(0, 24).map(pub), events: _events.slice(0, 20), hiring: { open, new_30d: ri(20, 400), removed_30d: ri(10, 300), momentum_30d: row.hiring_momentum_30d }, series: series(90, row.activity_score ?? 50, 6), countries: Object.entries(cc).map(([country, companies]) => ({ country, companies })).sort((a, b) => b.companies - a.companies), trending: trends.slice(0, 8) });1459    }1460    if (path === '/countries') return json(res, 200, { items: countryRows().map(({ _companies, _events, ...r }) => r) });1461    if (seg[0] === 'countries' && seg[1]) {1462      const code = seg[1].toUpperCase();1463      if (!COUNTRY_META[code]) return notFound(res, 'country not found');1464      const r = countryRow(code);1465      const { _companies, _events, ...row } = r;1466      const cs = r._companies;1467      return json(res, 200, { ...row, companies: [...cs].sort((a, b) => b.metrics.activity_score - a.metrics.activity_score).slice(0, 24).map(pub), events: _events.slice(0, 20), movers: [...cs].sort((a, b) => b.metrics.corporate_change_index - a.metrics.corporate_change_index).slice(0, 10).map(pub), new_entrants: [...cs].sort((a, b) => b._created - a._created).slice(0, 10).map(pub), series: series(90, row.activity_score ?? 50, 6), industries: industryRows().filter((i) => cs.some((c) => c.industries.includes(i.slug))).map(({ _companies, _events, ...x }) => x) });1468    }1469    if (path === '/signals') {1470      let list = signals;1471      if (q.get('kind')) list = list.filter((s) => s.kind === q.get('kind'));1472      if (q.get('scope')) list = list.filter((s) => s.scope === q.get('scope'));1473      return json(res, 200, { items: list.slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) });1474    }1475    if (path === '/trends') return json(res, 200, { items: trends.slice(0, clamp(Number(q.get('limit') ?? 30), 1, 100)) });1476    if (path === '/map') return json(res, 200, { buckets: mapBuckets(q.get('metric') === 'companies' ? 'companies' : q.get('metric') === 'hiring' ? 'jobs_open' : 'events_30d') });1477    if (path === '/index') {1478      const s = history.map((h) => ({ day: h.day, value: h.activity_index, confidence: 0.9 }));1479      const last = s[s.length - 1].value;1480      const byType = {};1481      for (const e of events.filter((e) => e.detected_at > iso(NOW - 30 * DAY))) byType[e.event_type] = (byType[e.event_type] ?? 0) + 1;1482      return json(res, 200, { value: last, baseline: 100, delta_7d: r1(((last - s[s.length - 8].value) / s[s.length - 8].value) * 100), delta_30d: r1(((last - s[s.length - 31].value) / s[s.length - 31].value) * 100), series: s, by_type: byType, by_country: countryRows().slice(0, 12).map((c) => ({ key: c.code, value: r1(80 + rnd() * 60) })), by_industry: industryRows().slice(0, 12).map((i) => ({ key: i.slug, value: r1(80 + rnd() * 60) })), formula_version: 'gcai-v1.0' });1483    }14841485    // ---- search1486    if (path === '/search/suggest') {1487      const s = (q.get('q') ?? '').toLowerCase().trim();1488      const items = [];1489      if (s) {1490        for (const c of companies) if (c.display_name.toLowerCase().includes(s) || c.canonical_domain.includes(s)) items.push({ kind: 'company', label: c.display_name, sublabel: `${c.canonical_domain} · ${c.country}`, href: `/company/${c.slug}` });1491        for (const i of Object.values(IND)) if (i.name.toLowerCase().includes(s)) items.push({ kind: 'industry', label: i.name, sublabel: 'Industry', href: `/industry/${i.slug}` });1492        for (const [code, [name]] of Object.entries(COUNTRY_META)) if (name.toLowerCase().includes(s) || code.toLowerCase() === s) items.push({ kind: 'country', label: name, sublabel: code, href: `/country/${code.toLowerCase()}` });1493        for (const t of [...new Set(EVENT_TEMPLATES.map((t) => t[0]))]) if (t.toLowerCase().includes(s)) items.push({ kind: 'event_type', label: t, sublabel: 'Event type', href: `/events?event_type=${encodeURIComponent(t)}` });1494      }1495      return json(res, 200, { items: items.slice(0, 10) }, { 'cache-control': 'no-store' });1496    }1497    if (path === '/search') {1498      const s = (q.get('q') ?? '').toLowerCase().trim();1499      const t0 = performance.now();1500      const limit = clamp(Number(q.get('limit') ?? 10), 1, 50);1501      const words = s.split(/\s+/).filter(Boolean);1502      const hit = (txt) => words.some((w) => txt.toLowerCase().includes(w));1503      const cs = companies.filter((c) => hit(c.display_name) || hit(c.canonical_domain) || c.industries.some((i) => hit(IND[i].name)) || hit(COUNTRY_META[c.country][0]));1504      return json(res, 200, { query: q.get('q') ?? '', companies: cs.slice(0, limit).map(pub), events: events.filter((e) => e.status === 'active' && (hit(e.title) || hit(e.summary ?? ''))).slice(0, limit), industries: Object.keys(IND).filter((k) => hit(IND[k].name)).map(industryRow).map(({ _companies, _events, ...r }) => r), countries: Object.keys(COUNTRY_META).filter((k) => hit(COUNTRY_META[k][0])).map(countryRow).map(({ _companies, _events, ...r }) => r), people: companies.flatMap((c) => c._people.filter((p) => hit(p.name) || hit(p.title ?? '')).map((p) => ({ ...p, company: ref(c) }))).slice(0, limit), products: companies.flatMap((c) => c._products.filter((p) => hit(p.name)).map((p) => ({ ...p, company: ref(c) }))).slice(0, limit), took_ms: Math.round(performance.now() - t0) }, { 'cache-control': 'no-store' });1505    }1506    if (path === '/ask') {1507      const s = (q.get('q') ?? '').toLowerCase();1508      const filters = {};1509      for (const [code, [name]] of Object.entries(COUNTRY_META)) if (s.includes(name.toLowerCase())) filters.country = code;1510      for (const i of Object.values(IND)) if (s.includes(i.name.toLowerCase())) filters.industry = i.slug;1511      if (/\bai\b|machine learning|llm/.test(s)) filters.ai = true;1512      if (/hiring|jobs|engineer/.test(s)) filters.event_type = 'HIRING';1513      if (/pric/.test(s)) filters.event_type = 'PRICING';1514      if (/office|expan|countr/.test(s)) filters.event_type = 'LOCATION';1515      if (/launch|product/.test(s)) filters.event_type = 'PRODUCT';1516      if (/leader|exec|ceo|cto/.test(s)) filters.event_type = 'LEADERSHIP';1517      let cs = companies.filter((c) => (!filters.country || c.country === filters.country) && (!filters.industry || c.industries.includes(filters.industry)));1518      if (filters.ai) cs = cs.filter((c) => (c.metrics.ai_adoption ?? 0) > 40 || c._jobs.some((j) => j.is_ai && j.status === 'open'));1519      cs.sort((a, b) => b.metrics.activity_score - a.metrics.activity_score);1520      let evs = events.filter((e) => e.status === 'active' && (!filters.event_type || e.event_type === filters.event_type) && (!filters.country || e.company.country === filters.country) && (!filters.industry || companiesBySlug.get(e.company.slug)?.industries.includes(filters.industry)));1521      const answer = cs.length ? `${cs.length} monitored ${cs.length === 1 ? 'company matches' : 'companies match'} this question${filters.country ? ` in ${COUNTRY_META[filters.country][0]}` : ''}${filters.industry ? ` (${IND[filters.industry].name})` : ''}. ${evs.length ? `${Math.min(evs.length, 200)} related structured events were detected in the monitored record; the most recent are listed below with their sources.` : 'No related structured events were detected yet.'} This is a routed structured query over observed public pages, not an opinion.` : 'No monitored company matches this question yet. Try a broader industry or country, or search company names directly.';1522      return json(res, 200, { interpretation: filters, answer, companies: cs.slice(0, 10).map(pub), events: evs.slice(0, 10), sources: evs.slice(0, 10).map((e) => e.source_url).filter(Boolean) }, { 'cache-control': 'no-store' });1523    }15241525    // ---- watchlist & alerts1526    if (path.startsWith('/watchlist') || path.startsWith('/alerts')) {1527      if (!owner || String(owner).length < 24) return json(res, 401, { detail: 'X-CA-Owner-Token required (≥ 24 chars)' });1528      const key = String(owner);1529      if (!watchlists.has(key)) watchlists.set(key, new Set());1530      if (!alerts.has(key)) alerts.set(key, []);1531      const wl = watchlists.get(key);1532      if (path === '/watchlist' && req.method === 'GET') {1533        const cs = [...wl].map((s) => companiesBySlug.get(s)).filter(Boolean);1534        const evs = events.filter((e) => e.status === 'active' && wl.has(e.company.slug)).slice(0, 30);1535        return json(res, 200, { items: cs.map(pub), events: evs }, { 'cache-control': 'no-store' });1536      }1537      if (path === '/watchlist' && req.method === 'POST') {1538        const body = await readBody(req);1539        const c = companiesBySlug.get(body.company);1540        if (!c) return notFound(res, 'company not found');1541        wl.add(c.slug);1542        return json(res, 201, { ok: true, company: c.slug, items: wl.size });1543      }1544      if (seg[0] === 'watchlist' && seg[1] && req.method === 'DELETE') {1545        wl.delete(decodeURIComponent(seg[1]));1546        return json(res, 200, { ok: true, items: wl.size });1547      }1548      const al = alerts.get(key);1549      if (path === '/alerts' && req.method === 'GET') return json(res, 200, { items: al }, { 'cache-control': 'no-store' });1550      if (path === '/alerts' && req.method === 'POST') {1551        const body = await readBody(req);1552        if (!body.name) return json(res, 422, { detail: 'name required' });1553        const a = { id: id('alr'), name: body.name, company: body.company ?? null, condition: body.condition ?? {}, channel: body.channel === 'webhook' ? 'webhook' : 'web', target: body.target ?? null, created_at: new Date().toISOString(), status: 'active' };1554        al.push(a);1555        return json(res, 201, a);1556      }1557      if (path === '/alerts/deliveries') {1558        const items = al.slice(0, 5).flatMap((a) => events.filter((e) => e.status === 'active' && (!a.company || e.company.slug === a.company) && (!a.condition.event_types?.length || a.condition.event_types.includes(e.event_type))).slice(0, 4).map((e) => ({ id: id('dlv'), alert_id: a.id, alert_name: a.name, event_id: e.id, event: e, channel: a.channel, status: 'delivered', delivered_at: e.detected_at })));1559        return json(res, 200, { items: items.slice(0, clamp(Number(q.get('limit') ?? 50), 1, 200)) }, { 'cache-control': 'no-store' });1560      }1561      if (seg[0] === 'alerts' && seg[1] && req.method === 'DELETE') {1562        alerts.set(key, al.filter((a) => a.id !== seg[1]));1563        return json(res, 200, { ok: true });1564      }1565    }15661567    // ---- exports & docs1568    if (seg[0] === 'export') {1569      const [name, fmt] = (seg[1] ?? '').split('.');1570      let rows = [];1571      if (name === 'events') rows = filterEvents(events.filter((e) => e.status === 'active'), q).slice(0, clamp(Number(q.get('limit') ?? 10000), 1, 10000)).map(({ sources, payload, entities, company, ...e }) => ({ ...e, company_slug: company.slug, company: company.display_name, tags: e.tags.join('|') }));1572      else if (name === 'companies') rows = companies.filter((c) => (!q.get('country') || c.country === q.get('country').toUpperCase()) && (!q.get('industry') || c.industries.includes(q.get('industry')))).map((c) => ({ ...pub(c), industries: c.industries.join('|'), metrics: undefined, counts: undefined, sparkline: undefined, activity_score: c.metrics.activity_score, sensors: c.counts.sensors, events: c.counts.events }));1573      else if (name === 'jobs') rows = companies.filter((c) => !q.get('company') || c.slug === q.get('company')).flatMap((c) => c._jobs.map((j) => ({ ...j, company: c.slug })));1574      else return notFound(res);1575      if (fmt === 'csv') {1576        res.writeHead(200, { 'content-type': 'text/csv; charset=utf-8', 'content-disposition': `attachment; filename="${name}.csv"` });1577        return res.end(csv(rows));1578      }1579      if (fmt === 'ndjson') {1580        res.writeHead(200, { 'content-type': 'application/x-ndjson' });1581        return res.end(rows.map((r) => JSON.stringify(r)).join('\n'));1582      }1583      return json(res, 200, { items: rows });1584    }1585    if (path === '/sitemap') {1586      const kind = q.get('kind') ?? 'companies';1587      const page = Number(q.get('page') ?? 0);1588      const items = kind === 'companies' ? companies.filter((c) => c.counts.events > 2).map((c) => ({ slug: c.slug, updated_at: c.last_event_at })) : kind === 'industries' ? industryRows().map((i) => ({ slug: i.slug, updated_at: null })) : countryRows().map((c) => ({ slug: c.code.toLowerCase(), updated_at: null }));1589      const per = 5000;1590      return json(res, 200, { items: items.slice(page * per, (page + 1) * per), pages: Math.max(1, Math.ceil(items.length / per)) });1591    }1592    if (path === '/methodology') {1593      return json(res, 200, {1594        metrics: [1595          { metric: 'activity_score', formula_version: 'v1.0', description: 'Coverage-normalised rate of meaningful changes and structured events across a company’s monitored surfaces over 30 days, weighted by surface importance, scaled 0–100 against the population baseline.', inputs: ['meaningful_changes_30d', 'events_30d', 'surface_weights', 'sensors_active', 'population_baseline'] },1596          { metric: 'hiring_momentum_30d', formula_version: 'v1.0', description: 'Percentage change in publicly listed open roles over 30 days, adjusted for listings that disappeared and re-appeared within 48 h.', inputs: ['jobs_open_t0', 'jobs_open_t1', 'jobs_new', 'jobs_removed'] },1597          { metric: 'product_velocity', formula_version: 'v1.0', description: 'Rate of product, documentation, changelog and API events per 90 days, normalised by the number of product-related sensors.', inputs: ['product_events_90d', 'developer_events_90d', 'sensors_product'] },1598          { metric: 'ai_adoption', formula_version: 'v1.1', description: 'Observable public AI signals only: AI-tagged listings share, AI product pages, AI mentions in documentation and first-party communications. Never claims internal usage.', inputs: ['ai_jobs_share', 'ai_products', 'ai_docs_mentions', 'ai_news_mentions'] },1599          { metric: 'geo_expansion', formula_version: 'v1.0', description: 'New countries and cities appearing on locations and careers surfaces over 90 days.', inputs: ['new_countries_90d', 'new_cities_90d', 'jobs_new_countries'] },1600          { metric: 'developer_momentum', formula_version: 'v1.0', description: 'Documentation, changelog and API surface change rate; public repository activity where available.', inputs: ['docs_changes_90d', 'changelog_entries_90d', 'api_events_90d'] },1601          { metric: 'corporate_change_index', formula_version: 'v1.0', description: '0.25 hiring + 0.20 product + 0.15 geographic + 0.15 leadership + 0.10 developer + 0.10 communication + 0.05 pricing; weights are provisional.', inputs: ['hiring_momentum_30d', 'product_velocity', 'geo_expansion', 'leadership_activity', 'developer_momentum', 'communication_activity', 'pricing_activity'] },1602          { metric: 'anomaly_score', formula_version: 'v1.0', description: 'Deviation of the last 7 days from the company’s own 90-day baseline of changes and events (z-score mapped to 0–100).', inputs: ['baseline_changes_per_week', 'changes_7d', 'events_7d'] },1603          { metric: 'historical_coverage', formula_version: 'v1.0', description: 'Historical Completeness Score: share of days since onboarding with at least one successful observation per active surface, penalised for failed periods.', inputs: ['days_observed', 'days_since_onboarding', 'sensor_uptime', 'failed_periods'] },1604        ],1605        significance_bands: [1606          { label: 'noise', min: 0, max: 0.2 },1607          { label: 'minor', min: 0.2, max: 0.4 },1608          { label: 'meaningful', min: 0.4, max: 0.65 },1609          { label: 'major', min: 0.65, max: 0.85 },1610          { label: 'critical', min: 0.85, max: 1 },1611        ],1612        event_types: [...new Set(EVENT_TEMPLATES.map((t) => t[0]))],1613        confidence_labels: { VERIFIED: 'Confirmed by two independent first-party surfaces or a structured feed.', HIGH_CONFIDENCE: 'Deterministic extraction from a structured surface, single source.', LIKELY: 'Deterministic extraction from unstructured HTML with a stable block identity.', INFERRED: 'Interpretation produced by an enrichment model from a deterministic change.', LOW_CONFIDENCE: 'Weak or partially corroborated signal; kept for transparency.' },1614      });1615    }16161617    // ---- admin1618    if (seg[0] === 'admin') {1619      if (!admin || String(admin) !== adminToken) return json(res, 401, { detail: 'invalid admin token' });1620      const sub = seg[1];1621      if (sub === 'overview') {1622        const byS = {};1623        const byT = {};1624        for (const s of sensors.values()) {1625          byS[s.status] = (byS[s.status] ?? 0) + 1;1626          byT[s.tier] = (byT[s.tier] ?? 0) + 1;1627        }1628        const failures = {};1629        for (const f of FAILURE_CLASSES) failures[f] = ri(0, 60);1630        return json(res, 200, { companies_by_status: { ACTIVE: companies.filter((c) => c.status === 'ACTIVE').length, POSSIBLY_INACTIVE: companies.filter((c) => c.status !== 'ACTIVE').length }, sensors_by_status: byS, sensors_by_tier: byT, queue: { pending: ri(120, 900), running: ri(8, 32), dead: ri(0, 14), oldest_pending_s: ri(5, 600) }, llm: { pending: ri(0, 40), done_today: ri(80, 400), failed_today: ri(0, 6), budget_left: r1(rnd() * 100) }, failures_24h_by_class: failures, fetch_rate_1h: ri(15000, 26000), change_rate_1h: ri(600, 1400), meaningful_rate_1h: ri(120, 400), storage: stats().archive, workers: ['M2U64-w1', 'M2U64-w2', 'M2U64-w3', 'M2U64-browser'].map((name) => ({ name, last_seen_at: iso(Date.now() - ri(1, 60) * 1000), inflight: ri(0, 8) })), cost_today: { fetch: r1(rnd() * 3), browser: r1(rnd() * 2), llm: r1(rnd() * 6) } });1631      }1632      if (sub === 'connectors') return json(res, 200, { items: CONNECTORS.map(([cid, name, version, category]) => ({ id: cid, name, version, category, enabled: cid !== 'lever_connector', sensors_active: [...sensors.values()].filter((s) => s.connector_id === cid && s.status === 'active').length, sensors_failing: [...sensors.values()].filter((s) => s.connector_id === cid && s.status === 'failing').length, success_rate_24h: r1(90 + rnd() * 10), avg_latency_ms: ri(200, 2400), change_rate_24h: r1(rnd() * 12), errors_24h: ri(0, 40), last_run_at: iso(Date.now() - ri(1, 400) * 1000) })) });1633      if (sub === 'sensors' && !seg[2]) {1634        let list = [...sensors.values()];1635        const g = (k) => q.get(k);1636        if (g('status')) list = list.filter((s) => s.status === g('status'));1637        if (g('domain')) list = list.filter((s) => s.domain.includes(g('domain')));1638        if (g('connector')) list = list.filter((s) => s.connector_id === g('connector'));1639        if (g('company')) list = list.filter((s) => companies.find((c) => c.id === s.company_id)?.slug === g('company'));1640        const f = g('filter');1641        if (f === 'healthy') list = list.filter((s) => s.status === 'active' && s.consecutive_failures === 0);1642        if (f === 'failing') list = list.filter((s) => s.status === 'failing');1643        if (f === 'stale') list = list.filter((s) => new Date(s.last_success_at).getTime() < NOW - 2 * DAY);1644        if (f === 'blocked') list = list.filter((s) => s.last_failure_class === 'BOT_CHALLENGE' || s.last_status === 403);1645        if (f === 'redirected') list = list.filter((s) => s.last_failure_class === 'REDIRECT');1646        if (f === 'low_quality') list = list.filter((s) => s.quality_score < 55);1647        if (f === 'high_activity') list = list.filter((s) => s.change_count > 8);1648        const page = paginate(list, q, 50);1649        page.items = page.items.map((s) => ({ ...pubSensor(s), company: ref(companies.find((c) => c.id === s.company_id)) }));1650        return json(res, 200, page);1651      }1652      if (sub === 'sensors' && seg[2] && seg[3] && req.method === 'POST') {1653        const s = sensors.get(seg[2]);1654        if (!s) return notFound(res, 'sensor not found');1655        const body = await readBody(req);1656        const action = seg[3];1657        if (action === 'pause') s.status = 'paused';1658        if (action === 'resume') s.status = 'active';1659        if (action === 'retire') s.status = 'retired';1660        if (action === 'retry' || action === 'run_now') {1661          s.status = 'active';1662          s.consecutive_failures = 0;1663          s.next_run_at = new Date().toISOString();1664        }1665        if (action === 'set_interval' && body.interval_s) s.current_interval_s = Number(body.interval_s);1666        if (action === 'set_connector' && body.connector_id) s.connector_id = body.connector_id;1667        return json(res, 200, { ok: true, sensor: pubSensor(s), action });1668      }1669      if (sub === 'companies' && !seg[2]) {1670        if (req.method === 'POST') {1671          const body = await readBody(req);1672          if (!body.website) return json(res, 422, { detail: 'website required' });1673          const domain = String(body.website).replace(/^https?:\/\//, '').replace(/\/.*$/, '');1674          const c = buildCompany([body.display_name || domain.split('.')[0], domain, (body.country || 'US').toUpperCase(), 'Unknown', 0, 0, body.industries?.length ? body.industries : ['technology'], false, null, null, null, null, 30, null], companies.length);1675          c.onboarding_status = 'pending';1676          return json(res, 201, pub(c));1677        }1678        let list = [...companies];1679        if (q.get('onboarding_status')) list = list.filter((c) => c.onboarding_status === q.get('onboarding_status'));1680        const page = paginate(list, q, 50);1681        page.items = page.items.map(pub);1682        return json(res, 200, page);1683      }1684      if (sub === 'companies' && seg[2] && seg[3] === 'rediscover') return json(res, 200, { ok: true, queued: true });1685      if (sub === 'failures') {1686        const items = Array.from({ length: 120 }, () => {1687          const s = pick([...sensors.values()]);1688          return { id: id('fail'), sensor_id: s.id, company: ref(companies.find((c) => c.id === s.company_id)), domain: s.domain, failure_class: pick(FAILURE_CLASSES), status_code: pick([0, 403, 404, 429, 500, 503]), message: pick(['connect timeout after 20 s', 'challenge page detected (cf-mitigated)', 'schema validation failed: jobs[3].title missing', 'redirected to /careers-new (301)', 'rate limited by domain governor', 'dns: NXDOMAIN']), occurred_at: iso(Date.now() - ri(1, 1440) * 60_000), retry_at: iso(Date.now() + ri(5, 600) * 60_000) };1689        }).sort((a, b) => b.occurred_at.localeCompare(a.occurred_at));1690        return json(res, 200, paginate(q.get('class') ? items.filter((i) => i.failure_class === q.get('class')) : items, q, 50));1691      }1692      if (sub === 'queue' && !seg[2]) {1693        const items = Array.from({ length: 80 }, () => ({ id: id('job'), kind: pick(['fetch', 'fetch', 'fetch', 'discover', 'enrich', 'metrics', 'daily']), status: pick(['pending', 'pending', 'running', 'done', 'dead']), priority: ri(1, 100), attempts: ri(0, 4), scheduled_at: iso(Date.now() - ri(0, 3600) * 1000), started_at: chance(0.5) ? iso(Date.now() - ri(0, 600) * 1000) : null, finished_at: null, worker: chance(0.5) ? pick(['M2U64-w1', 'M2U64-w2', 'M2U64-w3']) : null, ref: pick([...sensors.keys()]), error: chance(0.1) ? 'TIMEOUT' : null }));1694        const filtered = items.filter((i) => (!q.get('kind') || i.kind === q.get('kind')) && (!q.get('status') || i.status === q.get('status')));1695        const counts = {};1696        for (const i of items) counts[i.status] = (counts[i.status] ?? 0) + 1;1697        return json(res, 200, { items: filtered, counts });1698      }1699      if (sub === 'queue' && seg[2] === 'requeue-dead') return json(res, 200, { ok: true, requeued: ri(0, 14) });1700      if (sub === 'llm') {1701        const items = Array.from({ length: 60 }, () => ({ id: id('llm'), kind: pick(['classify', 'summarize', 'extract_event', 'industry_tag']), status: pick(['done', 'done', 'done', 'pending', 'failed']), model: pick(['qwen3-4b-instruct-2507-4bit', 'qwen3.6-35b-a3b-4bit']), prompt_version: pick(['event-classifier/v3', 'event-summary/v2']), change_id: pick([...changes.keys()]), event_id: chance(0.7) ? pick(events).id : null, tokens_in: ri(400, 6000), tokens_out: ri(50, 600), cost_estimate: r1(rnd() * 0.02 * 100) / 100, created_at: iso(Date.now() - ri(0, 1440) * 60_000), finished_at: chance(0.8) ? iso(Date.now() - ri(0, 1000) * 60_000) : null, error: chance(0.1) ? 'schema validation failed' : null }));1702        return json(res, 200, paginate(q.get('status') ? items.filter((i) => i.status === q.get('status')) : items, q, 50));1703      }1704      if (sub === 'reviews' && !seg[2]) {1705        const items = Array.from({ length: 24 }, () => {1706          const e = pick(events);1707          return { id: id('rev'), kind: pick(['major_event', 'low_confidence_extraction', 'company_merge', 'sensor_migration', 'legal_sensitive']), status: chance(0.8) ? 'open' : 'resolved', subject: e.title, ref_id: e.id, company: e.company, reason: pick(['importance ≥ 0.9', 'confidence < 0.55', 'two companies share a domain', 'URL moved; content identity uncertain', 'mentions litigation']), created_at: iso(Date.now() - ri(1, 5000) * 60_000), resolved_at: null, resolution: null };1708        });1709        return json(res, 200, { items: q.get('status') ? items.filter((i) => i.status === q.get('status')) : items });1710      }1711      if (sub === 'reviews' && seg[2] && req.method === 'POST') return json(res, 200, { ok: true, id: seg[2], ...(await readBody(req)) });1712      if (sub === 'events' && seg[2] && seg[3] && req.method === 'POST') {1713        const e = eventsById.get(seg[2]);1714        if (!e) return notFound(res, 'event not found');1715        e.status = seg[3] === 'retract' ? 'retracted' : 'active';1716        return json(res, 200, { ok: true, status: e.status });1717      }1718      if (sub === 'quality') return json(res, 200, { coverage: { companies_active_pct: r1(92 + rnd() * 6), sensors_active_pct: r1(84 + rnd() * 8) }, freshness: { sensors_checked_24h_pct: r1(88 + rnd() * 10), stale: ri(20, 140) }, duplicate_rate: r1(rnd() * 4), event_confidence_avg: r1(0.7 + rnd() * 0.2), unknown_surfaces: ri(30, 200), failed_sensors: [...sensors.values()].filter((s) => s.status === 'failing').length, calibration: { correct: ri(300, 600), duplicate: ri(5, 40), noise: ri(10, 60), misclassified: ri(3, 30) } });1719      if (sub === 'costs') {1720        const days = clamp(Number(q.get('days') ?? 30), 1, 90);1721        const items = [];1722        for (let i = days - 1; i >= 0; i--) for (const [dimension, key] of [['fetch', 'http'], ['fetch', 'browser'], ['llm', 'qwen3.6-35b'], ['storage', 'objects']]) items.push({ day: day(NOW - i * DAY), dimension, key, units: ri(1000, 30000), cost_estimate: r1(rnd() * 8 * 100) / 100 });1723        return json(res, 200, { items, per_1000_companies: r1(rnd() * 40 + 10), per_million_observations: r1(rnd() * 6 + 1), per_meaningful_event: r1(rnd() * 0.05 * 1000) / 1000 });1724      }1725      if (sub === 'cache' && seg[2] === 'clear') return json(res, 200, { ok: true, cleared: ri(4, 40) });1726    }1727    return notFound(res);1728  } catch (e) {1729    console.error(e);1730    return json(res, 500, { detail: `mock error: ${e.message}` });1731  }1732});17331734server.listen(PORT, '127.0.0.1', () => {1735  console.log(`Company Atlas mock API on http://127.0.0.1:${PORT}/api/v1 — ${companies.length} companies, ${sensors.size} sensors, ${events.length} events, ${changes.size} changes. Admin token: ${adminToken}`);1736});1737