@flamingo-stack/openframe-frontend-core 0.0.685 → 0.0.686-2463.7461.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/{chunk-T6KWNJDW.js → chunk-22RSJ57Z.js} +470 -465
  2. package/dist/chunk-22RSJ57Z.js.map +1 -0
  3. package/dist/{chunk-65KTXRBY.js → chunk-2G2CB7ZZ.js} +6 -6
  4. package/dist/{chunk-K2HZG4RM.cjs → chunk-2OD2PPNO.cjs} +32 -32
  5. package/dist/{chunk-K2HZG4RM.cjs.map → chunk-2OD2PPNO.cjs.map} +1 -1
  6. package/dist/{chunk-HMXUOLZR.cjs → chunk-3DO76UAB.cjs} +20 -20
  7. package/dist/{chunk-HMXUOLZR.cjs.map → chunk-3DO76UAB.cjs.map} +1 -1
  8. package/dist/{chunk-XW7MUIW4.js → chunk-3NIZ2VLQ.js} +3 -3
  9. package/dist/{chunk-5LJZPJHX.cjs → chunk-4LBCUBVL.cjs} +59 -59
  10. package/dist/{chunk-5LJZPJHX.cjs.map → chunk-4LBCUBVL.cjs.map} +1 -1
  11. package/dist/{chunk-ZF4Z36G4.js → chunk-4RJQMOQW.js} +7 -7
  12. package/dist/{chunk-TJI5V5BR.cjs → chunk-AF4XOURN.cjs} +7 -7
  13. package/dist/{chunk-TJI5V5BR.cjs.map → chunk-AF4XOURN.cjs.map} +1 -1
  14. package/dist/{chunk-7XF63THA.js → chunk-ASBBGP36.js} +6 -6
  15. package/dist/{chunk-O7LOZW4R.js → chunk-C5TFPMOM.js} +5 -5
  16. package/dist/{chunk-CMQXPD5C.cjs → chunk-GETQTNRV.cjs} +28 -28
  17. package/dist/{chunk-CMQXPD5C.cjs.map → chunk-GETQTNRV.cjs.map} +1 -1
  18. package/dist/{chunk-TUG7IT6H.js → chunk-HLEXYTOE.js} +2 -2
  19. package/dist/{chunk-M4HYIXP6.cjs → chunk-IBUFFKAK.cjs} +399 -383
  20. package/dist/chunk-IBUFFKAK.cjs.map +1 -0
  21. package/dist/{chunk-GXV6UEVY.js → chunk-IELOPDTA.js} +3 -3
  22. package/dist/{chunk-T4DUTP5C.cjs → chunk-II5GVLTN.cjs} +5 -5
  23. package/dist/{chunk-T4DUTP5C.cjs.map → chunk-II5GVLTN.cjs.map} +1 -1
  24. package/dist/{chunk-B32YLOYQ.js → chunk-JBJZL5PI.js} +4 -4
  25. package/dist/{chunk-UT333EOT.js → chunk-LCOMNQLX.js} +4 -4
  26. package/dist/{chunk-VDPCK437.js → chunk-MHTB67ZH.js} +21 -5
  27. package/dist/{chunk-VDPCK437.js.map → chunk-MHTB67ZH.js.map} +1 -1
  28. package/dist/{chunk-EFBMNVSH.cjs → chunk-MZAZUSPQ.cjs} +15 -15
  29. package/dist/{chunk-EFBMNVSH.cjs.map → chunk-MZAZUSPQ.cjs.map} +1 -1
  30. package/dist/{chunk-C3B36OYZ.cjs → chunk-N3I53B5W.cjs} +3 -3
  31. package/dist/{chunk-C3B36OYZ.cjs.map → chunk-N3I53B5W.cjs.map} +1 -1
  32. package/dist/{chunk-WJK2EETW.js → chunk-NINO3PI7.js} +2 -2
  33. package/dist/{chunk-YCTVLN47.cjs → chunk-P4SBHO7U.cjs} +10 -10
  34. package/dist/{chunk-YCTVLN47.cjs.map → chunk-P4SBHO7U.cjs.map} +1 -1
  35. package/dist/{chunk-PSQKD6ZN.js → chunk-PG65AE3I.js} +3 -3
  36. package/dist/{chunk-DQOQ3KTP.cjs → chunk-QNWQQ4RT.cjs} +78 -73
  37. package/dist/chunk-QNWQQ4RT.cjs.map +1 -0
  38. package/dist/{chunk-BZ2XOEHW.js → chunk-QOH45QSE.js} +3 -3
  39. package/dist/{chunk-DWXWEGGO.cjs → chunk-QTXDXHMD.cjs} +22 -22
  40. package/dist/{chunk-DWXWEGGO.cjs.map → chunk-QTXDXHMD.cjs.map} +1 -1
  41. package/dist/{chunk-B25CF3PO.cjs → chunk-RBCMBRI7.cjs} +30 -30
  42. package/dist/{chunk-B25CF3PO.cjs.map → chunk-RBCMBRI7.cjs.map} +1 -1
  43. package/dist/{chunk-QHLJSB2J.cjs → chunk-RKH7DEDF.cjs} +12 -12
  44. package/dist/{chunk-QHLJSB2J.cjs.map → chunk-RKH7DEDF.cjs.map} +1 -1
  45. package/dist/{chunk-Y7ZYOQNB.js → chunk-RSMAVO2Z.js} +3 -3
  46. package/dist/{chunk-A3CSQB4A.cjs → chunk-SI77HF4U.cjs} +1247 -805
  47. package/dist/chunk-SI77HF4U.cjs.map +1 -0
  48. package/dist/{chunk-GHLGXFNU.js → chunk-U6JC3RS2.js} +3 -3
  49. package/dist/{chunk-B5N3AHPG.cjs → chunk-UBXEETWQ.cjs} +81 -81
  50. package/dist/{chunk-B5N3AHPG.cjs.map → chunk-UBXEETWQ.cjs.map} +1 -1
  51. package/dist/{chunk-N4Z6X5BV.js → chunk-WFH52OVQ.js} +5 -5
  52. package/dist/{chunk-U3IXG7TR.cjs → chunk-WJX3ZSOE.cjs} +33 -33
  53. package/dist/{chunk-U3IXG7TR.cjs.map → chunk-WJX3ZSOE.cjs.map} +1 -1
  54. package/dist/{chunk-XGX6JSEU.js → chunk-YPXXXX6R.js} +2086 -1644
  55. package/dist/chunk-YPXXXX6R.js.map +1 -0
  56. package/dist/{chunk-P2ASEPA2.cjs → chunk-ZS3YCJYY.cjs} +6 -6
  57. package/dist/{chunk-P2ASEPA2.cjs.map → chunk-ZS3YCJYY.cjs.map} +1 -1
  58. package/dist/components/case-studies/index.cjs +16 -16
  59. package/dist/components/case-studies/index.js +4 -4
  60. package/dist/components/chat/ai-assistant-info.d.ts.map +1 -1
  61. package/dist/components/chat/approval-request-message.d.ts.map +1 -1
  62. package/dist/components/chat/chat-appearance-context.d.ts +9 -0
  63. package/dist/components/chat/chat-appearance-context.d.ts.map +1 -0
  64. package/dist/components/chat/chat-input.d.ts.map +1 -1
  65. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  66. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  67. package/dist/components/chat/chat-nav-sidebar.d.ts +50 -0
  68. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -0
  69. package/dist/components/chat/chat-top-navigation.d.ts +29 -0
  70. package/dist/components/chat/chat-top-navigation.d.ts.map +1 -0
  71. package/dist/components/chat/error-message-display.d.ts.map +1 -1
  72. package/dist/components/chat/index.cjs +21 -3
  73. package/dist/components/chat/index.cjs.map +1 -1
  74. package/dist/components/chat/index.d.ts +6 -0
  75. package/dist/components/chat/index.d.ts.map +1 -1
  76. package/dist/components/chat/index.js +20 -2
  77. package/dist/components/chat/msp-organization-card.d.ts +7 -1
  78. package/dist/components/chat/msp-organization-card.d.ts.map +1 -1
  79. package/dist/components/chat/onboarding-carousel.d.ts +29 -0
  80. package/dist/components/chat/onboarding-carousel.d.ts.map +1 -0
  81. package/dist/components/chat/system-event-message.d.ts +14 -0
  82. package/dist/components/chat/system-event-message.d.ts.map +1 -0
  83. package/dist/components/chat/types/chat.types.d.ts +13 -0
  84. package/dist/components/chat/types/chat.types.d.ts.map +1 -1
  85. package/dist/components/chat/types/component.types.d.ts +20 -1
  86. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  87. package/dist/components/contact/index.cjs +5 -5
  88. package/dist/components/contact/index.js +4 -4
  89. package/dist/components/docs/index.cjs +8 -8
  90. package/dist/components/docs/index.js +7 -7
  91. package/dist/components/embeds/index.cjs +5 -5
  92. package/dist/components/embeds/index.js +4 -4
  93. package/dist/components/faq/index.cjs +5 -5
  94. package/dist/components/faq/index.js +4 -4
  95. package/dist/components/features/index.cjs +4 -4
  96. package/dist/components/features/index.js +3 -3
  97. package/dist/components/help-center-pages/index.cjs +91 -91
  98. package/dist/components/help-center-pages/index.js +15 -15
  99. package/dist/components/index.cjs +73 -55
  100. package/dist/components/index.cjs.map +1 -1
  101. package/dist/components/index.js +33 -15
  102. package/dist/components/index.js.map +1 -1
  103. package/dist/components/meeting-scheduler/index.cjs +41 -41
  104. package/dist/components/meeting-scheduler/index.js +3 -3
  105. package/dist/components/navigation/index.cjs +7 -7
  106. package/dist/components/navigation/index.js +6 -6
  107. package/dist/components/onboarding-guides/index.cjs +7 -7
  108. package/dist/components/onboarding-guides/index.js +6 -6
  109. package/dist/components/related-content/index.cjs +3 -3
  110. package/dist/components/related-content/index.js +2 -2
  111. package/dist/components/tickets/index.cjs +8 -8
  112. package/dist/components/tickets/index.js +7 -7
  113. package/dist/components/ui/avatar-stack.d.ts +5 -1
  114. package/dist/components/ui/avatar-stack.d.ts.map +1 -1
  115. package/dist/components/ui/index.cjs +22 -4
  116. package/dist/components/ui/index.cjs.map +1 -1
  117. package/dist/components/ui/index.js +21 -3
  118. package/dist/components/ui/reopen-ticket-request-modal.d.ts +4 -1
  119. package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -1
  120. package/dist/components/ui/tab-navigation.d.ts +7 -0
  121. package/dist/components/ui/tab-navigation.d.ts.map +1 -1
  122. package/dist/index.cjs +27 -9
  123. package/dist/index.cjs.map +1 -1
  124. package/dist/index.js +25 -7
  125. package/dist/index.js.map +1 -1
  126. package/package.json +1 -1
  127. package/src/components/chat/__tests__/chat-appearance.test.tsx +79 -0
  128. package/src/components/chat/__tests__/onboarding-carousel.test.tsx +56 -0
  129. package/src/components/chat/ai-assistant-info.tsx +7 -1
  130. package/src/components/chat/approval-request-message.tsx +6 -1
  131. package/src/components/chat/chat-appearance-context.tsx +15 -0
  132. package/src/components/chat/chat-input.tsx +26 -1
  133. package/src/components/chat/chat-message-enhanced.tsx +180 -142
  134. package/src/components/chat/chat-message-list.tsx +169 -164
  135. package/src/components/chat/chat-nav-sidebar.tsx +163 -0
  136. package/src/components/chat/chat-top-navigation.tsx +109 -0
  137. package/src/components/chat/error-message-display.tsx +21 -3
  138. package/src/components/chat/index.ts +6 -0
  139. package/src/components/chat/msp-organization-card.tsx +35 -0
  140. package/src/components/chat/onboarding-carousel.tsx +162 -0
  141. package/src/components/chat/system-event-message.tsx +54 -0
  142. package/src/components/chat/types/chat.types.ts +15 -0
  143. package/src/components/chat/types/component.types.ts +20 -0
  144. package/src/components/ui/__tests__/avatar-stack.test.tsx +25 -0
  145. package/src/components/ui/__tests__/tab-navigation.test.tsx +56 -2
  146. package/src/components/ui/avatar-stack.tsx +15 -5
  147. package/src/components/ui/reopen-ticket-request-modal.tsx +6 -2
  148. package/src/components/ui/tab-navigation.tsx +23 -0
  149. package/src/stories/FaeLayoutV2.stories.tsx +150 -0
  150. package/src/stories/FaeOnboardingV2.stories.tsx +68 -0
  151. package/src/stories/FaeThreadV2.stories.tsx +192 -0
  152. package/src/stories/fae-brand-decorator.tsx +28 -0
  153. package/src/styles/ods-colors.css +10 -0
  154. package/src/styles/ods-effects.css +13 -0
  155. package/dist/chunk-A3CSQB4A.cjs.map +0 -1
  156. package/dist/chunk-DQOQ3KTP.cjs.map +0 -1
  157. package/dist/chunk-M4HYIXP6.cjs.map +0 -1
  158. package/dist/chunk-T6KWNJDW.js.map +0 -1
  159. package/dist/chunk-XGX6JSEU.js.map +0 -1
  160. /package/dist/{chunk-65KTXRBY.js.map → chunk-2G2CB7ZZ.js.map} +0 -0
  161. /package/dist/{chunk-XW7MUIW4.js.map → chunk-3NIZ2VLQ.js.map} +0 -0
  162. /package/dist/{chunk-ZF4Z36G4.js.map → chunk-4RJQMOQW.js.map} +0 -0
  163. /package/dist/{chunk-7XF63THA.js.map → chunk-ASBBGP36.js.map} +0 -0
  164. /package/dist/{chunk-O7LOZW4R.js.map → chunk-C5TFPMOM.js.map} +0 -0
  165. /package/dist/{chunk-TUG7IT6H.js.map → chunk-HLEXYTOE.js.map} +0 -0
  166. /package/dist/{chunk-GXV6UEVY.js.map → chunk-IELOPDTA.js.map} +0 -0
  167. /package/dist/{chunk-B32YLOYQ.js.map → chunk-JBJZL5PI.js.map} +0 -0
  168. /package/dist/{chunk-UT333EOT.js.map → chunk-LCOMNQLX.js.map} +0 -0
  169. /package/dist/{chunk-WJK2EETW.js.map → chunk-NINO3PI7.js.map} +0 -0
  170. /package/dist/{chunk-PSQKD6ZN.js.map → chunk-PG65AE3I.js.map} +0 -0
  171. /package/dist/{chunk-BZ2XOEHW.js.map → chunk-QOH45QSE.js.map} +0 -0
  172. /package/dist/{chunk-Y7ZYOQNB.js.map → chunk-RSMAVO2Z.js.map} +0 -0
  173. /package/dist/{chunk-GHLGXFNU.js.map → chunk-U6JC3RS2.js.map} +0 -0
  174. /package/dist/{chunk-N4Z6X5BV.js.map → chunk-WFH52OVQ.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-K2HZG4RM.cjs","../src/components/related-content/related-content-section.tsx","../src/components/related-content/card-registry.tsx"],"names":["jsx","buildListUrl"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-K2HZG4RM.cjs","sourcesContent":[null,"'use client';\n\n/**\n * RelatedContentSection\n *\n * Renders content references grouped by type using the canonical card\n * components. MOVED from the hub (`components/shared/related-content-card.tsx`)\n * so any consuming app can embed it; the hub keeps a thin wrapper that\n * pre-binds its host-specific injections (nav hook, URL recomposition,\n * program configs, admin campaign card).\n *\n * THREE data modes (precedence top-down):\n * 1. CONTROLLED — `contentRefs` provided (even `[]`): render exactly those\n * refs, no suggestion fetch (the original investor-update behavior).\n * 2. SUGGESTION — `entityType` + `entityId` provided: self-fetch\n * `GET {apiBaseUrl}/api/related-content?entityType&entityId[&count][&excludeTypes]`\n * (the generic 5-tier engine's second web service). `minResults` maps to\n * `count`; absent → param not sent (server default applies). Each ref\n * carries a `reason` (data-only — never rendered, matching the\n * FaqSection/FaqWithReason precedent).\n * 3. SSR-HYDRATED suggestion — also pass `initialItems` (the server page\n * called the engine directly); the first client fetch is skipped per the\n * `useSelfFetch` initialData contract.\n *\n * Group layout (list vs grid) + card size (lg vs default) come from\n * `CONTENT_REF_GROUPS` in `../../utils/content-ref-groups` — single source of\n * truth, no per-type logic in this file. Skeletons come from\n * `renderSkeletonForType` so the placeholder height matches the loaded card\n * exactly (zero layout shift on resolve).\n *\n * One API call per content type via the shared list-URL builder\n * (`buildListUrl` — injectable; defaults to the lib's byte-parity-tested\n * builder prefixed with `apiBaseUrl`). Fetching uses `useSelfFetch` (plain\n * fetch, NO react-query) so third-party embedders need no QueryClientProvider;\n * cards are imported via DEEP module paths (not the chat barrel) so this\n * chunk never reaches `@tanstack/react-query`.\n *\n * The per-type card + skeleton dispatch lives in `./card-registry`\n * (`RELATED_CARD_REGISTRY`): ONE entry per content type, each carrying that\n * type's own row type and the `unknown` → row decode, so a fetched row reaches\n * its card concretely typed. This file only picks the entry and builds the\n * shared render context.\n *\n * LOCKSTEP NOTE: that registry is the SIZED sibling of the chat-side\n * `CHAT_CARD_REGISTRY` (`../chat/entity-cards/dispatch.tsx`), which renders\n * compact `size='sm'` cards wired to the chat runtime. Two dispatchers by\n * design — when registering a new fetch-mode content type, add it BOTH there\n * and in `./card-registry` (card + skeleton + list URL).\n */\n\nimport type React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport type { ContentRef, ContentRefWithReason } from '../../types/content-ref';\nimport {\n CONTENT_REF_GROUPS,\n getContentRefLabelOrTitleCase,\n orderContentRefTypes,\n type ContentRefGroupConfig,\n} from '../../utils/content-ref-groups';\nimport { extractItems, extractItemId } from '../../utils/extract-items';\nimport { buildListUrl as libBuildListUrl, canonicalContentRefType } from '../../utils/list-url';\nimport { pageCount } from '../../utils/search-params';\nimport { buildSuggestionUrl } from '../../utils/suggestion-url';\n// Type-only — erased at build, no runtime dependency on the dispatch module.\nimport type { ChatCardDispatchExtras } from '../chat/entity-cards/dispatch';\nimport { decideNewTab } from '../chat/utils/decide-new-tab';\nimport { Pagination } from '../pagination';\n// The per-type card dispatch — one registry entry per content type, each\n// carrying its OWN row type — plus the DEEP card imports that keep this\n// chunk's source graph free of @tanstack/react-query (the `../chat` barrel\n// reaches it via embeddable-chat; deep paths don't). `CardSize` and\n// `CardLinkAnchorProps` are declared alongside the registry, its primary\n// consumer, and re-exported below so the subpath barrel is unchanged.\nimport {\n RELATED_CARD_REGISTRY,\n rowString,\n type CardLinkAnchorProps,\n type CardSize,\n type RelatedCardRegistryEntry,\n} from './card-registry';\n\nexport type { CardLinkAnchorProps };\n\n/** Render-prop component injection for the navigation decision — keeps hook\n * calls legal (hooks live INSIDE the injected component; `CardForType`\n * itself calls zero hooks). The hub injects a `useNavLink`-backed provider;\n * the default is hook-free (pure `decideNewTab`). MUST be defined at module\n * scope by hosts — an inline arrow would remount every card each render. */\nexport interface CardLinkProviderProps {\n href: string | null;\n targetPlatform: string | null;\n children: (linkProps: CardLinkAnchorProps | null) => React.ReactElement | null;\n}\nexport type CardLinkProvider = React.ComponentType<CardLinkProviderProps>;\n\n/** Default link provider for standalone embeds: relative/-same-origin hrefs\n * stay same-tab, cross-origin pops a new tab (pure `decideNewTab` with no\n * platform context — `currentSource: ''` falls through to the origin\n * check). No router integration, no hooks. */\nfunction DefaultLinkPropsProvider({\n href,\n targetPlatform,\n children,\n}: CardLinkProviderProps): React.ReactElement | null {\n if (!href) return children(null);\n const newTab = decideNewTab({ href, targetPlatform, currentSource: '' });\n return children(newTab ? { href, target: '_blank', rel: 'noopener noreferrer' } : { href });\n}\n\n/** Default href resolution: trust the ref's stored url/targetPlatform as the\n * API composed them. The hub overrides this with its `buildContentURL`\n * re-composition so dev gets localhost and prod gets platform domains. */\nfunction defaultResolveHref(ref: ContentRef): { href: string | null; targetPlatform: string | null } {\n return { href: ref.url || null, targetPlatform: ref.targetPlatform ?? null };\n}\n\n/** Host-injected renderer pair for the admin-only `marketing_campaign` type.\n * Absent (every non-hub embed) → the type renders nothing (its list URL\n * hits `/api/admin`, unreachable outside the hub anyway). */\nexport interface AdminCampaignCardSlot {\n /**\n * The host's own campaign card. Its `campaign` prop is a HOST row type this\n * lib cannot name (the hub's `MarketingCampaign`), and React props are\n * CONTRAVARIANT, so this is the one seam in the rail with no honest type.\n * All three alternatives were compiled against the hub's real\n * `CampaignCardAdmin`:\n *\n * - `unknown` — the host can no longer inject: TS2322, \"Type\n * '{ campaign: unknown }' is not assignable to\n * '{ campaign: MarketingCampaign }'\".\n * - `never` — every host component assigns, but the rail can no longer\n * hand it a row: TS2769, \"Type 'unknown' is not assignable to 'never'\".\n * - a generic on the slot's OWNER (`AdminCampaignCardSlot<TCampaign>`\n * threaded through `RelatedContentSectionProps`) — inference from the\n * host's card works, but the render still fails: TS2769, \"'TCampaign'\n * could be instantiated with an arbitrary type\". The rail holds an\n * unvalidated row; only the HOST knows how to narrow it.\n *\n * Typing this means changing the injection contract (a host-supplied\n * `render(row: unknown)` closure instead of a component), which is a\n * breaking change for every embedder that passes `{ Card, Skeleton }`.\n * Everything the rail renders ITSELF now goes through the per-type\n * `RELATED_CARD_REGISTRY`; this is the only `any` left.\n */\n Card: React.ComponentType<{ campaign: any }>;\n Skeleton: React.ComponentType<{ size?: 'default' | 'sm' }>;\n}\n\n/**\n * Per-type skeleton dispatch — returns the SAME colocated skeleton the\n * resolved card renders, sized to match (zero layout shift on resolve). One\n * lookup through `RELATED_CARD_REGISTRY`, exactly the way the chat-side\n * `CHAT_CARD_REGISTRY` does it via `entry.skeleton()`.\n *\n * `marketing_campaign` is the one type with no registry entry: BOTH its card\n * and its skeleton are host-injected (`AdminCampaignCardSlot`), so there is no\n * lib component to register.\n */\nfunction renderSkeletonForType(\n type: string,\n size: CardSize,\n adminCampaignCard?: AdminCampaignCardSlot,\n): React.ReactNode {\n if (type === 'marketing_campaign') {\n return adminCampaignCard ? <adminCampaignCard.Skeleton size={size === 'sm' ? 'sm' : 'default'} /> : null;\n }\n const entry: RelatedCardRegistryEntry | undefined = RELATED_CARD_REGISTRY[type];\n return entry ? entry.skeleton(size) : null;\n}\n\n/**\n * Per-type card dispatch — ONE lookup through `RELATED_CARD_REGISTRY`, whose\n * entries each pair a skeleton with a card renderer that closes over that\n * type's OWN row type. Sized cards (`'lg'` / `'default'`) are unique to this\n * rail; the chat dispatcher only renders `'sm'`.\n *\n * PURE FUNCTION COMPONENT WITH ZERO HOOK CALLS: the placeholder comes from a\n * plain `extras.buildOgPlaceholderUrl` call (the chat `dispatch.tsx`\n * pattern) and the anchor-prop bundle arrives via the `LinkProvider`\n * render-prop from the parent — so per-card hook legality is owned by the\n * injected provider component, not by this dispatcher.\n *\n * `href` comes from the host's `resolveHref(ref)` (hub: live\n * `buildContentURL` recomposition; default: the ref's stored url).\n */\nfunction CardForType({\n type,\n item,\n size,\n href,\n targetPlatform,\n linkProps,\n extras,\n adminCampaignCard,\n}: {\n type: string;\n /** One fetched row, still unvalidated. Its shape is decided at RUNTIME by\n * `type` (each group fetches from its own per-type list API), so it stays\n * `unknown` right up to the registry entry for that type — which owns the\n * `unknown` → row-type decode and hands its card a concretely typed row. */\n item: unknown;\n size: CardSize;\n href: string;\n targetPlatform: string | null;\n linkProps: CardLinkAnchorProps | null;\n extras?: ChatCardDispatchExtras;\n adminCampaignCard?: AdminCampaignCardSlot;\n}): React.ReactNode {\n // Host-injected admin slot — the only type with no registry entry, because\n // both its card and its skeleton come from the host (see\n // `AdminCampaignCardSlot`). Handled before the lookup so the registry stays\n // a pure lib-card table.\n if (type === 'marketing_campaign') {\n return adminCampaignCard ? <adminCampaignCard.Card campaign={item} /> : null;\n }\n\n const entry: RelatedCardRegistryEntry | undefined = RELATED_CARD_REGISTRY[type];\n if (!entry) return null;\n\n return entry.render(item, {\n size,\n // Most card variants accept only `{default, sm}`. `'lg'` collapses to\n // `'default'` for those. ProductReleaseCard uses its own lg/sm pair.\n legacySize: size === 'sm' ? 'sm' : 'default',\n href,\n targetPlatform,\n linkProps,\n // Top-level target/rel for cards that take them as separate props\n // (BlogCard, CaseStudyCard, …). ProductReleaseCard takes the bundle as a\n // single `anchorProps={...}` and uses `linkProps` directly. When the host\n // didn't surface a URL, `linkProps` is null and the card stays in\n // non-anchor mode.\n anchorAttrs: linkProps ? { target: linkProps.target, rel: linkProps.rel } : {},\n // OG placeholder URL — injected into the pure-presentation cards so they\n // render a branded fallback when the row's featured image is null. Plain\n // function call (NOT a hook). Title is the universal field across all card\n // row shapes used here.\n // `item?.title as string | undefined` claimed a string without checking one:\n // a numeric title reached `buildOgPlaceholderUrl` as a number. `rowString`\n // makes the claim true.\n placeholderUrl: extras?.buildOgPlaceholderUrl?.(rowString(item, 'title') ?? '') ?? undefined,\n extras,\n });\n}\n\n// =============================================================================\n// Fetch all items for a type in ONE server-sorted call, via the injectable\n// list-URL builder. `useSelfFetch` (URL = cache key) replaces the hub's old\n// react-query usage: `enabled` ≙ `url === null`, `!res.ok`/network error ≙\n// `error → items null → group renders nothing`. Accepted deltas vs\n// react-query: no retry/backoff, no focus refetch, no cross-mount cache.\n// =============================================================================\n\nfunction useGroupItems(type: string, refs: ContentRef[], buildUrl: (type: string, ids: string[]) => string | null) {\n const ids = refs.map(r => r.id);\n const url = ids.length > 0 ? buildUrl(type, ids) : null;\n const { data, isLoading } = useSelfFetch<unknown>(url);\n const items = data != null ? extractItems(data) : null;\n return { items, isLoading };\n}\n\n// =============================================================================\n// Per-group renderer — one API call, server-sorted, then render cards via the\n// dispatcher with per-type skeletons + per-type layout from CONTENT_REF_GROUPS.\n// =============================================================================\n\n/** Map columns prop → tailwind grid class. Only consulted for grid-layout\n * groups; list-layout groups stack vertically. */\nfunction gridClassFor(columns: 2 | 3): string {\n return columns === 3\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6'\n : 'grid grid-cols-1 sm:grid-cols-2 gap-6';\n}\n\n/** Resolve the group config for a type, falling back to a grid layout with\n * the default card size for unregistered types so the section still renders\n * rather than silently dropping them. The `label` field on the fallback is\n * intentionally a placeholder — the section heading goes through\n * `getContentRefLabelOrTitleCase(type)` instead so cross-surface labels\n * stay consistent between this rail and the investor-email builder. */\nfunction resolveGroupConfig(type: string): ContentRefGroupConfig {\n return (\n CONTENT_REF_GROUPS[type] ?? {\n label: type,\n order: 999,\n layout: 'grid',\n gridSize: 'default',\n }\n );\n}\n\n/** Items per page within one type group. Groups larger than this paginate\n * with the standard Pagination control (NO nested scrolling — a bounded\n * scrollbox inside the page traps wheel events and hides the sections\n * below it). MUST stay at or above the largest suggestion fill\n * (RELATED_SAME_TYPE_COUNT in the hub's lib/constants/suggestions.ts) so\n * current rails never paginate — only genuinely big groups (author pages)\n * do. Exported through the subpath barrel for the hub's module-load\n * assertion of that relation (entity-suggestion-sections.tsx). */\nexport const GROUP_PAGE_SIZE = 12;\n\nfunction ContentGroup({\n type,\n refs,\n columns,\n buildUrl,\n resolveHref,\n LinkProvider,\n extras,\n adminCampaignCard,\n heading,\n}: {\n type: string;\n refs: ContentRef[];\n columns: 2 | 3;\n buildUrl: (type: string, ids: string[]) => string | null;\n resolveHref: (ref: ContentRef) => { href: string | null; targetPlatform: string | null };\n LinkProvider: CardLinkProvider;\n extras?: ChatCardDispatchExtras;\n adminCampaignCard?: AdminCampaignCardSlot;\n /** Group heading, rendered INSIDE the group so a group that resolves to\n * nothing (fetch miss / unsupported type / missing program config) drops\n * its heading too — no orphaned titles. */\n heading: React.ReactNode;\n}) {\n const { items, isLoading } = useGroupItems(type, refs, buildUrl);\n const config = resolveGroupConfig(type);\n const isListLayout = config.layout === 'list';\n const cardSize = config.gridSize;\n\n // Per-group pagination for big groups (author pages): GROUP_PAGE_SIZE items\n // per page with the standard Pagination control below the group. Client-side\n // slicing — useGroupItems already fetched every row in one batched call, so\n // page flips are instant. Hooks live above every early return (file\n // convention). Page is clamped so a shrinking refs array (suggestion\n // refetch) can never strand the view past the last page, and RESET when the\n // ref set actually changes (shrink→grow must not return to a stale page).\n const [page, setPage] = useState(1);\n const refsKey = refs.map(r => r.id).join('|');\n const prevRefsKeyRef = useRef(refsKey);\n useEffect(() => {\n if (prevRefsKeyRef.current !== refsKey) {\n prevRefsKeyRef.current = refsKey;\n setPage(1);\n }\n }, [refsKey]);\n const totalGroupPages = pageCount(refs.length, GROUP_PAGE_SIZE);\n const safePage = Math.min(page, totalGroupPages);\n const visibleGroupRefs =\n refs.length > GROUP_PAGE_SIZE ? refs.slice((safePage - 1) * GROUP_PAGE_SIZE, safePage * GROUP_PAGE_SIZE) : refs;\n const groupPagination =\n totalGroupPages > 1 ? (\n <Pagination currentPage={safePage} totalPages={totalGroupPages} onPageChange={setPage} />\n ) : null;\n\n // Skeleton gate: `isLoading && !items` — SSR HTML and the client's first\n // paint render identical skeletons (useSelfFetch starts isLoading=true on\n // both sides), and once items exist they are never replaced by skeletons.\n if (isLoading && !items) {\n const skeletons = visibleGroupRefs.map(r => (\n <div key={r.id}>{renderSkeletonForType(type, cardSize, adminCampaignCard)}</div>\n ));\n return (\n <div className=\"space-y-4\">\n {heading}\n {isListLayout ? (\n <div className=\"space-y-4\">{skeletons}</div>\n ) : (\n <div className={gridClassFor(columns)}>{skeletons}</div>\n )}\n </div>\n );\n }\n\n if (!items || items.length === 0) return null;\n\n // Index fetched rows by id, then render in REF order — refs carry the\n // intended sequence (suggestion mode: the engine's tier order, so\n // same-platform/tag-matched items lead; controlled mode: the curated\n // display_order). The list APIs return rows date-sorted, which would\n // otherwise scramble that ordering (same-platform items sinking below\n // newer cross-platform ones).\n // Shared extractor (NOT raw `.id`) — some API shapes key items differently\n // (e.g. external_id types); raw access would silently drop valid items.\n const rowKey = (it: unknown): string => {\n const extracted = extractItemId(type, it);\n if (extracted !== null) return extracted;\n // Fallback for shapes `extractItemId` doesn't recognise — same\n // `String(it?.id)` the untyped version produced.\n return String(typeof it === 'object' && it !== null ? (it as { id?: unknown }).id : undefined);\n };\n const itemById = new Map(items.map(it => [rowKey(it), it]));\n\n const cards = visibleGroupRefs\n .map(contentRef => {\n const itemId = String(contentRef.id);\n const item = itemById.get(itemId);\n if (!item) return null;\n // Re-compose the URL via the host's resolver (hub: buildContentURL so\n // dev gets localhost and prod the right platform domain; default: the\n // ref's stored url as the API composed it).\n const resolved = resolveHref(contentRef);\n const href = resolved.href ?? '';\n const targetPlatform = resolved.targetPlatform ?? contentRef.targetPlatform ?? null;\n return (\n <div key={itemId}>\n <LinkProvider href={href || null} targetPlatform={targetPlatform}>\n {linkProps => (\n <CardForType\n type={type}\n item={item}\n size={cardSize}\n href={href}\n targetPlatform={targetPlatform}\n linkProps={linkProps}\n extras={extras}\n adminCampaignCard={adminCampaignCard}\n />\n )}\n </LinkProvider>\n </div>\n );\n })\n .filter(Boolean);\n\n if (cards.length === 0) {\n // Current PAGE resolved zero cards (rows deleted between the ref fetch\n // and the group fetch, or a stricter list-API gate dropped them). When a\n // pager exists the user must keep the controls to navigate back —\n // dropping the whole group would strand them. A genuinely empty group\n // (no pager) still vanishes with its heading.\n if (groupPagination) {\n return (\n <div className=\"space-y-4\">\n {heading}\n {groupPagination}\n </div>\n );\n }\n return null;\n }\n\n return (\n <div className=\"space-y-4\">\n {heading}\n {isListLayout ? <div className=\"space-y-4\">{cards}</div> : <div className={gridClassFor(columns)}>{cards}</div>}\n {groupPagination}\n </div>\n );\n}\n\n// =============================================================================\n// Main component\n// =============================================================================\n\ninterface RelatedContentResponse {\n refs: ContentRefWithReason[];\n}\n\nexport interface RelatedContentSectionProps {\n /** CONTROLLED mode (the original behavior). When defined — even `[]` — no\n * suggestion fetch runs and exactly these refs render. */\n contentRefs?: ContentRef[];\n /** SUGGESTION mode (with `entityId`): self-fetch suggestions for this host\n * entity from `{apiBaseUrl}/api/related-content`. Ignored when\n * `contentRefs` is provided. */\n entityType?: string;\n entityId?: number | string;\n /** AUTHOR mode: self-fetch ALL published content authored by this profile\n * from `{apiBaseUrl}/api/related-content?authorId=…` (grouped per type,\n * endless within each group). Ignored when `contentRefs` is provided;\n * takes precedence over the entityType/entityId suggestion scope.\n * SSR-hydrate via `initialItems`, same as suggestion mode. */\n authorId?: string;\n /** Maps to the suggestion API's `count` param — the PER-TYPE fill target\n * for every candidate type EXCEPT the host's own. Absent → param not sent\n * (server default applies). */\n minResults?: number;\n /** Maps to the suggestion API's `sameTypeCount` param — the budget for the\n * candidate type MATCHING the host's own `entityType` (same-type boost:\n * a blog post's rail leads with more blog posts). Absent → param not\n * sent (host's type uses the server's `count`). */\n sameTypeMinResults?: number;\n /** SSR hydrate for suggestion mode — the server page ran the engine and\n * drills the refs here; the first client fetch is skipped (useSelfFetch\n * initialData contract). */\n initialItems?: ContentRefWithReason[];\n /** Section title (default: \"Related Content\") */\n title?: string;\n /**\n * Grid columns at desktop. 2 = denser cards / wider summary (original\n * investor-update layout); 3 = more cards per row for dashboards.\n * Only consulted for grid-layout groups. Default: 2.\n */\n columns?: 2 | 3;\n /**\n * ContentRef.type values to exclude. Honored in ALL modes — controlled\n * mode post-filters (original behavior); suggestion mode ALSO forwards the\n * list verbatim as the API's `excludeTypes=` param so excluded types never\n * consume engine fill slots (`minResults` stays honored). The subtraction\n * happens SERVER-side — this component never mirrors the hub's candidate\n * list.\n */\n excludeTypes?: string[];\n /**\n * SUGGESTION-mode allow-list (rail vocabulary): which content types\n * participate in this rail. Sent verbatim as the API's `types=` param —\n * the SERVER intersects it with its own allowed candidate set, and\n * platform policy gates (e.g. internal-only types) ALWAYS win: the client\n * cannot request its way past them. Absent → all server-side candidates.\n */\n includeTypes?: string[];\n /** Fetch-URL prefix for third-party embeds / reverse proxies\n * ('' = same-origin). Applied to BOTH the suggestion fetch and the\n * default per-group list fetches. */\n apiBaseUrl?: string;\n /** Host injection bundle — REUSES the chat dispatcher's\n * `ChatCardDispatchExtras` (programConfigs, buildOgPlaceholderUrl,\n * buildProductReleaseCardProps override). Program groups render nothing\n * when their config is absent. */\n extras?: ChatCardDispatchExtras;\n /** Hub injects its `buildContentURL` recomposition; default uses the\n * ref's stored `url`/`targetPlatform` as the API composed them. */\n resolveHref?: (ref: ContentRef) => { href: string | null; targetPlatform: string | null };\n /** Hub injects its registry-driven entity-list-api builder; default = the\n * lib's `buildListUrl(type, ids, apiBaseUrl)`. */\n buildListUrl?: (type: string, ids: string[]) => string | null;\n /** Hub injects a `useNavLink`-backed render-prop provider; default = pure\n * anchor via `decideNewTab`. MUST be a module-scope component. */\n LinkProvider?: CardLinkProvider;\n /** Renderer pair for the admin-only `marketing_campaign` type. Absent →\n * the type renders nothing. */\n adminCampaignCard?: AdminCampaignCardSlot;\n /** When true, render the section shell (title + an empty-state line) even\n * with ZERO refs, instead of returning null. Default false (the original\n * behavior — empty rail = no shell). Opt-in per host page (e.g. people-hub's\n * \"What I Shipped\", where the section should always be present). */\n showWhenEmpty?: boolean;\n /** Empty-state copy shown under the title when `showWhenEmpty` and no refs.\n * Default: \"No related content yet.\" */\n emptyStateText?: string;\n /** Custom empty-state node (e.g. a hub `<EmptyState/>`) rendered under the\n * title when `showWhenEmpty` and there are no refs — overrides\n * `emptyStateText`. Lets a host match its canonical empty state. */\n emptyState?: React.ReactNode;\n}\n\nexport function RelatedContentSection({\n contentRefs,\n entityType,\n entityId,\n authorId,\n minResults,\n sameTypeMinResults,\n includeTypes,\n initialItems,\n title = 'Related Content',\n columns = 2,\n excludeTypes,\n apiBaseUrl = '',\n extras,\n resolveHref = defaultResolveHref,\n buildListUrl,\n LinkProvider = DefaultLinkPropsProvider,\n adminCampaignCard,\n showWhenEmpty = false,\n emptyStateText = 'No related content yet.',\n emptyState,\n}: RelatedContentSectionProps) {\n // ── Hooks above EVERY early return (the original `if (!contentRefs.length)\n // return null` guard moved below them). ──\n\n // Suggestion-mode fetch URL — null in controlled mode (contentRefs defined,\n // even []) or when the entity scope is incomplete.\n // `includeTypes: []` is an explicit \"nothing participates\" — skip the fetch\n // entirely (an empty-string `types=` param would be dropped by the URL\n // builder and read server-side as \"all candidates\") AND ignore SSR refs.\n const suggestionsDisabled = includeTypes?.length === 0;\n // Shared type-filter params — one spelling for both fetch modes so a future\n // normalization (trim/dedupe) can't diverge between them.\n const typeFilterParams = {\n types: includeTypes !== undefined ? includeTypes.join(',') : undefined,\n excludeTypes: excludeTypes && excludeTypes.length > 0 ? excludeTypes.join(',') : undefined,\n };\n // AUTHOR mode beats suggestion mode: when `authorId` is set the rail lists\n // everything that profile authored (the server returns ALL, no count).\n const authorUrl =\n contentRefs === undefined && authorId && !suggestionsDisabled\n ? buildSuggestionUrl('/api/related-content', {\n apiBaseUrl,\n extraParams: { authorId, ...typeFilterParams },\n })\n : null;\n const suggestUrl =\n authorUrl ??\n (contentRefs === undefined &&\n entityType &&\n entityId !== undefined &&\n entityId !== null &&\n entityId !== '' &&\n !suggestionsDisabled\n ? buildSuggestionUrl('/api/related-content', {\n apiBaseUrl,\n entityType,\n entityId,\n count: minResults,\n extraParams: {\n sameTypeCount: sameTypeMinResults !== undefined ? String(sameTypeMinResults) : undefined,\n ...typeFilterParams,\n },\n })\n : null);\n // Memoize the initialData wrapper — useSelfFetch re-syncs on [initialData],\n // and a fresh per-render object would loop setState under re-rendering\n // parents (the latent FaqSection bug, fixed there in the same change).\n const initialData = useMemo<RelatedContentResponse | undefined>(\n // An explicitly disabled rail (includeTypes: []) must ignore SSR-hydrated\n // refs too — otherwise useSelfFetch(null, {initialData}) keeps serving\n // initialItems and the \"nothing participates\" contract silently breaks.\n () => (!suggestionsDisabled && initialItems ? { refs: initialItems } : undefined),\n [initialItems, suggestionsDisabled],\n );\n const { data, isLoading } = useSelfFetch<RelatedContentResponse>(suggestUrl, { initialData });\n\n // Default group fetcher: the lib's byte-parity-tested builder, prefixed for\n // embeds. Memoized so group-fetch URLs stay value-stable across renders.\n const effectiveBuildListUrl = useMemo(\n () => buildListUrl ?? ((type: string, ids: string[]) => libBuildListUrl(type, ids, apiBaseUrl)),\n [buildListUrl, apiBaseUrl],\n );\n\n const refs: ContentRef[] = contentRefs ?? data?.refs ?? [];\n\n // Per-consumer type gating — drops refs whose `type` is in the exclude\n // list. In suggestion mode the server already subtracted these (the param\n // is forwarded above); the client filter stays as an idempotent guard and\n // IS the mechanism in controlled mode (original behavior).\n const exclude = new Set(excludeTypes || []);\n const visibleRefs = exclude.size > 0 ? refs.filter(r => !exclude.has(r.type)) : refs;\n // Zero refs (still loading in suggestion mode, or genuinely empty). Default:\n // no empty shell. Opt-in (`showWhenEmpty`): render the title + an empty-state\n // line so the section is always present (e.g. people-hub \"What I Shipped\").\n if (!visibleRefs.length) {\n if (!showWhenEmpty) return null; // non-showWhenEmpty consumers stay blank (unchanged)\n // Client-fetch loading (author/suggestion mode, no SSR initialItems): render a\n // SKELETON grid — reserves height + matches the rest of the app's loading, so\n // there's no blank-then-pop jump (the prior `return null` collapsed the tab to\n // zero height during the fetch). SSR controlled mode has isLoading=false → it\n // skips straight to the empty state below. Skeleton type = the requested rail\n // type (author mode passes a single `includeTypes`).\n if (isLoading) {\n const skeletonType = includeTypes?.[0] ?? entityType ?? 'blog_post_existing';\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-ods-text-primary text-h2\">{title}</h2>\n <div className={gridClassFor(columns)}>\n {Array.from({ length: 3 }).map((_, i) => (\n <div key={i}>{renderSkeletonForType(skeletonType, 'default', adminCampaignCard)}</div>\n ))}\n </div>\n </div>\n );\n }\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-2xl font-bold text-ods-text-primary\">{title}</h2>\n {emptyState ?? <p className=\"text-ods-text-secondary\">{emptyStateText}</p>}\n </div>\n );\n }\n\n const grouped: Record<string, ContentRef[]> = {};\n for (const ref of visibleRefs) {\n if (!grouped[ref.type]) grouped[ref.type] = [];\n grouped[ref.type].push(ref);\n }\n\n // Registered types in CONTENT_REF_GROUPS order, then any unregistered\n // types appended (same shape the investor-email builder uses — both\n // consume `orderContentRefTypes` so cross-surface ordering matches).\n // SAME-TYPE FIRST: when a host entityType is known (suggestion / SSR\n // modes), its own content-type group is hoisted to the top — a blog\n // post's rail leads with blog posts. Rail group keys are compared via\n // the shared alias canonicalizer (blog_post_existing ↔ blog_post).\n let orderedTypes = orderContentRefTypes(Object.keys(grouped));\n if (entityType) {\n // Canonicalize BOTH sides — hosts pass registry vocab ('blog_post') but\n // rail-vocab aliases ('blog_post_existing') are also legal inputs; a raw\n // comparison would silently lose the same-type-first hoist for aliases.\n const canonicalEntityType = canonicalContentRefType(entityType);\n const sameType = orderedTypes.filter(t => canonicalContentRefType(t) === canonicalEntityType);\n if (sameType.length > 0) {\n orderedTypes = [...sameType, ...orderedTypes.filter(t => canonicalContentRefType(t) !== canonicalEntityType)];\n }\n }\n\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-ods-text-primary text-h2\">{title}</h2>\n {orderedTypes.map(type => (\n <ContentGroup\n key={type}\n type={type}\n refs={grouped[type]}\n columns={columns}\n buildUrl={effectiveBuildListUrl}\n resolveHref={resolveHref}\n LinkProvider={LinkProvider}\n extras={extras}\n adminCampaignCard={adminCampaignCard}\n heading={\n <h3 className=\"font-semibold text-ods-text-secondary text-h5\">{getContentRefLabelOrTitleCase(type)}</h3>\n }\n />\n ))}\n </div>\n );\n}\n","'use client';\n\n/**\n * Per-type card registry for the related-content rail — the SIZED sibling of\n * the chat side's `CHAT_CARD_REGISTRY` (`../chat/entity-cards/dispatch.tsx`),\n * and built to the same shape: ONE entry per content type, carrying that\n * type's skeleton and its card renderer, dispatched once from\n * `related-content-section.tsx`.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * The rail fetches one row per ref from that type's public list API. The row\n * arrives as `unknown` (`useSelfFetch<unknown>` → `extractItems`), and its\n * SHAPE is decided at runtime by the ref's `type`. The rail used to bridge\n * that with a single `item: any` threaded through a ten-branch switch, so\n * every `prop={item}` forward was an unchecked promise: a row missing a field\n * reached a card that declared it required, and the card rendered the miss\n * (an empty heading, a version pill reading \"vundefined\").\n *\n * Each entry here closes over its OWN row type instead. `cardEntry<Row>` pairs\n * a `decode` (runtime `unknown` → `Row`) with a `render` that receives `Row`,\n * and erases `Row` from the stored entry — so the registry is homogeneous\n * (`Record<string, RelatedCardRegistryEntry>`) while every branch stays\n * concretely typed. No `any`, no assertions into card row types.\n *\n * ON THE `*_ROW_DEFAULTS` CONSTANTS\n * ---------------------------------\n * Each card declares the FULL entity type as its row prop (`BlogPostSummary`,\n * `CaseStudy`, …) while reading only a handful of fields off it, and the list\n * APIs return projections rather than whole rows. A decoder therefore reads\n * the card's read set from the row and takes every other required field from\n * a `*_ROW_DEFAULTS` literal. Those placeholders are inert BY CONSTRUCTION —\n * the decoded row goes nowhere but its own card, and the card never reads\n * them. They are annotated with the entity type on purpose: a new required\n * field on `CaseStudy` surfaces as a compile error here instead of silently\n * reaching a card as `undefined`, which is the exact failure this file\n * replaces.\n *\n * ADDING A TYPE: one entry below (skeleton + decode + render), plus the chat\n * side's `CHAT_CARD_REGISTRY` and the list-URL builder — see the LOCKSTEP note\n * in `related-content-section.tsx`.\n */\n\nimport type React from 'react';\nimport type { BlogPostSummary } from '../../types/blog';\nimport type { CaseStudy } from '../../types/case-study';\nimport type { CustomerInterview } from '../../types/customer-interview';\nimport type { EntityAuthor } from '../../types/entity-author';\nimport type { MSP } from '../../types/stack';\nimport type { UserProfile } from '../../types/user';\nimport { AiPromptCard, AiPromptCardSkeleton, type AiPromptCardData } from '../chat/entity-cards/ai-prompt-card';\n// DEEP card imports — NOT the `../chat` barrel. See the import note in\n// `related-content-section.tsx`: the barrel statically reaches\n// @tanstack/react-query, deep paths keep this module's source graph free of it.\nimport { BlogCard, BlogCardSkeleton } from '../chat/entity-cards/blog-card';\nimport { CaseStudyCard, CaseStudyCardSkeleton } from '../chat/entity-cards/case-study-card';\nimport { CustomerInterviewCard, CustomerInterviewCardSkeleton } from '../chat/entity-cards/customer-interview-card';\n// Type-only — erased at build, no runtime dependency on the dispatch module.\nimport type { ChatCardDispatchExtras } from '../chat/entity-cards/dispatch';\nimport { HowIWorkCard, HowIWorkCardSkeleton, type HowIWorkCardData } from '../chat/entity-cards/how-i-work-card';\nimport { InvestorUpdateCard, InvestorUpdateCardSkeleton } from '../chat/entity-cards/investor-update-card';\nimport { OnboardingGuideCard, OnboardingGuideCardSkeleton } from '../chat/entity-cards/onboarding-guide-card';\nimport { ProductReleaseCard, ProductReleaseCardSkeleton } from '../chat/entity-cards/product-release-card';\nimport { buildProductReleaseCardProps } from '../chat/entity-cards/product-release-card-defaults';\nimport { ProgramCard, ProgramCardSkeleton } from '../chat/entity-cards/program-card';\nimport { RoadmapCard, RoadmapCardSkeleton } from '../chat/entity-cards/roadmap-card';\nimport {\n WhatIShippedCard,\n WhatIShippedCardSkeleton,\n type WhatIShippedCardData,\n} from '../chat/entity-cards/what-i-shipped-card';\nimport type { InvestorUpdate } from '../chat/types/entities/investor-update';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport type { BaseProgramItem, ProgramHost } from '../chat/types/entities/program-types';\nimport type { RoadmapItem } from '../chat/types/entities/roadmap-item';\n\nexport type CardSize = 'lg' | 'default' | 'sm';\n\n/** Anchor prop bundle the per-card link surface receives — same shape the\n * hub's `useNavLink` returns and the chat dispatcher's anchor builders\n * produce. `null` = non-anchor mode (no URL). */\nexport interface CardLinkAnchorProps {\n href: string;\n target?: '_blank';\n /** `noopener` alone for a new-tab link to one of our own platforms (keeps the\n * Referer for cross-domain attribution); `noreferrer` for third parties. */\n rel?: 'noopener' | 'noopener noreferrer';\n onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;\n}\n\n// =============================================================================\n// Row reading — the ONE place a runtime row is inspected.\n// =============================================================================\n\n/**\n * View an unvalidated row as a keyed bag. This claims nothing about the row's\n * SHAPE — every read below still checks the value's type — it only says \"this\n * is an object I may look keys up on\". A non-object row (null, a string, a\n * number) collapses to `{}`, so every decoder degrades to its defaults instead\n * of throwing.\n */\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n}\n\n/** A STRING field, or `undefined` when missing or wrong-typed. */\nexport function rowString(row: unknown, key: string): string | undefined {\n const value = asRecord(row)[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction str(row: Record<string, unknown>, key: string): string {\n const value = row[key];\n return typeof value === 'string' ? value : '';\n}\n\nfunction strOrNull(row: Record<string, unknown>, key: string): string | null {\n const value = row[key];\n return typeof value === 'string' ? value : null;\n}\n\nfunction numOrNull(row: Record<string, unknown>, key: string): number | null {\n const value = row[key];\n return typeof value === 'number' ? value : null;\n}\n\n/** `Array.isArray` narrows `unknown` to `any[]`; annotating the result keeps\n * the elements `unknown` so nothing downstream inherits `any`. Same guard the\n * shared `extractItems` normalizer uses. */\nfunction arrayOf(value: unknown): unknown[] {\n const list: unknown[] = Array.isArray(value) ? value : [];\n return list;\n}\n\nfunction stringList(value: unknown): string[] {\n return arrayOf(value).filter((entry): entry is string => typeof entry === 'string');\n}\n\n// =============================================================================\n// Shared nested-shape decoders (joins hydrated onto several row types).\n// =============================================================================\n\n/** `{ name, slug }` tag / category joins. Rows that carry only `name` (the\n * chat-side row shape) keep their label and get an empty slug — the cards\n * read `name` and nothing else. */\nfunction namedSlugList(value: unknown): { name: string; slug: string }[] {\n const out: { name: string; slug: string }[] = [];\n for (const entry of arrayOf(value)) {\n const record = asRecord(entry);\n const name = strOrNull(record, 'name');\n if (name === null) continue;\n out.push({ name, slug: str(record, 'slug') });\n }\n return out;\n}\n\nconst USER_PROFILE_DEFAULTS: UserProfile = {\n id: '',\n full_name: null,\n email: '',\n avatar_url: null,\n job_title: null,\n company: null,\n bio: null,\n employee_id: null,\n department_id: null,\n department: null,\n role: 'user',\n msp_id: null,\n created_at: '',\n updated_at: '',\n};\n\n/** The customer/author join on case studies + customer interviews. The cards\n * read `full_name`, `avatar_url` and `job_title`; absent join → `undefined`,\n * which is what those cards already fall back on. */\nfunction decodeUserProfile(value: unknown): UserProfile | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n ...USER_PROFILE_DEFAULTS,\n id: str(record, 'id'),\n full_name: strOrNull(record, 'full_name'),\n avatar_url: strOrNull(record, 'avatar_url'),\n job_title: strOrNull(record, 'job_title'),\n };\n}\n\nconst MSP_DEFAULTS: MSP = { id: '', name: '', seat_count: 0, created_at: '', updated_at: '' };\n\n/** The MSP join — cards read `name` and `icon_url`. */\nfunction decodeMsp(value: unknown): MSP | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n ...MSP_DEFAULTS,\n id: str(record, 'id'),\n name: str(record, 'name'),\n icon_url: rowString(record, 'icon_url'),\n };\n}\n\n/** The shared hydrated-author shape (`EntityAuthor`) — only `full_name` and\n * `avatar_url` are required, so this needs no defaults literal. */\nfunction decodeEntityAuthor(value: unknown): EntityAuthor | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n id: rowString(record, 'id'),\n full_name: strOrNull(record, 'full_name'),\n avatar_url: strOrNull(record, 'avatar_url'),\n job_title: strOrNull(record, 'job_title'),\n about: strOrNull(record, 'about'),\n slug: rowString(record, 'slug'),\n };\n}\n\n/** Program hosts / speakers. `name` is the only required field; an entry\n * without one is dropped rather than rendered as a nameless avatar. */\nfunction decodeProgramHosts(value: unknown): ProgramHost[] | null {\n if (value == null) return null;\n const out: ProgramHost[] = [];\n for (const entry of arrayOf(value)) {\n const record = asRecord(entry);\n const name = strOrNull(record, 'name');\n if (name === null) continue;\n out.push({\n name,\n id: rowString(record, 'id'),\n email: rowString(record, 'email'),\n avatar_url: rowString(record, 'avatar_url'),\n bio: rowString(record, 'bio'),\n role: rowString(record, 'role'),\n });\n }\n return out;\n}\n\n// =============================================================================\n// Per-type row decoders. Each reads the fields ITS card renders; every other\n// required field comes from the type-annotated defaults literal above it.\n// =============================================================================\n\nfunction decodeBlogRow(row: unknown): BlogPostSummary {\n const record = asRecord(row);\n return {\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n slug: str(record, 'slug'),\n summary: strOrNull(record, 'summary'),\n featured_image: strOrNull(record, 'featured_image'),\n published_at: strOrNull(record, 'published_at'),\n author_name: strOrNull(record, 'author_name'),\n author_avatar: strOrNull(record, 'author_avatar'),\n categories: namedSlugList(record.categories),\n tags: namedSlugList(record.tags),\n view_count: numOrNull(record, 'view_count') ?? undefined,\n };\n}\n\nconst CASE_STUDY_ROW_DEFAULTS: CaseStudy = {\n id: 0,\n title: '',\n slug: '',\n summary: null,\n featured_image: null,\n user_id: null,\n challenge: null,\n solution: null,\n results: null,\n testimonial_video_url: null,\n main_video_url: null,\n video_source_type: null,\n video_source: null,\n video_bites: [],\n customer_interview_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n status: 'published',\n published_at: null,\n author_id: null,\n created_at: '',\n updated_at: '',\n view_count: 0,\n};\n\nfunction decodeCaseStudyRow(row: unknown): CaseStudy {\n const record = asRecord(row);\n return {\n ...CASE_STUDY_ROW_DEFAULTS,\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n summary: strOrNull(record, 'summary'),\n featured_image: strOrNull(record, 'featured_image'),\n msp: decodeMsp(record.msp),\n user: decodeUserProfile(record.user),\n };\n}\n\nconst CUSTOMER_INTERVIEW_ROW_DEFAULTS: CustomerInterview = {\n id: 0,\n title: '',\n slug: '',\n video_summary: null,\n transcript: null,\n user_id: null,\n main_video_url: null,\n teasers: [],\n quotes: [],\n case_study_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n featured_image: null,\n status: 'completed',\n completed_at: null,\n author_id: null,\n custom_instructions: null,\n created_at: '',\n updated_at: '',\n view_count: 0,\n};\n\nfunction decodeCustomerInterviewRow(row: unknown): CustomerInterview {\n const record = asRecord(row);\n return {\n ...CUSTOMER_INTERVIEW_ROW_DEFAULTS,\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n video_summary: strOrNull(record, 'video_summary'),\n main_video_url: strOrNull(record, 'main_video_url'),\n featured_image: strOrNull(record, 'featured_image'),\n msp: decodeMsp(record.msp),\n user: decodeUserProfile(record.user),\n };\n}\n\nconst INVESTOR_UPDATE_ROW_DEFAULTS: InvestorUpdate = {\n id: '',\n title: '',\n slug: '',\n update_number: null,\n period_start: null,\n period_end: null,\n platform_id: null,\n content: null,\n video_summary: null,\n transcript: null,\n main_video_url: null,\n highlight_video_url: null,\n highlight_video_thumbnail: null,\n main_video_thumbnail: null,\n video_bites: [],\n featured_image: null,\n strategic_update: null,\n financials: {},\n metrics_snapshot: {},\n content_refs: [],\n highlights: null,\n section_visibility: {},\n status: 'published',\n published_at: null,\n author_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n hubspot_email_id: null,\n custom_instructions: null,\n created_at: '',\n updated_at: '',\n created_by: null,\n updated_by: null,\n};\n\nfunction decodeInvestorUpdateRow(row: unknown): InvestorUpdate {\n const record = asRecord(row);\n return {\n ...INVESTOR_UPDATE_ROW_DEFAULTS,\n id: str(record, 'id'),\n title: str(record, 'title'),\n update_number: numOrNull(record, 'update_number'),\n period_start: strOrNull(record, 'period_start'),\n period_end: strOrNull(record, 'period_end'),\n content: strOrNull(record, 'content'),\n strategic_update: strOrNull(record, 'strategic_update'),\n featured_image: strOrNull(record, 'featured_image'),\n };\n}\n\nconst ONBOARDING_GUIDE_ROW_DEFAULTS: OnboardingGuide = {\n id: '',\n title: '',\n slug: '',\n section: '',\n step_order: 0,\n section_order: 0,\n content: null,\n video_summary: null,\n transcript: null,\n transcript_words_data: null,\n srt_content: null,\n ai_transcript_formatted: null,\n main_video_url: null,\n main_video_thumbnail: null,\n youtube_url: null,\n highlight_video_url: null,\n highlight_video_thumbnail: null,\n highlight_video_duration_ms: null,\n highlight_video_source: null,\n video_bites: [],\n featured_image: null,\n og_image_url: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n status: 'published',\n published_at: null,\n author_id: null,\n custom_instructions: null,\n config: null,\n ai_effort_score: null,\n created_at: '',\n updated_at: '',\n created_by: null,\n updated_by: null,\n};\n\nfunction decodeOnboardingGuideRow(row: unknown): OnboardingGuide {\n const record = asRecord(row);\n return {\n ...ONBOARDING_GUIDE_ROW_DEFAULTS,\n id: str(record, 'id'),\n title: str(record, 'title'),\n section: str(record, 'section'),\n step_order: numOrNull(record, 'step_order') ?? 0,\n content: strOrNull(record, 'content'),\n video_summary: strOrNull(record, 'video_summary'),\n featured_image: strOrNull(record, 'featured_image'),\n og_image_url: strOrNull(record, 'og_image_url'),\n main_video_thumbnail: strOrNull(record, 'main_video_thumbnail'),\n highlight_video_thumbnail: strOrNull(record, 'highlight_video_thumbnail'),\n highlight_video_duration_ms: numOrNull(record, 'highlight_video_duration_ms'),\n author: decodeEntityAuthor(record.author),\n };\n}\n\n/**\n * `RoadmapItem` is the one row type the card reads WHOLE, so this decoder\n * carries no defaults literal — every field is read off the row.\n *\n * `upvotes` / `downvotes` / `quarter` are required `number`/`string` props the\n * card renders directly. Read off an untyped row they were `any`, so a row\n * without them rendered the miss (a blank vote count). They now fall back to\n * `0` / `''`, matching how the card renders a genuinely zero-vote item.\n */\nfunction decodeRoadmapRow(row: unknown): RoadmapItem {\n const record = asRecord(row);\n const assignees: NonNullable<RoadmapItem['assignees']> = [];\n for (const entry of arrayOf(record.assignees)) {\n const assignee = asRecord(entry);\n assignees.push({\n id: numOrNull(assignee, 'id') ?? 0,\n name: strOrNull(assignee, 'name'),\n avatarUrl: strOrNull(assignee, 'avatarUrl'),\n });\n }\n return {\n id: str(record, 'id'),\n title: str(record, 'title'),\n description: str(record, 'description'),\n status: str(record, 'status'),\n statusColor: str(record, 'statusColor'),\n icon: strOrNull(record, 'icon'),\n figmaUrl: strOrNull(record, 'figmaUrl'),\n screenshots: stringList(record.screenshots),\n targetVersion: strOrNull(record, 'targetVersion'),\n upvotes: numOrNull(record, 'upvotes') ?? 0,\n downvotes: numOrNull(record, 'downvotes') ?? 0,\n quarter: str(record, 'quarter'),\n clickupUrl: str(record, 'clickupUrl'),\n customItemId: numOrNull(record, 'customItemId'),\n assignees,\n };\n}\n\n/**\n * Program rows (podcast / webinar / event) keep their EXTRA columns.\n *\n * `<ProgramCard>` is generic over the item, and the config the rail threads in\n * is an `AnyProgramConfig` — `ProgramConfig<BaseProgramItem &\n * Record<string, unknown>>` — precisely so per-program columns ride along. The\n * card branches on `'duration_seconds' in item` / `'location_name' in item` /\n * `'start_at' in item`, so dropping the unrecognised columns would silently\n * strip a podcast's runtime and an event's venue. The raw row is spread first\n * and the base fields are decoded over it.\n */\nfunction decodeProgramRow(row: unknown): BaseProgramItem & Record<string, unknown> {\n const record = asRecord(row);\n return {\n ...record,\n id: str(record, 'id'),\n title: str(record, 'title'),\n description: strOrNull(record, 'description'),\n cover_url: strOrNull(record, 'cover_url'),\n date: str(record, 'date'),\n external_url: strOrNull(record, 'external_url'),\n hosts: decodeProgramHosts(record.hosts),\n };\n}\n\n/** The two employee-entry cards already declare MINIMAL row shapes\n * (`EmployeeEntryCardData` + one date column each), so their decoders read\n * exactly the card's fields with nothing left over. */\nfunction decodeEmployeeEntryRow(row: unknown): WhatIShippedCardData & HowIWorkCardData & AiPromptCardData {\n const record = asRecord(row);\n const department = asRecord(record.department);\n const stepCount = record.step_count;\n return {\n title: strOrNull(record, 'title'),\n summary: strOrNull(record, 'summary'),\n status: strOrNull(record, 'status'),\n featured_image: strOrNull(record, 'featured_image'),\n main_video_thumbnail: strOrNull(record, 'main_video_thumbnail'),\n author: decodeEntityAuthor(record.author),\n entry_month: strOrNull(record, 'entry_month'),\n session_date: strOrNull(record, 'session_date'),\n discipline: strOrNull(record, 'discipline'),\n published_at: strOrNull(record, 'published_at'),\n department: record.department\n ? { name: strOrNull(department, 'name'), color: strOrNull(department, 'color') }\n : null,\n step_count: typeof stepCount === 'number' ? stepCount : null,\n };\n}\n\n// =============================================================================\n// Registry\n// =============================================================================\n\n/** Everything a card branch needs beyond its own row — computed once per card\n * by the rail's dispatcher and shared by every entry. */\nexport interface RelatedCardContext {\n /** Card density for this group, from `CONTENT_REF_GROUPS`. */\n size: CardSize;\n /** `size` with `'lg'` collapsed to `'default'` — most card variants accept\n * only that pair. */\n legacySize: 'default' | 'sm';\n href: string;\n targetPlatform: string | null;\n /** `null` = the host surfaced no URL; the card stays in non-anchor mode. */\n linkProps: CardLinkAnchorProps | null;\n /** `{ target, rel }` for the cards that take them as separate props. */\n anchorAttrs: Pick<CardLinkAnchorProps, 'target' | 'rel'>;\n /** Branded OG fallback for cards whose row has no featured image. */\n placeholderUrl?: string;\n extras?: ChatCardDispatchExtras;\n}\n\n/** One registered content type. `Row` is erased here — `cardEntry` below is\n * what keeps each branch's row type concrete. */\nexport interface RelatedCardRegistryEntry {\n /** Sized to match the resolved card exactly (zero layout shift). */\n skeleton: (size: CardSize) => React.ReactNode;\n render: (row: unknown, ctx: RelatedCardContext) => React.ReactNode;\n}\n\n/**\n * Register one type with its OWN row type.\n *\n * `Row` is bound by the definition object and never escapes: `render` is\n * handed a decoded `Row`, and the stored entry exposes only\n * `(row: unknown) => ReactNode`. That is what lets a single registry hold ten\n * heterogeneous row shapes without a shared supertype — the job `item: any`\n * used to do, minus the promise it could not keep.\n */\nfunction cardEntry<Row>(definition: {\n skeleton: (size: CardSize) => React.ReactNode;\n decode: (row: unknown) => Row;\n render: (row: Row, ctx: RelatedCardContext) => React.ReactNode;\n}): RelatedCardRegistryEntry {\n return {\n skeleton: definition.skeleton,\n render: (row, ctx) => definition.render(definition.decode(row), ctx),\n };\n}\n\n/** Podcast / webinar / event share one card + one skeleton and differ only by\n * which host-supplied `ProgramConfig` they render with. A group whose config\n * the host didn't supply renders nothing (the skeleton still reserves its\n * height, so a rail that never resolves collapses instead of flickering). */\nfunction programEntry(configKey: 'podcast' | 'webinar' | 'event'): RelatedCardRegistryEntry {\n return cardEntry({\n skeleton: size => <ProgramCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeProgramRow,\n render: (item, ctx) => {\n const config = ctx.extras?.programConfigs?.[configKey];\n if (!config) return null;\n return (\n <ProgramCard\n config={config}\n item={item}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n );\n },\n });\n}\n\n/** The three ClickUp-backed types render the same card, distinguished by\n * `cardType` (drives the compact icon slot). */\nfunction roadmapEntry(cardType: 'roadmap_item' | 'delivery_item' | 'internal_task'): RelatedCardRegistryEntry {\n return cardEntry({\n skeleton: size => <RoadmapCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeRoadmapRow,\n render: (item, ctx) => (\n <RoadmapCard\n item={item}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n userVote={null}\n onVote={() => {}}\n size={ctx.legacySize}\n cardType={cardType}\n {...ctx.anchorAttrs}\n />\n ),\n });\n}\n\nexport const RELATED_CARD_REGISTRY: Record<string, RelatedCardRegistryEntry> = {\n blog_post_existing: cardEntry({\n skeleton: size => <BlogCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeBlogRow,\n render: (post, ctx) => (\n <BlogCard\n post={post}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n case_study: cardEntry({\n skeleton: size => <CaseStudyCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeCaseStudyRow,\n render: (study, ctx) => (\n <CaseStudyCard\n study={study}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n customer_interview: cardEntry({\n skeleton: size => <CustomerInterviewCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeCustomerInterviewRow,\n render: (interview, ctx) => (\n <CustomerInterviewCard\n interview={interview}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /**\n * Product releases derive their whole prop bundle through a HOST seam\n * (`extras.buildProductReleaseCardProps`, defaulting to the lib builder)\n * which already takes `unknown` — so this entry's row type is the bare\n * record, and the three props the rail sets itself are read off it.\n */\n product_release: cardEntry({\n skeleton: size => <ProductReleaseCardSkeleton size={size === 'sm' ? 'sm' : 'lg'} />,\n decode: asRecord,\n render: (release, ctx) => {\n const buildReleaseProps = ctx.extras?.buildProductReleaseCardProps ?? buildProductReleaseCardProps;\n const releaseProps = buildReleaseProps(release);\n return (\n <ProductReleaseCard\n size={ctx.size === 'sm' ? 'sm' : 'lg'}\n // `title` and `version` are REQUIRED `string` props. Read off an\n // untyped row they were `any`, so a release row missing either one\n // rendered an empty heading and a version pill reading \"vundefined\".\n // `summary` is `string | null | undefined` and the card renders\n // `summary ?? ''`, so a dropped one is indistinguishable from null.\n title={str(release, 'title')}\n summary={rowString(release, 'summary')}\n version={str(release, 'version')}\n {...releaseProps}\n // The card wraps in `<a {...anchorProps}>` ONLY when\n // `anchorProps.href` is set — `undefined` (not an empty object)\n // keeps it in non-anchor mode instead of rendering a dead <a>.\n anchorProps={ctx.linkProps ?? undefined}\n />\n );\n },\n }),\n\n podcast: programEntry('podcast'),\n webinar: programEntry('webinar'),\n event: programEntry('event'),\n\n investor_update: cardEntry({\n skeleton: size => <InvestorUpdateCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeInvestorUpdateRow,\n render: (update, ctx) => (\n <InvestorUpdateCard\n update={update}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /** The rich CATALOG variant (hero + author grid, clamped description) — the\n * step-numbered 'default' variant is for the guide detail page's \"More in\n * section\" rail, not this full-width row. */\n onboarding_guide: cardEntry({\n skeleton: size => <OnboardingGuideCardSkeleton size={size === 'sm' ? 'sm' : 'catalog'} />,\n decode: decodeOnboardingGuideRow,\n render: (guide, ctx) => (\n <OnboardingGuideCard\n guide={guide}\n size={ctx.size === 'sm' ? 'sm' : 'catalog'}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /** THE single What I Shipped card — same lib component the people-hub\n * dashboard renders. `anchorProps` makes the whole card a click-through\n * (the rail is read-only — no owner actions). Only pass it with a REAL\n * href: an object with `href: undefined` is still truthy and would wrap the\n * card in a dead <a>. */\n what_i_shipped: cardEntry({\n skeleton: () => <WhatIShippedCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <WhatIShippedCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n /** Same shared employee-entry shape + anchor contract as What I Shipped. */\n how_i_work: cardEntry({\n skeleton: () => <HowIWorkCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <HowIWorkCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n /** Same shared employee-entry shape + anchor contract as What I Shipped. */\n ai_prompt: cardEntry({\n skeleton: () => <AiPromptCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <AiPromptCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n roadmap_item: roadmapEntry('roadmap_item'),\n delivery_item: roadmapEntry('delivery_item'),\n internal_task: roadmapEntry('internal_task'),\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-2OD2PPNO.cjs","../src/components/related-content/related-content-section.tsx","../src/components/related-content/card-registry.tsx"],"names":["jsx","buildListUrl"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-2OD2PPNO.cjs","sourcesContent":[null,"'use client';\n\n/**\n * RelatedContentSection\n *\n * Renders content references grouped by type using the canonical card\n * components. MOVED from the hub (`components/shared/related-content-card.tsx`)\n * so any consuming app can embed it; the hub keeps a thin wrapper that\n * pre-binds its host-specific injections (nav hook, URL recomposition,\n * program configs, admin campaign card).\n *\n * THREE data modes (precedence top-down):\n * 1. CONTROLLED — `contentRefs` provided (even `[]`): render exactly those\n * refs, no suggestion fetch (the original investor-update behavior).\n * 2. SUGGESTION — `entityType` + `entityId` provided: self-fetch\n * `GET {apiBaseUrl}/api/related-content?entityType&entityId[&count][&excludeTypes]`\n * (the generic 5-tier engine's second web service). `minResults` maps to\n * `count`; absent → param not sent (server default applies). Each ref\n * carries a `reason` (data-only — never rendered, matching the\n * FaqSection/FaqWithReason precedent).\n * 3. SSR-HYDRATED suggestion — also pass `initialItems` (the server page\n * called the engine directly); the first client fetch is skipped per the\n * `useSelfFetch` initialData contract.\n *\n * Group layout (list vs grid) + card size (lg vs default) come from\n * `CONTENT_REF_GROUPS` in `../../utils/content-ref-groups` — single source of\n * truth, no per-type logic in this file. Skeletons come from\n * `renderSkeletonForType` so the placeholder height matches the loaded card\n * exactly (zero layout shift on resolve).\n *\n * One API call per content type via the shared list-URL builder\n * (`buildListUrl` — injectable; defaults to the lib's byte-parity-tested\n * builder prefixed with `apiBaseUrl`). Fetching uses `useSelfFetch` (plain\n * fetch, NO react-query) so third-party embedders need no QueryClientProvider;\n * cards are imported via DEEP module paths (not the chat barrel) so this\n * chunk never reaches `@tanstack/react-query`.\n *\n * The per-type card + skeleton dispatch lives in `./card-registry`\n * (`RELATED_CARD_REGISTRY`): ONE entry per content type, each carrying that\n * type's own row type and the `unknown` → row decode, so a fetched row reaches\n * its card concretely typed. This file only picks the entry and builds the\n * shared render context.\n *\n * LOCKSTEP NOTE: that registry is the SIZED sibling of the chat-side\n * `CHAT_CARD_REGISTRY` (`../chat/entity-cards/dispatch.tsx`), which renders\n * compact `size='sm'` cards wired to the chat runtime. Two dispatchers by\n * design — when registering a new fetch-mode content type, add it BOTH there\n * and in `./card-registry` (card + skeleton + list URL).\n */\n\nimport type React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport type { ContentRef, ContentRefWithReason } from '../../types/content-ref';\nimport {\n CONTENT_REF_GROUPS,\n getContentRefLabelOrTitleCase,\n orderContentRefTypes,\n type ContentRefGroupConfig,\n} from '../../utils/content-ref-groups';\nimport { extractItems, extractItemId } from '../../utils/extract-items';\nimport { buildListUrl as libBuildListUrl, canonicalContentRefType } from '../../utils/list-url';\nimport { pageCount } from '../../utils/search-params';\nimport { buildSuggestionUrl } from '../../utils/suggestion-url';\n// Type-only — erased at build, no runtime dependency on the dispatch module.\nimport type { ChatCardDispatchExtras } from '../chat/entity-cards/dispatch';\nimport { decideNewTab } from '../chat/utils/decide-new-tab';\nimport { Pagination } from '../pagination';\n// The per-type card dispatch — one registry entry per content type, each\n// carrying its OWN row type — plus the DEEP card imports that keep this\n// chunk's source graph free of @tanstack/react-query (the `../chat` barrel\n// reaches it via embeddable-chat; deep paths don't). `CardSize` and\n// `CardLinkAnchorProps` are declared alongside the registry, its primary\n// consumer, and re-exported below so the subpath barrel is unchanged.\nimport {\n RELATED_CARD_REGISTRY,\n rowString,\n type CardLinkAnchorProps,\n type CardSize,\n type RelatedCardRegistryEntry,\n} from './card-registry';\n\nexport type { CardLinkAnchorProps };\n\n/** Render-prop component injection for the navigation decision — keeps hook\n * calls legal (hooks live INSIDE the injected component; `CardForType`\n * itself calls zero hooks). The hub injects a `useNavLink`-backed provider;\n * the default is hook-free (pure `decideNewTab`). MUST be defined at module\n * scope by hosts — an inline arrow would remount every card each render. */\nexport interface CardLinkProviderProps {\n href: string | null;\n targetPlatform: string | null;\n children: (linkProps: CardLinkAnchorProps | null) => React.ReactElement | null;\n}\nexport type CardLinkProvider = React.ComponentType<CardLinkProviderProps>;\n\n/** Default link provider for standalone embeds: relative/-same-origin hrefs\n * stay same-tab, cross-origin pops a new tab (pure `decideNewTab` with no\n * platform context — `currentSource: ''` falls through to the origin\n * check). No router integration, no hooks. */\nfunction DefaultLinkPropsProvider({\n href,\n targetPlatform,\n children,\n}: CardLinkProviderProps): React.ReactElement | null {\n if (!href) return children(null);\n const newTab = decideNewTab({ href, targetPlatform, currentSource: '' });\n return children(newTab ? { href, target: '_blank', rel: 'noopener noreferrer' } : { href });\n}\n\n/** Default href resolution: trust the ref's stored url/targetPlatform as the\n * API composed them. The hub overrides this with its `buildContentURL`\n * re-composition so dev gets localhost and prod gets platform domains. */\nfunction defaultResolveHref(ref: ContentRef): { href: string | null; targetPlatform: string | null } {\n return { href: ref.url || null, targetPlatform: ref.targetPlatform ?? null };\n}\n\n/** Host-injected renderer pair for the admin-only `marketing_campaign` type.\n * Absent (every non-hub embed) → the type renders nothing (its list URL\n * hits `/api/admin`, unreachable outside the hub anyway). */\nexport interface AdminCampaignCardSlot {\n /**\n * The host's own campaign card. Its `campaign` prop is a HOST row type this\n * lib cannot name (the hub's `MarketingCampaign`), and React props are\n * CONTRAVARIANT, so this is the one seam in the rail with no honest type.\n * All three alternatives were compiled against the hub's real\n * `CampaignCardAdmin`:\n *\n * - `unknown` — the host can no longer inject: TS2322, \"Type\n * '{ campaign: unknown }' is not assignable to\n * '{ campaign: MarketingCampaign }'\".\n * - `never` — every host component assigns, but the rail can no longer\n * hand it a row: TS2769, \"Type 'unknown' is not assignable to 'never'\".\n * - a generic on the slot's OWNER (`AdminCampaignCardSlot<TCampaign>`\n * threaded through `RelatedContentSectionProps`) — inference from the\n * host's card works, but the render still fails: TS2769, \"'TCampaign'\n * could be instantiated with an arbitrary type\". The rail holds an\n * unvalidated row; only the HOST knows how to narrow it.\n *\n * Typing this means changing the injection contract (a host-supplied\n * `render(row: unknown)` closure instead of a component), which is a\n * breaking change for every embedder that passes `{ Card, Skeleton }`.\n * Everything the rail renders ITSELF now goes through the per-type\n * `RELATED_CARD_REGISTRY`; this is the only `any` left.\n */\n Card: React.ComponentType<{ campaign: any }>;\n Skeleton: React.ComponentType<{ size?: 'default' | 'sm' }>;\n}\n\n/**\n * Per-type skeleton dispatch — returns the SAME colocated skeleton the\n * resolved card renders, sized to match (zero layout shift on resolve). One\n * lookup through `RELATED_CARD_REGISTRY`, exactly the way the chat-side\n * `CHAT_CARD_REGISTRY` does it via `entry.skeleton()`.\n *\n * `marketing_campaign` is the one type with no registry entry: BOTH its card\n * and its skeleton are host-injected (`AdminCampaignCardSlot`), so there is no\n * lib component to register.\n */\nfunction renderSkeletonForType(\n type: string,\n size: CardSize,\n adminCampaignCard?: AdminCampaignCardSlot,\n): React.ReactNode {\n if (type === 'marketing_campaign') {\n return adminCampaignCard ? <adminCampaignCard.Skeleton size={size === 'sm' ? 'sm' : 'default'} /> : null;\n }\n const entry: RelatedCardRegistryEntry | undefined = RELATED_CARD_REGISTRY[type];\n return entry ? entry.skeleton(size) : null;\n}\n\n/**\n * Per-type card dispatch — ONE lookup through `RELATED_CARD_REGISTRY`, whose\n * entries each pair a skeleton with a card renderer that closes over that\n * type's OWN row type. Sized cards (`'lg'` / `'default'`) are unique to this\n * rail; the chat dispatcher only renders `'sm'`.\n *\n * PURE FUNCTION COMPONENT WITH ZERO HOOK CALLS: the placeholder comes from a\n * plain `extras.buildOgPlaceholderUrl` call (the chat `dispatch.tsx`\n * pattern) and the anchor-prop bundle arrives via the `LinkProvider`\n * render-prop from the parent — so per-card hook legality is owned by the\n * injected provider component, not by this dispatcher.\n *\n * `href` comes from the host's `resolveHref(ref)` (hub: live\n * `buildContentURL` recomposition; default: the ref's stored url).\n */\nfunction CardForType({\n type,\n item,\n size,\n href,\n targetPlatform,\n linkProps,\n extras,\n adminCampaignCard,\n}: {\n type: string;\n /** One fetched row, still unvalidated. Its shape is decided at RUNTIME by\n * `type` (each group fetches from its own per-type list API), so it stays\n * `unknown` right up to the registry entry for that type — which owns the\n * `unknown` → row-type decode and hands its card a concretely typed row. */\n item: unknown;\n size: CardSize;\n href: string;\n targetPlatform: string | null;\n linkProps: CardLinkAnchorProps | null;\n extras?: ChatCardDispatchExtras;\n adminCampaignCard?: AdminCampaignCardSlot;\n}): React.ReactNode {\n // Host-injected admin slot — the only type with no registry entry, because\n // both its card and its skeleton come from the host (see\n // `AdminCampaignCardSlot`). Handled before the lookup so the registry stays\n // a pure lib-card table.\n if (type === 'marketing_campaign') {\n return adminCampaignCard ? <adminCampaignCard.Card campaign={item} /> : null;\n }\n\n const entry: RelatedCardRegistryEntry | undefined = RELATED_CARD_REGISTRY[type];\n if (!entry) return null;\n\n return entry.render(item, {\n size,\n // Most card variants accept only `{default, sm}`. `'lg'` collapses to\n // `'default'` for those. ProductReleaseCard uses its own lg/sm pair.\n legacySize: size === 'sm' ? 'sm' : 'default',\n href,\n targetPlatform,\n linkProps,\n // Top-level target/rel for cards that take them as separate props\n // (BlogCard, CaseStudyCard, …). ProductReleaseCard takes the bundle as a\n // single `anchorProps={...}` and uses `linkProps` directly. When the host\n // didn't surface a URL, `linkProps` is null and the card stays in\n // non-anchor mode.\n anchorAttrs: linkProps ? { target: linkProps.target, rel: linkProps.rel } : {},\n // OG placeholder URL — injected into the pure-presentation cards so they\n // render a branded fallback when the row's featured image is null. Plain\n // function call (NOT a hook). Title is the universal field across all card\n // row shapes used here.\n // `item?.title as string | undefined` claimed a string without checking one:\n // a numeric title reached `buildOgPlaceholderUrl` as a number. `rowString`\n // makes the claim true.\n placeholderUrl: extras?.buildOgPlaceholderUrl?.(rowString(item, 'title') ?? '') ?? undefined,\n extras,\n });\n}\n\n// =============================================================================\n// Fetch all items for a type in ONE server-sorted call, via the injectable\n// list-URL builder. `useSelfFetch` (URL = cache key) replaces the hub's old\n// react-query usage: `enabled` ≙ `url === null`, `!res.ok`/network error ≙\n// `error → items null → group renders nothing`. Accepted deltas vs\n// react-query: no retry/backoff, no focus refetch, no cross-mount cache.\n// =============================================================================\n\nfunction useGroupItems(type: string, refs: ContentRef[], buildUrl: (type: string, ids: string[]) => string | null) {\n const ids = refs.map(r => r.id);\n const url = ids.length > 0 ? buildUrl(type, ids) : null;\n const { data, isLoading } = useSelfFetch<unknown>(url);\n const items = data != null ? extractItems(data) : null;\n return { items, isLoading };\n}\n\n// =============================================================================\n// Per-group renderer — one API call, server-sorted, then render cards via the\n// dispatcher with per-type skeletons + per-type layout from CONTENT_REF_GROUPS.\n// =============================================================================\n\n/** Map columns prop → tailwind grid class. Only consulted for grid-layout\n * groups; list-layout groups stack vertically. */\nfunction gridClassFor(columns: 2 | 3): string {\n return columns === 3\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6'\n : 'grid grid-cols-1 sm:grid-cols-2 gap-6';\n}\n\n/** Resolve the group config for a type, falling back to a grid layout with\n * the default card size for unregistered types so the section still renders\n * rather than silently dropping them. The `label` field on the fallback is\n * intentionally a placeholder — the section heading goes through\n * `getContentRefLabelOrTitleCase(type)` instead so cross-surface labels\n * stay consistent between this rail and the investor-email builder. */\nfunction resolveGroupConfig(type: string): ContentRefGroupConfig {\n return (\n CONTENT_REF_GROUPS[type] ?? {\n label: type,\n order: 999,\n layout: 'grid',\n gridSize: 'default',\n }\n );\n}\n\n/** Items per page within one type group. Groups larger than this paginate\n * with the standard Pagination control (NO nested scrolling — a bounded\n * scrollbox inside the page traps wheel events and hides the sections\n * below it). MUST stay at or above the largest suggestion fill\n * (RELATED_SAME_TYPE_COUNT in the hub's lib/constants/suggestions.ts) so\n * current rails never paginate — only genuinely big groups (author pages)\n * do. Exported through the subpath barrel for the hub's module-load\n * assertion of that relation (entity-suggestion-sections.tsx). */\nexport const GROUP_PAGE_SIZE = 12;\n\nfunction ContentGroup({\n type,\n refs,\n columns,\n buildUrl,\n resolveHref,\n LinkProvider,\n extras,\n adminCampaignCard,\n heading,\n}: {\n type: string;\n refs: ContentRef[];\n columns: 2 | 3;\n buildUrl: (type: string, ids: string[]) => string | null;\n resolveHref: (ref: ContentRef) => { href: string | null; targetPlatform: string | null };\n LinkProvider: CardLinkProvider;\n extras?: ChatCardDispatchExtras;\n adminCampaignCard?: AdminCampaignCardSlot;\n /** Group heading, rendered INSIDE the group so a group that resolves to\n * nothing (fetch miss / unsupported type / missing program config) drops\n * its heading too — no orphaned titles. */\n heading: React.ReactNode;\n}) {\n const { items, isLoading } = useGroupItems(type, refs, buildUrl);\n const config = resolveGroupConfig(type);\n const isListLayout = config.layout === 'list';\n const cardSize = config.gridSize;\n\n // Per-group pagination for big groups (author pages): GROUP_PAGE_SIZE items\n // per page with the standard Pagination control below the group. Client-side\n // slicing — useGroupItems already fetched every row in one batched call, so\n // page flips are instant. Hooks live above every early return (file\n // convention). Page is clamped so a shrinking refs array (suggestion\n // refetch) can never strand the view past the last page, and RESET when the\n // ref set actually changes (shrink→grow must not return to a stale page).\n const [page, setPage] = useState(1);\n const refsKey = refs.map(r => r.id).join('|');\n const prevRefsKeyRef = useRef(refsKey);\n useEffect(() => {\n if (prevRefsKeyRef.current !== refsKey) {\n prevRefsKeyRef.current = refsKey;\n setPage(1);\n }\n }, [refsKey]);\n const totalGroupPages = pageCount(refs.length, GROUP_PAGE_SIZE);\n const safePage = Math.min(page, totalGroupPages);\n const visibleGroupRefs =\n refs.length > GROUP_PAGE_SIZE ? refs.slice((safePage - 1) * GROUP_PAGE_SIZE, safePage * GROUP_PAGE_SIZE) : refs;\n const groupPagination =\n totalGroupPages > 1 ? (\n <Pagination currentPage={safePage} totalPages={totalGroupPages} onPageChange={setPage} />\n ) : null;\n\n // Skeleton gate: `isLoading && !items` — SSR HTML and the client's first\n // paint render identical skeletons (useSelfFetch starts isLoading=true on\n // both sides), and once items exist they are never replaced by skeletons.\n if (isLoading && !items) {\n const skeletons = visibleGroupRefs.map(r => (\n <div key={r.id}>{renderSkeletonForType(type, cardSize, adminCampaignCard)}</div>\n ));\n return (\n <div className=\"space-y-4\">\n {heading}\n {isListLayout ? (\n <div className=\"space-y-4\">{skeletons}</div>\n ) : (\n <div className={gridClassFor(columns)}>{skeletons}</div>\n )}\n </div>\n );\n }\n\n if (!items || items.length === 0) return null;\n\n // Index fetched rows by id, then render in REF order — refs carry the\n // intended sequence (suggestion mode: the engine's tier order, so\n // same-platform/tag-matched items lead; controlled mode: the curated\n // display_order). The list APIs return rows date-sorted, which would\n // otherwise scramble that ordering (same-platform items sinking below\n // newer cross-platform ones).\n // Shared extractor (NOT raw `.id`) — some API shapes key items differently\n // (e.g. external_id types); raw access would silently drop valid items.\n const rowKey = (it: unknown): string => {\n const extracted = extractItemId(type, it);\n if (extracted !== null) return extracted;\n // Fallback for shapes `extractItemId` doesn't recognise — same\n // `String(it?.id)` the untyped version produced.\n return String(typeof it === 'object' && it !== null ? (it as { id?: unknown }).id : undefined);\n };\n const itemById = new Map(items.map(it => [rowKey(it), it]));\n\n const cards = visibleGroupRefs\n .map(contentRef => {\n const itemId = String(contentRef.id);\n const item = itemById.get(itemId);\n if (!item) return null;\n // Re-compose the URL via the host's resolver (hub: buildContentURL so\n // dev gets localhost and prod the right platform domain; default: the\n // ref's stored url as the API composed it).\n const resolved = resolveHref(contentRef);\n const href = resolved.href ?? '';\n const targetPlatform = resolved.targetPlatform ?? contentRef.targetPlatform ?? null;\n return (\n <div key={itemId}>\n <LinkProvider href={href || null} targetPlatform={targetPlatform}>\n {linkProps => (\n <CardForType\n type={type}\n item={item}\n size={cardSize}\n href={href}\n targetPlatform={targetPlatform}\n linkProps={linkProps}\n extras={extras}\n adminCampaignCard={adminCampaignCard}\n />\n )}\n </LinkProvider>\n </div>\n );\n })\n .filter(Boolean);\n\n if (cards.length === 0) {\n // Current PAGE resolved zero cards (rows deleted between the ref fetch\n // and the group fetch, or a stricter list-API gate dropped them). When a\n // pager exists the user must keep the controls to navigate back —\n // dropping the whole group would strand them. A genuinely empty group\n // (no pager) still vanishes with its heading.\n if (groupPagination) {\n return (\n <div className=\"space-y-4\">\n {heading}\n {groupPagination}\n </div>\n );\n }\n return null;\n }\n\n return (\n <div className=\"space-y-4\">\n {heading}\n {isListLayout ? <div className=\"space-y-4\">{cards}</div> : <div className={gridClassFor(columns)}>{cards}</div>}\n {groupPagination}\n </div>\n );\n}\n\n// =============================================================================\n// Main component\n// =============================================================================\n\ninterface RelatedContentResponse {\n refs: ContentRefWithReason[];\n}\n\nexport interface RelatedContentSectionProps {\n /** CONTROLLED mode (the original behavior). When defined — even `[]` — no\n * suggestion fetch runs and exactly these refs render. */\n contentRefs?: ContentRef[];\n /** SUGGESTION mode (with `entityId`): self-fetch suggestions for this host\n * entity from `{apiBaseUrl}/api/related-content`. Ignored when\n * `contentRefs` is provided. */\n entityType?: string;\n entityId?: number | string;\n /** AUTHOR mode: self-fetch ALL published content authored by this profile\n * from `{apiBaseUrl}/api/related-content?authorId=…` (grouped per type,\n * endless within each group). Ignored when `contentRefs` is provided;\n * takes precedence over the entityType/entityId suggestion scope.\n * SSR-hydrate via `initialItems`, same as suggestion mode. */\n authorId?: string;\n /** Maps to the suggestion API's `count` param — the PER-TYPE fill target\n * for every candidate type EXCEPT the host's own. Absent → param not sent\n * (server default applies). */\n minResults?: number;\n /** Maps to the suggestion API's `sameTypeCount` param — the budget for the\n * candidate type MATCHING the host's own `entityType` (same-type boost:\n * a blog post's rail leads with more blog posts). Absent → param not\n * sent (host's type uses the server's `count`). */\n sameTypeMinResults?: number;\n /** SSR hydrate for suggestion mode — the server page ran the engine and\n * drills the refs here; the first client fetch is skipped (useSelfFetch\n * initialData contract). */\n initialItems?: ContentRefWithReason[];\n /** Section title (default: \"Related Content\") */\n title?: string;\n /**\n * Grid columns at desktop. 2 = denser cards / wider summary (original\n * investor-update layout); 3 = more cards per row for dashboards.\n * Only consulted for grid-layout groups. Default: 2.\n */\n columns?: 2 | 3;\n /**\n * ContentRef.type values to exclude. Honored in ALL modes — controlled\n * mode post-filters (original behavior); suggestion mode ALSO forwards the\n * list verbatim as the API's `excludeTypes=` param so excluded types never\n * consume engine fill slots (`minResults` stays honored). The subtraction\n * happens SERVER-side — this component never mirrors the hub's candidate\n * list.\n */\n excludeTypes?: string[];\n /**\n * SUGGESTION-mode allow-list (rail vocabulary): which content types\n * participate in this rail. Sent verbatim as the API's `types=` param —\n * the SERVER intersects it with its own allowed candidate set, and\n * platform policy gates (e.g. internal-only types) ALWAYS win: the client\n * cannot request its way past them. Absent → all server-side candidates.\n */\n includeTypes?: string[];\n /** Fetch-URL prefix for third-party embeds / reverse proxies\n * ('' = same-origin). Applied to BOTH the suggestion fetch and the\n * default per-group list fetches. */\n apiBaseUrl?: string;\n /** Host injection bundle — REUSES the chat dispatcher's\n * `ChatCardDispatchExtras` (programConfigs, buildOgPlaceholderUrl,\n * buildProductReleaseCardProps override). Program groups render nothing\n * when their config is absent. */\n extras?: ChatCardDispatchExtras;\n /** Hub injects its `buildContentURL` recomposition; default uses the\n * ref's stored `url`/`targetPlatform` as the API composed them. */\n resolveHref?: (ref: ContentRef) => { href: string | null; targetPlatform: string | null };\n /** Hub injects its registry-driven entity-list-api builder; default = the\n * lib's `buildListUrl(type, ids, apiBaseUrl)`. */\n buildListUrl?: (type: string, ids: string[]) => string | null;\n /** Hub injects a `useNavLink`-backed render-prop provider; default = pure\n * anchor via `decideNewTab`. MUST be a module-scope component. */\n LinkProvider?: CardLinkProvider;\n /** Renderer pair for the admin-only `marketing_campaign` type. Absent →\n * the type renders nothing. */\n adminCampaignCard?: AdminCampaignCardSlot;\n /** When true, render the section shell (title + an empty-state line) even\n * with ZERO refs, instead of returning null. Default false (the original\n * behavior — empty rail = no shell). Opt-in per host page (e.g. people-hub's\n * \"What I Shipped\", where the section should always be present). */\n showWhenEmpty?: boolean;\n /** Empty-state copy shown under the title when `showWhenEmpty` and no refs.\n * Default: \"No related content yet.\" */\n emptyStateText?: string;\n /** Custom empty-state node (e.g. a hub `<EmptyState/>`) rendered under the\n * title when `showWhenEmpty` and there are no refs — overrides\n * `emptyStateText`. Lets a host match its canonical empty state. */\n emptyState?: React.ReactNode;\n}\n\nexport function RelatedContentSection({\n contentRefs,\n entityType,\n entityId,\n authorId,\n minResults,\n sameTypeMinResults,\n includeTypes,\n initialItems,\n title = 'Related Content',\n columns = 2,\n excludeTypes,\n apiBaseUrl = '',\n extras,\n resolveHref = defaultResolveHref,\n buildListUrl,\n LinkProvider = DefaultLinkPropsProvider,\n adminCampaignCard,\n showWhenEmpty = false,\n emptyStateText = 'No related content yet.',\n emptyState,\n}: RelatedContentSectionProps) {\n // ── Hooks above EVERY early return (the original `if (!contentRefs.length)\n // return null` guard moved below them). ──\n\n // Suggestion-mode fetch URL — null in controlled mode (contentRefs defined,\n // even []) or when the entity scope is incomplete.\n // `includeTypes: []` is an explicit \"nothing participates\" — skip the fetch\n // entirely (an empty-string `types=` param would be dropped by the URL\n // builder and read server-side as \"all candidates\") AND ignore SSR refs.\n const suggestionsDisabled = includeTypes?.length === 0;\n // Shared type-filter params — one spelling for both fetch modes so a future\n // normalization (trim/dedupe) can't diverge between them.\n const typeFilterParams = {\n types: includeTypes !== undefined ? includeTypes.join(',') : undefined,\n excludeTypes: excludeTypes && excludeTypes.length > 0 ? excludeTypes.join(',') : undefined,\n };\n // AUTHOR mode beats suggestion mode: when `authorId` is set the rail lists\n // everything that profile authored (the server returns ALL, no count).\n const authorUrl =\n contentRefs === undefined && authorId && !suggestionsDisabled\n ? buildSuggestionUrl('/api/related-content', {\n apiBaseUrl,\n extraParams: { authorId, ...typeFilterParams },\n })\n : null;\n const suggestUrl =\n authorUrl ??\n (contentRefs === undefined &&\n entityType &&\n entityId !== undefined &&\n entityId !== null &&\n entityId !== '' &&\n !suggestionsDisabled\n ? buildSuggestionUrl('/api/related-content', {\n apiBaseUrl,\n entityType,\n entityId,\n count: minResults,\n extraParams: {\n sameTypeCount: sameTypeMinResults !== undefined ? String(sameTypeMinResults) : undefined,\n ...typeFilterParams,\n },\n })\n : null);\n // Memoize the initialData wrapper — useSelfFetch re-syncs on [initialData],\n // and a fresh per-render object would loop setState under re-rendering\n // parents (the latent FaqSection bug, fixed there in the same change).\n const initialData = useMemo<RelatedContentResponse | undefined>(\n // An explicitly disabled rail (includeTypes: []) must ignore SSR-hydrated\n // refs too — otherwise useSelfFetch(null, {initialData}) keeps serving\n // initialItems and the \"nothing participates\" contract silently breaks.\n () => (!suggestionsDisabled && initialItems ? { refs: initialItems } : undefined),\n [initialItems, suggestionsDisabled],\n );\n const { data, isLoading } = useSelfFetch<RelatedContentResponse>(suggestUrl, { initialData });\n\n // Default group fetcher: the lib's byte-parity-tested builder, prefixed for\n // embeds. Memoized so group-fetch URLs stay value-stable across renders.\n const effectiveBuildListUrl = useMemo(\n () => buildListUrl ?? ((type: string, ids: string[]) => libBuildListUrl(type, ids, apiBaseUrl)),\n [buildListUrl, apiBaseUrl],\n );\n\n const refs: ContentRef[] = contentRefs ?? data?.refs ?? [];\n\n // Per-consumer type gating — drops refs whose `type` is in the exclude\n // list. In suggestion mode the server already subtracted these (the param\n // is forwarded above); the client filter stays as an idempotent guard and\n // IS the mechanism in controlled mode (original behavior).\n const exclude = new Set(excludeTypes || []);\n const visibleRefs = exclude.size > 0 ? refs.filter(r => !exclude.has(r.type)) : refs;\n // Zero refs (still loading in suggestion mode, or genuinely empty). Default:\n // no empty shell. Opt-in (`showWhenEmpty`): render the title + an empty-state\n // line so the section is always present (e.g. people-hub \"What I Shipped\").\n if (!visibleRefs.length) {\n if (!showWhenEmpty) return null; // non-showWhenEmpty consumers stay blank (unchanged)\n // Client-fetch loading (author/suggestion mode, no SSR initialItems): render a\n // SKELETON grid — reserves height + matches the rest of the app's loading, so\n // there's no blank-then-pop jump (the prior `return null` collapsed the tab to\n // zero height during the fetch). SSR controlled mode has isLoading=false → it\n // skips straight to the empty state below. Skeleton type = the requested rail\n // type (author mode passes a single `includeTypes`).\n if (isLoading) {\n const skeletonType = includeTypes?.[0] ?? entityType ?? 'blog_post_existing';\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-ods-text-primary text-h2\">{title}</h2>\n <div className={gridClassFor(columns)}>\n {Array.from({ length: 3 }).map((_, i) => (\n <div key={i}>{renderSkeletonForType(skeletonType, 'default', adminCampaignCard)}</div>\n ))}\n </div>\n </div>\n );\n }\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-2xl font-bold text-ods-text-primary\">{title}</h2>\n {emptyState ?? <p className=\"text-ods-text-secondary\">{emptyStateText}</p>}\n </div>\n );\n }\n\n const grouped: Record<string, ContentRef[]> = {};\n for (const ref of visibleRefs) {\n if (!grouped[ref.type]) grouped[ref.type] = [];\n grouped[ref.type].push(ref);\n }\n\n // Registered types in CONTENT_REF_GROUPS order, then any unregistered\n // types appended (same shape the investor-email builder uses — both\n // consume `orderContentRefTypes` so cross-surface ordering matches).\n // SAME-TYPE FIRST: when a host entityType is known (suggestion / SSR\n // modes), its own content-type group is hoisted to the top — a blog\n // post's rail leads with blog posts. Rail group keys are compared via\n // the shared alias canonicalizer (blog_post_existing ↔ blog_post).\n let orderedTypes = orderContentRefTypes(Object.keys(grouped));\n if (entityType) {\n // Canonicalize BOTH sides — hosts pass registry vocab ('blog_post') but\n // rail-vocab aliases ('blog_post_existing') are also legal inputs; a raw\n // comparison would silently lose the same-type-first hoist for aliases.\n const canonicalEntityType = canonicalContentRefType(entityType);\n const sameType = orderedTypes.filter(t => canonicalContentRefType(t) === canonicalEntityType);\n if (sameType.length > 0) {\n orderedTypes = [...sameType, ...orderedTypes.filter(t => canonicalContentRefType(t) !== canonicalEntityType)];\n }\n }\n\n return (\n <div className=\"space-y-8\">\n <h2 className=\"text-ods-text-primary text-h2\">{title}</h2>\n {orderedTypes.map(type => (\n <ContentGroup\n key={type}\n type={type}\n refs={grouped[type]}\n columns={columns}\n buildUrl={effectiveBuildListUrl}\n resolveHref={resolveHref}\n LinkProvider={LinkProvider}\n extras={extras}\n adminCampaignCard={adminCampaignCard}\n heading={\n <h3 className=\"font-semibold text-ods-text-secondary text-h5\">{getContentRefLabelOrTitleCase(type)}</h3>\n }\n />\n ))}\n </div>\n );\n}\n","'use client';\n\n/**\n * Per-type card registry for the related-content rail — the SIZED sibling of\n * the chat side's `CHAT_CARD_REGISTRY` (`../chat/entity-cards/dispatch.tsx`),\n * and built to the same shape: ONE entry per content type, carrying that\n * type's skeleton and its card renderer, dispatched once from\n * `related-content-section.tsx`.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * The rail fetches one row per ref from that type's public list API. The row\n * arrives as `unknown` (`useSelfFetch<unknown>` → `extractItems`), and its\n * SHAPE is decided at runtime by the ref's `type`. The rail used to bridge\n * that with a single `item: any` threaded through a ten-branch switch, so\n * every `prop={item}` forward was an unchecked promise: a row missing a field\n * reached a card that declared it required, and the card rendered the miss\n * (an empty heading, a version pill reading \"vundefined\").\n *\n * Each entry here closes over its OWN row type instead. `cardEntry<Row>` pairs\n * a `decode` (runtime `unknown` → `Row`) with a `render` that receives `Row`,\n * and erases `Row` from the stored entry — so the registry is homogeneous\n * (`Record<string, RelatedCardRegistryEntry>`) while every branch stays\n * concretely typed. No `any`, no assertions into card row types.\n *\n * ON THE `*_ROW_DEFAULTS` CONSTANTS\n * ---------------------------------\n * Each card declares the FULL entity type as its row prop (`BlogPostSummary`,\n * `CaseStudy`, …) while reading only a handful of fields off it, and the list\n * APIs return projections rather than whole rows. A decoder therefore reads\n * the card's read set from the row and takes every other required field from\n * a `*_ROW_DEFAULTS` literal. Those placeholders are inert BY CONSTRUCTION —\n * the decoded row goes nowhere but its own card, and the card never reads\n * them. They are annotated with the entity type on purpose: a new required\n * field on `CaseStudy` surfaces as a compile error here instead of silently\n * reaching a card as `undefined`, which is the exact failure this file\n * replaces.\n *\n * ADDING A TYPE: one entry below (skeleton + decode + render), plus the chat\n * side's `CHAT_CARD_REGISTRY` and the list-URL builder — see the LOCKSTEP note\n * in `related-content-section.tsx`.\n */\n\nimport type React from 'react';\nimport type { BlogPostSummary } from '../../types/blog';\nimport type { CaseStudy } from '../../types/case-study';\nimport type { CustomerInterview } from '../../types/customer-interview';\nimport type { EntityAuthor } from '../../types/entity-author';\nimport type { MSP } from '../../types/stack';\nimport type { UserProfile } from '../../types/user';\nimport { AiPromptCard, AiPromptCardSkeleton, type AiPromptCardData } from '../chat/entity-cards/ai-prompt-card';\n// DEEP card imports — NOT the `../chat` barrel. See the import note in\n// `related-content-section.tsx`: the barrel statically reaches\n// @tanstack/react-query, deep paths keep this module's source graph free of it.\nimport { BlogCard, BlogCardSkeleton } from '../chat/entity-cards/blog-card';\nimport { CaseStudyCard, CaseStudyCardSkeleton } from '../chat/entity-cards/case-study-card';\nimport { CustomerInterviewCard, CustomerInterviewCardSkeleton } from '../chat/entity-cards/customer-interview-card';\n// Type-only — erased at build, no runtime dependency on the dispatch module.\nimport type { ChatCardDispatchExtras } from '../chat/entity-cards/dispatch';\nimport { HowIWorkCard, HowIWorkCardSkeleton, type HowIWorkCardData } from '../chat/entity-cards/how-i-work-card';\nimport { InvestorUpdateCard, InvestorUpdateCardSkeleton } from '../chat/entity-cards/investor-update-card';\nimport { OnboardingGuideCard, OnboardingGuideCardSkeleton } from '../chat/entity-cards/onboarding-guide-card';\nimport { ProductReleaseCard, ProductReleaseCardSkeleton } from '../chat/entity-cards/product-release-card';\nimport { buildProductReleaseCardProps } from '../chat/entity-cards/product-release-card-defaults';\nimport { ProgramCard, ProgramCardSkeleton } from '../chat/entity-cards/program-card';\nimport { RoadmapCard, RoadmapCardSkeleton } from '../chat/entity-cards/roadmap-card';\nimport {\n WhatIShippedCard,\n WhatIShippedCardSkeleton,\n type WhatIShippedCardData,\n} from '../chat/entity-cards/what-i-shipped-card';\nimport type { InvestorUpdate } from '../chat/types/entities/investor-update';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport type { BaseProgramItem, ProgramHost } from '../chat/types/entities/program-types';\nimport type { RoadmapItem } from '../chat/types/entities/roadmap-item';\n\nexport type CardSize = 'lg' | 'default' | 'sm';\n\n/** Anchor prop bundle the per-card link surface receives — same shape the\n * hub's `useNavLink` returns and the chat dispatcher's anchor builders\n * produce. `null` = non-anchor mode (no URL). */\nexport interface CardLinkAnchorProps {\n href: string;\n target?: '_blank';\n /** `noopener` alone for a new-tab link to one of our own platforms (keeps the\n * Referer for cross-domain attribution); `noreferrer` for third parties. */\n rel?: 'noopener' | 'noopener noreferrer';\n onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;\n}\n\n// =============================================================================\n// Row reading — the ONE place a runtime row is inspected.\n// =============================================================================\n\n/**\n * View an unvalidated row as a keyed bag. This claims nothing about the row's\n * SHAPE — every read below still checks the value's type — it only says \"this\n * is an object I may look keys up on\". A non-object row (null, a string, a\n * number) collapses to `{}`, so every decoder degrades to its defaults instead\n * of throwing.\n */\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n}\n\n/** A STRING field, or `undefined` when missing or wrong-typed. */\nexport function rowString(row: unknown, key: string): string | undefined {\n const value = asRecord(row)[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction str(row: Record<string, unknown>, key: string): string {\n const value = row[key];\n return typeof value === 'string' ? value : '';\n}\n\nfunction strOrNull(row: Record<string, unknown>, key: string): string | null {\n const value = row[key];\n return typeof value === 'string' ? value : null;\n}\n\nfunction numOrNull(row: Record<string, unknown>, key: string): number | null {\n const value = row[key];\n return typeof value === 'number' ? value : null;\n}\n\n/** `Array.isArray` narrows `unknown` to `any[]`; annotating the result keeps\n * the elements `unknown` so nothing downstream inherits `any`. Same guard the\n * shared `extractItems` normalizer uses. */\nfunction arrayOf(value: unknown): unknown[] {\n const list: unknown[] = Array.isArray(value) ? value : [];\n return list;\n}\n\nfunction stringList(value: unknown): string[] {\n return arrayOf(value).filter((entry): entry is string => typeof entry === 'string');\n}\n\n// =============================================================================\n// Shared nested-shape decoders (joins hydrated onto several row types).\n// =============================================================================\n\n/** `{ name, slug }` tag / category joins. Rows that carry only `name` (the\n * chat-side row shape) keep their label and get an empty slug — the cards\n * read `name` and nothing else. */\nfunction namedSlugList(value: unknown): { name: string; slug: string }[] {\n const out: { name: string; slug: string }[] = [];\n for (const entry of arrayOf(value)) {\n const record = asRecord(entry);\n const name = strOrNull(record, 'name');\n if (name === null) continue;\n out.push({ name, slug: str(record, 'slug') });\n }\n return out;\n}\n\nconst USER_PROFILE_DEFAULTS: UserProfile = {\n id: '',\n full_name: null,\n email: '',\n avatar_url: null,\n job_title: null,\n company: null,\n bio: null,\n employee_id: null,\n department_id: null,\n department: null,\n role: 'user',\n msp_id: null,\n created_at: '',\n updated_at: '',\n};\n\n/** The customer/author join on case studies + customer interviews. The cards\n * read `full_name`, `avatar_url` and `job_title`; absent join → `undefined`,\n * which is what those cards already fall back on. */\nfunction decodeUserProfile(value: unknown): UserProfile | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n ...USER_PROFILE_DEFAULTS,\n id: str(record, 'id'),\n full_name: strOrNull(record, 'full_name'),\n avatar_url: strOrNull(record, 'avatar_url'),\n job_title: strOrNull(record, 'job_title'),\n };\n}\n\nconst MSP_DEFAULTS: MSP = { id: '', name: '', seat_count: 0, created_at: '', updated_at: '' };\n\n/** The MSP join — cards read `name` and `icon_url`. */\nfunction decodeMsp(value: unknown): MSP | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n ...MSP_DEFAULTS,\n id: str(record, 'id'),\n name: str(record, 'name'),\n icon_url: rowString(record, 'icon_url'),\n };\n}\n\n/** The shared hydrated-author shape (`EntityAuthor`) — only `full_name` and\n * `avatar_url` are required, so this needs no defaults literal. */\nfunction decodeEntityAuthor(value: unknown): EntityAuthor | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = asRecord(value);\n return {\n id: rowString(record, 'id'),\n full_name: strOrNull(record, 'full_name'),\n avatar_url: strOrNull(record, 'avatar_url'),\n job_title: strOrNull(record, 'job_title'),\n about: strOrNull(record, 'about'),\n slug: rowString(record, 'slug'),\n };\n}\n\n/** Program hosts / speakers. `name` is the only required field; an entry\n * without one is dropped rather than rendered as a nameless avatar. */\nfunction decodeProgramHosts(value: unknown): ProgramHost[] | null {\n if (value == null) return null;\n const out: ProgramHost[] = [];\n for (const entry of arrayOf(value)) {\n const record = asRecord(entry);\n const name = strOrNull(record, 'name');\n if (name === null) continue;\n out.push({\n name,\n id: rowString(record, 'id'),\n email: rowString(record, 'email'),\n avatar_url: rowString(record, 'avatar_url'),\n bio: rowString(record, 'bio'),\n role: rowString(record, 'role'),\n });\n }\n return out;\n}\n\n// =============================================================================\n// Per-type row decoders. Each reads the fields ITS card renders; every other\n// required field comes from the type-annotated defaults literal above it.\n// =============================================================================\n\nfunction decodeBlogRow(row: unknown): BlogPostSummary {\n const record = asRecord(row);\n return {\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n slug: str(record, 'slug'),\n summary: strOrNull(record, 'summary'),\n featured_image: strOrNull(record, 'featured_image'),\n published_at: strOrNull(record, 'published_at'),\n author_name: strOrNull(record, 'author_name'),\n author_avatar: strOrNull(record, 'author_avatar'),\n categories: namedSlugList(record.categories),\n tags: namedSlugList(record.tags),\n view_count: numOrNull(record, 'view_count') ?? undefined,\n };\n}\n\nconst CASE_STUDY_ROW_DEFAULTS: CaseStudy = {\n id: 0,\n title: '',\n slug: '',\n summary: null,\n featured_image: null,\n user_id: null,\n challenge: null,\n solution: null,\n results: null,\n testimonial_video_url: null,\n main_video_url: null,\n video_source_type: null,\n video_source: null,\n video_bites: [],\n customer_interview_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n status: 'published',\n published_at: null,\n author_id: null,\n created_at: '',\n updated_at: '',\n view_count: 0,\n};\n\nfunction decodeCaseStudyRow(row: unknown): CaseStudy {\n const record = asRecord(row);\n return {\n ...CASE_STUDY_ROW_DEFAULTS,\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n summary: strOrNull(record, 'summary'),\n featured_image: strOrNull(record, 'featured_image'),\n msp: decodeMsp(record.msp),\n user: decodeUserProfile(record.user),\n };\n}\n\nconst CUSTOMER_INTERVIEW_ROW_DEFAULTS: CustomerInterview = {\n id: 0,\n title: '',\n slug: '',\n video_summary: null,\n transcript: null,\n user_id: null,\n main_video_url: null,\n teasers: [],\n quotes: [],\n case_study_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n featured_image: null,\n status: 'completed',\n completed_at: null,\n author_id: null,\n custom_instructions: null,\n created_at: '',\n updated_at: '',\n view_count: 0,\n};\n\nfunction decodeCustomerInterviewRow(row: unknown): CustomerInterview {\n const record = asRecord(row);\n return {\n ...CUSTOMER_INTERVIEW_ROW_DEFAULTS,\n id: numOrNull(record, 'id') ?? 0,\n title: str(record, 'title'),\n video_summary: strOrNull(record, 'video_summary'),\n main_video_url: strOrNull(record, 'main_video_url'),\n featured_image: strOrNull(record, 'featured_image'),\n msp: decodeMsp(record.msp),\n user: decodeUserProfile(record.user),\n };\n}\n\nconst INVESTOR_UPDATE_ROW_DEFAULTS: InvestorUpdate = {\n id: '',\n title: '',\n slug: '',\n update_number: null,\n period_start: null,\n period_end: null,\n platform_id: null,\n content: null,\n video_summary: null,\n transcript: null,\n main_video_url: null,\n highlight_video_url: null,\n highlight_video_thumbnail: null,\n main_video_thumbnail: null,\n video_bites: [],\n featured_image: null,\n strategic_update: null,\n financials: {},\n metrics_snapshot: {},\n content_refs: [],\n highlights: null,\n section_visibility: {},\n status: 'published',\n published_at: null,\n author_id: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n og_image_url: null,\n hubspot_email_id: null,\n custom_instructions: null,\n created_at: '',\n updated_at: '',\n created_by: null,\n updated_by: null,\n};\n\nfunction decodeInvestorUpdateRow(row: unknown): InvestorUpdate {\n const record = asRecord(row);\n return {\n ...INVESTOR_UPDATE_ROW_DEFAULTS,\n id: str(record, 'id'),\n title: str(record, 'title'),\n update_number: numOrNull(record, 'update_number'),\n period_start: strOrNull(record, 'period_start'),\n period_end: strOrNull(record, 'period_end'),\n content: strOrNull(record, 'content'),\n strategic_update: strOrNull(record, 'strategic_update'),\n featured_image: strOrNull(record, 'featured_image'),\n };\n}\n\nconst ONBOARDING_GUIDE_ROW_DEFAULTS: OnboardingGuide = {\n id: '',\n title: '',\n slug: '',\n section: '',\n step_order: 0,\n section_order: 0,\n content: null,\n video_summary: null,\n transcript: null,\n transcript_words_data: null,\n srt_content: null,\n ai_transcript_formatted: null,\n main_video_url: null,\n main_video_thumbnail: null,\n youtube_url: null,\n highlight_video_url: null,\n highlight_video_thumbnail: null,\n highlight_video_duration_ms: null,\n highlight_video_source: null,\n video_bites: [],\n featured_image: null,\n og_image_url: null,\n seo_title: null,\n seo_description: null,\n seo_keywords: null,\n status: 'published',\n published_at: null,\n author_id: null,\n custom_instructions: null,\n config: null,\n ai_effort_score: null,\n created_at: '',\n updated_at: '',\n created_by: null,\n updated_by: null,\n};\n\nfunction decodeOnboardingGuideRow(row: unknown): OnboardingGuide {\n const record = asRecord(row);\n return {\n ...ONBOARDING_GUIDE_ROW_DEFAULTS,\n id: str(record, 'id'),\n title: str(record, 'title'),\n section: str(record, 'section'),\n step_order: numOrNull(record, 'step_order') ?? 0,\n content: strOrNull(record, 'content'),\n video_summary: strOrNull(record, 'video_summary'),\n featured_image: strOrNull(record, 'featured_image'),\n og_image_url: strOrNull(record, 'og_image_url'),\n main_video_thumbnail: strOrNull(record, 'main_video_thumbnail'),\n highlight_video_thumbnail: strOrNull(record, 'highlight_video_thumbnail'),\n highlight_video_duration_ms: numOrNull(record, 'highlight_video_duration_ms'),\n author: decodeEntityAuthor(record.author),\n };\n}\n\n/**\n * `RoadmapItem` is the one row type the card reads WHOLE, so this decoder\n * carries no defaults literal — every field is read off the row.\n *\n * `upvotes` / `downvotes` / `quarter` are required `number`/`string` props the\n * card renders directly. Read off an untyped row they were `any`, so a row\n * without them rendered the miss (a blank vote count). They now fall back to\n * `0` / `''`, matching how the card renders a genuinely zero-vote item.\n */\nfunction decodeRoadmapRow(row: unknown): RoadmapItem {\n const record = asRecord(row);\n const assignees: NonNullable<RoadmapItem['assignees']> = [];\n for (const entry of arrayOf(record.assignees)) {\n const assignee = asRecord(entry);\n assignees.push({\n id: numOrNull(assignee, 'id') ?? 0,\n name: strOrNull(assignee, 'name'),\n avatarUrl: strOrNull(assignee, 'avatarUrl'),\n });\n }\n return {\n id: str(record, 'id'),\n title: str(record, 'title'),\n description: str(record, 'description'),\n status: str(record, 'status'),\n statusColor: str(record, 'statusColor'),\n icon: strOrNull(record, 'icon'),\n figmaUrl: strOrNull(record, 'figmaUrl'),\n screenshots: stringList(record.screenshots),\n targetVersion: strOrNull(record, 'targetVersion'),\n upvotes: numOrNull(record, 'upvotes') ?? 0,\n downvotes: numOrNull(record, 'downvotes') ?? 0,\n quarter: str(record, 'quarter'),\n clickupUrl: str(record, 'clickupUrl'),\n customItemId: numOrNull(record, 'customItemId'),\n assignees,\n };\n}\n\n/**\n * Program rows (podcast / webinar / event) keep their EXTRA columns.\n *\n * `<ProgramCard>` is generic over the item, and the config the rail threads in\n * is an `AnyProgramConfig` — `ProgramConfig<BaseProgramItem &\n * Record<string, unknown>>` — precisely so per-program columns ride along. The\n * card branches on `'duration_seconds' in item` / `'location_name' in item` /\n * `'start_at' in item`, so dropping the unrecognised columns would silently\n * strip a podcast's runtime and an event's venue. The raw row is spread first\n * and the base fields are decoded over it.\n */\nfunction decodeProgramRow(row: unknown): BaseProgramItem & Record<string, unknown> {\n const record = asRecord(row);\n return {\n ...record,\n id: str(record, 'id'),\n title: str(record, 'title'),\n description: strOrNull(record, 'description'),\n cover_url: strOrNull(record, 'cover_url'),\n date: str(record, 'date'),\n external_url: strOrNull(record, 'external_url'),\n hosts: decodeProgramHosts(record.hosts),\n };\n}\n\n/** The two employee-entry cards already declare MINIMAL row shapes\n * (`EmployeeEntryCardData` + one date column each), so their decoders read\n * exactly the card's fields with nothing left over. */\nfunction decodeEmployeeEntryRow(row: unknown): WhatIShippedCardData & HowIWorkCardData & AiPromptCardData {\n const record = asRecord(row);\n const department = asRecord(record.department);\n const stepCount = record.step_count;\n return {\n title: strOrNull(record, 'title'),\n summary: strOrNull(record, 'summary'),\n status: strOrNull(record, 'status'),\n featured_image: strOrNull(record, 'featured_image'),\n main_video_thumbnail: strOrNull(record, 'main_video_thumbnail'),\n author: decodeEntityAuthor(record.author),\n entry_month: strOrNull(record, 'entry_month'),\n session_date: strOrNull(record, 'session_date'),\n discipline: strOrNull(record, 'discipline'),\n published_at: strOrNull(record, 'published_at'),\n department: record.department\n ? { name: strOrNull(department, 'name'), color: strOrNull(department, 'color') }\n : null,\n step_count: typeof stepCount === 'number' ? stepCount : null,\n };\n}\n\n// =============================================================================\n// Registry\n// =============================================================================\n\n/** Everything a card branch needs beyond its own row — computed once per card\n * by the rail's dispatcher and shared by every entry. */\nexport interface RelatedCardContext {\n /** Card density for this group, from `CONTENT_REF_GROUPS`. */\n size: CardSize;\n /** `size` with `'lg'` collapsed to `'default'` — most card variants accept\n * only that pair. */\n legacySize: 'default' | 'sm';\n href: string;\n targetPlatform: string | null;\n /** `null` = the host surfaced no URL; the card stays in non-anchor mode. */\n linkProps: CardLinkAnchorProps | null;\n /** `{ target, rel }` for the cards that take them as separate props. */\n anchorAttrs: Pick<CardLinkAnchorProps, 'target' | 'rel'>;\n /** Branded OG fallback for cards whose row has no featured image. */\n placeholderUrl?: string;\n extras?: ChatCardDispatchExtras;\n}\n\n/** One registered content type. `Row` is erased here — `cardEntry` below is\n * what keeps each branch's row type concrete. */\nexport interface RelatedCardRegistryEntry {\n /** Sized to match the resolved card exactly (zero layout shift). */\n skeleton: (size: CardSize) => React.ReactNode;\n render: (row: unknown, ctx: RelatedCardContext) => React.ReactNode;\n}\n\n/**\n * Register one type with its OWN row type.\n *\n * `Row` is bound by the definition object and never escapes: `render` is\n * handed a decoded `Row`, and the stored entry exposes only\n * `(row: unknown) => ReactNode`. That is what lets a single registry hold ten\n * heterogeneous row shapes without a shared supertype — the job `item: any`\n * used to do, minus the promise it could not keep.\n */\nfunction cardEntry<Row>(definition: {\n skeleton: (size: CardSize) => React.ReactNode;\n decode: (row: unknown) => Row;\n render: (row: Row, ctx: RelatedCardContext) => React.ReactNode;\n}): RelatedCardRegistryEntry {\n return {\n skeleton: definition.skeleton,\n render: (row, ctx) => definition.render(definition.decode(row), ctx),\n };\n}\n\n/** Podcast / webinar / event share one card + one skeleton and differ only by\n * which host-supplied `ProgramConfig` they render with. A group whose config\n * the host didn't supply renders nothing (the skeleton still reserves its\n * height, so a rail that never resolves collapses instead of flickering). */\nfunction programEntry(configKey: 'podcast' | 'webinar' | 'event'): RelatedCardRegistryEntry {\n return cardEntry({\n skeleton: size => <ProgramCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeProgramRow,\n render: (item, ctx) => {\n const config = ctx.extras?.programConfigs?.[configKey];\n if (!config) return null;\n return (\n <ProgramCard\n config={config}\n item={item}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n );\n },\n });\n}\n\n/** The three ClickUp-backed types render the same card, distinguished by\n * `cardType` (drives the compact icon slot). */\nfunction roadmapEntry(cardType: 'roadmap_item' | 'delivery_item' | 'internal_task'): RelatedCardRegistryEntry {\n return cardEntry({\n skeleton: size => <RoadmapCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeRoadmapRow,\n render: (item, ctx) => (\n <RoadmapCard\n item={item}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n userVote={null}\n onVote={() => {}}\n size={ctx.legacySize}\n cardType={cardType}\n {...ctx.anchorAttrs}\n />\n ),\n });\n}\n\nexport const RELATED_CARD_REGISTRY: Record<string, RelatedCardRegistryEntry> = {\n blog_post_existing: cardEntry({\n skeleton: size => <BlogCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeBlogRow,\n render: (post, ctx) => (\n <BlogCard\n post={post}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n case_study: cardEntry({\n skeleton: size => <CaseStudyCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeCaseStudyRow,\n render: (study, ctx) => (\n <CaseStudyCard\n study={study}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n customer_interview: cardEntry({\n skeleton: size => <CustomerInterviewCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeCustomerInterviewRow,\n render: (interview, ctx) => (\n <CustomerInterviewCard\n interview={interview}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /**\n * Product releases derive their whole prop bundle through a HOST seam\n * (`extras.buildProductReleaseCardProps`, defaulting to the lib builder)\n * which already takes `unknown` — so this entry's row type is the bare\n * record, and the three props the rail sets itself are read off it.\n */\n product_release: cardEntry({\n skeleton: size => <ProductReleaseCardSkeleton size={size === 'sm' ? 'sm' : 'lg'} />,\n decode: asRecord,\n render: (release, ctx) => {\n const buildReleaseProps = ctx.extras?.buildProductReleaseCardProps ?? buildProductReleaseCardProps;\n const releaseProps = buildReleaseProps(release);\n return (\n <ProductReleaseCard\n size={ctx.size === 'sm' ? 'sm' : 'lg'}\n // `title` and `version` are REQUIRED `string` props. Read off an\n // untyped row they were `any`, so a release row missing either one\n // rendered an empty heading and a version pill reading \"vundefined\".\n // `summary` is `string | null | undefined` and the card renders\n // `summary ?? ''`, so a dropped one is indistinguishable from null.\n title={str(release, 'title')}\n summary={rowString(release, 'summary')}\n version={str(release, 'version')}\n {...releaseProps}\n // The card wraps in `<a {...anchorProps}>` ONLY when\n // `anchorProps.href` is set — `undefined` (not an empty object)\n // keeps it in non-anchor mode instead of rendering a dead <a>.\n anchorProps={ctx.linkProps ?? undefined}\n />\n );\n },\n }),\n\n podcast: programEntry('podcast'),\n webinar: programEntry('webinar'),\n event: programEntry('event'),\n\n investor_update: cardEntry({\n skeleton: size => <InvestorUpdateCardSkeleton size={size === 'sm' ? 'sm' : 'default'} />,\n decode: decodeInvestorUpdateRow,\n render: (update, ctx) => (\n <InvestorUpdateCard\n update={update}\n size={ctx.legacySize}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /** The rich CATALOG variant (hero + author grid, clamped description) — the\n * step-numbered 'default' variant is for the guide detail page's \"More in\n * section\" rail, not this full-width row. */\n onboarding_guide: cardEntry({\n skeleton: size => <OnboardingGuideCardSkeleton size={size === 'sm' ? 'sm' : 'catalog'} />,\n decode: decodeOnboardingGuideRow,\n render: (guide, ctx) => (\n <OnboardingGuideCard\n guide={guide}\n size={ctx.size === 'sm' ? 'sm' : 'catalog'}\n href={ctx.href}\n targetPlatform={ctx.targetPlatform}\n placeholderUrl={ctx.placeholderUrl}\n {...ctx.anchorAttrs}\n />\n ),\n }),\n\n /** THE single What I Shipped card — same lib component the people-hub\n * dashboard renders. `anchorProps` makes the whole card a click-through\n * (the rail is read-only — no owner actions). Only pass it with a REAL\n * href: an object with `href: undefined` is still truthy and would wrap the\n * card in a dead <a>. */\n what_i_shipped: cardEntry({\n skeleton: () => <WhatIShippedCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <WhatIShippedCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n /** Same shared employee-entry shape + anchor contract as What I Shipped. */\n how_i_work: cardEntry({\n skeleton: () => <HowIWorkCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <HowIWorkCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n /** Same shared employee-entry shape + anchor contract as What I Shipped. */\n ai_prompt: cardEntry({\n skeleton: () => <AiPromptCardSkeleton />,\n decode: decodeEmployeeEntryRow,\n render: (entry, ctx) => (\n <AiPromptCard\n entry={entry}\n placeholderUrl={ctx.placeholderUrl}\n anchorProps={ctx.linkProps ?? (ctx.href ? { href: ctx.href, ...ctx.anchorAttrs } : undefined)}\n />\n ),\n }),\n\n roadmap_item: roadmapEntry('roadmap_item'),\n delivery_item: roadmapEntry('delivery_item'),\n internal_task: roadmapEntry('internal_task'),\n};\n"]}