sahin.io

All skills and agents

page-company

The per-company pages on sahin.io/angel-investments — the company profile (/{company}) and its investors page (/{company}/angel-investors). Use when building, rebuilding or changing either page, its React view, its static body, titles, descriptions or schema.

Source file
.claude/skills/page-company/SKILL.md
Routes covered
kind:companykind:companyInvestors

Company pages

Routes

URL Route kind Count today Render path
https://sahin.io/angel-investments/{company} (e.g. https://sahin.io/angel-investments/anthropic) company 234 static body + React companyView on top
https://sahin.io/angel-investments/{company}/angel-investors (e.g. https://sahin.io/angel-investments/anthropic/angel-investors) companyInvestors 215 static body + React companyInvestorsView on top
  • Paths: companyPath(slug) and companyInvestorsPath(slug) (shared/publicUrls.ts; investorRoutePath in server/publicPortfolio.ts).
  • createSeoRouteManifest() loops sortByMarketCap(portfolio.companies): one company route each; a companyInvestors route only when verifiedInvestorRecord(research, slug) exists.
  • Static body: renderRouteDocument() → renderPublicCompanyPage / renderPublicInvestorCompanyPage (server/seoRouteManifest.ts).
  • React view: reactPageForRoute() (server/publicReactPages.ts) maps kind company → companyView, companyInvestors → companyInvestorsView. server/publicReactDocument.ts lists both in EMBED_SERVER_BODY_KINDS, so the static body is kept below the React page (embeddedServerBody strips the company body's duplicate logo/name/one-liner block).

Files

  • Static renderers: renderPublicCompanyPage, renderPublicInvestorCompanyPage, helpers renderCategoryCrossLinks, renderSubcategoryCrossLinks, renderPortfolioRing (ringNeighbours), renderRelatedPublicPages, renderInvestorList, eligibleCollectionsFor, investorSiblings, companyBreadcrumbs — server/seoRouteManifest.ts.
  • Titles/descriptions: companyPageTitle, companyPageHeading, companyInvestorsPageTitle, companyPageDescription, companyInvestorsPageDescription (server/companyPageCopy.ts).
  • Props builders: server/publicPages/companyView.ts (buildCompanyViewProps), server/publicPages/companyInvestorsView.ts (buildCompanyInvestorsViewProps).
  • Components/types: client/src/publicPages/CompanyViewPage.tsx + companyViewTypes.ts, CompanyInvestorsViewPage.tsx + companyInvestorsViewTypes.ts; registered in client/src/publicPages/types.ts, components.client.ts, components.server.ts.
  • CSS: client/src/publicPages/styles/companyView.css, companyInvestorsView.css (imported from client/src/index.css); static body uses .company-page__* and .investor* rules in PUBLIC_PAGE_CSS (server/publicChrome.ts).
  • Copy: content/pages/company-page.yaml (companyCopy()), content/pages/investor-directories.yaml (investorCopy()), content/pages/company-view.yaml, content/pages/company-investors.yaml; short names in data/company-display-names.yaml.
  • Data: client/public/portfolio-data.json, data/public-investor-research.json, data/public-company-valuations.json (sourced company value via server/publicCompanyValue.ts), client/public/enriched-company-data.json (loadEnrichedRecords: public funding history, team, founders), client/public/company-locations.json, client/public/vc-data.json (firm links).

Sections on the page

/{company} — React companyView: header "{Company}: funding and company profile" (or "company profile" without public funding history), market/location/team line, Key Investors chips (verified research only, linking to firm pages), Funding Timeline table (when public history exists), Why I Invested, Other {Market} Investments. Then static body: badges (market, Category, Sub-Category) → facts cards (market, status, sourced company value with citation link) → "record" block with website link and link to /{company}/angel-investors → Category cross-links → Sub-Category cross-links → portfolio ring (neighbour companies) → related public pages → back-to-portfolio footer.

/{company}/angel-investors — React companyInvestorsView: "Who Invested in {Company}?" header, stat cards, angel and institutional investor cards, owner profile card, "Why Angel Investors Matter". Then static body: taxonomy badges + note → the cited investor list (renderInvestorList; sources only as /sources/{id} references) → browse panel (eligible Category/Sub-Category investor directories, /angel-investors, sibling companies in the same Category) → related pages → back link.

SEO

  • Title: companyPageTitle(name, hasFundingRounds) / companyInvestorsPageTitle(name, investorCount), fitted to PAGE_TITLE_MAX (60) by fitPageTitle.
  • Description: companyPageDescription / companyInvestorsPageDescription (owner's name left out of quoted names).
  • Canonical: canonicalFor(path). JSON-LD via buildPageGraph (server/publicSchema.ts): company → ProfilePage with mainEntity Organization (organizationId, website as sameAs, logo); investors page → WebPage + ItemList of investors + Organization with name and logo only (never a website/source URL). Breadcrumbs: hub → Category (→ Sub-Category) → company (→ "Angel investors").
  • All indexable; listed in the sitemap and llms files (server/llmsText.ts). See seo-head-and-schema.

Rules

  • Never show the owner's private deal data on either page — private-data-boundary. companyView.ts keeps a private-wording guard and runs findRestrictedTerms on text.
  • A company value appears only when data/public-company-valuations.json has a sourced figure, always with its citation.
  • Investor names only from verified public research; the "who invested" question belongs to /{company}/angel-investors, never to /{company} (keyword [redacted]).
  • Status words/counts via shared/portfolioStatus.ts — portfolio-counts-single-source. Slugs and new companies — public-url-conventions. Visible words in YAML — page-content-yaml.

Tests

server/seo-route-manifest.test.ts, server/company-page-copy.test.ts, server/public-company-value.test.ts, server/public-investor-research.test.ts, server/public-schema.test.ts, server/public-routing.test.ts, server/public-urls.test.ts, server/keyword-[redacted].test.ts, server/llms-text.test.ts, server/entity-facts.test.ts, server/public-data-endpoints.test.ts, server/content-in-yaml.test.ts, server/legacy-redirects.test.ts, server/seo-watchlist.test.ts.

Rebuild from scratch

  1. Confirm the company exists in client/public/portfolio-data.json with a valid slug (public-url-conventions) and logo in the generated logo map.
  2. Keep the manifest loop in createSeoRouteManifest() (company route always; investors route only with a verified record).
  3. Write company-page.yaml, investor-directories.yaml, company-view.yaml, company-investors.yaml; run node scripts/sync-site-copy-module.mjs.
  4. Implement the title/description helpers in server/companyPageCopy.ts, then the two static renderers and their renderRouteDocument cases.
  5. Implement buildCompanyViewProps / buildCompanyInvestorsViewProps, the two *Page.tsx components + types, register them (types.ts, components.client.ts, components.server.ts), add the CSS imports — follow new-react-page.
  6. Add both kinds to EMBED_SERVER_BODY_KINDS and the reactPageForRoute branches.
  7. Run deploy-gate → ui-before-after (a company with and without investors, desktop + mobile) → ship-to-production.