sahin.io

All skills and agents

page-status-sectors-markets

Spec for the Status guide, the Sectors pages, the Sector Heatmap and the market profiles on sahin.io/angel-investments (/status, /status/{slug}, /sectors, /sectors/{slug}, /sector-heatmap, /market/{slug}). Use when building, rebuilding or changing any of these pages, their slugs, their copy or the data they group by.

Source file
.claude/skills/page-status-sectors-markets/SKILL.md
Routes covered
/status/status/*/sectors/sectors/*/sector-heatmap/market/*

Status, sectors and markets pages

Routes

URL Route kind Count today Render
https://sahin.io/angel-investments/status legacyFeature (menu page) 1 static shell + React status
https://sahin.io/angel-investments/status/{slug} reactPage (family /status) 20 static shell + React statusDetail
https://sahin.io/angel-investments/sectors legacyFeature 1 static shell + React sectors
https://sahin.io/angel-investments/sectors/{slug} reactPage (family /sectors) 37 static shell + React sectorDetail
https://sahin.io/angel-investments/sector-heatmap legacyFeature 1 static shell + React sectorHeatmap
https://sahin.io/angel-investments/market/{slug} reactPage (family /market) 37 static shell + React marketDetail
  • Menu pages come from content/shared/navigation.yaml (/status in section explore, /sectors and /sector-heatmap in insights) → LEGACY_MENU_PAGES in shared/publicNavigation.ts → route(..., "legacyFeature", title) in createSeoRouteManifest() (server/seoRouteManifest.ts). Static body: renderLegacyFeaturePage().
  • Families live in FAMILIES in server/reactPageRoutes.ts; slugs from listStatusDetailSlugs, listSectorDetailSlugs, listMarketDetailSlugs. Static body: renderReactShellPage().
  • scripts/generate-static.ts replaces every shell body with the React page from reactPageForRoute() (server/publicReactPages.ts).
  • Sector and market slugs both use taxonomySlug() over company.market ("AI / ML" → ai-ml), so the two families always have the same number of members (asserted in server/seo-route-manifest.test.ts). Status slugs are the 20 fixed STATUS_DEFINITIONS in server/publicPages/status.ts.
  • URL rules: public-url-conventions.

Files

Page Props builder Component + types CSS Copy
/status buildStatusProps in server/publicPages/status.ts client/src/publicPages/StatusPage.tsx, statusTypes.ts styles/status.css content/pages/status.yaml (page words), content/pages/statuses.yaml (every status's title, descriptions, criteria, implications, FAQ, category labels)
/status/{slug} buildStatusDetailProps, statusDetailPageMeta in server/publicPages/statusDetail.ts StatusDetailPage.tsx, statusDetailTypes.ts styles/statusDetail.css content/pages/status-detail.yaml (meta = title/description templates)
/sectors buildSectors in server/publicReactPages.ts SectorsPage.tsx, types in types.ts (SectorsPageProps, SectorSummary) shared rules in client/src/index.css (.ci-head, .sx-grid, .sx-bar, .sx-other) content/pages/sectors.yaml
/sectors/{slug} buildSectorDetailProps, sectorDetailPageMeta in server/publicPages/sectorDetail.ts (reuses buildSectors) SectorDetailPage.tsx, sectorDetailTypes.ts styles/sectorDetail.css content/pages/sector-detail.yaml (pageTitle, description)
/sector-heatmap buildHeatmapSectors in server/publicReactPages.ts SectorHeatmapPage.tsx, HeatmapSector in types.ts client/src/index.css (.hm-*, .tl-filters) content/pages/sector-heatmap.yaml (incl. trends, macro signals)
/market/{slug} buildMarketDetailProps, marketDetailPageMeta in server/publicPages/marketDetail.ts MarketDetailPage.tsx, marketDetailTypes.ts styles/marketDetail.css content/pages/market-detail.yaml (pageTitle, description)

Registration: each kind is in the PublicReactPage union in client/src/publicPages/types.ts, components.client.ts (lazy) and components.server.ts (serverPageComponents). CSS files are @imported from client/src/index.css. Components read copy through @/generated/copy/pages/<file> (regenerate with node scripts/sync-site-copy-module.mjs); builders read it with loadPageContent() (server/pageContent.ts).

Data inputs: the public portfolio (ctx.companies), research records via loadEnrichedRecords() (client/public/enriched-company-data.json, slug aliases in ENRICHED_SLUG_ALIASES), the sourced company value via sourcedValueUsd() / sourced() (server/publicCompanyValue.ts), logos via publicCompanyLogo().

Sections on the page

  • /status: header (eyebrow, title, subtitle) → status cards grouped by the statuses.yaml categories, each with icon, color, short description and member count → per-status detail (criteria, implications, matching companies, related statuses).
  • /status/{slug}: back link → header → stats → criteria → implications → market distribution and company-value distribution (donut) → FAQ → companies carrying the status (or empty) → related statuses → browse all.
  • /sectors: header → sector cards (count, share bar) sorted by count → "other" small sectors → expandable sector detail and table (companies sorted by sourced value, stage mix, funding raised).
  • /sectors/{slug}: the /sectors view opened on one sector (expanded and scrolled into view), with its own title/description and links to other sectors.
  • /sector-heatmap: header → summary tiles → view toggle and sort (.tl-filters) → heat tiles per sector (trend: hot / warm / neutral / cooling from macro) → table → detail panel.
  • /market/{slug}: header + link to the matching sector → headline stats → most funded companies → stage mix → funding vs company value bubbles → investors → every company in the market → compare with other markets.

SEO

  • Menu pages: <title> and description from content/shared/navigation.yaml; /status, /sectors, /sector-heatmap may instead take the hero intro from content/pages/recovery-surfaces.yaml (RECOVERY_SURFACE_BY_PATH, unless listed in MENU_DESCRIPTION_FEATURES). JSON-LD: buildPageGraph({ kind: "legacyFeature", itemList }) with the related links from legacyFeatureContextLinks() (category links for /sectors and /sector-heatmap).
  • Families: title/description from each builder's *PageMeta (YAML templates); breadcrumb from the family crumb; JSON-LD buildPageGraph({ kind: "reactPage" }). All members are index (no indexing function), so all are in the sitemap and llms files.
  • Canonical: https://sahin.io/angel-investments<path> via headSpec(). Head and schema details: seo-head-and-schema.
  • /sectors/, /market/, /status/ are in KNOWN_UNLINKED_FAMILIES in server/public-link-graph.test.ts: they need not be linked from the hub.

Rules

  • Status member counts must agree with shared/portfolioStatus.ts (hasExitStatus, isActiveCompany); see portfolio-counts-single-source. The public data records only Live / Acquired / Public / Failed; no company may be shown as "Realized".
  • statuses.yaml and STATUS_DEFINITIONS must match one to one; buildStatusProps throws on a missing or extra status or category.
  • Company values: only the sourced value with its citation (sourcedValueUsd, SourcedValueText); never research figures or sums of them. No present-tense "today" claims on value tiers (server/public-company-value.test.ts).
  • No private deal data on any of these pages: private-data-boundary.
  • All words in YAML: page-content-yaml. The heatmap's macro signals are curated copy in sector-heatmap.yaml, not data.

Tests

  • server/seo-route-manifest.test.ts: 20 /status/ routes; /sectors/ count equals /market/ count; restricted-term checks on /status.
  • server/public-react-document.test.ts: /status keeps its crawlable head and body inside the React root; buildSectors puts every company in exactly one sector.
  • server/portfolio-status-single-source.test.ts: /status card counts equal the shared status summary.
  • server/public-company-value.test.ts: sourced-only values on /status, /status/1b-plus, /sectors, /sector-heatmap, sample /market/* and /sectors/*.
  • server/public-link-graph.test.ts, server/legacy-redirects.test.ts.

Rebuild from scratch

  1. Confirm the three menu entries (path, label, title, description, section) in content/shared/navigation.yaml; run node scripts/sync-site-copy-module.mjs.
  2. Write the copy files listed above under content/pages/.
  3. Status: define STATUS_DEFINITIONS (slug, category, icon, color, matcher, related) in server/publicPages/status.ts, matching via shared/portfolioStatus.ts; build buildStatusProps; then statusDetail.ts with listStatusDetailSlugs (from the same props), buildStatusDetailProps, statusDetailPageMeta.
  4. Sectors: buildSectors and buildHeatmapSectors in server/publicReactPages.ts (group by company.market || 'Other'); sectorDetail.ts with sectorDetailSlug = taxonomySlug.
  5. Markets: marketDetail.ts with listMarketDetailSlugs, buildMarketDetailProps, marketDetailPageMeta.
  6. Add the /status, /sectors, /market entries to FAMILIES in server/reactPageRoutes.ts, and the three menu paths to reactPageForRoute().
  7. Write the components and *Types.ts, register the kinds in types.ts, components.client.ts, components.server.ts, add CSS and its @import in client/src/index.css (pattern: new-react-page; themes: theme-system).
  8. Run deploy-gate, then ui-before-after (desktop and mobile), then ship-to-production.