@fullstackdatasolutions/articles 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +12 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +23 -25
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +16 -0
- package/dist/nextjs.d.ts +16 -0
- package/dist/nextjs.js +23 -25
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +23 -8
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +16 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +23 -8
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/server-articles.test.ts +70 -0
- package/src/articlesConfig.ts +16 -0
- package/src/server-articles.ts +32 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/ArticleCategoryGrid.tsx","../src/articlesConfig.ts","../src/authorUtils.ts","../src/Breadcrumb.tsx","../src/ArticleSchemas.tsx","../src/ArticleSearchBar.tsx","../src/ArticlesHero.tsx","../src/FeaturedArticle.tsx","../src/LatestArticlesSection.tsx","../src/LatestArticles.tsx","../src/ArticleCard.tsx","../src/AuthorCard.tsx","../src/events.ts","../src/PaginationNav.tsx","../src/pagination.ts","../src/useArticles.ts","../src/ArticlesPage.tsx","../src/ArticleDetailHero.tsx","../src/CategoryArticlesPage.tsx","../src/SeriesArticlesPage.tsx","../src/AuthorArticlesPage.tsx","../src/AuthorDetailHero.tsx","../src/eventTracking.tsx","../src/ArticleSocialShare.tsx","../src/ArticleNavigation.tsx","../src/RelatedArticlesSection.tsx","../src/ArticleBackLink.tsx","../src/ArticleTOC.tsx","../src/ArticleAnswer.tsx","../src/ScrollToTop.tsx","../src/CommentsSection.tsx","../src/CommentForm.tsx","../src/CommentItem.tsx","../src/CommentThread.tsx"],"sourcesContent":["'use client'\n\n// Components\nexport { ArticlesPage } from './ArticlesPage'\nexport { ArticleCard } from './ArticleCard'\nexport { ArticleCategoryGrid } from './ArticleCategoryGrid'\nexport { ArticlesHero } from './ArticlesHero'\nexport { ArticleDetailHero } from './ArticleDetailHero'\nexport { ArticleSearchBar } from './ArticleSearchBar'\nexport { FeaturedArticle } from './FeaturedArticle'\nexport { LatestArticles } from './LatestArticles'\nexport { LatestArticlesSection } from './LatestArticlesSection'\nexport { CategoryArticlesPage } from './CategoryArticlesPage'\nexport { SeriesArticlesPage } from './SeriesArticlesPage'\nexport { AuthorArticlesPage } from './AuthorArticlesPage'\nexport { AuthorCard, AuthorSocialLinks } from './AuthorCard'\nexport { AuthorDetailHero } from './AuthorDetailHero'\nexport { Breadcrumb, BreadcrumbSchema } from './Breadcrumb'\nexport {\n ArticleSEO,\n CollectionPageSchema,\n FAQPageSchema,\n OrganizationSchema,\n WebSiteSchema,\n} from './ArticleSchemas'\n\nexport { ArticleSocialShare } from './ArticleSocialShare'\nexport { ArticleNavigation } from './ArticleNavigation'\nexport { RelatedArticlesSection } from './RelatedArticlesSection'\nexport { ArticleBackLink } from './ArticleBackLink'\nexport { ArticleTOC } from './ArticleTOC'\nexport { ArticleAnswer } from './ArticleAnswer'\nexport { ScrollToTop } from './ScrollToTop'\nexport { PaginationNav } from './PaginationNav'\nexport { ArticleViewTracker, CtaViewTracker } from './eventTracking'\n\n// Comments\nexport { CommentsSection } from './CommentsSection'\nexport { CommentThread } from './CommentThread'\nexport { CommentItem } from './CommentItem'\nexport { CommentForm } from './CommentForm'\n\n// Hooks\nexport { useArticles } from './useArticles'\nexport type { UseArticlesReturn } from './useArticles'\n\n// Config + types\nexport type {\n ArticlesConfig,\n ArticlesTheme,\n ArticlesSection,\n CategoryDescription,\n CommentsConfig,\n LinkTargetStrategy,\n ListingPagination,\n MdxComponents,\n BreadcrumbsConfig,\n BreadcrumbLabels,\n ArticleBreadcrumbToken,\n CategoryBreadcrumbToken,\n AuthorBreadcrumbToken,\n ArticleBreadcrumbEntry,\n CategoryBreadcrumbEntry,\n AuthorBreadcrumbEntry,\n CustomBreadcrumbItem,\n OrganizationConfig,\n AiCrawlEvent,\n} from './articlesConfig'\nexport {\n DEFAULT_LAYOUT,\n DEFAULT_PAGE_SIZE,\n DEFAULT_CATEGORIES_PAGE_SIZE,\n DEFAULT_TITLE_TEMPLATE,\n breadcrumbsAreEnabled,\n formatPageTitle,\n getBreadcrumbsConfig,\n getOrganizationId,\n getPersonId,\n getWebSiteId,\n} from './articlesConfig'\nexport type {\n Article,\n AuthorProfile,\n AuthorSocial,\n BreadcrumbItem,\n CategoryInfo,\n CitationReference,\n EntityReference,\n FaqItem,\n HowToStep,\n PathDefinition,\n ProofItem,\n RichText,\n RichTextSection,\n TocItem,\n} from './articleTypes'\nexport type { ArticleComment, ArticleCommentWithReplies } from './commentTypes'\nexport type { CollectionPageItem } from './ArticleSchemas'\nexport type { ListingPaginationContext } from './pagination'\nexport type { AuthorPageSection } from './AuthorArticlesPage'\nexport type {\n ArticleEvent,\n ArticleEventHandler,\n ArticleEventName,\n ArticleViewedEvent,\n MeaningfulReadEvent,\n AuthorClickedEvent,\n CtaViewedEvent,\n CtaClickedEvent,\n SharedEvent,\n RelatedArticleClickedEvent,\n PathStepAdvancedEvent,\n} from './events'\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { useState, useEffect } from 'react'\nimport type { CategoryInfo } from './articleTypes'\n\ntype ArticleCategoryGridProps = Readonly<{\n categories: CategoryInfo[]\n pageSize?: number\n}>\n\nexport function ArticleCategoryGrid({ categories, pageSize = 8 }: ArticleCategoryGridProps) {\n const [visibleCount, setVisibleCount] = useState(pageSize)\n\n useEffect(() => {\n setVisibleCount(pageSize)\n }, [categories, pageSize])\n\n if (categories.length === 0) return null\n\n const visible = categories.slice(0, visibleCount)\n const hasMore = visibleCount < categories.length\n\n return (\n <section className=\"py-16 bg-background\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">Browse by Category</h2>\n <p className=\"text-lg text-muted-foreground\">\n Explore our articles by topic to find the insights most relevant to your interests.\n </p>\n </div>\n\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {visible.map((cat) => (\n <Link key={cat.slug} href={`/articles/category/${cat.slug}`}>\n <div className=\"group overflow-hidden rounded-xl border border-border shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer bg-card\">\n <div className=\"overflow-hidden\" style={{ position: 'relative', height: '13rem' }}>\n <Image\n src={cat.featuredImage}\n alt={cat.name}\n fill\n sizes=\"(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-300\"\n />\n </div>\n <div className=\"p-4 text-center\">\n <h3 className=\"font-semibold text-foreground text-base\">{cat.name}</h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {cat.count} {cat.count === 1 ? 'article' : 'articles'}\n </p>\n </div>\n </div>\n </Link>\n ))}\n </div>\n\n {hasMore && (\n <div className=\"mt-10 text-center\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((c) => c + pageSize)}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Load more categories\n </button>\n </div>\n )}\n </div>\n </section>\n )\n}\n","import type { ComponentType } from 'react'\nimport type { AuthorProfile, EntityReference, PathDefinition } from './articleTypes'\nimport type { ArticleEventHandler } from './events'\n\n/** Keys for each renderable section of the articles listing page. */\nexport type ArticlesSection =\n | 'hero'\n | 'search'\n | 'featured'\n | 'latest'\n | 'categories'\n | 'newsletter'\n\n/**\n * CSS custom-property overrides applied to the library's wrapper div.\n * All fields are optional; omitted fields fall back to the consuming app's Tailwind theme.\n */\nexport interface ArticlesTheme {\n /** Font family for the articles section. Example: `\"'Inter', sans-serif\"` */\n fontFamily?: string\n /**\n * Font family for headings only (article title, card titles, section\n * headings) - falls back to `fontFamily` when omitted. Lets a site use a\n * distinct display face for headings (e.g. a serif) while keeping a\n * separate body font, without hardcoding either into the package.\n * Example: `\"'Cinzel', serif\"`\n */\n headerFontFamily?: string\n /** Color for article card titles and section headings. Example: `'#111827'` */\n headerColor?: string\n /** Color for body and excerpt text. Example: `'#6b7280'` */\n textColor?: string\n /** Background color for sections and cards. Example: `'#ffffff'` */\n backgroundColor?: string\n /** Color for \"Read More\" links and inline links. Example: `'#4f46e5'` */\n linkColor?: string\n /** Hover color for links. Example: `'#4338ca'` */\n linkHoverColor?: string\n /** Text decoration for links. Values: `'underline'` | `'none'` */\n linkTextDecoration?: string\n /** Font weight for headings. Example: `700` */\n headerFontWeight?: string | number\n /** Font size for article card titles. Example: `'1.25rem'` */\n headerFontSize?: string\n /** Font size for body text. Example: `'1rem'` */\n bodyFontSize?: string\n /** Line height for body text. Example: `'1.75'` */\n lineHeight?: string\n /** Border radius for cards. Example: `'0.5rem'` */\n borderRadius?: string\n}\n\n/** Per-category text used in meta descriptions and on the category hero page. */\nexport interface CategoryDescription {\n /** Short description — used in meta description and category grid card. */\n short: string\n /** Long description — rendered as a hero paragraph on the category page. */\n long?: string\n}\n\n/** Controls the comments feature. Attach to `ArticlesConfig.comments`. */\nexport interface CommentsConfig {\n /** Set to `true` to enable comments globally across all articles. */\n enabled: boolean\n /** Maximum reply nesting depth. Default: `1` (replies to top-level only). */\n maxDepth?: number\n /**\n * Per-article overrides keyed by slug.\n * Example: `{ 'sensitive-article': false }` disables comments on that article only.\n */\n perArticleOverride?: Record<string, boolean>\n}\n\n/** Text content for the hero section at the top of the articles listing page. */\nexport interface HeroConfig {\n /** Main heading displayed in the hero. Default: `'Vox Populus Insights'` */\n title?: string\n /** Subheading paragraph displayed beneath the title. Default: the built-in description. */\n description?: string\n}\n\n/** Controls how article body links set target/rel attributes. */\nexport type LinkTargetStrategy = 'external-new-tab' | 'all-new-tab' | 'same-tab'\n\n/**\n * Controls how listing pages (the articles index, category pages, author\n * pages) surface articles beyond the first `pageSize`.\n * - `'load-more'` (default): client-only \"Load more\" button, no URL change.\n * Byte-for-byte identical to pre-27D behavior.\n * - `'pages'`: real, directly-navigable paginated routes (`/articles/page/2`,\n * `/articles/category/[category]/page/2`, `/articles/authors/[author]/page/2`)\n * with SSR content, prev/next links, and per-page canonical metadata. The\n * route *files* live in the consuming app - see the pagination primitives\n * exported from `./server` and the `PaginationNav` component.\n */\nexport type ListingPagination = 'load-more' | 'pages'\n\n/** React components that article MDX bodies can reference by JSX tag name. */\nexport type MdxComponents = Record<string, ComponentType<never>>\n\nexport type ArticleBreadcrumbToken =\n | 'home'\n | 'articles'\n | 'primaryCategory'\n | 'folderPath'\n | 'articleTitle'\nexport type CategoryBreadcrumbToken = 'home' | 'articles' | 'category'\nexport type AuthorBreadcrumbToken = 'home' | 'articles' | 'authors' | 'authorName'\n\nexport interface CustomBreadcrumbItem {\n /** Label displayed in the breadcrumb trail. */\n name: string\n /** Custom URL. Relative paths are resolved against `siteUrl` by server builders. */\n url: string\n}\n\nexport type ArticleBreadcrumbEntry = ArticleBreadcrumbToken | CustomBreadcrumbItem\nexport type CategoryBreadcrumbEntry = CategoryBreadcrumbToken | CustomBreadcrumbItem\nexport type AuthorBreadcrumbEntry = AuthorBreadcrumbToken | CustomBreadcrumbItem\n\nexport interface BreadcrumbLabels {\n home?: string\n articles?: string\n authors?: string\n}\n\nexport interface BreadcrumbsConfig {\n /** Set to false to hide visible breadcrumbs and breadcrumb JSON-LD generated by the helper builders. */\n show?: boolean\n /** Separator used by the visible Breadcrumb component. Default: '>'. */\n separator?: string\n /** Set to false to render visible breadcrumbs without JSON-LD. Default: true. */\n showSchema?: boolean\n /** Article breadcrumb trail. Example: ['primaryCategory', { name: 'Guides', url: '/guides' }, 'articleTitle']. */\n article?: ArticleBreadcrumbEntry[]\n /** Category breadcrumb trail. Default: ['home', 'articles', 'category']. */\n category?: CategoryBreadcrumbEntry[]\n /** Author breadcrumb trail. Default: ['home', 'articles', 'authors', 'authorName']. */\n author?: AuthorBreadcrumbEntry[]\n /** Optional label overrides for built-in breadcrumb items. */\n labels?: BreadcrumbLabels\n}\n\n/** Payload passed to `ArticlesConfig.onAiCrawl`. */\nexport interface AiCrawlEvent {\n /** Article slug whose markdown twin was fetched. */\n slug: string\n /** Matched crawler name (e.g. `'GPTBot'`), or `'unknown'` when the agent is not recognized. */\n crawler: string\n /** Raw `User-Agent` header, or an empty string when absent. */\n userAgent: string\n}\n\n/**\n * The publishing entity behind the site. Drives `OrganizationSchema`,\n * `WebSiteSchema`, and the `publisher` reference on every article - so all\n * three point at one `@id` instead of repeating an inline, unlinked\n * `Organization` stub per page.\n */\nexport interface OrganizationConfig {\n /** schema.org type. Use `'Person'` for a personal brand. Default: `'Organization'`. */\n type?: 'Organization' | 'Person'\n /** Entity name. Falls back to `siteName`. */\n name?: string\n /** Entity homepage. Falls back to `siteUrl`. */\n url?: string\n /** Logo URL. Site-relative paths are resolved against `siteUrl`. */\n logo?: string\n /** Entity description. Falls back to `ArticlesConfig.description`. */\n description?: string\n /** Profile URLs that identify the same entity elsewhere (social, Crunchbase, Wikidata). */\n sameAs?: string[]\n /**\n * The umbrella entity this site belongs to, for a network of sites run by\n * one publisher. Emitted as `parentOrganization` - the link that lets\n * authority earned by the network attach to each site in it, instead of\n * each site standing alone.\n */\n parentOrganization?: {\n name: string\n url: string\n }\n /**\n * Search URL template for the `WebSite` `SearchAction`, e.g.\n * `'/search?q={search_term_string}'`. Omitted by default - the library's\n * own search is client-side with no crawlable results URL, so declaring one\n * that does not exist would be a false claim. Only set this if the app\n * actually serves search results at that URL. Must contain the literal\n * `{search_term_string}` placeholder.\n */\n searchUrlTemplate?: string\n}\n\n/** Top-level configuration object. Pass one instance to every library component. */\nexport interface ArticlesConfig {\n /** Canonical base URL of the site, used in metadata and JSON-LD. Example: `'https://yoursite.com'` */\n siteUrl: string\n /** Site name shown in metadata titles and JSON-LD publisher fields. Example: `'Vox Populus'` */\n siteName: string\n /** Number of articles shown per page in the grid and loaded on each \"Load more\" click. Default: `6` */\n pageSize?: number\n /** Number of category cards shown before a \"Load more categories\" button appears. Default: `8` */\n categoriesPageSize?: number\n /**\n * Ordered list of sections to render on the articles listing page.\n * Omit a key to hide that section entirely. Default: `['hero','search','featured','latest','categories']`\n */\n layout?: ArticlesSection[]\n /** CSS custom-property overrides for colors, fonts, and spacing. All fields optional. */\n theme?: ArticlesTheme\n /**\n * Descriptive text for each category, keyed by category slug.\n * A plain string is treated as the short description only.\n * Example: `{ 'campaigns': { short: 'Campaign tips', long: 'Full paragraph...' } }`\n */\n categoryDescriptions?: Record<string, string | CategoryDescription>\n /** Comments configuration. Omit or set `enabled: false` to hide comments entirely. */\n comments?: CommentsConfig\n /** Hero section title and description. Omit to use the built-in defaults. */\n hero?: HeroConfig\n /** Short description used in the RSS feed channel. Falls back to siteName if omitted. */\n description?: string\n /** Set to false to hide the table of contents on article detail pages. Default: true. */\n showToc?: boolean\n /** Set to false to hide the \"Back to Articles\" navigation link on article detail pages. Default: true. */\n showBackToArticles?: boolean\n /** Set to false to hide author names from UI and metadata. Default: true. */\n showAuthor?: boolean\n /** Author profiles keyed by slug. Omit to keep plain string author display. */\n authors?: Record<string, AuthorProfile>\n /** Author slug used when article frontmatter omits author fields. */\n defaultAuthor?: string\n /** Set to false to disable copied author page routes in consuming apps. Default: true. */\n showAuthorPage?: boolean\n /** Set to false to disable breadcrumbs, or pass a config object to customize breadcrumb trails. */\n breadcrumbs?: false | BreadcrumbsConfig\n /** Article body link target behavior. Default: `'external-new-tab'`. */\n linkTargetStrategy?: LinkTargetStrategy\n /** Extra components exposed to article MDX bodies by JSX tag name. */\n mdxComponents?: MdxComponents\n /**\n * Chooses how listing pages surface articles beyond the first `pageSize`.\n * Default: `'load-more'` (unchanged pre-27D behavior). Set to `'pages'` to\n * opt into real, crawlable paginated routes instead.\n */\n listingPagination?: ListingPagination\n /**\n * \"Start here\" curated reader journeys, keyed by an app-chosen path key.\n * Distinct from the label-only `series` field/`seriesSlug` pair - a path\n * can cross series and categories. Every `PathDefinition.articles` slug\n * must exist and not be `draft: true`; validate with `validateArticles`\n * before publishing, since a broken reference produces a dead journey\n * step rather than a build-time failure otherwise.\n */\n paths?: Record<string, PathDefinition>\n /**\n * Template for the `<title>` tag on article, category, series, and author\n * pages. Supports `{title}` and `{siteName}` placeholders.\n * Default: `'{title} | {siteName}'`.\n *\n * Google truncates a result title around 60 characters, and a site name\n * suffix spends that budget on every page. Set `'{title}'` to drop it when\n * your titles are already long and your brand draws little search volume -\n * the suffix is only earning its characters if people search for the brand.\n */\n titleTemplate?: string\n /**\n * BCP 47 language tag for the site's content. Emitted as the Article\n * schema's `inLanguage` and the RSS channel `<language>`. Default: `'en'`.\n */\n language?: string\n /**\n * Set to `false` when articles sit behind a paywall or registration wall.\n * Default: `true`, emitted as the Article schema's `isAccessibleForFree` -\n * an explicit \"this is readable\" signal, since consumers that cannot tell\n * tend to skip suspected-paywalled sources.\n */\n isAccessibleForFree?: boolean\n /**\n * CSS selectors marking the parts of an article suitable for text-to-speech,\n * emitted as the Article schema's `speakable`. Omitted by default - the\n * correct selectors depend on the consuming app's own markup, and guessing\n * them would point at elements that may not exist.\n */\n speakableSelectors?: string[]\n /**\n * Set to `true` to derive `FAQPage` entries from question-shaped `##`\n * headings and the paragraph that follows each one. Default: `false` -\n * turning prose into structured data without the author's intent can\n * promote a rhetorical heading into a published Q&A pair, so this is\n * opt-in. Explicit `faq` frontmatter always wins over derived entries.\n */\n deriveFaqFromHeadings?: boolean\n /**\n * Shared entity vocabulary, keyed by an app-chosen slug. Article `about`\n * entries may reference a key here instead of repeating a name/`sameAs`\n * pair - the point of `about` is that a consumer can resolve one entity\n * across a corpus, which free-text names spelled three different ways\n * defeat. `validateArticles` warns on `about` keys with no registry entry.\n */\n entities?: Record<string, EntityReference>\n /**\n * Where `lastmod` comes from when frontmatter omits it.\n * - `'published'` (default): reuse the publish date, as before 1.3.0.\n * - `'none'`: leave `lastmod` unset, so `dateModified` is omitted rather\n * than repeating a stale publish date.\n * - `'fileMtime'`: read the article file's modification time. Accurate\n * locally; on a CI runner that clones fresh, every file's mtime is the\n * checkout time, which would report the whole corpus as updated today.\n * Only use it where the build preserves mtimes.\n */\n lastmodFallback?: 'published' | 'none' | 'fileMtime'\n /**\n * Called when an AI crawler fetches an article's markdown twin. The one\n * choke point where those requests land, so it is the only place a site\n * can measure whether any of its AI-readable content is being read, and\n * by which bot. No PII: the payload carries the slug, the matched crawler\n * name, and the raw user agent string only.\n */\n onAiCrawl?: (event: AiCrawlEvent) => void\n /**\n * The publishing entity behind the site. Omit to keep the pre-1.3.0 inline\n * `{'@type':'Organization', name: siteName}` publisher stub on articles.\n * Set it to emit `OrganizationSchema`/`WebSiteSchema` in the root layout and\n * have every article, author, and collection page reference the same `@id`.\n */\n organization?: OrganizationConfig\n /**\n * Default `aiCrawl` value for articles whose frontmatter omits the key.\n * Default: `false` (every article stays opted out unless it sets\n * `aiCrawl: true`). Set to `true` on a site whose goal is being cited by\n * answer engines to opt the whole corpus in at once; per-article\n * `aiCrawl: false` still wins and keeps that article blocked.\n */\n aiCrawlDefault?: boolean\n /**\n * Set to `false` to serve article markdown twins as the bare body, with no\n * attribution header. Default: `true` - the twin is prefixed with the\n * title, excerpt, canonical source URL, dates, author, and site name so a\n * model reading `/articles/[slug].md` can attribute it. Only applies when\n * a config is available (i.e. via `getArticleMarkdownResponse`).\n */\n markdownTwinHeader?: boolean\n /**\n * Vendor-neutral event callback (Phase 27F). Fired by components/hooks at\n * meaningful reader-journey moments (see `ArticleEvent` in `events.ts`).\n * No PII in any payload. The package never talks to an analytics/email\n * vendor directly - translate events to PostHog/etc. in this callback.\n */\n onEvent?: ArticleEventHandler\n}\n\nexport const DEFAULT_PAGE_SIZE = 6\nexport const DEFAULT_CATEGORIES_PAGE_SIZE = 8\n\nexport const DEFAULT_LAYOUT: ArticlesSection[] = [\n 'hero',\n 'search',\n 'featured',\n 'latest',\n 'categories',\n]\n\nexport function breadcrumbsAreEnabled(config: ArticlesConfig): boolean {\n return config.breadcrumbs !== false && config.breadcrumbs?.show !== false\n}\n\nexport function getBreadcrumbsConfig(config: ArticlesConfig): BreadcrumbsConfig {\n if (config.breadcrumbs === false) return {}\n return config.breadcrumbs ?? {}\n}\n\n/** Stable `@id` for the site's publishing entity. */\nexport function getOrganizationId(config: Pick<ArticlesConfig, 'siteUrl'>): string {\n return `${config.siteUrl.replace(/\\/$/, '')}/#organization`\n}\n\n/** Stable `@id` for the site itself. */\nexport function getWebSiteId(config: Pick<ArticlesConfig, 'siteUrl'>): string {\n return `${config.siteUrl.replace(/\\/$/, '')}/#website`\n}\n\n/** Stable `@id` for an author, so every article by them resolves to one Person. */\nexport function getPersonId(authorUrl: string): string {\n return `${authorUrl.replace(/\\/$/, '')}#person`\n}\n\n/** Resolves a possibly site-relative asset path against `siteUrl`. */\nexport function resolveEntityUrl(value: string, config: Pick<ArticlesConfig, 'siteUrl'>): string {\n if (/^https?:\\/\\//.test(value)) return value\n return `${config.siteUrl.replace(/\\/$/, '')}/${value.replace(/^\\/+/, '')}`\n}\n\nexport const DEFAULT_TITLE_TEMPLATE = '{title} | {siteName}'\n\n/**\n * Applies `ArticlesConfig.titleTemplate` to a page title. Kept here rather\n * than inlined at each call site so article, category, series, and author\n * pages can never drift apart on how the site name is appended.\n */\nexport function formatPageTitle(\n title: string,\n config: Pick<ArticlesConfig, 'siteName' | 'titleTemplate'>\n): string {\n return (config.titleTemplate ?? DEFAULT_TITLE_TEMPLATE)\n .replaceAll('{title}', title)\n .replaceAll('{siteName}', config.siteName)\n}\n","import type { ArticlesConfig } from './articlesConfig'\nimport type { AuthorProfile, AuthorSocial } from './articleTypes'\n\n/**\n * The canonical identity URL a Person `@id` derives from. Prefers the\n * explicit cross-site `identityUrl`, then `url`, then this site's own author\n * page - so an author who configures nothing keeps a per-site identity, and\n * one who sets `identityUrl` gets the same `@id` on every site.\n */\nexport function getAuthorIdentityUrl(\n author: AuthorProfile,\n config?: Pick<ArticlesConfig, 'siteUrl'>\n): string | undefined {\n if (author.identityUrl) return author.identityUrl\n if (author.url) return author.url\n if (!config) return undefined\n return `${config.siteUrl.replace(/\\/$/, '')}/articles/authors/${author.slug}`\n}\n\nexport function getAuthorUrl(author: AuthorProfile): string {\n return author.url ?? `/articles/authors/${author.slug}`\n}\n\nexport function getAuthorAvatar(\n author: AuthorProfile,\n config?: ArticlesConfig\n): string | undefined {\n if (!author.avatar) return undefined\n if (author.avatar.startsWith('http://') || author.avatar.startsWith('https://')) {\n return author.avatar\n }\n const path = `/articles/authors/${author.slug}/${author.avatar.replace(/^\\/+/, '')}`\n if (!config) return path\n return `${config.siteUrl.replace(/\\/$/, '')}${path}`\n}\n\nexport function getAuthorSameAs(author: AuthorProfile): string[] {\n const derived = getAuthorSocialLinks(author).map((link) => link.href)\n const explicit = author.sameAs?.filter((url) => url.trim() !== '') ?? []\n return [...new Set([...derived, ...explicit])]\n}\n\nexport interface AuthorSocialLink {\n label: string\n href: string\n}\n\nfunction normalizeHandle(value: string): string {\n return value.replace(/^@/, '')\n}\n\nfunction normalizeUrl(value: string, baseUrl?: string): string {\n if (value.startsWith('http://') || value.startsWith('https://')) return value\n if (!baseUrl) return value\n return `${baseUrl}${normalizeHandle(value)}`\n}\n\nfunction getConfiguredSocialLinks(social: AuthorSocial): AuthorSocialLink[] {\n return [\n { label: 'Website', href: social.website ?? '' },\n {\n label: 'Facebook',\n href: social.facebook ? normalizeUrl(social.facebook, 'https://www.facebook.com/') : '',\n },\n {\n label: 'Twitter',\n href: social.twitter ? normalizeUrl(social.twitter, 'https://twitter.com/') : '',\n },\n { label: 'X', href: social.x ? normalizeUrl(social.x, 'https://x.com/') : '' },\n {\n label: 'LinkedIn',\n href: social.linkedin ? normalizeUrl(social.linkedin, 'https://www.linkedin.com/in/') : '',\n },\n {\n label: 'Instagram',\n href: social.instagram ? normalizeUrl(social.instagram, 'https://www.instagram.com/') : '',\n },\n {\n label: 'YouTube',\n href: social.youtube ? normalizeUrl(social.youtube, 'https://www.youtube.com/') : '',\n },\n {\n label: 'TikTok',\n href: social.tiktok ? normalizeUrl(social.tiktok, 'https://www.tiktok.com/@') : '',\n },\n {\n label: 'GitHub',\n href: social.github ? normalizeUrl(social.github, 'https://github.com/') : '',\n },\n {\n label: 'Bluesky',\n href: social.bluesky ? normalizeUrl(social.bluesky, 'https://bsky.app/profile/') : '',\n },\n {\n label: 'Threads',\n href: social.threads ? normalizeUrl(social.threads, 'https://www.threads.net/@') : '',\n },\n { label: 'Mastodon', href: social.mastodon ? normalizeUrl(social.mastodon) : '' },\n {\n label: 'Medium',\n href: social.medium ? normalizeUrl(social.medium, 'https://medium.com/@') : '',\n },\n { label: 'Newsletter', href: social.newsletter ?? '' },\n ]\n}\n\nexport function getAuthorSocialLinks(author: AuthorProfile): AuthorSocialLink[] {\n const social = author.social\n if (!social) return []\n const configuredLinks = getConfiguredSocialLinks(social)\n const otherLinks = Object.entries(social.other ?? {}).map(([label, href]) => ({ label, href }))\n return [...configuredLinks, ...otherLinks].filter((link) => link.href.trim().length > 0)\n}\n","import Link from 'next/link'\nimport type { BreadcrumbItem } from './articleTypes'\n\ntype BreadcrumbProps = Readonly<{\n items: BreadcrumbItem[]\n className?: string\n /** Max-width utility class applied to the breadcrumb list. Default: 'max-w-7xl'. */\n containerClassName?: string\n showSchema?: boolean\n separator?: string\n}>\n\nfunction buildBreadcrumbSchema(items: readonly BreadcrumbItem[]) {\n return {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n ...(item.url && { item: item.url }),\n })),\n }\n}\n\nfunction getDisplayItems(items: readonly BreadcrumbItem[]): BreadcrumbItem[] {\n if (items.length <= 4) return [...items]\n return [items[0], { name: '...' }, ...items.slice(-2)]\n}\n\nexport function Breadcrumb({\n items,\n className = '',\n containerClassName = 'max-w-7xl',\n showSchema = true,\n separator = '>',\n}: BreadcrumbProps) {\n if (items.length === 0) return null\n\n const displayItems = getDisplayItems(items)\n\n return (\n <>\n <nav\n aria-label=\"Breadcrumb\"\n className={`border-b border-border bg-background/80 px-4 py-3 text-sm text-muted-foreground ${className}`}\n >\n <ol className={`mx-auto flex items-center gap-2 overflow-hidden ${containerClassName}`}>\n {displayItems.map((item, index) => {\n const isLast = index === displayItems.length - 1\n const key = `${item.name}-${index}`\n return (\n <li key={key} className=\"flex min-w-0 items-center gap-2\">\n {index > 0 && <span aria-hidden=\"true\">{separator}</span>}\n {item.url && !isLast ? (\n <Link href={item.url} className=\"truncate hover:text-foreground\">\n {item.name}\n </Link>\n ) : (\n <span className=\"truncate\" aria-current={isLast ? 'page' : undefined}>\n {item.name}\n </span>\n )}\n </li>\n )\n })}\n </ol>\n </nav>\n {showSchema && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(buildBreadcrumbSchema(items)) }}\n />\n )}\n </>\n )\n}\n\nexport function BreadcrumbSchema({ items }: Readonly<{ items: BreadcrumbItem[] }>) {\n return <Breadcrumb items={items} showSchema className=\"sr-only\" />\n}\n","import {\n getOrganizationId,\n getPersonId,\n getWebSiteId,\n resolveEntityUrl,\n type ArticlesConfig,\n} from './articlesConfig'\nimport type { Article, AuthorProfile, FaqItem, HowToStep } from './articleTypes'\nimport type { ArticleComment } from './commentTypes'\nimport { getAuthorAvatar, getAuthorIdentityUrl, getAuthorSameAs } from './authorUtils'\n\nexport { BreadcrumbSchema } from './Breadcrumb'\n\nfunction getPersonSchemas(\n article: Article,\n authors?: readonly AuthorProfile[],\n config?: ArticlesConfig\n) {\n let resolvedAuthors: AuthorProfile[] = []\n if (authors?.length) {\n resolvedAuthors = [...authors]\n } else if (article.author) {\n resolvedAuthors = [{ name: article.author, slug: '', bio: '' }]\n }\n // Phase 27E audit: same decision as AuthorArticlesPage's getPersonSchema -\n // the new AuthorProfile fields (promise/servesWho/principles/credentials/\n // proof) are marketing copy, audience segments, or unverifiable claims, not\n // schema.org-appropriate facts, so none of them are added here. `url`/\n // `image`/`sameAs` stay sourced exactly as before (author.url, resolved\n // avatar, approved social links only).\n return resolvedAuthors.map((author) => {\n const identityUrl = getAuthorIdentityUrl(author, config)\n return {\n '@type': 'Person',\n ...(identityUrl && { '@id': getPersonId(identityUrl) }),\n name: author.name,\n ...(author.url && { url: author.url }),\n ...(getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }),\n ...(getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }),\n }\n })\n}\n\ntype ArticleSEOProps = Readonly<{\n article: Article\n articleUrl: string\n siteName: string\n siteLogo?: string\n showAuthor?: boolean\n authors?: AuthorProfile[]\n config?: ArticlesConfig\n /**\n * Top-level comments to emit as `commentCount`/`comment`. Real discussion\n * is a quality signal that is otherwise invisible in structured data.\n * Deleted comments are excluded, and only the author name, body, and\n * timestamp are emitted - never the commenter's id.\n */\n comments?: readonly ArticleComment[]\n}>\n\n// `lastmodFallback: 'none'` opts out of reusing the publish date - an article\n// edited three times with no `lastmod` otherwise reports its original date as\n// `dateModified` forever.\nfunction resolveModifiedAt(\n article: Article,\n publishedAt: string | undefined,\n config?: ArticlesConfig\n): string | undefined {\n if (article.lastmod) return new Date(article.lastmod).toISOString()\n return config?.lastmodFallback === 'none' ? undefined : publishedAt\n}\n\n/**\n * Absolutizes an article image for structured data.\n *\n * `featuredImage` is stored site-relative (`/articles/<slug>/hero.jpg`), and\n * Open Graph already absolutizes it via `resolveImageUrl` - JSON-LD did not,\n * so every article was publishing a relative `ImageObject.url`, which\n * consumers of structured data reject. Prefers `config.siteUrl`, falling back\n * to the origin of `articleUrl` for callers that pass no config.\n *\n * No `width`/`height`: the package never measures the file, and asserting\n * dimensions it has not read would be a guess in structured data.\n */\nfunction resolveSchemaImageUrl(image: string, articleUrl: string, config?: ArticlesConfig): string {\n if (/^https?:\\/\\//.test(image)) return image\n if (config) return resolveEntityUrl(image, config)\n try {\n return `${new URL(articleUrl).origin}/${image.replace(/^\\/+/, '')}`\n } catch {\n return image\n }\n}\n\nfunction buildPublisher(siteName: string, siteLogo?: string, config?: ArticlesConfig) {\n if (config?.organization) return { '@id': getOrganizationId(config) }\n return {\n '@type': 'Organization',\n name: siteName,\n ...(siteLogo && { logo: { '@type': 'ImageObject', url: siteLogo } }),\n }\n}\n\n// `articleType: 'QAPage'` already retypes the Article node, and a QAPage's\n// required shape is a `mainEntity` Question - so the Q&A belongs on that node\n// rather than in a second script that would duplicate one page as two\n// competing entities.\nfunction buildQuestionEntity(article: Article, articleUrl: string, publishedAt?: string) {\n if (article.articleType !== 'QAPage' || !article.answer) return undefined\n return {\n '@type': 'Question',\n name: article.title,\n text: article.excerpt || article.title,\n ...(publishedAt && { dateCreated: publishedAt }),\n answerCount: 1,\n acceptedAnswer: { '@type': 'Answer', text: article.answer, url: articleUrl },\n }\n}\n\nfunction buildCommentFields(comments?: readonly ArticleComment[]) {\n const visible = comments?.filter((comment) => !comment.isDeleted && !comment.parentId) ?? []\n if (visible.length === 0) return {}\n return {\n commentCount: visible.length,\n comment: visible.map((comment) => ({\n '@type': 'Comment',\n text: comment.body,\n dateCreated: comment.createdAt,\n author: { '@type': 'Person', name: comment.authorName },\n })),\n }\n}\n\nfunction buildTopicFields(article: Article, config?: ArticlesConfig) {\n const about = article.about?.map((entity) => ({\n '@type': 'Thing',\n name: entity.name,\n ...(entity.sameAs && { sameAs: entity.sameAs }),\n }))\n const citation = article.citation?.map((source) => ({\n '@type': 'CreativeWork',\n name: source.name,\n ...(source.url && { url: source.url }),\n }))\n const selectors = config?.speakableSelectors\n\n return {\n ...(about?.length && { about }),\n ...(citation?.length && { citation }),\n ...(selectors?.length && {\n speakable: { '@type': 'SpeakableSpecification', cssSelector: selectors },\n }),\n }\n}\n\nfunction buildArticleSchema({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor,\n personSchemas,\n config,\n comments,\n}: Readonly<{\n article: Article\n articleUrl: string\n siteName: string\n siteLogo?: string\n showAuthor: boolean\n personSchemas: ReturnType<typeof getPersonSchemas>\n config?: ArticlesConfig\n comments?: readonly ArticleComment[]\n}>) {\n const publishedAt = article.date ? new Date(article.date).toISOString() : undefined\n const modifiedAt = resolveModifiedAt(article, publishedAt, config)\n const mainEntity = buildQuestionEntity(article, articleUrl, publishedAt)\n\n return {\n '@context': 'https://schema.org',\n '@type': article.articleType || 'Article',\n mainEntityOfPage: { '@type': 'WebPage', '@id': articleUrl },\n headline: article.title,\n image: article.featuredImage\n ? {\n '@type': 'ImageObject',\n url: resolveSchemaImageUrl(article.featuredImage, articleUrl, config),\n }\n : undefined,\n ...(publishedAt && { datePublished: publishedAt }),\n ...(modifiedAt && { dateModified: modifiedAt }),\n ...(showAuthor && personSchemas.length > 0 && { author: personSchemas }),\n publisher: buildPublisher(siteName, siteLogo, config),\n description: article.excerpt,\n ...(article.answer && { abstract: article.answer }),\n articleSection: article.category,\n ...(article.tags?.length && { keywords: article.tags.join(', ') }),\n ...(article.wordCount !== undefined && { wordCount: article.wordCount }),\n ...(article.series && { isPartOf: { '@type': 'Blog', name: article.series } }),\n inLanguage: config?.language ?? 'en',\n isAccessibleForFree: config?.isAccessibleForFree !== false,\n ...buildTopicFields(article, config),\n ...buildCommentFields(comments),\n ...(mainEntity && { mainEntity }),\n }\n}\n\nexport function ArticleSEO({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor = true,\n authors,\n config,\n comments,\n}: ArticleSEOProps) {\n const personSchemas = getPersonSchemas(article, authors, config)\n const structuredData = buildArticleSchema({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor,\n personSchemas,\n config,\n comments,\n })\n\n return (\n <>\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}\n />\n {article.faq && article.faq.length > 0 && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify({\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: article.faq.map(({ question, answer }: FaqItem) => ({\n '@type': 'Question',\n name: question,\n acceptedAnswer: { '@type': 'Answer', text: answer },\n })),\n }),\n }}\n />\n )}\n {article.howTo && article.howTo.length > 0 && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify({\n '@context': 'https://schema.org',\n '@type': 'HowTo',\n name: article.title,\n step: article.howTo.map(({ name, text }: HowToStep) => ({\n '@type': 'HowToStep',\n name,\n text,\n })),\n }),\n }}\n />\n )}\n </>\n )\n}\n\ntype FAQPageSchemaProps = Readonly<{\n items: ReadonlyArray<{ question: string; answer: string }>\n}>\n\nexport function FAQPageSchema({ items }: FAQPageSchemaProps) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: items.map(({ question, answer }) => ({\n '@type': 'Question',\n name: question,\n acceptedAnswer: { '@type': 'Answer', text: answer },\n })),\n }\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\nexport type CollectionPageItem = Readonly<{\n position: number\n url: string\n name: string\n}>\n\ntype CollectionPageSchemaProps = Readonly<{\n title: string\n description: string\n url: string\n articleCount: number\n // The articles actually linked in the page's initial HTML (e.g. the first\n // pageSize before \"Load more\"/pagination), not necessarily every article\n // in the underlying collection - matches what a crawler can see.\n items?: CollectionPageItem[]\n}>\n\nexport function CollectionPageSchema({\n title,\n description,\n url,\n articleCount,\n items,\n}: CollectionPageSchemaProps) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'CollectionPage',\n name: title,\n description,\n url,\n numberOfItems: articleCount,\n ...(items &&\n items.length > 0 && {\n mainEntity: {\n '@type': 'ItemList',\n itemListElement: items.map((item) => ({\n '@type': 'ListItem',\n position: item.position,\n url: item.url,\n name: item.name,\n })),\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\ntype OrganizationSchemaProps = Readonly<{ config: ArticlesConfig }>\n\n/**\n * Site-level publisher entity, rendered once in the root layout.\n *\n * Articles reference this by `@id` rather than repeating an inline\n * `Organization` stub, so every page on the site resolves to one entity - the\n * thing that lets a search or answer engine attribute a whole corpus to a\n * single publisher instead of treating each page as an orphan.\n * Returns `null` when `config.organization` is unset.\n */\nexport function OrganizationSchema({ config }: OrganizationSchemaProps) {\n const org = config.organization\n if (!org) return null\n\n const schema = {\n '@context': 'https://schema.org',\n '@type': org.type ?? 'Organization',\n '@id': getOrganizationId(config),\n name: org.name ?? config.siteName,\n url: org.url ?? config.siteUrl.replace(/\\/$/, ''),\n ...(org.logo && {\n logo: { '@type': 'ImageObject', url: resolveEntityUrl(org.logo, config) },\n }),\n ...((org.description ?? config.description) && {\n description: org.description ?? config.description,\n }),\n ...(org.sameAs && org.sameAs.length > 0 && { sameAs: org.sameAs }),\n ...(org.parentOrganization && {\n parentOrganization: {\n '@type': 'Organization',\n name: org.parentOrganization.name,\n url: org.parentOrganization.url,\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\ntype WebSiteSchemaProps = Readonly<{ config: ArticlesConfig }>\n\n/**\n * Site entity, rendered once in the root layout alongside `OrganizationSchema`.\n * Emits a `SearchAction` only when `organization.searchUrlTemplate` is set -\n * the library's own search is client-side with no crawlable results URL, so\n * declaring one unconditionally would advertise an endpoint that does not exist.\n * Returns `null` when `config.organization` is unset.\n */\nexport function WebSiteSchema({ config }: WebSiteSchemaProps) {\n const org = config.organization\n if (!org) return null\n\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const template = org.searchUrlTemplate\n\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n '@id': getWebSiteId(config),\n name: config.siteName,\n url: siteUrl,\n publisher: { '@id': getOrganizationId(config) },\n ...(config.description && { description: config.description }),\n ...(template && {\n potentialAction: {\n '@type': 'SearchAction',\n target: {\n '@type': 'EntryPoint',\n urlTemplate: resolveEntityUrl(template, config),\n },\n 'query-input': 'required name=search_term_string',\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n","import { Search, X } from 'lucide-react'\n\ntype ArticleSearchBarProps = Readonly<{\n value: string\n onChange: (query: string) => void\n disabled?: boolean\n resultCount?: number\n}>\n\nexport function ArticleSearchBar({\n value,\n onChange,\n disabled,\n resultCount,\n}: ArticleSearchBarProps) {\n return (\n <section className=\"py-8 bg-background border-b border-border\">\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-5 w-5\" />\n <input\n type=\"text\"\n placeholder=\"Search articles (3+ characters required)...\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n disabled={disabled}\n className=\"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 pl-10 pr-10 py-3 text-lg\"\n />\n {value && (\n <button\n type=\"button\"\n onClick={() => onChange('')}\n aria-label=\"Clear search\"\n className=\"absolute right-3 top-1/2 transform -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n {value && (\n <div className=\"mt-2 text-sm text-muted-foreground\">\n {resultCount === 0\n ? 'No articles found matching your search.'\n : `Found ${resultCount} article${resultCount === 1 ? '' : 's'} matching \"${value}\"`}\n </div>\n )}\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\n\nconst DEFAULT_TITLE = 'Vox Populus Insights'\nconst DEFAULT_DESCRIPTION =\n 'Expert analysis, campaign strategies, and voter engagement insights from the frontlines of democracy.'\n\ntype ArticlesHeroProps = Readonly<{\n title?: string\n description?: string\n}>\n\nexport function ArticlesHero({\n title = DEFAULT_TITLE,\n description = DEFAULT_DESCRIPTION,\n}: ArticlesHeroProps) {\n return (\n <section className=\"bg-linear-to-r from-gray-50 to-gray-100 py-16 md:py-24\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"max-w-3xl mx-auto text-center\">\n <h1 className=\"text-4xl md:text-5xl font-bold text-foreground mb-6\">{title}</h1>\n <p className=\"text-xl text-muted-foreground mb-8\">{description}</p>\n <div className=\"flex flex-col sm:flex-row gap-4 justify-center\">\n <Link\n href=\"#latest\"\n className=\"inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors\"\n >\n Browse Latest Articles\n </Link>\n <Link\n href=\"/contact\"\n className=\"inline-flex items-center justify-center px-4 py-2 border border-primary text-primary hover:bg-primary/10 rounded-md font-medium text-sm transition-colors\"\n >\n Contact Our Team\n </Link>\n </div>\n </div>\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\nimport Image from 'next/image'\nimport { ArrowRight, Calendar, Clock, User } from 'lucide-react'\nimport type { Article } from './articleTypes'\n\ntype FeaturedArticleProps = Readonly<{\n article: Article\n showAuthor?: boolean\n}>\n\nexport function FeaturedArticle({ article, showAuthor = true }: FeaturedArticleProps) {\n return (\n <section className=\"py-16 bg-background\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"grid lg:grid-cols-2 gap-12 items-center\">\n <div>\n <span className=\"inline-block bg-primary/10 text-primary text-sm font-medium px-3 py-1 rounded-full mb-4\">\n FEATURED ARTICLE\n </span>\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">{article.title}</h2>\n <p className=\"text-lg text-muted-foreground mb-6\">{article.excerpt}</p>\n <div className=\"flex items-center gap-6 text-sm text-muted-foreground mb-6\">\n {article.date && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4\" />\n <span>{article.date}</span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-4 w-4\" />\n <span>{article.readTime}</span>\n </div>\n {showAuthor && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4\" />\n <span>{article.author}</span>\n </div>\n )}\n </div>\n <Link\n href={`/articles/${article.slug}`}\n className=\"inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors\"\n >\n Read Full Article\n <ArrowRight className=\"ml-2 h-4 w-4\" />\n </Link>\n </div>\n <div className=\"bg-card rounded-lg p-8\">\n <Image\n src={article.featuredImage}\n alt={article.title}\n width={800}\n height={450}\n className=\"w-full h-64 object-cover rounded-lg mb-4\"\n />\n <div className=\"flex gap-2\">\n <span className=\"bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded\">\n {article.category}\n </span>\n </div>\n </div>\n </div>\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { Search } from 'lucide-react'\nimport type { Article } from './articleTypes'\nimport { LatestArticles } from './LatestArticles'\nimport type { ListingPaginationContext } from './pagination'\n\ninterface LatestArticlesSectionProps {\n readonly articles: Article[]\n readonly searchQuery: string\n readonly onClearSearch: () => void\n readonly pageSize?: number\n /**\n * `'pages'` mode pagination context. Ignored while `searchQuery` is set -\n * search results stay unpaginated/client-only in both `listingPagination`\n * modes (search URLs aren't meant to be indexed, so real pagination adds\n * no SEO value there).\n */\n readonly pagination?: ListingPaginationContext\n}\n\nfunction DescriptionText({\n searchQuery,\n count,\n}: {\n readonly searchQuery: string\n readonly count: number\n}) {\n const pluralSuffix = count === 1 ? '' : 's'\n const text = searchQuery\n ? `Found ${count} article${pluralSuffix} matching your search.`\n : 'Stay informed with our latest insights on voter engagement, campaign strategies, and civic technology.'\n return <p className=\"text-lg text-muted-foreground\">{text}</p>\n}\n\nexport function LatestArticlesSection({\n articles,\n searchQuery,\n onClearSearch,\n pageSize = 6,\n pagination,\n}: Readonly<LatestArticlesSectionProps>) {\n return (\n <section id=\"latest\" className=\"py-16 bg-muted/50\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">\n {searchQuery ? 'Search Results' : 'Latest Articles'}\n </h2>\n <DescriptionText searchQuery={searchQuery} count={articles.length} />\n </div>\n\n {articles.length === 0 && searchQuery ? (\n <div className=\"text-center py-12\">\n <div className=\"max-w-md mx-auto\">\n <Search className=\"h-12 w-12 text-muted-foreground mx-auto mb-4\" />\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">No articles found</h3>\n <p className=\"text-muted-foreground mb-4\">\n We couldn't find any articles matching "{searchQuery}". Try adjusting\n your search terms.\n </p>\n <button\n type=\"button\"\n onClick={onClearSearch}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Clear search\n </button>\n </div>\n </div>\n ) : (\n <LatestArticles\n articles={articles}\n pageSize={pageSize}\n pagination={searchQuery ? undefined : pagination}\n key={searchQuery}\n />\n )}\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { Article } from './articleTypes'\nimport { ArticleCard } from './ArticleCard'\nimport { PaginationNav } from './PaginationNav'\nimport type { ListingPaginationContext } from './pagination'\n\ninterface LatestArticlesProps {\n readonly articles: Article[]\n readonly pageSize: number\n /**\n * Switches from the default client-only \"Load more\" button to real\n * paginated routes: renders every article in `articles` as-is (the caller\n * has already sliced to the current page, e.g. via `paginateArticles`) and\n * a `PaginationNav` instead of the button. Omit to keep the existing\n * `listingPagination: 'load-more'` behavior, unchanged.\n */\n readonly pagination?: ListingPaginationContext\n}\n\nexport function LatestArticles({ articles, pageSize, pagination }: Readonly<LatestArticlesProps>) {\n const [visibleCount, setVisibleCount] = useState(pageSize)\n\n useEffect(() => {\n setVisibleCount(pageSize)\n }, [articles, pageSize])\n\n if (pagination) {\n return (\n <div>\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8\">\n {articles.map((article) => (\n <ArticleCard key={article.slug} article={article} />\n ))}\n </div>\n <PaginationNav\n basePath={pagination.basePath}\n page={pagination.page}\n totalPages={pagination.totalPages}\n />\n </div>\n )\n }\n\n const visible = articles.slice(0, visibleCount)\n const hasMore = visibleCount < articles.length\n\n return (\n <div>\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8\">\n {visible.map((article) => (\n <ArticleCard key={article.slug} article={article} />\n ))}\n </div>\n {hasMore && (\n <div className=\"mt-10 text-center\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((c) => c + pageSize)}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Load more articles\n </button>\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { ArrowRight, Calendar, Clock } from 'lucide-react'\nimport { getInitials } from './AuthorCard'\nimport { emitArticleEvent } from './events'\nimport type { Article } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleCardProps = Readonly<{\n article: Article\n /** Optional (Phase 27F) - when passed with `config.onEvent`, clicking the author link fires an `author_clicked` event. Omitting it changes nothing. */\n config?: ArticlesConfig\n}>\n\nexport function ArticleCard({ article, config }: ArticleCardProps) {\n return (\n <div className=\"rounded-lg border border-border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-shadow duration-300\">\n <div className=\"p-0\">\n <Image\n src={article.featuredImage}\n alt={article.title}\n className=\"w-full h-48 object-cover rounded-t-lg\"\n width={400}\n height={200}\n />\n </div>\n <div className=\"p-6\">\n <div className=\"flex gap-2 mb-3\">\n <span className=\"bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded\">\n {article.category}\n </span>\n </div>\n <h3\n className=\"text-lg font-semibold leading-none tracking-tight mb-2\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n <Link href={`/articles/${article.slug}`} className=\"hover:text-primary transition-colors\">\n {article.title}\n </Link>\n </h3>\n <p className=\"text-muted-foreground mb-4 line-clamp-3\">{article.excerpt}</p>\n {article.authorSlug && (\n <Link\n href={`/articles/authors/${article.authorSlug}`}\n className=\"mb-4 flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors\"\n onClick={() =>\n emitArticleEvent(config?.onEvent, {\n name: 'author_clicked',\n articleSlug: article.slug,\n authorSlug: article.authorSlug as string,\n })\n }\n >\n {article.authorAvatar ? (\n <Image\n src={article.authorAvatar}\n alt=\"\"\n width={24}\n height={24}\n className=\"h-6 w-6 rounded-full object-cover\"\n />\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground\"\n >\n {getInitials(article.author)}\n </span>\n )}\n <span>{article.author}</span>\n </Link>\n )}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground border-t pt-4\">\n <div className=\"flex items-center gap-3\">\n {article.date && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-3 w-3\" />\n <span>{article.date}</span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-3 w-3\" />\n <span>{article.readTime}</span>\n </div>\n </div>\n <Link\n href={`/articles/${article.slug}`}\n className=\"inline-flex items-center justify-center h-8 w-8 p-0 rounded-md hover:bg-accent transition-colors\"\n aria-label={`Read ${article.title}`}\n >\n <ArrowRight className=\"h-4 w-4\" />\n </Link>\n </div>\n </div>\n </div>\n )\n}\n","import Image from 'next/image'\nimport Link from 'next/link'\nimport {\n Facebook,\n Github,\n Globe,\n Instagram,\n Linkedin,\n Mail,\n MessageCircle,\n Twitter,\n Youtube,\n type LucideIcon,\n} from 'lucide-react'\nimport type { AuthorProfile } from './articleTypes'\nimport { getAuthorAvatar, getAuthorSocialLinks, getAuthorUrl } from './authorUtils'\n\ntype AuthorCardProps = Readonly<{\n author: AuthorProfile\n linkToPage?: boolean\n showBio?: boolean\n showSocial?: boolean\n}>\n\nexport function getInitials(name: string): string {\n return name\n .split(' ')\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('')\n}\n\nexport function AuthorCard({\n author,\n linkToPage = true,\n showBio = false,\n showSocial = false,\n}: AuthorCardProps) {\n const avatar = getAuthorAvatar(author)\n const name = linkToPage ? (\n <Link href={getAuthorUrl(author)} className=\"font-medium text-foreground hover:text-primary\">\n {author.name}\n </Link>\n ) : (\n <span className=\"font-medium text-foreground\">{author.name}</span>\n )\n\n return (\n <div className=\"flex items-start gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground\">\n {avatar ? (\n <Image\n src={avatar}\n alt={author.name}\n width={48}\n height={48}\n className=\"h-12 w-12 rounded-full object-cover\"\n />\n ) : (\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-muted-foreground\">\n {getInitials(author.name)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n {name}\n {showBio && author.bio && (\n <p className=\"mt-1 line-clamp-2 text-sm text-muted-foreground\">{author.bio}</p>\n )}\n {showSocial && <AuthorSocialLinks author={author} />}\n </div>\n </div>\n )\n}\n\nconst SOCIAL_ICONS: Record<string, LucideIcon> = {\n Website: Globe,\n Facebook: Facebook,\n Twitter: Twitter,\n X: Twitter,\n LinkedIn: Linkedin,\n Instagram: Instagram,\n YouTube: Youtube,\n GitHub: Github,\n Newsletter: Mail,\n}\n\nfunction getSocialIcon(label: string): LucideIcon {\n return SOCIAL_ICONS[label] ?? MessageCircle\n}\n\nexport function AuthorSocialLinks({ author }: Readonly<{ author: AuthorProfile }>) {\n const links = getAuthorSocialLinks(author)\n if (links.length === 0) return null\n\n return (\n <div className=\"mt-3 flex items-center gap-2\">\n {links.map(({ href, label }) => {\n const Icon = getSocialIcon(label)\n return (\n <Link\n key={`${label}-${href}`}\n href={href}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n aria-label={`${author.name} on ${label}`}\n >\n <Icon className=\"h-4 w-4\" />\n </Link>\n )\n })}\n </div>\n )\n}\n","// Vendor-neutral article event contract (Phase 27F). The package emits\n// these typed events at the right places in existing components/hooks and\n// hands them to `config.onEvent` - it never talks to PostHog/Plunk/any\n// analytics or email vendor directly (see package.json dependencies, which\n// stay clean of them). No PII in any payload: only slugs/IDs/enums, never\n// emails, names-as-identifiers, or free text.\n\nexport type ArticleEventName =\n | 'article_viewed'\n | 'meaningful_read'\n | 'author_clicked'\n | 'cta_viewed'\n | 'cta_clicked'\n | 'shared'\n | 'related_article_clicked'\n | 'path_step_advanced'\n\ninterface ArticleEventBase<Name extends ArticleEventName> {\n name: Name\n /** `Date.now()` at emit time. */\n timestamp: number\n}\n\nexport interface ArticleViewedEvent extends ArticleEventBase<'article_viewed'> {\n articleSlug: string\n category?: string\n seriesSlug?: string\n}\n\n/** Fired once per view after the reader has spent roughly half the article's estimated read time on the page (see `ArticleViewTracker`). */\nexport interface MeaningfulReadEvent extends ArticleEventBase<'meaningful_read'> {\n articleSlug: string\n}\n\nexport interface AuthorClickedEvent extends ArticleEventBase<'author_clicked'> {\n articleSlug: string\n authorSlug: string\n}\n\n/** `ctaId` is `primaryAction.actionId`, an `AuthorProfile.primaryCta` slug, or a `PathDefinition` key - always an app-chosen ID, never label text. */\nexport interface CtaViewedEvent extends ArticleEventBase<'cta_viewed'> {\n ctaId: string\n articleSlug?: string\n}\n\nexport interface CtaClickedEvent extends ArticleEventBase<'cta_clicked'> {\n ctaId: string\n articleSlug?: string\n}\n\nexport interface SharedEvent extends ArticleEventBase<'shared'> {\n articleSlug: string\n /** Share channel key, e.g. `'linkedin'`, `'copy-link'` - never the shared URL/message text. */\n channel: string\n}\n\nexport interface RelatedArticleClickedEvent extends ArticleEventBase<'related_article_clicked'> {\n fromSlug: string\n toSlug: string\n source: 'path' | 'series' | 'category'\n}\n\nexport interface PathStepAdvancedEvent extends ArticleEventBase<'path_step_advanced'> {\n pathKey: string\n fromSlug: string\n toSlug: string\n direction: 'previous' | 'next'\n}\n\nexport type ArticleEvent =\n | ArticleViewedEvent\n | MeaningfulReadEvent\n | AuthorClickedEvent\n | CtaViewedEvent\n | CtaClickedEvent\n | SharedEvent\n | RelatedArticleClickedEvent\n | PathStepAdvancedEvent\n\n/** Register this on `ArticlesConfig.onEvent` to receive every emitted event and translate it to your own analytics stack. */\nexport type ArticleEventHandler = (event: ArticleEvent) => void\n\n// `Omit<ArticleEvent, 'timestamp'>` alone would collapse the discriminated\n// union to its common keys (TypeScript computes `keyof` on a union as the\n// intersection of each member's keys), losing every event-specific field.\n// A distributive conditional type over the naked `T` preserves each\n// member's own shape instead.\ntype DistributiveOmitTimestamp<T> = T extends ArticleEvent ? Omit<T, 'timestamp'> : never\n\n/**\n * Safely invokes `handler` with `event`, stamping `timestamp`. Swallows any\n * error thrown by the consuming app's handler - a broken analytics\n * integration must never break article rendering.\n */\nexport function emitArticleEvent(\n handler: ArticleEventHandler | undefined,\n event: DistributiveOmitTimestamp<ArticleEvent>\n): void {\n if (!handler) return\n try {\n handler({ ...event, timestamp: Date.now() } as ArticleEvent)\n } catch {\n // consuming app's handler errors must never break rendering\n }\n}\n","import Link from 'next/link'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { buildPaginationLinks } from './pagination'\n\ninterface PaginationNavProps {\n readonly basePath: string\n readonly page: number\n readonly totalPages: number\n}\n\n/**\n * Renders real `<a href>` prev/next links between listing pages (used only\n * in `listingPagination: 'pages'` mode), plus `<link rel=\"prev\"/\"next\">`\n * tags for crawlers/tools that still read them.\n *\n * Google stopped using rel=next/prev as an indexing signal in 2019 (Google\n * Search Central's pagination guidance now recommends a unique\n * self-referencing canonical per page - see `generateArticlesIndexPageMetadata`\n * et al in seoUtils.ts - plus real crawlable links between pages, which is\n * what this component's visible Previous/Next links provide). rel=next/prev\n * remains valid HTML and is still read by Bing and some third-party tools,\n * so it's emitted here for free: React 19 hoists `<link>`/`<meta>` elements\n * rendered anywhere in a Server Component tree into `<head>` automatically\n * (not just from layout.js/page.js directly), so no separate Head API call\n * is needed.\n */\nexport function PaginationNav({ basePath, page, totalPages }: PaginationNavProps) {\n if (totalPages <= 1) return null\n\n const { prevUrl, nextUrl } = buildPaginationLinks(basePath, page, totalPages)\n\n return (\n <nav\n className=\"mt-10 flex items-center justify-center gap-4\"\n aria-label=\"Article listing pagination\"\n >\n {prevUrl && <link rel=\"prev\" href={prevUrl} />}\n {nextUrl && <link rel=\"next\" href={nextUrl} />}\n {prevUrl ? (\n <Link\n href={prevUrl}\n className=\"inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n Previous\n </Link>\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </span>\n )}\n <span className=\"text-sm text-muted-foreground\">\n Page {page} of {totalPages}\n </span>\n {nextUrl ? (\n <Link\n href={nextUrl}\n className=\"inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Link>\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n )}\n </nav>\n )\n}\n","// Pure pagination math shared by both the server data-prep side (route files\n// slicing articles per page, generateStaticParams) and the client-safe\n// PaginationNav component (building prev/next hrefs). No fs/next dependency\n// so it's safe to import from both `index.ts` and `server.ts` entry points.\nimport type { Article } from './articleTypes'\n\nexport interface PaginatedArticles {\n /** Articles belonging to this page only (already sliced). */\n articles: Article[]\n /** Clamped to the range `[1, totalPages]`. */\n page: number\n totalPages: number\n hasPrevious: boolean\n hasNext: boolean\n}\n\n/** Context threaded from a listing page component down into `LatestArticles`/`PaginationNav` in `'pages'` mode. */\nexport interface ListingPaginationContext {\n page: number\n totalPages: number\n /** Un-paginated route path for this listing, e.g. `/articles` or `/articles/category/campaigns`. */\n basePath: string\n}\n\nexport interface PaginationLinks {\n /** This page's own canonical URL - never points back to page 1 for page > 1. */\n canonicalUrl: string\n prevUrl: string | null\n nextUrl: string | null\n}\n\nexport function getTotalPages(totalCount: number, pageSize: number): number {\n if (totalCount <= 0 || pageSize <= 0) return 1\n return Math.max(1, Math.ceil(totalCount / pageSize))\n}\n\n/** Slices `articles` to the requested page, clamping out-of-range page numbers into `[1, totalPages]`. */\nexport function paginateArticles(\n articles: Article[],\n page: number,\n pageSize: number\n): PaginatedArticles {\n const totalPages = getTotalPages(articles.length, pageSize)\n const requestedPage = Math.trunc(page) || 1\n const clampedPage = Math.min(Math.max(requestedPage, 1), totalPages)\n const start = (clampedPage - 1) * pageSize\n return {\n articles: articles.slice(start, start + pageSize),\n page: clampedPage,\n totalPages,\n hasPrevious: clampedPage > 1,\n hasNext: clampedPage < totalPages,\n }\n}\n\n/** Page 1 is the un-suffixed `basePath` itself; page N>1 is `${basePath}/page/${N}`. */\nexport function buildPageUrl(basePath: string, page: number): string {\n const base = basePath.replace(/\\/$/, '')\n return page > 1 ? `${base}/page/${page}` : base\n}\n\nexport function buildPaginationLinks(\n basePath: string,\n page: number,\n totalPages: number\n): PaginationLinks {\n return {\n canonicalUrl: buildPageUrl(basePath, page),\n prevUrl: page > 1 ? buildPageUrl(basePath, page - 1) : null,\n nextUrl: page < totalPages ? buildPageUrl(basePath, page + 1) : null,\n }\n}\n\n/**\n * Static params for pages 2..totalPages (page 1 has no `/page/1` route - it's\n * served by the un-paginated base route). For nested dynamic segments (e.g.\n * `/articles/category/[category]/page/[page]`), combine this per-category in\n * the consuming app's `generateStaticParams` - see README.\n */\nexport function generateListingPageStaticParams(totalPages: number): { page: string }[] {\n const params: { page: string }[] = []\n for (let page = 2; page <= totalPages; page++) params.push({ page: String(page) })\n return params\n}\n\nexport function parsePageParam(raw: string | undefined | null): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 1\n}\n\nexport function isPageOutOfRange(page: number, totalPages: number): boolean {\n return page < 1 || page > totalPages\n}\n","'use client'\n\nimport { useState, useCallback, useEffect, useRef } from 'react'\nimport { Article, CategoryInfo } from './articleTypes'\n\nexport interface UseArticlesReturn {\n articles: Article[]\n categories: CategoryInfo[]\n searchQuery: string\n loading: boolean\n error: string | null\n handleSearch: (query: string) => void\n}\n\nexport function useArticles(\n initialArticles?: Article[],\n initialCategories?: CategoryInfo[]\n): UseArticlesReturn {\n const [searchQuery, setSearchQuery] = useState('')\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const [articles, setArticles] = useState<Article[]>(initialArticles ?? [])\n const [categories, setCategories] = useState<CategoryInfo[]>(initialCategories ?? [])\n const [loading, setLoading] = useState(initialArticles === undefined)\n const [error, setError] = useState<string | null>(null)\n\n const fetchArticles = useCallback(async (query: string) => {\n try {\n setLoading(true)\n const url = query ? `/api/articles?q=${encodeURIComponent(query)}` : '/api/articles'\n const response = await fetch(url)\n\n if (!response.ok) throw new Error('Failed to fetch articles')\n\n const data = await response.json()\n setArticles(data.articles)\n\n if (!query?.trim()) {\n const catMap = new Map<string, { count: number; featuredImage: string }>()\n for (const article of data.articles as Article[]) {\n for (const cat of article.categories) {\n if (!catMap.has(cat)) {\n catMap.set(cat, { count: 0, featuredImage: article.featuredImage })\n }\n catMap.get(cat)!.count++\n }\n }\n setCategories(\n Array.from(catMap.entries())\n .map(([name, { count, featuredImage }]) => ({\n name,\n slug: name\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, ''),\n count,\n featuredImage,\n }))\n .sort((a, b) => b.count - a.count)\n )\n }\n\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load articles')\n setArticles([])\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n if (initialArticles === undefined) fetchArticles('')\n // Seeded data came from the server render; skip the redundant client fetch on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fetchArticles])\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query)\n if (debounceRef.current) clearTimeout(debounceRef.current)\n if (query === '') {\n fetchArticles('')\n } else if (query.length >= 3) {\n debounceRef.current = setTimeout(() => fetchArticles(query), 1000)\n }\n },\n [fetchArticles]\n )\n\n useEffect(\n () => () => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n },\n []\n )\n\n return { articles, categories, searchQuery, loading, error, handleSearch }\n}\n","'use client'\n\nimport React from 'react'\nimport type { Article, CategoryInfo } from './articleTypes'\nimport { ArticleCategoryGrid } from './ArticleCategoryGrid'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { ArticleSearchBar } from './ArticleSearchBar'\nimport { ArticlesHero } from './ArticlesHero'\nimport { FeaturedArticle } from './FeaturedArticle'\nimport { LatestArticlesSection } from './LatestArticlesSection'\nimport {\n ArticlesConfig,\n ArticlesSection,\n ArticlesTheme,\n DEFAULT_CATEGORIES_PAGE_SIZE,\n DEFAULT_LAYOUT,\n DEFAULT_PAGE_SIZE,\n} from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport { useArticles } from './useArticles'\n\ninterface SectionContext {\n articles: Article[]\n displayedArticles: Article[]\n categories: CategoryInfo[]\n searchQuery: string\n loading: boolean\n error: string | null\n handleSearch: (query: string) => void\n pageSize: number\n categoriesPageSize: number\n pagination?: ListingPaginationContext\n}\n\nfunction buildThemeVars(theme?: ArticlesTheme): React.CSSProperties {\n if (!theme) return {}\n const vars: Record<string, string> = {}\n if (theme.fontFamily) vars['--articles-font-family'] = theme.fontFamily\n if (theme.headerFontFamily) vars['--articles-header-font-family'] = theme.headerFontFamily\n if (theme.headerColor) vars['--articles-header-color'] = theme.headerColor\n if (theme.textColor) vars['--articles-text-color'] = theme.textColor\n if (theme.backgroundColor) vars['--articles-bg-color'] = theme.backgroundColor\n if (theme.linkColor) vars['--articles-link-color'] = theme.linkColor\n if (theme.linkHoverColor) vars['--articles-link-hover-color'] = theme.linkHoverColor\n if (theme.linkTextDecoration) vars['--articles-link-decoration'] = theme.linkTextDecoration\n if (theme.headerFontWeight) vars['--articles-header-font-weight'] = String(theme.headerFontWeight)\n if (theme.headerFontSize) vars['--articles-header-font-size'] = theme.headerFontSize\n if (theme.bodyFontSize) vars['--articles-body-font-size'] = theme.bodyFontSize\n if (theme.lineHeight) vars['--articles-line-height'] = theme.lineHeight\n if (theme.borderRadius) vars['--articles-border-radius'] = theme.borderRadius\n return vars as React.CSSProperties\n}\n\nfunction renderSection(\n section: ArticlesSection,\n ctx: SectionContext,\n config: ArticlesConfig\n): React.ReactNode {\n const {\n articles,\n displayedArticles,\n categories,\n searchQuery,\n loading,\n error,\n handleSearch,\n pageSize,\n categoriesPageSize,\n pagination,\n } = ctx\n\n switch (section) {\n case 'hero':\n return (\n <ArticlesHero\n key=\"hero\"\n title={config.hero?.title}\n description={config.hero?.description}\n />\n )\n\n case 'search':\n return (\n <ArticleSearchBar\n key=\"search\"\n value={searchQuery}\n onChange={handleSearch}\n disabled={loading}\n resultCount={articles.length}\n />\n )\n\n case 'featured':\n return articles.length > 0 && !searchQuery ? (\n <FeaturedArticle\n key=\"featured\"\n article={articles[0]}\n showAuthor={config.showAuthor !== false}\n />\n ) : null\n\n case 'latest':\n if (loading && articles.length === 0) {\n return (\n <section key=\"latest\" id=\"latest\" className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center py-24\">\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4\" />\n <p className=\"text-muted-foreground\">Loading articles...</p>\n </div>\n </div>\n </section>\n )\n }\n if (error) {\n return (\n <section key=\"latest\" id=\"latest\" className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center\">\n <div className=\"bg-red-50 border border-red-200 rounded-lg p-6 max-w-md text-center\">\n <h3 className=\"text-lg font-semibold text-red-800 mb-2\">Error Loading Articles</h3>\n <p className=\"text-red-600 mb-4\">{error}</p>\n <button\n type=\"button\"\n onClick={() => handleSearch('')}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Try again\n </button>\n </div>\n </div>\n </section>\n )\n }\n return (\n <LatestArticlesSection\n key=\"latest\"\n articles={displayedArticles}\n searchQuery={searchQuery}\n onClearSearch={() => handleSearch('')}\n pageSize={pageSize}\n pagination={pagination}\n />\n )\n\n case 'categories':\n return searchQuery ? null : (\n <ArticleCategoryGrid\n key=\"categories\"\n categories={categories}\n pageSize={categoriesPageSize}\n />\n )\n\n // 'newsletter' is intentionally omitted — inject it via the layout prop in the consuming app\n default:\n return null\n }\n}\n\nexport function ArticlesPage({\n config,\n initialArticles,\n initialCategories,\n page,\n totalPages,\n totalCount,\n}: Readonly<{\n config: ArticlesConfig\n initialArticles?: Article[]\n initialCategories?: CategoryInfo[]\n /**\n * Current page number in `listingPagination: 'pages'` mode. Ignored\n * (along with `totalPages`/`totalCount`) unless `config.listingPagination\n * === 'pages'` - the default `'load-more'` behavior never reads these.\n * `initialArticles` should already be this page's slice (see\n * `paginateArticles` from `./server`).\n */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /**\n * True total article count across every page (not just `initialArticles`'\n * length) - used for `CollectionPageSchema.articleCount`. Defaults to\n * `state.articles.length`, matching prior behavior when omitted.\n */\n totalCount?: number\n}>) {\n const state = useArticles(initialArticles, initialCategories)\n const layout = config.layout ?? DEFAULT_LAYOUT\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const categoriesPageSize = config.categoriesPageSize ?? DEFAULT_CATEGORIES_PAGE_SIZE\n const themeVars = buildThemeVars(config.theme)\n\n const isPagesMode =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n const currentPage = page ?? 1\n // The pinned \"featured\" hero only makes sense on page 1 (it always pulls\n // state.articles[0], which on page 2+ would just be that page's first\n // article, not the site's actual latest) - drop it from the layout for\n // page > 1 rather than rendering a misleading featured pick.\n const effectiveLayout =\n isPagesMode && currentPage > 1 ? layout.filter((section) => section !== 'featured') : layout\n\n const hasFeatured = effectiveLayout.includes('featured')\n const articlesWithoutFeatured = hasFeatured ? state.articles.slice(1) : state.articles\n const displayedArticles = state.searchQuery ? state.articles : articlesWithoutFeatured\n\n const pagination: ListingPaginationContext | undefined = isPagesMode\n ? { page: currentPage, totalPages: totalPages as number, basePath: '/articles' }\n : undefined\n\n const ctx: SectionContext = {\n ...state,\n displayedArticles,\n pageSize,\n categoriesPageSize,\n pagination,\n }\n\n const articleCount = totalCount ?? state.articles.length\n\n return (\n <div style={themeVars}>\n {effectiveLayout.map((section) => renderSection(section, ctx, config))}\n {state.articles.length > 0 && (\n <CollectionPageSchema\n title={`Articles | ${config.siteName}`}\n description={`Browse all ${articleCount} articles on ${config.siteName}.`}\n url={`${config.siteUrl}/articles`}\n articleCount={articleCount}\n items={displayedArticles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${config.siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n )}\n </div>\n )\n}\n","import { Fragment } from 'react'\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { Calendar, Clock, User } from 'lucide-react'\nimport type { Article, AuthorProfile } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleDetailHeroProps = Readonly<{\n article: Article\n categoryBasePath?: string\n showDate?: boolean\n showAuthor?: boolean\n authors?: AuthorProfile[]\n /** Optional - when `config.theme.headerFontFamily` is set, applies it to the title. */\n config?: ArticlesConfig\n}>\n\nfunction toSlug(cat: string): string {\n return cat\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n}\n\nfunction resolveAvatarUrl(author: AuthorProfile, config?: ArticlesConfig): string | null {\n if (!author.avatar) return null\n if (author.avatar.startsWith('http://') || author.avatar.startsWith('https://')) {\n return author.avatar\n }\n if (!config?.siteUrl) return null\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n return `${siteUrl}/articles/authors/${author.slug}/${author.avatar.replace(/^\\/+/, '')}`\n}\n\nexport function ArticleDetailHero({\n article,\n categoryBasePath = '/articles/category',\n showDate = false,\n showAuthor = true,\n authors = [],\n config,\n}: ArticleDetailHeroProps) {\n const showConfiguredAuthors = showAuthor && authors.length > 0\n const showLegacyAuthor = showAuthor && authors.length === 0 && article.author.trim().length > 0\n\n return (\n <section\n style={{\n position: 'relative',\n height: '18rem',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n }}\n >\n <Image\n src={article.featuredImage}\n alt={article.title}\n fill\n sizes=\"100vw\"\n className=\"object-cover\"\n style={{ objectFit: 'cover' }}\n priority\n />\n <div\n style={{\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0,0,0,0.6)',\n }}\n />\n <div\n style={{\n position: 'relative',\n zIndex: 10,\n textAlign: 'center',\n color: 'white',\n padding: '0 1rem',\n maxWidth: '56rem',\n margin: '0 auto',\n width: '100%',\n }}\n >\n {article.categories && article.categories.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'center',\n gap: '0.5rem',\n marginBottom: '1rem',\n }}\n >\n {article.categories.slice(0, 4).map((cat) =>\n categoryBasePath ? (\n <Link\n key={cat}\n href={`${categoryBasePath}/${toSlug(cat)}`}\n className=\"inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors\"\n >\n {cat}\n </Link>\n ) : (\n <span\n key={cat}\n className=\"inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full\"\n >\n {cat}\n </span>\n )\n )}\n </div>\n )}\n <h1\n className=\"text-4xl md:text-5xl font-bold mb-4\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n {article.title}\n </h1>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '1.5rem',\n fontSize: '0.875rem',\n color: 'rgba(255,255,255,0.8)',\n }}\n >\n {showDate && article.date && (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <Calendar className=\"h-4 w-4\" />\n <span>{article.date}</span>\n </div>\n )}\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <Clock className=\"h-4 w-4\" />\n <span>{article.readTime}</span>\n </div>\n {showConfiguredAuthors &&\n (() => {\n const singleAvatarUrl =\n authors.length === 1 ? resolveAvatarUrl(authors[0], config) : null\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n {singleAvatarUrl ? (\n <Image\n src={singleAvatarUrl}\n alt=\"\"\n width={20}\n height={20}\n className=\"h-5 w-5 rounded-full object-cover\"\n />\n ) : (\n <User className=\"h-4 w-4\" />\n )}\n <span>\n By{' '}\n {authors.map((author, index) => (\n <Fragment key={author.slug}>\n {index > 0 && ', '}\n <Link\n href={`/articles/authors/${author.slug}`}\n className=\"font-medium text-white underline-offset-4 hover:underline\"\n >\n {author.name}\n </Link>\n </Fragment>\n ))}\n </span>\n </div>\n )\n })()}\n {showLegacyAuthor && (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <User className=\"h-4 w-4\" />\n <span>By {article.author}</span>\n </div>\n )}\n </div>\n {/*\n Phase 27E: one-sentence author authority statement, sourced from\n the configured author's `promise`. Scoped here (the article detail\n byline) rather than `ArticleCard` - a listing grid repeats the same\n author across many cards, so a second line of text per card adds\n clutter without adding trust; the detail page byline is the one\n high-intent placement where establishing authority right as a\n reader commits to the article actually helps. Only renders for a\n single configured author (ambiguous which author's promise \"wins\"\n with more than one) and only when `promise` is set - omitted\n entirely otherwise, so this never changes existing output.\n */}\n {showConfiguredAuthors && authors.length === 1 && authors[0].promise && (\n <p\n style={{\n marginTop: '0.75rem',\n fontSize: '0.9375rem',\n color: 'rgba(255,255,255,0.85)',\n }}\n >\n {authors[0].promise}\n </p>\n )}\n </div>\n </section>\n )\n}\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { Breadcrumb } from './Breadcrumb'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { LatestArticles } from './LatestArticles'\nimport {\n breadcrumbsAreEnabled,\n getBreadcrumbsConfig,\n ArticlesConfig,\n DEFAULT_PAGE_SIZE,\n type CategoryBreadcrumbEntry,\n type CustomBreadcrumbItem,\n} from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport type { Article, BreadcrumbItem } from './articleTypes'\n\nfunction getCategoryDescription(\n config: ArticlesConfig,\n slug: string,\n categoryName: string\n): { short: string; long?: string } {\n const raw = config.categoryDescriptions?.[slug]\n if (!raw) return { short: `Browse all articles in ${categoryName}.` }\n if (typeof raw === 'string') return { short: raw }\n return raw\n}\n\ntype CategoryArticlesPageProps = Readonly<{\n category: string\n articles: Article[]\n config: ArticlesConfig\n /** Current page number in `listingPagination: 'pages'` mode. Ignored (with `totalPages`/`totalCount`) unless `config.listingPagination === 'pages'`. `articles` should already be this page's slice. */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /** True total article count across every page. Defaults to `articles.length`. */\n totalCount?: number\n}>\n\nfunction buildCategoryBreadcrumbItems(\n config: ArticlesConfig,\n categoryName: string\n): BreadcrumbItem[] {\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const breadcrumbConfig = getBreadcrumbsConfig(config)\n const trail = breadcrumbConfig.category ?? ['home', 'articles', 'category']\n return trail.flatMap((entry): BreadcrumbItem[] =>\n buildCategoryBreadcrumbEntry(entry, {\n categoryName,\n siteUrl,\n labels: breadcrumbConfig.labels ?? {},\n })\n )\n}\n\nfunction isCustomBreadcrumbItem(entry: unknown): entry is CustomBreadcrumbItem {\n return typeof entry === 'object' && entry !== null && 'name' in entry && 'url' in entry\n}\n\nfunction resolveCustomBreadcrumbItem(item: CustomBreadcrumbItem, siteUrl: string): BreadcrumbItem {\n if (item.url.startsWith('/')) return { name: item.name, url: `${siteUrl}${item.url}` }\n return { name: item.name, url: item.url }\n}\n\nfunction buildCategoryBreadcrumbEntry(\n entry: CategoryBreadcrumbEntry,\n context: Readonly<{\n categoryName: string\n siteUrl: string\n labels: NonNullable<ReturnType<typeof getBreadcrumbsConfig>['labels']>\n }>\n): BreadcrumbItem[] {\n if (isCustomBreadcrumbItem(entry)) {\n return [resolveCustomBreadcrumbItem(entry, context.siteUrl)]\n }\n if (entry === 'home') return [{ name: context.labels.home ?? 'Home', url: context.siteUrl }]\n if (entry === 'articles') {\n return [{ name: context.labels.articles ?? 'Articles', url: `${context.siteUrl}/articles` }]\n }\n return [{ name: context.categoryName }]\n}\n\nexport function CategoryArticlesPage({\n category,\n articles,\n config,\n page,\n totalPages,\n totalCount,\n}: CategoryArticlesPageProps) {\n if (articles.length === 0) return null\n\n const categoryName = articles[0].category\n const heroImage = articles[0].featuredImage\n const description = getCategoryDescription(config, category, categoryName)\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const breadcrumbConfig = getBreadcrumbsConfig(config)\n const breadcrumbItems = buildCategoryBreadcrumbItems(config, categoryName)\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleCount = totalCount ?? articles.length\n const pagination: ListingPaginationContext | undefined =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n ? { page, totalPages, basePath: `/articles/category/${category}` }\n : undefined\n\n return (\n <div>\n <CollectionPageSchema\n title={`${categoryName} | ${config.siteName}`}\n description={description.short}\n url={`${siteUrl}/articles/category/${category}`}\n articleCount={articleCount}\n items={articles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n {breadcrumbsAreEnabled(config) && (\n <Breadcrumb\n items={breadcrumbItems}\n separator={breadcrumbConfig.separator}\n showSchema={breadcrumbConfig.showSchema !== false}\n />\n )}\n {/* Hero */}\n <section\n style={{\n position: 'relative',\n height: '20rem',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n }}\n >\n <Image\n src={heroImage}\n alt={categoryName}\n fill\n sizes=\"100vw\"\n style={{ objectFit: 'cover' }}\n priority\n />\n <div style={{ position: 'absolute', inset: 0, backgroundColor: 'rgba(0,0,0,0.6)' }} />\n <div\n style={{\n position: 'relative',\n zIndex: 10,\n textAlign: 'center',\n color: 'white',\n padding: '0 1rem',\n }}\n >\n <p className=\"text-sm font-semibold uppercase tracking-widest mb-3 opacity-80\">\n Category\n </p>\n <h1 className=\"text-4xl md:text-5xl font-bold mb-3\">{categoryName}</h1>\n {description.long && (\n <p className=\"text-lg opacity-90 max-w-2xl mx-auto mt-2\">{description.long}</p>\n )}\n <p className=\"text-lg opacity-70 mt-2\">\n {articles.length} article{articles.length === 1 ? '' : 's'}\n </p>\n </div>\n </section>\n\n {/* Articles */}\n <section className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n ← All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">{description.short}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n </div>\n )\n}\n","'use client'\n\nimport Link from 'next/link'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { LatestArticles } from './LatestArticles'\nimport { DEFAULT_PAGE_SIZE, type ArticlesConfig } from './articlesConfig'\nimport type { Article } from './articleTypes'\n\ntype SeriesArticlesPageProps = Readonly<{\n /** Machine-safe series identifier - matches `Article.seriesSlug`. */\n seriesSlug: string\n /** Already resolved via `getArticlesBySeries` (seriesOrder-sorted). */\n articles: Article[]\n config: ArticlesConfig\n}>\n\n/**\n * Lightweight series landing page (Phase 27F) - a thinner sibling of\n * `CategoryArticlesPage` for a `seriesSlug`'s ordered reader journey rather\n * than a category's chronological listing. Displays series position badges\n * (1-indexed, following `getArticlesBySeries`' order) so a reader can see\n * where in the journey each article sits; doesn't attempt real ('pages'\n * mode) pagination - series are expected to be short, curated lists rather\n * than open-ended listings, so `LatestArticles` is used purely for its\n * card grid, sized to show every article without a \"Load more\" step.\n */\nexport function SeriesArticlesPage({ seriesSlug, articles, config }: SeriesArticlesPageProps) {\n if (articles.length === 0) return null\n\n const seriesName = articles[0].series ?? seriesSlug\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const seriesUrl = `${siteUrl}/articles/series/${seriesSlug}`\n\n return (\n <div>\n <CollectionPageSchema\n title={`${seriesName} | ${config.siteName}`}\n description={`Follow the ${seriesName} series on ${config.siteName}.`}\n url={seriesUrl}\n articleCount={articles.length}\n items={articles.map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n <section className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n ← All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">\n {articles.length} article{articles.length === 1 ? '' : 's'} in this series\n </p>\n </div>\n <h1 className=\"text-3xl font-bold text-foreground mb-8\">{seriesName}</h1>\n <LatestArticles\n articles={articles}\n pageSize={Math.max(articles.length, DEFAULT_PAGE_SIZE)}\n />\n </div>\n </section>\n </div>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport Link from 'next/link'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { AuthorDetailHero } from './AuthorDetailHero'\nimport { LatestArticles } from './LatestArticles'\nimport { getAuthorAvatar, getAuthorIdentityUrl, getAuthorSameAs } from './authorUtils'\nimport { emitArticleEvent } from './events'\nimport { CtaViewTracker } from './eventTracking'\nimport { DEFAULT_PAGE_SIZE, getOrganizationId, getPersonId } from './articlesConfig'\nimport type { ArticlesConfig } from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport type { Article, AuthorProfile } from './articleTypes'\n\n/**\n * Ordered section keys for the composable author-page render API (Phase\n * 27E). `'custom'` is the one consumer-supplied slot - pass its content via\n * the `customSection` prop. Passing `sections` is fully opt-in: omitting it\n * keeps `AuthorArticlesPage`'s original output (Person/CollectionPage JSON-LD\n * + article list only, no hero) byte-for-byte unchanged.\n */\nexport type AuthorPageSection =\n | 'hero'\n | 'promise'\n | 'servesWho'\n | 'originStory'\n | 'principles'\n | 'proof'\n | 'cta'\n | 'articles'\n | 'custom'\n\ntype AuthorArticlesPageProps = Readonly<{\n author: AuthorProfile\n articles: Article[]\n config: ArticlesConfig\n /** Current page number in `listingPagination: 'pages'` mode. Ignored (with `totalPages`/`totalCount`) unless `config.listingPagination === 'pages'`. `articles` should already be this page's slice. */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /** True total article count across every page. Defaults to `articles.length` (also used as the `Person` schema's `interactionStatistic` count). */\n totalCount?: number\n /**\n * Ordered list of sections to render (Phase 27E composable render API).\n * When omitted, `AuthorArticlesPage` renders exactly as it did before this\n * prop existed - the article list only, no hero and no rich-profile\n * sections, even if `author` has the new optional fields populated.\n * Include `'hero'` to render `AuthorDetailHero` here instead of composing\n * it separately; each other section renders nothing when its backing\n * field (`author.promise`, `author.servesWho`, etc.) is unset.\n */\n sections?: AuthorPageSection[]\n /** Content for the one `'custom'` slot in `sections`. Ignored unless `sections` includes `'custom'`. */\n customSection?: ReactNode\n}>\n\n/**\n * Topics the author demonstrably writes about, taken from the categories of\n * their own published articles - `knowsAbout` takes subject matter, and\n * before 1.3.0 this field was filled with `config.siteName`, which is a\n * publisher name rather than a topic and told a consumer nothing.\n * `AuthorProfile.knowsAbout`, when set, replaces the derived list.\n */\nfunction getKnowsAbout(author: AuthorProfile, articles: readonly Article[]): string[] {\n if (author.knowsAbout?.length) return [...author.knowsAbout]\n return [...new Set(articles.flatMap((article) => article.categories ?? []))].filter(Boolean)\n}\n\nfunction getPersonSchema(\n author: AuthorProfile,\n config: ArticlesConfig,\n articleCount: number,\n knowsAbout: readonly string[]\n) {\n // Phase 27E audit: `promise`/`servesWho`/`principles`/`credentials`/`proof`\n // are intentionally NOT added here. `promise` and `principles` are\n // audience-facing marketing copy, not encyclopedic facts; `servesWho` is an\n // audience segment, not a `knowsAbout` topic; `credentials`/`proof` are\n // unverifiable/self-reported claims. None meet the bar for schema.org\n // structured data, so `description`/`knowsAbout`/`sameAs`/`image` stay\n // sourced exactly as they were before this phase (bio, siteName, approved\n // social links, resolved avatar).\n // Three distinct URLs, deliberately not collapsed into one:\n // - `@id` is the cross-site identity (see `getAuthorIdentityUrl`); it must\n // match what every article's `author` reference emits, and be absolute so\n // it can resolve to the same node from any page.\n // - `url` is the author's own canonical profile.\n // - `mainEntityOfPage` is *this* page, always - it says where the entity is\n // described, so it can never point at another site.\n const pageUrl = `${config.siteUrl.replace(/\\/$/, '')}/articles/authors/${author.slug}`\n const identityUrl = getAuthorIdentityUrl(author, config) ?? pageUrl\n const authorUrl = author.url ?? pageUrl\n return {\n '@context': 'https://schema.org',\n '@type': 'Person',\n '@id': getPersonId(identityUrl),\n name: author.name,\n description: author.bio,\n url: authorUrl,\n ...(getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }),\n ...(getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }),\n ...(knowsAbout.length > 0 && { knowsAbout: [...knowsAbout] }),\n ...(config.organization && { worksFor: { '@id': getOrganizationId(config) } }),\n mainEntityOfPage: pageUrl,\n interactionStatistic: {\n '@type': 'InteractionCounter',\n interactionType: 'https://schema.org/WriteAction',\n userInteractionCount: articleCount,\n },\n }\n}\n\ntype AuthorSectionContext = Readonly<{\n author: AuthorProfile\n articleCount: number\n articles: Article[]\n pageSize: number\n pagination?: ListingPaginationContext\n customSection?: ReactNode\n config?: ArticlesConfig\n}>\n\nfunction renderHeroSection(author: AuthorProfile, articleCount: number): ReactNode {\n return <AuthorDetailHero key=\"hero\" author={author} articleCount={articleCount} />\n}\n\nfunction renderPromiseSection(author: AuthorProfile): ReactNode {\n return author.promise ? (\n <section key=\"promise\" aria-label=\"Author promise\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-3xl px-4 text-center sm:px-6 lg:px-8\">\n <p className=\"text-xl font-medium text-foreground md:text-2xl\">{author.promise}</p>\n </div>\n </section>\n ) : null\n}\n\nfunction renderServesWhoSection(author: AuthorProfile): ReactNode {\n return author.servesWho && author.servesWho.length > 0 ? (\n <section key=\"servesWho\" aria-label=\"Who this author serves\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-4 text-center text-2xl font-bold text-foreground\">Who I help</h2>\n <ul className=\"grid gap-3 sm:grid-cols-2\">\n {author.servesWho.map((who) => (\n <li\n key={who}\n className=\"rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground\"\n >\n {who}\n </li>\n ))}\n </ul>\n </div>\n </section>\n ) : null\n}\n\nfunction renderOriginStorySection(author: AuthorProfile): ReactNode {\n return author.originStory && author.originStory.length > 0 ? (\n <section key=\"originStory\" aria-label=\"Author origin story\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-6 text-2xl font-bold text-foreground\">My story</h2>\n <div className=\"space-y-6\">\n {author.originStory.map((block, blockIndex) => (\n <div key={block.heading ?? blockIndex}>\n {block.heading && (\n <h3 className=\"mb-2 text-lg font-semibold text-foreground\">{block.heading}</h3>\n )}\n {block.paragraphs.map((paragraph, paragraphIndex) => (\n <p\n key={`${block.heading ?? blockIndex}-${paragraph.slice(0, 40)}-${paragraphIndex}`}\n className=\"mb-3 text-muted-foreground\"\n >\n {paragraph}\n </p>\n ))}\n </div>\n ))}\n </div>\n </div>\n </section>\n ) : null\n}\n\nfunction renderPrinciplesSection(author: AuthorProfile): ReactNode {\n return author.principles && author.principles.length > 0 ? (\n <section key=\"principles\" aria-label=\"Author principles\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-4 text-center text-2xl font-bold text-foreground\">What I believe</h2>\n <ul className=\"space-y-3\">\n {author.principles.map((principle) => (\n <li\n key={principle}\n className=\"rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground\"\n >\n {principle}\n </li>\n ))}\n </ul>\n </div>\n </section>\n ) : null\n}\n\nfunction renderProofSection(author: AuthorProfile): ReactNode {\n return author.proof && author.proof.length > 0 ? (\n <section key=\"proof\" aria-label=\"Author proof\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-4xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-6 text-center text-2xl font-bold text-foreground\">Proof</h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {author.proof.map((item) => (\n <div\n key={`${item.claim}-${item.url ?? item.source ?? ''}`}\n className=\"rounded-lg border border-border bg-card p-4 text-card-foreground\"\n >\n <p className=\"font-medium\">{item.claim}</p>\n {(item.source || item.url) && (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {item.url ? (\n <Link href={item.url} className=\"hover:text-primary hover:underline\">\n {item.source ?? item.url}\n </Link>\n ) : (\n item.source\n )}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n </section>\n ) : null\n}\n\nfunction renderCtaSection(author: AuthorProfile, config?: ArticlesConfig): ReactNode {\n return author.primaryCta ? (\n <CtaViewTracker key=\"cta\" ctaId={`author-cta:${author.slug}`} config={config}>\n <section aria-label=\"Author call to action\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-2xl px-4 text-center sm:px-6 lg:px-8\">\n <Link\n href={author.primaryCta.href}\n onClick={() =>\n emitArticleEvent(config?.onEvent, {\n name: 'cta_clicked',\n ctaId: `author-cta:${author.slug}`,\n })\n }\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground hover:bg-primary/90\"\n >\n {author.primaryCta.label}\n </Link>\n </div>\n </section>\n </CtaViewTracker>\n ) : null\n}\n\nfunction renderArticlesSection(\n author: AuthorProfile,\n articles: Article[],\n pageSize: number,\n pagination?: ListingPaginationContext\n): ReactNode {\n return (\n <section key=\"articles\" aria-label=\"Articles by this author\" className=\"bg-background py-16\">\n <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">Articles by {author.name}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n )\n}\n\nfunction renderAuthorSection(section: AuthorPageSection, ctx: AuthorSectionContext): ReactNode {\n const { author, articleCount, articles, pageSize, pagination, customSection, config } = ctx\n\n switch (section) {\n case 'hero':\n return renderHeroSection(author, articleCount)\n case 'promise':\n return renderPromiseSection(author)\n case 'servesWho':\n return renderServesWhoSection(author)\n case 'originStory':\n return renderOriginStorySection(author)\n case 'principles':\n return renderPrinciplesSection(author)\n case 'proof':\n return renderProofSection(author)\n case 'cta':\n return renderCtaSection(author, config)\n case 'articles':\n return renderArticlesSection(author, articles, pageSize, pagination)\n case 'custom':\n return customSection ?? null\n default:\n return null\n }\n}\n\nexport function AuthorArticlesPage({\n author,\n articles,\n config,\n page,\n totalPages,\n totalCount,\n sections,\n customSection,\n}: AuthorArticlesPageProps) {\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleCount = totalCount ?? articles.length\n const pagination: ListingPaginationContext | undefined =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n ? { page, totalPages, basePath: `/articles/authors/${author.slug}` }\n : undefined\n\n const schemas = (\n <>\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(\n getPersonSchema(author, config, articleCount, getKnowsAbout(author, articles))\n ),\n }}\n />\n <CollectionPageSchema\n title={`${author.name} | ${config.siteName}`}\n description={`Articles by ${author.name} on ${config.siteName}.`}\n url={author.url ?? `${siteUrl}/articles/authors/${author.slug}`}\n articleCount={articleCount}\n items={articles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n </>\n )\n\n if (!sections) {\n return (\n <div>\n {schemas}\n <section className=\"bg-background py-16\">\n <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">Articles by {author.name}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n </div>\n )\n }\n\n const sectionCtx: AuthorSectionContext = {\n author,\n articleCount,\n articles,\n pageSize,\n pagination,\n customSection,\n config,\n }\n\n return (\n <div>\n {schemas}\n {sections.map((section) => renderAuthorSection(section, sectionCtx))}\n </div>\n )\n}\n","import Image from 'next/image'\nimport type { AuthorProfile } from './articleTypes'\nimport { AuthorSocialLinks } from './AuthorCard'\nimport { getAuthorAvatar } from './authorUtils'\n\ntype AuthorDetailHeroProps = Readonly<{\n author: AuthorProfile\n articleCount?: number\n}>\n\nfunction getInitials(name: string): string {\n return name\n .split(' ')\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('')\n}\n\nexport function AuthorDetailHero({ author, articleCount }: AuthorDetailHeroProps) {\n const avatar = getAuthorAvatar(author)\n\n return (\n <section className=\"bg-muted/40 py-16\">\n <div className=\"mx-auto flex max-w-4xl flex-col items-center gap-6 px-4 text-center sm:px-6 lg:px-8\">\n {avatar ? (\n <Image\n src={avatar}\n alt={author.name}\n width={120}\n height={120}\n className=\"h-28 w-28 rounded-full object-cover\"\n priority\n />\n ) : (\n <div className=\"flex h-28 w-28 items-center justify-center rounded-full bg-card text-3xl font-semibold text-muted-foreground shadow-sm\">\n {getInitials(author.name)}\n </div>\n )}\n <div>\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-widest text-muted-foreground\">\n Author\n </p>\n <h1 className=\"text-4xl font-bold text-foreground md:text-5xl\">{author.name}</h1>\n <p className=\"mx-auto mt-4 max-w-2xl text-lg text-muted-foreground\">{author.bio}</p>\n {typeof articleCount === 'number' && (\n <p className=\"mt-3 text-sm text-muted-foreground\">\n {articleCount} article{articleCount === 1 ? '' : 's'}\n </p>\n )}\n </div>\n <AuthorSocialLinks author={author} />\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { useEffect, useRef } from 'react'\nimport type { ReactNode } from 'react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\nimport type { Article } from './articleTypes'\n\ntype ArticleViewTrackerProps = Readonly<{\n article: Pick<Article, 'slug'> & Partial<Pick<Article, 'category' | 'seriesSlug' | 'wordCount'>>\n config?: ArticlesConfig\n}>\n\nconst AVERAGE_WORDS_PER_MINUTE = 200\nconst DEFAULT_MEANINGFUL_READ_MS = 15_000\n\n/**\n * Renders nothing - fires `article_viewed` on mount and `meaningful_read`\n * once, after roughly half the article's estimated read time has elapsed\n * (a deterministic, testable timing approximation rather than scroll-depth\n * tracking, which this package has no reliable cross-app way to measure\n * since it doesn't own the article body's scroll container). Place once on\n * the article detail page alongside `ArticleContent`.\n */\nexport function ArticleViewTracker({ article, config }: ArticleViewTrackerProps) {\n const firedMeaningfulRead = useRef(false)\n\n useEffect(() => {\n emitArticleEvent(config?.onEvent, {\n name: 'article_viewed',\n articleSlug: article.slug,\n category: article.category,\n seriesSlug: article.seriesSlug,\n })\n\n const estimatedMs = article.wordCount\n ? (article.wordCount / AVERAGE_WORDS_PER_MINUTE) * 60_000 * 0.5\n : DEFAULT_MEANINGFUL_READ_MS\n const timer = setTimeout(() => {\n if (firedMeaningfulRead.current) return\n firedMeaningfulRead.current = true\n emitArticleEvent(config?.onEvent, { name: 'meaningful_read', articleSlug: article.slug })\n }, estimatedMs)\n\n return () => clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [article.slug])\n\n return null\n}\n\ntype CtaViewTrackerProps = Readonly<{\n /** Opaque CTA/offer ID - never label text (no PII/marketing copy in event payloads). */\n ctaId: string\n articleSlug?: string\n config?: ArticlesConfig\n children: ReactNode\n}>\n\n/**\n * Wraps any CTA block and fires `cta_viewed` once, the first time at least\n * half of it scrolls into the viewport (`IntersectionObserver`). Falls back\n * to firing immediately when `IntersectionObserver` isn't available (older\n * browsers, non-DOM test environments) rather than never firing.\n */\nexport function CtaViewTracker({ ctaId, articleSlug, config, children }: CtaViewTrackerProps) {\n const ref = useRef<HTMLDivElement | null>(null)\n const fired = useRef(false)\n\n useEffect(() => {\n const node = ref.current\n if (!node) return\n\n function fire() {\n if (fired.current) return\n fired.current = true\n emitArticleEvent(config?.onEvent, { name: 'cta_viewed', ctaId, articleSlug })\n }\n\n if (typeof IntersectionObserver === 'undefined') {\n fire()\n return\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) fire()\n },\n { threshold: 0.5 }\n )\n observer.observe(node)\n return () => observer.disconnect()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ctaId, articleSlug])\n\n return <div ref={ref}>{children}</div>\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { Share2, Copy, Check, Mail, MessageCircle, Users, FileText } from 'lucide-react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\n\ninterface ArticleSocialShareProps {\n readonly title: string\n readonly url: string\n readonly excerpt?: string\n readonly shareMessage?: string\n /** Optional (Phase 27F). Enables `shared` events - requires `articleSlug` too. */\n readonly config?: ArticlesConfig\n /** The article slug being shared. Required to emit `shared`. */\n readonly articleSlug?: string\n}\n\nexport function ArticleSocialShare({\n title,\n url,\n excerpt,\n shareMessage,\n config,\n articleSlug,\n}: ArticleSocialShareProps) {\n const [copied, setCopied] = useState(false)\n\n function trackShare(channel: string) {\n if (!articleSlug) return\n emitArticleEvent(config?.onEvent, { name: 'shared', articleSlug, channel })\n }\n\n const encodedTitle = encodeURIComponent(title)\n const encodedUrl = encodeURIComponent(url)\n const encodedExcerpt = encodeURIComponent(excerpt || '')\n\n const shareLinks = {\n linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,\n facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,\n twitter: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,\n reddit: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,\n email: `mailto:?subject=${encodedTitle}&body=${encodedExcerpt}%0A%0A${encodedUrl}`,\n whatsapp: `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`,\n telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`,\n }\n\n const copyToClipboard = async () => {\n try {\n await navigator.clipboard.writeText(url)\n setCopied(true)\n trackShare('copy-link')\n setTimeout(() => setCopied(false), 2000)\n } catch {\n // clipboard unavailable\n }\n }\n\n const openShareWindow = (shareUrl: string, channel: string) => {\n trackShare(channel)\n globalThis.open(shareUrl, '_blank', 'width=600,height=400,scrollbars=yes,resizable=yes')\n }\n\n const btnClass =\n 'inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground hover:bg-primary/10 hover:border-primary hover:text-primary transition-colors cursor-pointer'\n\n return (\n <div className=\"border-t border-border pt-8 mt-8\">\n <div className=\"flex items-center gap-2 mb-4\">\n <Share2 className=\"h-5 w-5 text-muted-foreground\" />\n <h3 className=\"text-lg font-semibold text-foreground\">Share this article</h3>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.linkedin, 'linkedin')}\n >\n <Users className=\"h-4 w-4\" />\n LinkedIn\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.facebook, 'facebook')}\n >\n <span className=\"text-xs font-bold\">f</span> Facebook\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.twitter, 'twitter')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n Twitter\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.reddit, 'reddit')}\n >\n <FileText className=\"h-4 w-4\" />\n Reddit\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.whatsapp, 'whatsapp')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n WhatsApp\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.telegram, 'telegram')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n Telegram\n </button>\n <a className={btnClass} href={shareLinks.email} onClick={() => trackShare('email')}>\n <Mail className=\"h-4 w-4\" />\n Email\n </a>\n <button type=\"button\" className={btnClass} onClick={copyToClipboard}>\n {copied ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copied ? 'Copied!' : 'Copy Link'}\n </button>\n </div>\n\n {shareMessage && <p className=\"text-sm text-muted-foreground mt-3\">{shareMessage}</p>}\n </div>\n )\n}\n","import Link from 'next/link'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\n\ninterface ArticleNavigationProps {\n readonly previous: { slug: string; title: string } | null\n readonly next: { slug: string; title: string } | null\n readonly basePath: string\n /** The article slug this navigation is shown on. Combined with `pathKey`, enables `path_step_advanced` events. */\n readonly fromSlug?: string\n /**\n * Set when this navigation walks a configured `Path` (as opposed to plain\n * chronological/series adjacency) - fires `path_step_advanced` on click.\n * Omit for ordinary previous/next navigation; no event fires without it.\n */\n readonly pathKey?: string\n readonly config?: ArticlesConfig\n}\n\nfunction truncateTitle(title: string, maxLength = 60): string {\n return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title\n}\n\nexport function ArticleNavigation({\n previous,\n next,\n basePath,\n fromSlug,\n pathKey,\n config,\n}: ArticleNavigationProps) {\n if (!previous && !next) return null\n\n function emitStep(toSlug: string, direction: 'previous' | 'next') {\n if (!pathKey || !fromSlug) return\n emitArticleEvent(config?.onEvent, {\n name: 'path_step_advanced',\n pathKey,\n fromSlug,\n toSlug,\n direction,\n })\n }\n\n return (\n <nav className=\"mt-12 pt-8 border-t border-border\" aria-label=\"Article navigation\">\n <div className=\"flex justify-between gap-4\">\n <div className=\"flex-1 min-w-0\">\n {previous && (\n <Link\n href={`${basePath}/${previous.slug}`}\n onClick={() => emitStep(previous.slug, 'previous')}\n className=\"group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors\"\n >\n <ChevronLeft className=\"h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-muted-foreground mb-1\">Previous Article</div>\n <div className=\"font-medium text-foreground truncate\" title={previous.title}>\n {truncateTitle(previous.title)}\n </div>\n </div>\n </Link>\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n {next && (\n <Link\n href={`${basePath}/${next.slug}`}\n onClick={() => emitStep(next.slug, 'next')}\n className=\"group flex items-center justify-end gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"min-w-0 flex-1 text-right\">\n <div className=\"text-sm text-muted-foreground mb-1\">Next Article</div>\n <div className=\"font-medium text-foreground truncate\" title={next.title}>\n {truncateTitle(next.title)}\n </div>\n </div>\n <ChevronRight className=\"h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0\" />\n </Link>\n )}\n </div>\n </div>\n </nav>\n )\n}\n","import type { Article } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\nimport { ArticleCard } from './ArticleCard'\nimport { emitArticleEvent } from './events'\nimport type { RelatedContentSource } from './server-articles'\n\ntype RelatedArticlesSectionProps = Readonly<{\n articles: Article[]\n category: string\n /** Overrides the default \"More in {category}\" heading - used by `getRelatedContent` callers when `source` is `'path'`/`'series'`. Omit to keep the default category-based heading. */\n heading?: string\n /** Optional (Phase 27F). Enables `related_article_clicked` events - requires `fromSlug` too. */\n config?: ArticlesConfig\n /** The article slug this related section is shown on. Required to emit `related_article_clicked`. */\n fromSlug?: string\n /** Selection source that produced `articles`, from `getRelatedContent`. Defaults to `'category'`. */\n source?: RelatedContentSource\n}>\n\nexport function RelatedArticlesSection({\n articles,\n category,\n heading,\n config,\n fromSlug,\n source = 'category',\n}: RelatedArticlesSectionProps) {\n if (articles.length === 0) return null\n\n return (\n <section className=\"mt-12 pt-8 border-t border-border\" aria-label=\"Related articles\">\n <h2\n className=\"text-xl font-semibold text-foreground mb-6\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n {heading ?? `More in ${category}`}\n </h2>\n <div className=\"grid gap-6 sm:grid-cols-2 lg:grid-cols-3\">\n {articles.map((article) => (\n <div\n key={article.slug}\n onClickCapture={() => {\n if (!fromSlug) return\n emitArticleEvent(config?.onEvent, {\n name: 'related_article_clicked',\n fromSlug,\n toSlug: article.slug,\n source,\n })\n }}\n >\n <ArticleCard article={article} config={config} />\n </div>\n ))}\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\nimport { ArrowLeft } from 'lucide-react'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleBackLinkProps = Readonly<{\n config: Pick<ArticlesConfig, 'showBackToArticles'>\n href?: string\n label?: string\n className?: string\n}>\n\nexport function ArticleBackLink({\n config,\n href = '/articles',\n label = 'Back to Articles',\n className,\n}: ArticleBackLinkProps) {\n if (config.showBackToArticles === false) return null\n\n return (\n <Link\n href={href}\n className={\n className ??\n 'inline-flex items-center gap-2 mb-6 -ml-3 px-3 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors'\n }\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {label}\n </Link>\n )\n}\n","import type { TocItem } from './articleTypes'\n\ntype ArticleTOCProps = Readonly<{ toc: TocItem[]; className?: string }>\n\nexport function ArticleTOC({ toc, className }: ArticleTOCProps) {\n if (!toc.length) return null\n return (\n <nav\n aria-label=\"Table of contents\"\n className={`mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className ?? ''}`}\n >\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n On this page\n </p>\n <ul className=\"space-y-1 text-sm\">\n {toc.map((item) => (\n <li key={item.id} style={{ paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }}>\n <a\n href={`#${item.id}`}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n {item.text}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n","import type { Article } from './articleTypes'\n\ntype ArticleAnswerProps = Readonly<{\n article: Pick<Article, 'answer'>\n /** Heading shown above the answer. Default: `'The short answer'`. */\n label?: string\n className?: string\n}>\n\n/**\n * Renders `article.answer` as a callout above the article body.\n *\n * The same text is emitted as the Article schema's `abstract` and placed at\n * the top of the article's markdown twin, so the passage an answer engine is\n * most likely to lift is also the one a reader sees first. Returns `null`\n * when the article has no `answer`, so it is safe to render unconditionally.\n */\nexport function ArticleAnswer({\n article,\n label = 'The short answer',\n className,\n}: ArticleAnswerProps) {\n if (!article.answer?.trim()) return null\n return (\n <aside\n className={`mb-8 rounded-lg border-l-4 border-primary bg-muted/40 px-6 py-4 ${className ?? ''}`}\n style={{ borderLeftWidth: '4px' }}\n >\n <p className=\"mb-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n {label}\n </p>\n <p className=\"text-base leading-relaxed\">{article.answer}</p>\n </aside>\n )\n}\n","'use client'\nimport { useEffect, useState } from 'react'\nimport { ArrowUp } from 'lucide-react'\n\nexport function ScrollToTop() {\n const [visible, setVisible] = useState(false)\n\n useEffect(() => {\n const onScroll = () => setVisible(globalThis.scrollY > 300)\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n return () => globalThis.removeEventListener('scroll', onScroll)\n }, [])\n\n if (!visible) return null\n\n return (\n <button\n aria-label=\"Scroll to top\"\n onClick={() => globalThis.scrollTo({ top: 0, behavior: 'smooth' })}\n className=\"fixed bottom-8 right-8 z-50 rounded-full bg-primary p-2 shadow-md text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <ArrowUp className=\"h-5 w-5\" />\n </button>\n )\n}\n","'use client'\n\nimport { useSession, signIn } from 'next-auth/react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { CommentForm } from './CommentForm'\nimport { CommentThread } from './CommentThread'\nimport type { CommentsConfig } from './articlesConfig'\nimport type { ArticleCommentWithReplies } from './commentTypes'\n\ninterface CommentsSectionProps {\n readonly articleSlug: string\n readonly config: CommentsConfig\n}\n\nexport function CommentsSection({ articleSlug, config }: CommentsSectionProps) {\n const { data: session, status } = useSession()\n const [comments, setComments] = useState<ArticleCommentWithReplies[]>([])\n const [loading, setLoading] = useState(true)\n const [fetchError, setFetchError] = useState<string | null>(null)\n\n const perArticleEnabled = config.perArticleOverride?.[articleSlug] ?? true\n const enabled = config.enabled && perArticleEnabled\n\n const currentUserId = session?.user?.email ?? session?.user?.name ?? undefined\n\n const fetchComments = useCallback(async () => {\n setLoading(true)\n setFetchError(null)\n try {\n const res = await fetch(`/api/articles/${articleSlug}/comments`)\n if (!res.ok) throw new Error('Failed to load comments')\n const data = (await res.json()) as { comments: ArticleCommentWithReplies[] }\n setComments(data.comments)\n } catch {\n setFetchError('Could not load comments. Please try again.')\n } finally {\n setLoading(false)\n }\n }, [articleSlug])\n\n useEffect(() => {\n if (enabled) {\n fetchComments().catch(() => undefined)\n }\n }, [fetchComments, enabled])\n\n if (!enabled) return null\n\n const count = comments.length\n const label = count === 1 ? '1 comment' : `${count} comments`\n\n return (\n <section aria-label=\"Comments\" className=\"mt-12 pt-8 border-t border-border space-y-6\">\n <h2 className=\"text-xl font-semibold text-foreground\">{loading ? 'Comments' : label}</h2>\n\n {status === 'authenticated' ? (\n <CommentForm articleSlug={articleSlug} onSuccess={fetchComments} />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n <button\n onClick={() => signIn()}\n className=\"text-primary underline hover:no-underline focus:outline-none\"\n >\n Sign in\n </button>{' '}\n to join the discussion.\n </p>\n )}\n\n {fetchError && <p className=\"text-sm text-destructive\">{fetchError}</p>}\n\n {loading ? (\n <p className=\"text-sm text-muted-foreground\">Loading comments…</p>\n ) : (\n <CommentThread\n comments={comments}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n onRefresh={fetchComments}\n />\n )}\n </section>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\n\nconst MAX_LENGTH = 2000\nconst WARN_THRESHOLD = 1800\n\nfunction submitLabel(submitting: boolean, parentId?: string): string {\n if (submitting) return 'Posting…'\n return parentId ? 'Reply' : 'Post comment'\n}\n\ninterface CommentFormProps {\n readonly articleSlug: string\n readonly parentId?: string\n readonly onSuccess: () => void\n readonly onCancel?: () => void\n}\n\nexport function CommentForm({ articleSlug, parentId, onSuccess, onCancel }: CommentFormProps) {\n const [body, setBody] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const remaining = MAX_LENGTH - body.length\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!body.trim() || submitting) return\n\n setSubmitting(true)\n setError(null)\n\n try {\n const res = await fetch(`/api/articles/${articleSlug}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ body: body.trim(), parentId: parentId ?? null }),\n })\n\n if (!res.ok) {\n const data = (await res.json()) as { error?: string }\n setError(data.error ?? 'Failed to post comment')\n return\n }\n\n setBody('')\n onSuccess()\n } catch {\n setError('Network error — please try again')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-2\">\n <textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n maxLength={MAX_LENGTH}\n rows={parentId ? 3 : 4}\n placeholder={parentId ? 'Write a reply…' : 'Join the discussion…'}\n disabled={submitting}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 resize-none\"\n aria-label={parentId ? 'Reply text' : 'Comment text'}\n />\n\n {remaining <= MAX_LENGTH - WARN_THRESHOLD && (\n <p className={`text-xs ${remaining < 0 ? 'text-destructive' : 'text-muted-foreground'}`}>\n {remaining} characters remaining\n </p>\n )}\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n\n <div className=\"flex gap-2\">\n <button\n type=\"submit\"\n disabled={submitting || body.trim().length === 0 || body.length > MAX_LENGTH}\n className=\"rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {submitLabel(submitting, parentId)}\n </button>\n\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-md px-4 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n )}\n </div>\n </form>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { CommentForm } from './CommentForm'\nimport type { ArticleComment, ArticleCommentWithReplies } from './commentTypes'\n\nfunction relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const minutes = Math.floor(diff / 60_000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h ago`\n const days = Math.floor(hours / 24)\n if (days < 30) return `${days}d ago`\n const months = Math.floor(days / 30)\n if (months < 12) return `${months}mo ago`\n return `${Math.floor(months / 12)}y ago`\n}\n\ninterface CommentItemProps {\n readonly comment: ArticleCommentWithReplies | ArticleComment\n readonly articleSlug: string\n readonly currentUserId?: string\n readonly isReply?: boolean\n readonly onRefresh: () => void\n}\n\nexport function CommentItem({\n comment,\n articleSlug,\n currentUserId,\n isReply = false,\n onRefresh,\n}: CommentItemProps) {\n const [showReplyForm, setShowReplyForm] = useState(false)\n const [deleting, setDeleting] = useState(false)\n\n const replies = 'replies' in comment ? comment.replies : []\n const canReply = !isReply && !!currentUserId && !comment.isDeleted\n const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted\n\n async function handleDelete() {\n if (deleting) return\n setDeleting(true)\n try {\n await fetch(`/api/articles/${articleSlug}/comments/${comment.id}`, { method: 'DELETE' })\n onRefresh()\n } finally {\n setDeleting(false)\n }\n }\n\n return (\n <div className={`${isReply ? 'ml-8 border-l-2 border-border pl-4' : ''}`}>\n <div className=\"rounded-lg bg-muted/40 p-3 space-y-1\">\n {comment.isDeleted ? (\n <p className=\"text-sm italic text-muted-foreground\">Comment removed</p>\n ) : (\n <>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-foreground\">{comment.authorName}</span>\n <span className=\"text-xs text-muted-foreground\">\n {relativeTime(comment.createdAt)}\n </span>\n </div>\n <p className=\"text-sm text-foreground whitespace-pre-wrap break-words\">\n {comment.body}\n </p>\n <div className=\"flex gap-3 pt-1\">\n {canReply && (\n <button\n onClick={() => setShowReplyForm((v) => !v)}\n className=\"text-xs text-muted-foreground hover:text-foreground\"\n >\n {showReplyForm ? 'Cancel' : 'Reply'}\n </button>\n )}\n {canDelete && (\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"text-xs text-muted-foreground hover:text-destructive disabled:opacity-50\"\n >\n {deleting ? 'Deleting…' : 'Delete'}\n </button>\n )}\n </div>\n </>\n )}\n </div>\n\n {showReplyForm && (\n <div className=\"mt-2 ml-2\">\n <CommentForm\n articleSlug={articleSlug}\n parentId={comment.id}\n onSuccess={() => {\n setShowReplyForm(false)\n onRefresh()\n }}\n onCancel={() => setShowReplyForm(false)}\n />\n </div>\n )}\n\n {replies.length > 0 && (\n <div className=\"mt-2 space-y-2\">\n {replies.map((reply) => (\n <CommentItem\n key={reply.id}\n comment={{ ...reply, replies: [] }}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n isReply\n onRefresh={onRefresh}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { CommentItem } from './CommentItem'\nimport type { ArticleCommentWithReplies } from './commentTypes'\n\ninterface CommentThreadProps {\n readonly comments: ArticleCommentWithReplies[]\n readonly articleSlug: string\n readonly currentUserId?: string\n readonly onRefresh: () => void\n}\n\nexport function CommentThread({\n comments,\n articleSlug,\n currentUserId,\n onRefresh,\n}: CommentThreadProps) {\n if (comments.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground text-center py-6\">\n No comments yet. Be the first to share your thoughts.\n </p>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {comments.map((comment) => (\n <CommentItem\n key={comment.id}\n comment={comment}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n onRefresh={onRefresh}\n />\n ))}\n </div>\n )\n}\n"],"mappings":"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","names":["Link","Image","import_link","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_link","import_jsx_runtime","Link","import_link","import_image","import_lucide_react","import_jsx_runtime","Link","Image","import_lucide_react","import_react","import_image","import_link","import_lucide_react","import_image","import_link","import_lucide_react","import_jsx_runtime","Link","Image","import_jsx_runtime","Image","Link","import_link","import_lucide_react","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_image","import_link","import_lucide_react","import_jsx_runtime","Image","Link","import_image","import_link","import_jsx_runtime","_a","Image","Link","import_link","import_jsx_runtime","Link","import_link","import_image","import_jsx_runtime","getInitials","Image","import_react","import_jsx_runtime","import_jsx_runtime","_a","Link","import_react","import_lucide_react","import_jsx_runtime","import_link","import_lucide_react","import_jsx_runtime","toSlug","Link","import_jsx_runtime","import_link","import_lucide_react","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","e","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ArticleCategoryGrid.tsx","../src/articlesConfig.ts","../src/authorUtils.ts","../src/Breadcrumb.tsx","../src/ArticleSchemas.tsx","../src/ArticleSearchBar.tsx","../src/ArticlesHero.tsx","../src/FeaturedArticle.tsx","../src/LatestArticlesSection.tsx","../src/LatestArticles.tsx","../src/ArticleCard.tsx","../src/AuthorCard.tsx","../src/events.ts","../src/PaginationNav.tsx","../src/pagination.ts","../src/useArticles.ts","../src/ArticlesPage.tsx","../src/ArticleDetailHero.tsx","../src/CategoryArticlesPage.tsx","../src/SeriesArticlesPage.tsx","../src/AuthorArticlesPage.tsx","../src/AuthorDetailHero.tsx","../src/eventTracking.tsx","../src/ArticleSocialShare.tsx","../src/ArticleNavigation.tsx","../src/RelatedArticlesSection.tsx","../src/ArticleBackLink.tsx","../src/ArticleTOC.tsx","../src/ArticleAnswer.tsx","../src/ScrollToTop.tsx","../src/CommentsSection.tsx","../src/CommentForm.tsx","../src/CommentItem.tsx","../src/CommentThread.tsx"],"sourcesContent":["'use client'\n\n// Components\nexport { ArticlesPage } from './ArticlesPage'\nexport { ArticleCard } from './ArticleCard'\nexport { ArticleCategoryGrid } from './ArticleCategoryGrid'\nexport { ArticlesHero } from './ArticlesHero'\nexport { ArticleDetailHero } from './ArticleDetailHero'\nexport { ArticleSearchBar } from './ArticleSearchBar'\nexport { FeaturedArticle } from './FeaturedArticle'\nexport { LatestArticles } from './LatestArticles'\nexport { LatestArticlesSection } from './LatestArticlesSection'\nexport { CategoryArticlesPage } from './CategoryArticlesPage'\nexport { SeriesArticlesPage } from './SeriesArticlesPage'\nexport { AuthorArticlesPage } from './AuthorArticlesPage'\nexport { AuthorCard, AuthorSocialLinks } from './AuthorCard'\nexport { AuthorDetailHero } from './AuthorDetailHero'\nexport { Breadcrumb, BreadcrumbSchema } from './Breadcrumb'\nexport {\n ArticleSEO,\n CollectionPageSchema,\n FAQPageSchema,\n OrganizationSchema,\n WebSiteSchema,\n} from './ArticleSchemas'\n\nexport { ArticleSocialShare } from './ArticleSocialShare'\nexport { ArticleNavigation } from './ArticleNavigation'\nexport { RelatedArticlesSection } from './RelatedArticlesSection'\nexport { ArticleBackLink } from './ArticleBackLink'\nexport { ArticleTOC } from './ArticleTOC'\nexport { ArticleAnswer } from './ArticleAnswer'\nexport { ScrollToTop } from './ScrollToTop'\nexport { PaginationNav } from './PaginationNav'\nexport { ArticleViewTracker, CtaViewTracker } from './eventTracking'\n\n// Comments\nexport { CommentsSection } from './CommentsSection'\nexport { CommentThread } from './CommentThread'\nexport { CommentItem } from './CommentItem'\nexport { CommentForm } from './CommentForm'\n\n// Hooks\nexport { useArticles } from './useArticles'\nexport type { UseArticlesReturn } from './useArticles'\n\n// Config + types\nexport type {\n ArticlesConfig,\n ArticlesTheme,\n ArticlesSection,\n CategoryDescription,\n CommentsConfig,\n LinkTargetStrategy,\n ListingPagination,\n MdxComponents,\n BreadcrumbsConfig,\n BreadcrumbLabels,\n ArticleBreadcrumbToken,\n CategoryBreadcrumbToken,\n AuthorBreadcrumbToken,\n ArticleBreadcrumbEntry,\n CategoryBreadcrumbEntry,\n AuthorBreadcrumbEntry,\n CustomBreadcrumbItem,\n OrganizationConfig,\n AiCrawlEvent,\n} from './articlesConfig'\nexport {\n DEFAULT_LAYOUT,\n DEFAULT_PAGE_SIZE,\n DEFAULT_CATEGORIES_PAGE_SIZE,\n DEFAULT_TITLE_TEMPLATE,\n breadcrumbsAreEnabled,\n formatPageTitle,\n getBreadcrumbsConfig,\n getOrganizationId,\n getPersonId,\n getWebSiteId,\n} from './articlesConfig'\nexport type {\n Article,\n AuthorProfile,\n AuthorSocial,\n BreadcrumbItem,\n CategoryInfo,\n CitationReference,\n EntityReference,\n FaqItem,\n HowToStep,\n PathDefinition,\n ProofItem,\n RichText,\n RichTextSection,\n TocItem,\n} from './articleTypes'\nexport type { ArticleComment, ArticleCommentWithReplies } from './commentTypes'\nexport type { CollectionPageItem } from './ArticleSchemas'\nexport type { ListingPaginationContext } from './pagination'\nexport type { AuthorPageSection } from './AuthorArticlesPage'\nexport type {\n ArticleEvent,\n ArticleEventHandler,\n ArticleEventName,\n ArticleViewedEvent,\n MeaningfulReadEvent,\n AuthorClickedEvent,\n CtaViewedEvent,\n CtaClickedEvent,\n SharedEvent,\n RelatedArticleClickedEvent,\n PathStepAdvancedEvent,\n} from './events'\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { useState, useEffect } from 'react'\nimport type { CategoryInfo } from './articleTypes'\n\ntype ArticleCategoryGridProps = Readonly<{\n categories: CategoryInfo[]\n pageSize?: number\n}>\n\nexport function ArticleCategoryGrid({ categories, pageSize = 8 }: ArticleCategoryGridProps) {\n const [visibleCount, setVisibleCount] = useState(pageSize)\n\n useEffect(() => {\n setVisibleCount(pageSize)\n }, [categories, pageSize])\n\n if (categories.length === 0) return null\n\n const visible = categories.slice(0, visibleCount)\n const hasMore = visibleCount < categories.length\n\n return (\n <section className=\"py-16 bg-background\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">Browse by Category</h2>\n <p className=\"text-lg text-muted-foreground\">\n Explore our articles by topic to find the insights most relevant to your interests.\n </p>\n </div>\n\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {visible.map((cat) => (\n <Link key={cat.slug} href={`/articles/category/${cat.slug}`}>\n <div className=\"group overflow-hidden rounded-xl border border-border shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer bg-card\">\n <div className=\"overflow-hidden\" style={{ position: 'relative', height: '13rem' }}>\n <Image\n src={cat.featuredImage}\n alt={cat.name}\n fill\n sizes=\"(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-300\"\n />\n </div>\n <div className=\"p-4 text-center\">\n <h3 className=\"font-semibold text-foreground text-base\">{cat.name}</h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {cat.count} {cat.count === 1 ? 'article' : 'articles'}\n </p>\n </div>\n </div>\n </Link>\n ))}\n </div>\n\n {hasMore && (\n <div className=\"mt-10 text-center\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((c) => c + pageSize)}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Load more categories\n </button>\n </div>\n )}\n </div>\n </section>\n )\n}\n","import type { ComponentType } from 'react'\nimport type { AuthorProfile, EntityReference, PathDefinition } from './articleTypes'\nimport type { ArticleEventHandler } from './events'\n\n/** Keys for each renderable section of the articles listing page. */\nexport type ArticlesSection =\n | 'hero'\n | 'search'\n | 'featured'\n | 'latest'\n | 'categories'\n | 'newsletter'\n\n/**\n * CSS custom-property overrides applied to the library's wrapper div.\n * All fields are optional; omitted fields fall back to the consuming app's Tailwind theme.\n */\nexport interface ArticlesTheme {\n /** Font family for the articles section. Example: `\"'Inter', sans-serif\"` */\n fontFamily?: string\n /**\n * Font family for headings only (article title, card titles, section\n * headings) - falls back to `fontFamily` when omitted. Lets a site use a\n * distinct display face for headings (e.g. a serif) while keeping a\n * separate body font, without hardcoding either into the package.\n * Example: `\"'Cinzel', serif\"`\n */\n headerFontFamily?: string\n /** Color for article card titles and section headings. Example: `'#111827'` */\n headerColor?: string\n /** Color for body and excerpt text. Example: `'#6b7280'` */\n textColor?: string\n /** Background color for sections and cards. Example: `'#ffffff'` */\n backgroundColor?: string\n /** Color for \"Read More\" links and inline links. Example: `'#4f46e5'` */\n linkColor?: string\n /** Hover color for links. Example: `'#4338ca'` */\n linkHoverColor?: string\n /** Text decoration for links. Values: `'underline'` | `'none'` */\n linkTextDecoration?: string\n /** Font weight for headings. Example: `700` */\n headerFontWeight?: string | number\n /** Font size for article card titles. Example: `'1.25rem'` */\n headerFontSize?: string\n /** Font size for body text. Example: `'1rem'` */\n bodyFontSize?: string\n /** Line height for body text. Example: `'1.75'` */\n lineHeight?: string\n /** Border radius for cards. Example: `'0.5rem'` */\n borderRadius?: string\n}\n\n/** Per-category text used in meta descriptions and on the category hero page. */\nexport interface CategoryDescription {\n /** Short description — used in meta description and category grid card. */\n short: string\n /** Long description — rendered as a hero paragraph on the category page. */\n long?: string\n}\n\n/** Controls the comments feature. Attach to `ArticlesConfig.comments`. */\nexport interface CommentsConfig {\n /** Set to `true` to enable comments globally across all articles. */\n enabled: boolean\n /** Maximum reply nesting depth. Default: `1` (replies to top-level only). */\n maxDepth?: number\n /**\n * Per-article overrides keyed by slug.\n * Example: `{ 'sensitive-article': false }` disables comments on that article only.\n */\n perArticleOverride?: Record<string, boolean>\n}\n\n/** Text content for the hero section at the top of the articles listing page. */\nexport interface HeroConfig {\n /** Main heading displayed in the hero. Default: `'Vox Populus Insights'` */\n title?: string\n /** Subheading paragraph displayed beneath the title. Default: the built-in description. */\n description?: string\n}\n\n/** Controls how article body links set target/rel attributes. */\nexport type LinkTargetStrategy = 'external-new-tab' | 'all-new-tab' | 'same-tab'\n\n/**\n * Controls how listing pages (the articles index, category pages, author\n * pages) surface articles beyond the first `pageSize`.\n * - `'load-more'` (default): client-only \"Load more\" button, no URL change.\n * Byte-for-byte identical to pre-27D behavior.\n * - `'pages'`: real, directly-navigable paginated routes (`/articles/page/2`,\n * `/articles/category/[category]/page/2`, `/articles/authors/[author]/page/2`)\n * with SSR content, prev/next links, and per-page canonical metadata. The\n * route *files* live in the consuming app - see the pagination primitives\n * exported from `./server` and the `PaginationNav` component.\n */\nexport type ListingPagination = 'load-more' | 'pages'\n\n/** React components that article MDX bodies can reference by JSX tag name. */\nexport type MdxComponents = Record<string, ComponentType<never>>\n\nexport type ArticleBreadcrumbToken =\n | 'home'\n | 'articles'\n | 'primaryCategory'\n | 'folderPath'\n | 'articleTitle'\nexport type CategoryBreadcrumbToken = 'home' | 'articles' | 'category'\nexport type AuthorBreadcrumbToken = 'home' | 'articles' | 'authors' | 'authorName'\n\nexport interface CustomBreadcrumbItem {\n /** Label displayed in the breadcrumb trail. */\n name: string\n /** Custom URL. Relative paths are resolved against `siteUrl` by server builders. */\n url: string\n}\n\nexport type ArticleBreadcrumbEntry = ArticleBreadcrumbToken | CustomBreadcrumbItem\nexport type CategoryBreadcrumbEntry = CategoryBreadcrumbToken | CustomBreadcrumbItem\nexport type AuthorBreadcrumbEntry = AuthorBreadcrumbToken | CustomBreadcrumbItem\n\nexport interface BreadcrumbLabels {\n home?: string\n articles?: string\n authors?: string\n}\n\nexport interface BreadcrumbsConfig {\n /** Set to false to hide visible breadcrumbs and breadcrumb JSON-LD generated by the helper builders. */\n show?: boolean\n /** Separator used by the visible Breadcrumb component. Default: '>'. */\n separator?: string\n /** Set to false to render visible breadcrumbs without JSON-LD. Default: true. */\n showSchema?: boolean\n /** Article breadcrumb trail. Example: ['primaryCategory', { name: 'Guides', url: '/guides' }, 'articleTitle']. */\n article?: ArticleBreadcrumbEntry[]\n /** Category breadcrumb trail. Default: ['home', 'articles', 'category']. */\n category?: CategoryBreadcrumbEntry[]\n /** Author breadcrumb trail. Default: ['home', 'articles', 'authors', 'authorName']. */\n author?: AuthorBreadcrumbEntry[]\n /** Optional label overrides for built-in breadcrumb items. */\n labels?: BreadcrumbLabels\n}\n\n/** Payload passed to `ArticlesConfig.onAiCrawl`. */\nexport interface AiCrawlEvent {\n /** Article slug whose markdown twin was fetched. */\n slug: string\n /** Matched crawler name (e.g. `'GPTBot'`), or `'unknown'` when the agent is not recognized. */\n crawler: string\n /** Raw `User-Agent` header, or an empty string when absent. */\n userAgent: string\n}\n\n/**\n * The publishing entity behind the site. Drives `OrganizationSchema`,\n * `WebSiteSchema`, and the `publisher` reference on every article - so all\n * three point at one `@id` instead of repeating an inline, unlinked\n * `Organization` stub per page.\n */\nexport interface OrganizationConfig {\n /** schema.org type. Use `'Person'` for a personal brand. Default: `'Organization'`. */\n type?: 'Organization' | 'Person'\n /** Entity name. Falls back to `siteName`. */\n name?: string\n /** Entity homepage. Falls back to `siteUrl`. */\n url?: string\n /** Logo URL. Site-relative paths are resolved against `siteUrl`. */\n logo?: string\n /** Entity description. Falls back to `ArticlesConfig.description`. */\n description?: string\n /** Profile URLs that identify the same entity elsewhere (social, Crunchbase, Wikidata). */\n sameAs?: string[]\n /**\n * The umbrella entity this site belongs to, for a network of sites run by\n * one publisher. Emitted as `parentOrganization` - the link that lets\n * authority earned by the network attach to each site in it, instead of\n * each site standing alone.\n */\n parentOrganization?: {\n name: string\n url: string\n }\n /**\n * Search URL template for the `WebSite` `SearchAction`, e.g.\n * `'/search?q={search_term_string}'`. Omitted by default - the library's\n * own search is client-side with no crawlable results URL, so declaring one\n * that does not exist would be a false claim. Only set this if the app\n * actually serves search results at that URL. Must contain the literal\n * `{search_term_string}` placeholder.\n */\n searchUrlTemplate?: string\n}\n\n/** Top-level configuration object. Pass one instance to every library component. */\nexport interface ArticlesConfig {\n /** Canonical base URL of the site, used in metadata and JSON-LD. Example: `'https://yoursite.com'` */\n siteUrl: string\n /** Site name shown in metadata titles and JSON-LD publisher fields. Example: `'Vox Populus'` */\n siteName: string\n /** Number of articles shown per page in the grid and loaded on each \"Load more\" click. Default: `6` */\n pageSize?: number\n /** Number of category cards shown before a \"Load more categories\" button appears. Default: `8` */\n categoriesPageSize?: number\n /**\n * Ordered list of sections to render on the articles listing page.\n * Omit a key to hide that section entirely. Default: `['hero','search','featured','latest','categories']`\n */\n layout?: ArticlesSection[]\n /** CSS custom-property overrides for colors, fonts, and spacing. All fields optional. */\n theme?: ArticlesTheme\n /**\n * Descriptive text for each category, keyed by category slug.\n * A plain string is treated as the short description only.\n * Example: `{ 'campaigns': { short: 'Campaign tips', long: 'Full paragraph...' } }`\n */\n categoryDescriptions?: Record<string, string | CategoryDescription>\n /** Comments configuration. Omit or set `enabled: false` to hide comments entirely. */\n comments?: CommentsConfig\n /** Hero section title and description. Omit to use the built-in defaults. */\n hero?: HeroConfig\n /** Short description used in the RSS feed channel. Falls back to siteName if omitted. */\n description?: string\n /** Set to false to hide the table of contents on article detail pages. Default: true. */\n showToc?: boolean\n /** Set to false to hide the \"Back to Articles\" navigation link on article detail pages. Default: true. */\n showBackToArticles?: boolean\n /** Set to false to hide author names from UI and metadata. Default: true. */\n showAuthor?: boolean\n /** Author profiles keyed by slug. Omit to keep plain string author display. */\n authors?: Record<string, AuthorProfile>\n /** Author slug used when article frontmatter omits author fields. */\n defaultAuthor?: string\n /** Set to false to disable copied author page routes in consuming apps. Default: true. */\n showAuthorPage?: boolean\n /** Set to false to disable breadcrumbs, or pass a config object to customize breadcrumb trails. */\n breadcrumbs?: false | BreadcrumbsConfig\n /** Article body link target behavior. Default: `'external-new-tab'`. */\n linkTargetStrategy?: LinkTargetStrategy\n /** Extra components exposed to article MDX bodies by JSX tag name. */\n mdxComponents?: MdxComponents\n /**\n * Chooses how listing pages surface articles beyond the first `pageSize`.\n * Default: `'load-more'` (unchanged pre-27D behavior). Set to `'pages'` to\n * opt into real, crawlable paginated routes instead.\n */\n listingPagination?: ListingPagination\n /**\n * \"Start here\" curated reader journeys, keyed by an app-chosen path key.\n * Distinct from the label-only `series` field/`seriesSlug` pair - a path\n * can cross series and categories. Every `PathDefinition.articles` slug\n * must exist and not be `draft: true`; validate with `validateArticles`\n * before publishing, since a broken reference produces a dead journey\n * step rather than a build-time failure otherwise.\n */\n paths?: Record<string, PathDefinition>\n /**\n * Template for the `<title>` tag on article, category, series, and author\n * pages. Supports `{title}` and `{siteName}` placeholders.\n * Default: `'{title} | {siteName}'`.\n *\n * Google truncates a result title around 60 characters, and a site name\n * suffix spends that budget on every page. Set `'{title}'` to drop it when\n * your titles are already long and your brand draws little search volume -\n * the suffix is only earning its characters if people search for the brand.\n */\n titleTemplate?: string\n /**\n * BCP 47 language tag for the site's content. Emitted as the Article\n * schema's `inLanguage` and the RSS channel `<language>`. Default: `'en'`.\n */\n language?: string\n /**\n * Set to `false` when articles sit behind a paywall or registration wall.\n * Default: `true`, emitted as the Article schema's `isAccessibleForFree` -\n * an explicit \"this is readable\" signal, since consumers that cannot tell\n * tend to skip suspected-paywalled sources.\n */\n isAccessibleForFree?: boolean\n /**\n * CSS selectors marking the parts of an article suitable for text-to-speech,\n * emitted as the Article schema's `speakable`. Omitted by default - the\n * correct selectors depend on the consuming app's own markup, and guessing\n * them would point at elements that may not exist.\n */\n speakableSelectors?: string[]\n /**\n * Set to `true` to derive `FAQPage` entries from question-shaped `##`\n * headings and the paragraph that follows each one. Default: `false` -\n * turning prose into structured data without the author's intent can\n * promote a rhetorical heading into a published Q&A pair, so this is\n * opt-in. Explicit `faq` frontmatter always wins over derived entries.\n */\n deriveFaqFromHeadings?: boolean\n /**\n * Shared entity vocabulary, keyed by an app-chosen slug. Article `about`\n * entries may reference a key here instead of repeating a name/`sameAs`\n * pair - the point of `about` is that a consumer can resolve one entity\n * across a corpus, which free-text names spelled three different ways\n * defeat. `validateArticles` warns on `about` keys with no registry entry.\n */\n entities?: Record<string, EntityReference>\n /**\n * Where `lastmod` comes from when frontmatter omits it.\n * - `'published'` (default): reuse the publish date, as before 1.3.0.\n * - `'none'`: leave `lastmod` unset, so `dateModified` is omitted rather\n * than repeating a stale publish date.\n * - `'fileMtime'`: read the article file's modification time. Accurate\n * locally; on a CI runner that clones fresh, every file's mtime is the\n * checkout time, which would report the whole corpus as updated today.\n * Only use it where the build preserves mtimes.\n */\n lastmodFallback?: 'published' | 'none' | 'fileMtime'\n /**\n * Called when an AI crawler fetches an article's markdown twin. The one\n * choke point where those requests land, so it is the only place a site\n * can measure whether any of its AI-readable content is being read, and\n * by which bot. No PII: the payload carries the slug, the matched crawler\n * name, and the raw user agent string only.\n */\n onAiCrawl?: (event: AiCrawlEvent) => void\n /**\n * The publishing entity behind the site. Omit to keep the pre-1.3.0 inline\n * `{'@type':'Organization', name: siteName}` publisher stub on articles.\n * Set it to emit `OrganizationSchema`/`WebSiteSchema` in the root layout and\n * have every article, author, and collection page reference the same `@id`.\n */\n organization?: OrganizationConfig\n /**\n * Default `aiCrawl` value for articles whose frontmatter omits the key.\n * Default: `false` (every article stays opted out unless it sets\n * `aiCrawl: true`). Set to `true` on a site whose goal is being cited by\n * answer engines to opt the whole corpus in at once; per-article\n * `aiCrawl: false` still wins and keeps that article blocked.\n */\n aiCrawlDefault?: boolean\n /**\n * Set to `false` to serve article markdown twins as the bare body, with no\n * attribution header. Default: `true` - the twin is prefixed with the\n * title, excerpt, canonical source URL, dates, author, and site name so a\n * model reading `/articles/[slug].md` can attribute it. Only applies when\n * a config is available (i.e. via `getArticleMarkdownResponse`).\n */\n markdownTwinHeader?: boolean\n /**\n * Set to `false` to stop serving the `category/`, `authors/`, and `series/`\n * listing twins with `X-Robots-Tag: noindex`. Default: `true`.\n *\n * Only the listing twins are configurable. Article twins are always\n * `noindex`: an article twin is the same text as exactly one HTML article,\n * so indexing it can only ever split that article's own signal between two\n * URLs. A listing twin is a different case - it is a generated index of a\n * category or author rather than a copy of one page - so a site may have a\n * reason to let it rank, and this leaves that open.\n *\n * Either way AI crawlers can still fetch every twin; `noindex` only removes\n * them from search results, and never blocks the fetch that is the point of\n * publishing them.\n */\n listingTwinNoindex?: boolean\n /**\n * Vendor-neutral event callback (Phase 27F). Fired by components/hooks at\n * meaningful reader-journey moments (see `ArticleEvent` in `events.ts`).\n * No PII in any payload. The package never talks to an analytics/email\n * vendor directly - translate events to PostHog/etc. in this callback.\n */\n onEvent?: ArticleEventHandler\n}\n\nexport const DEFAULT_PAGE_SIZE = 6\nexport const DEFAULT_CATEGORIES_PAGE_SIZE = 8\n\nexport const DEFAULT_LAYOUT: ArticlesSection[] = [\n 'hero',\n 'search',\n 'featured',\n 'latest',\n 'categories',\n]\n\nexport function breadcrumbsAreEnabled(config: ArticlesConfig): boolean {\n return config.breadcrumbs !== false && config.breadcrumbs?.show !== false\n}\n\nexport function getBreadcrumbsConfig(config: ArticlesConfig): BreadcrumbsConfig {\n if (config.breadcrumbs === false) return {}\n return config.breadcrumbs ?? {}\n}\n\n/** Stable `@id` for the site's publishing entity. */\nexport function getOrganizationId(config: Pick<ArticlesConfig, 'siteUrl'>): string {\n return `${config.siteUrl.replace(/\\/$/, '')}/#organization`\n}\n\n/** Stable `@id` for the site itself. */\nexport function getWebSiteId(config: Pick<ArticlesConfig, 'siteUrl'>): string {\n return `${config.siteUrl.replace(/\\/$/, '')}/#website`\n}\n\n/** Stable `@id` for an author, so every article by them resolves to one Person. */\nexport function getPersonId(authorUrl: string): string {\n return `${authorUrl.replace(/\\/$/, '')}#person`\n}\n\n/** Resolves a possibly site-relative asset path against `siteUrl`. */\nexport function resolveEntityUrl(value: string, config: Pick<ArticlesConfig, 'siteUrl'>): string {\n if (/^https?:\\/\\//.test(value)) return value\n return `${config.siteUrl.replace(/\\/$/, '')}/${value.replace(/^\\/+/, '')}`\n}\n\nexport const DEFAULT_TITLE_TEMPLATE = '{title} | {siteName}'\n\n/**\n * Applies `ArticlesConfig.titleTemplate` to a page title. Kept here rather\n * than inlined at each call site so article, category, series, and author\n * pages can never drift apart on how the site name is appended.\n */\nexport function formatPageTitle(\n title: string,\n config: Pick<ArticlesConfig, 'siteName' | 'titleTemplate'>\n): string {\n return (config.titleTemplate ?? DEFAULT_TITLE_TEMPLATE)\n .replaceAll('{title}', title)\n .replaceAll('{siteName}', config.siteName)\n}\n","import type { ArticlesConfig } from './articlesConfig'\nimport type { AuthorProfile, AuthorSocial } from './articleTypes'\n\n/**\n * The canonical identity URL a Person `@id` derives from. Prefers the\n * explicit cross-site `identityUrl`, then `url`, then this site's own author\n * page - so an author who configures nothing keeps a per-site identity, and\n * one who sets `identityUrl` gets the same `@id` on every site.\n */\nexport function getAuthorIdentityUrl(\n author: AuthorProfile,\n config?: Pick<ArticlesConfig, 'siteUrl'>\n): string | undefined {\n if (author.identityUrl) return author.identityUrl\n if (author.url) return author.url\n if (!config) return undefined\n return `${config.siteUrl.replace(/\\/$/, '')}/articles/authors/${author.slug}`\n}\n\nexport function getAuthorUrl(author: AuthorProfile): string {\n return author.url ?? `/articles/authors/${author.slug}`\n}\n\nexport function getAuthorAvatar(\n author: AuthorProfile,\n config?: ArticlesConfig\n): string | undefined {\n if (!author.avatar) return undefined\n if (author.avatar.startsWith('http://') || author.avatar.startsWith('https://')) {\n return author.avatar\n }\n const path = `/articles/authors/${author.slug}/${author.avatar.replace(/^\\/+/, '')}`\n if (!config) return path\n return `${config.siteUrl.replace(/\\/$/, '')}${path}`\n}\n\nexport function getAuthorSameAs(author: AuthorProfile): string[] {\n const derived = getAuthorSocialLinks(author).map((link) => link.href)\n const explicit = author.sameAs?.filter((url) => url.trim() !== '') ?? []\n return [...new Set([...derived, ...explicit])]\n}\n\nexport interface AuthorSocialLink {\n label: string\n href: string\n}\n\nfunction normalizeHandle(value: string): string {\n return value.replace(/^@/, '')\n}\n\nfunction normalizeUrl(value: string, baseUrl?: string): string {\n if (value.startsWith('http://') || value.startsWith('https://')) return value\n if (!baseUrl) return value\n return `${baseUrl}${normalizeHandle(value)}`\n}\n\nfunction getConfiguredSocialLinks(social: AuthorSocial): AuthorSocialLink[] {\n return [\n { label: 'Website', href: social.website ?? '' },\n {\n label: 'Facebook',\n href: social.facebook ? normalizeUrl(social.facebook, 'https://www.facebook.com/') : '',\n },\n {\n label: 'Twitter',\n href: social.twitter ? normalizeUrl(social.twitter, 'https://twitter.com/') : '',\n },\n { label: 'X', href: social.x ? normalizeUrl(social.x, 'https://x.com/') : '' },\n {\n label: 'LinkedIn',\n href: social.linkedin ? normalizeUrl(social.linkedin, 'https://www.linkedin.com/in/') : '',\n },\n {\n label: 'Instagram',\n href: social.instagram ? normalizeUrl(social.instagram, 'https://www.instagram.com/') : '',\n },\n {\n label: 'YouTube',\n href: social.youtube ? normalizeUrl(social.youtube, 'https://www.youtube.com/') : '',\n },\n {\n label: 'TikTok',\n href: social.tiktok ? normalizeUrl(social.tiktok, 'https://www.tiktok.com/@') : '',\n },\n {\n label: 'GitHub',\n href: social.github ? normalizeUrl(social.github, 'https://github.com/') : '',\n },\n {\n label: 'Bluesky',\n href: social.bluesky ? normalizeUrl(social.bluesky, 'https://bsky.app/profile/') : '',\n },\n {\n label: 'Threads',\n href: social.threads ? normalizeUrl(social.threads, 'https://www.threads.net/@') : '',\n },\n { label: 'Mastodon', href: social.mastodon ? normalizeUrl(social.mastodon) : '' },\n {\n label: 'Medium',\n href: social.medium ? normalizeUrl(social.medium, 'https://medium.com/@') : '',\n },\n { label: 'Newsletter', href: social.newsletter ?? '' },\n ]\n}\n\nexport function getAuthorSocialLinks(author: AuthorProfile): AuthorSocialLink[] {\n const social = author.social\n if (!social) return []\n const configuredLinks = getConfiguredSocialLinks(social)\n const otherLinks = Object.entries(social.other ?? {}).map(([label, href]) => ({ label, href }))\n return [...configuredLinks, ...otherLinks].filter((link) => link.href.trim().length > 0)\n}\n","import Link from 'next/link'\nimport type { BreadcrumbItem } from './articleTypes'\n\ntype BreadcrumbProps = Readonly<{\n items: BreadcrumbItem[]\n className?: string\n /** Max-width utility class applied to the breadcrumb list. Default: 'max-w-7xl'. */\n containerClassName?: string\n showSchema?: boolean\n separator?: string\n}>\n\nfunction buildBreadcrumbSchema(items: readonly BreadcrumbItem[]) {\n return {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n ...(item.url && { item: item.url }),\n })),\n }\n}\n\nfunction getDisplayItems(items: readonly BreadcrumbItem[]): BreadcrumbItem[] {\n if (items.length <= 4) return [...items]\n return [items[0], { name: '...' }, ...items.slice(-2)]\n}\n\nexport function Breadcrumb({\n items,\n className = '',\n containerClassName = 'max-w-7xl',\n showSchema = true,\n separator = '>',\n}: BreadcrumbProps) {\n if (items.length === 0) return null\n\n const displayItems = getDisplayItems(items)\n\n return (\n <>\n <nav\n aria-label=\"Breadcrumb\"\n className={`border-b border-border bg-background/80 px-4 py-3 text-sm text-muted-foreground ${className}`}\n >\n <ol className={`mx-auto flex items-center gap-2 overflow-hidden ${containerClassName}`}>\n {displayItems.map((item, index) => {\n const isLast = index === displayItems.length - 1\n const key = `${item.name}-${index}`\n return (\n <li key={key} className=\"flex min-w-0 items-center gap-2\">\n {index > 0 && <span aria-hidden=\"true\">{separator}</span>}\n {item.url && !isLast ? (\n <Link href={item.url} className=\"truncate hover:text-foreground\">\n {item.name}\n </Link>\n ) : (\n <span className=\"truncate\" aria-current={isLast ? 'page' : undefined}>\n {item.name}\n </span>\n )}\n </li>\n )\n })}\n </ol>\n </nav>\n {showSchema && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(buildBreadcrumbSchema(items)) }}\n />\n )}\n </>\n )\n}\n\nexport function BreadcrumbSchema({ items }: Readonly<{ items: BreadcrumbItem[] }>) {\n return <Breadcrumb items={items} showSchema className=\"sr-only\" />\n}\n","import {\n getOrganizationId,\n getPersonId,\n getWebSiteId,\n resolveEntityUrl,\n type ArticlesConfig,\n} from './articlesConfig'\nimport type { Article, AuthorProfile, FaqItem, HowToStep } from './articleTypes'\nimport type { ArticleComment } from './commentTypes'\nimport { getAuthorAvatar, getAuthorIdentityUrl, getAuthorSameAs } from './authorUtils'\n\nexport { BreadcrumbSchema } from './Breadcrumb'\n\nfunction getPersonSchemas(\n article: Article,\n authors?: readonly AuthorProfile[],\n config?: ArticlesConfig\n) {\n let resolvedAuthors: AuthorProfile[] = []\n if (authors?.length) {\n resolvedAuthors = [...authors]\n } else if (article.author) {\n resolvedAuthors = [{ name: article.author, slug: '', bio: '' }]\n }\n // Phase 27E audit: same decision as AuthorArticlesPage's getPersonSchema -\n // the new AuthorProfile fields (promise/servesWho/principles/credentials/\n // proof) are marketing copy, audience segments, or unverifiable claims, not\n // schema.org-appropriate facts, so none of them are added here. `url`/\n // `image`/`sameAs` stay sourced exactly as before (author.url, resolved\n // avatar, approved social links only).\n return resolvedAuthors.map((author) => {\n const identityUrl = getAuthorIdentityUrl(author, config)\n return {\n '@type': 'Person',\n ...(identityUrl && { '@id': getPersonId(identityUrl) }),\n name: author.name,\n ...(author.url && { url: author.url }),\n ...(getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }),\n ...(getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }),\n }\n })\n}\n\ntype ArticleSEOProps = Readonly<{\n article: Article\n articleUrl: string\n siteName: string\n siteLogo?: string\n showAuthor?: boolean\n authors?: AuthorProfile[]\n config?: ArticlesConfig\n /**\n * Top-level comments to emit as `commentCount`/`comment`. Real discussion\n * is a quality signal that is otherwise invisible in structured data.\n * Deleted comments are excluded, and only the author name, body, and\n * timestamp are emitted - never the commenter's id.\n */\n comments?: readonly ArticleComment[]\n}>\n\n// `lastmodFallback: 'none'` opts out of reusing the publish date - an article\n// edited three times with no `lastmod` otherwise reports its original date as\n// `dateModified` forever.\nfunction resolveModifiedAt(\n article: Article,\n publishedAt: string | undefined,\n config?: ArticlesConfig\n): string | undefined {\n if (article.lastmod) return new Date(article.lastmod).toISOString()\n return config?.lastmodFallback === 'none' ? undefined : publishedAt\n}\n\n/**\n * Absolutizes an article image for structured data.\n *\n * `featuredImage` is stored site-relative (`/articles/<slug>/hero.jpg`), and\n * Open Graph already absolutizes it via `resolveImageUrl` - JSON-LD did not,\n * so every article was publishing a relative `ImageObject.url`, which\n * consumers of structured data reject. Prefers `config.siteUrl`, falling back\n * to the origin of `articleUrl` for callers that pass no config.\n *\n * No `width`/`height`: the package never measures the file, and asserting\n * dimensions it has not read would be a guess in structured data.\n */\nfunction resolveSchemaImageUrl(image: string, articleUrl: string, config?: ArticlesConfig): string {\n if (/^https?:\\/\\//.test(image)) return image\n if (config) return resolveEntityUrl(image, config)\n try {\n return `${new URL(articleUrl).origin}/${image.replace(/^\\/+/, '')}`\n } catch {\n return image\n }\n}\n\nfunction buildPublisher(siteName: string, siteLogo?: string, config?: ArticlesConfig) {\n if (config?.organization) return { '@id': getOrganizationId(config) }\n return {\n '@type': 'Organization',\n name: siteName,\n ...(siteLogo && { logo: { '@type': 'ImageObject', url: siteLogo } }),\n }\n}\n\n// `articleType: 'QAPage'` already retypes the Article node, and a QAPage's\n// required shape is a `mainEntity` Question - so the Q&A belongs on that node\n// rather than in a second script that would duplicate one page as two\n// competing entities.\nfunction buildQuestionEntity(article: Article, articleUrl: string, publishedAt?: string) {\n if (article.articleType !== 'QAPage' || !article.answer) return undefined\n return {\n '@type': 'Question',\n name: article.title,\n text: article.excerpt || article.title,\n ...(publishedAt && { dateCreated: publishedAt }),\n answerCount: 1,\n acceptedAnswer: { '@type': 'Answer', text: article.answer, url: articleUrl },\n }\n}\n\nfunction buildCommentFields(comments?: readonly ArticleComment[]) {\n const visible = comments?.filter((comment) => !comment.isDeleted && !comment.parentId) ?? []\n if (visible.length === 0) return {}\n return {\n commentCount: visible.length,\n comment: visible.map((comment) => ({\n '@type': 'Comment',\n text: comment.body,\n dateCreated: comment.createdAt,\n author: { '@type': 'Person', name: comment.authorName },\n })),\n }\n}\n\nfunction buildTopicFields(article: Article, config?: ArticlesConfig) {\n const about = article.about?.map((entity) => ({\n '@type': 'Thing',\n name: entity.name,\n ...(entity.sameAs && { sameAs: entity.sameAs }),\n }))\n const citation = article.citation?.map((source) => ({\n '@type': 'CreativeWork',\n name: source.name,\n ...(source.url && { url: source.url }),\n }))\n const selectors = config?.speakableSelectors\n\n return {\n ...(about?.length && { about }),\n ...(citation?.length && { citation }),\n ...(selectors?.length && {\n speakable: { '@type': 'SpeakableSpecification', cssSelector: selectors },\n }),\n }\n}\n\nfunction buildArticleSchema({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor,\n personSchemas,\n config,\n comments,\n}: Readonly<{\n article: Article\n articleUrl: string\n siteName: string\n siteLogo?: string\n showAuthor: boolean\n personSchemas: ReturnType<typeof getPersonSchemas>\n config?: ArticlesConfig\n comments?: readonly ArticleComment[]\n}>) {\n const publishedAt = article.date ? new Date(article.date).toISOString() : undefined\n const modifiedAt = resolveModifiedAt(article, publishedAt, config)\n const mainEntity = buildQuestionEntity(article, articleUrl, publishedAt)\n\n return {\n '@context': 'https://schema.org',\n '@type': article.articleType || 'Article',\n mainEntityOfPage: { '@type': 'WebPage', '@id': articleUrl },\n headline: article.title,\n image: article.featuredImage\n ? {\n '@type': 'ImageObject',\n url: resolveSchemaImageUrl(article.featuredImage, articleUrl, config),\n }\n : undefined,\n ...(publishedAt && { datePublished: publishedAt }),\n ...(modifiedAt && { dateModified: modifiedAt }),\n ...(showAuthor && personSchemas.length > 0 && { author: personSchemas }),\n publisher: buildPublisher(siteName, siteLogo, config),\n description: article.excerpt,\n ...(article.answer && { abstract: article.answer }),\n articleSection: article.category,\n ...(article.tags?.length && { keywords: article.tags.join(', ') }),\n ...(article.wordCount !== undefined && { wordCount: article.wordCount }),\n ...(article.series && { isPartOf: { '@type': 'Blog', name: article.series } }),\n inLanguage: config?.language ?? 'en',\n isAccessibleForFree: config?.isAccessibleForFree !== false,\n ...buildTopicFields(article, config),\n ...buildCommentFields(comments),\n ...(mainEntity && { mainEntity }),\n }\n}\n\nexport function ArticleSEO({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor = true,\n authors,\n config,\n comments,\n}: ArticleSEOProps) {\n const personSchemas = getPersonSchemas(article, authors, config)\n const structuredData = buildArticleSchema({\n article,\n articleUrl,\n siteName,\n siteLogo,\n showAuthor,\n personSchemas,\n config,\n comments,\n })\n\n return (\n <>\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}\n />\n {article.faq && article.faq.length > 0 && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify({\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: article.faq.map(({ question, answer }: FaqItem) => ({\n '@type': 'Question',\n name: question,\n acceptedAnswer: { '@type': 'Answer', text: answer },\n })),\n }),\n }}\n />\n )}\n {article.howTo && article.howTo.length > 0 && (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify({\n '@context': 'https://schema.org',\n '@type': 'HowTo',\n name: article.title,\n step: article.howTo.map(({ name, text }: HowToStep) => ({\n '@type': 'HowToStep',\n name,\n text,\n })),\n }),\n }}\n />\n )}\n </>\n )\n}\n\ntype FAQPageSchemaProps = Readonly<{\n items: ReadonlyArray<{ question: string; answer: string }>\n}>\n\nexport function FAQPageSchema({ items }: FAQPageSchemaProps) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: items.map(({ question, answer }) => ({\n '@type': 'Question',\n name: question,\n acceptedAnswer: { '@type': 'Answer', text: answer },\n })),\n }\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\nexport type CollectionPageItem = Readonly<{\n position: number\n url: string\n name: string\n}>\n\ntype CollectionPageSchemaProps = Readonly<{\n title: string\n description: string\n url: string\n articleCount: number\n // The articles actually linked in the page's initial HTML (e.g. the first\n // pageSize before \"Load more\"/pagination), not necessarily every article\n // in the underlying collection - matches what a crawler can see.\n items?: CollectionPageItem[]\n}>\n\nexport function CollectionPageSchema({\n title,\n description,\n url,\n articleCount,\n items,\n}: CollectionPageSchemaProps) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'CollectionPage',\n name: title,\n description,\n url,\n numberOfItems: articleCount,\n ...(items &&\n items.length > 0 && {\n mainEntity: {\n '@type': 'ItemList',\n itemListElement: items.map((item) => ({\n '@type': 'ListItem',\n position: item.position,\n url: item.url,\n name: item.name,\n })),\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\ntype OrganizationSchemaProps = Readonly<{ config: ArticlesConfig }>\n\n/**\n * Site-level publisher entity, rendered once in the root layout.\n *\n * Articles reference this by `@id` rather than repeating an inline\n * `Organization` stub, so every page on the site resolves to one entity - the\n * thing that lets a search or answer engine attribute a whole corpus to a\n * single publisher instead of treating each page as an orphan.\n * Returns `null` when `config.organization` is unset.\n */\nexport function OrganizationSchema({ config }: OrganizationSchemaProps) {\n const org = config.organization\n if (!org) return null\n\n const schema = {\n '@context': 'https://schema.org',\n '@type': org.type ?? 'Organization',\n '@id': getOrganizationId(config),\n name: org.name ?? config.siteName,\n url: org.url ?? config.siteUrl.replace(/\\/$/, ''),\n ...(org.logo && {\n logo: { '@type': 'ImageObject', url: resolveEntityUrl(org.logo, config) },\n }),\n ...((org.description ?? config.description) && {\n description: org.description ?? config.description,\n }),\n ...(org.sameAs && org.sameAs.length > 0 && { sameAs: org.sameAs }),\n ...(org.parentOrganization && {\n parentOrganization: {\n '@type': 'Organization',\n name: org.parentOrganization.name,\n url: org.parentOrganization.url,\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n\ntype WebSiteSchemaProps = Readonly<{ config: ArticlesConfig }>\n\n/**\n * Site entity, rendered once in the root layout alongside `OrganizationSchema`.\n * Emits a `SearchAction` only when `organization.searchUrlTemplate` is set -\n * the library's own search is client-side with no crawlable results URL, so\n * declaring one unconditionally would advertise an endpoint that does not exist.\n * Returns `null` when `config.organization` is unset.\n */\nexport function WebSiteSchema({ config }: WebSiteSchemaProps) {\n const org = config.organization\n if (!org) return null\n\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const template = org.searchUrlTemplate\n\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n '@id': getWebSiteId(config),\n name: config.siteName,\n url: siteUrl,\n publisher: { '@id': getOrganizationId(config) },\n ...(config.description && { description: config.description }),\n ...(template && {\n potentialAction: {\n '@type': 'SearchAction',\n target: {\n '@type': 'EntryPoint',\n urlTemplate: resolveEntityUrl(template, config),\n },\n 'query-input': 'required name=search_term_string',\n },\n }),\n }\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )\n}\n","import { Search, X } from 'lucide-react'\n\ntype ArticleSearchBarProps = Readonly<{\n value: string\n onChange: (query: string) => void\n disabled?: boolean\n resultCount?: number\n}>\n\nexport function ArticleSearchBar({\n value,\n onChange,\n disabled,\n resultCount,\n}: ArticleSearchBarProps) {\n return (\n <section className=\"py-8 bg-background border-b border-border\">\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-5 w-5\" />\n <input\n type=\"text\"\n placeholder=\"Search articles (3+ characters required)...\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n disabled={disabled}\n className=\"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 pl-10 pr-10 py-3 text-lg\"\n />\n {value && (\n <button\n type=\"button\"\n onClick={() => onChange('')}\n aria-label=\"Clear search\"\n className=\"absolute right-3 top-1/2 transform -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n {value && (\n <div className=\"mt-2 text-sm text-muted-foreground\">\n {resultCount === 0\n ? 'No articles found matching your search.'\n : `Found ${resultCount} article${resultCount === 1 ? '' : 's'} matching \"${value}\"`}\n </div>\n )}\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\n\nconst DEFAULT_TITLE = 'Vox Populus Insights'\nconst DEFAULT_DESCRIPTION =\n 'Expert analysis, campaign strategies, and voter engagement insights from the frontlines of democracy.'\n\ntype ArticlesHeroProps = Readonly<{\n title?: string\n description?: string\n}>\n\nexport function ArticlesHero({\n title = DEFAULT_TITLE,\n description = DEFAULT_DESCRIPTION,\n}: ArticlesHeroProps) {\n return (\n <section className=\"bg-linear-to-r from-gray-50 to-gray-100 py-16 md:py-24\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"max-w-3xl mx-auto text-center\">\n <h1 className=\"text-4xl md:text-5xl font-bold text-foreground mb-6\">{title}</h1>\n <p className=\"text-xl text-muted-foreground mb-8\">{description}</p>\n <div className=\"flex flex-col sm:flex-row gap-4 justify-center\">\n <Link\n href=\"#latest\"\n className=\"inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors\"\n >\n Browse Latest Articles\n </Link>\n <Link\n href=\"/contact\"\n className=\"inline-flex items-center justify-center px-4 py-2 border border-primary text-primary hover:bg-primary/10 rounded-md font-medium text-sm transition-colors\"\n >\n Contact Our Team\n </Link>\n </div>\n </div>\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\nimport Image from 'next/image'\nimport { ArrowRight, Calendar, Clock, User } from 'lucide-react'\nimport type { Article } from './articleTypes'\n\ntype FeaturedArticleProps = Readonly<{\n article: Article\n showAuthor?: boolean\n}>\n\nexport function FeaturedArticle({ article, showAuthor = true }: FeaturedArticleProps) {\n return (\n <section className=\"py-16 bg-background\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"grid lg:grid-cols-2 gap-12 items-center\">\n <div>\n <span className=\"inline-block bg-primary/10 text-primary text-sm font-medium px-3 py-1 rounded-full mb-4\">\n FEATURED ARTICLE\n </span>\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">{article.title}</h2>\n <p className=\"text-lg text-muted-foreground mb-6\">{article.excerpt}</p>\n <div className=\"flex items-center gap-6 text-sm text-muted-foreground mb-6\">\n {article.date && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4\" />\n <span>{article.date}</span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-4 w-4\" />\n <span>{article.readTime}</span>\n </div>\n {showAuthor && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4\" />\n <span>{article.author}</span>\n </div>\n )}\n </div>\n <Link\n href={`/articles/${article.slug}`}\n className=\"inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors\"\n >\n Read Full Article\n <ArrowRight className=\"ml-2 h-4 w-4\" />\n </Link>\n </div>\n <div className=\"bg-card rounded-lg p-8\">\n <Image\n src={article.featuredImage}\n alt={article.title}\n width={800}\n height={450}\n className=\"w-full h-64 object-cover rounded-lg mb-4\"\n />\n <div className=\"flex gap-2\">\n <span className=\"bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded\">\n {article.category}\n </span>\n </div>\n </div>\n </div>\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { Search } from 'lucide-react'\nimport type { Article } from './articleTypes'\nimport { LatestArticles } from './LatestArticles'\nimport type { ListingPaginationContext } from './pagination'\n\ninterface LatestArticlesSectionProps {\n readonly articles: Article[]\n readonly searchQuery: string\n readonly onClearSearch: () => void\n readonly pageSize?: number\n /**\n * `'pages'` mode pagination context. Ignored while `searchQuery` is set -\n * search results stay unpaginated/client-only in both `listingPagination`\n * modes (search URLs aren't meant to be indexed, so real pagination adds\n * no SEO value there).\n */\n readonly pagination?: ListingPaginationContext\n}\n\nfunction DescriptionText({\n searchQuery,\n count,\n}: {\n readonly searchQuery: string\n readonly count: number\n}) {\n const pluralSuffix = count === 1 ? '' : 's'\n const text = searchQuery\n ? `Found ${count} article${pluralSuffix} matching your search.`\n : 'Stay informed with our latest insights on voter engagement, campaign strategies, and civic technology.'\n return <p className=\"text-lg text-muted-foreground\">{text}</p>\n}\n\nexport function LatestArticlesSection({\n articles,\n searchQuery,\n onClearSearch,\n pageSize = 6,\n pagination,\n}: Readonly<LatestArticlesSectionProps>) {\n return (\n <section id=\"latest\" className=\"py-16 bg-muted/50\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold text-foreground mb-4\">\n {searchQuery ? 'Search Results' : 'Latest Articles'}\n </h2>\n <DescriptionText searchQuery={searchQuery} count={articles.length} />\n </div>\n\n {articles.length === 0 && searchQuery ? (\n <div className=\"text-center py-12\">\n <div className=\"max-w-md mx-auto\">\n <Search className=\"h-12 w-12 text-muted-foreground mx-auto mb-4\" />\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">No articles found</h3>\n <p className=\"text-muted-foreground mb-4\">\n We couldn't find any articles matching "{searchQuery}". Try adjusting\n your search terms.\n </p>\n <button\n type=\"button\"\n onClick={onClearSearch}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Clear search\n </button>\n </div>\n </div>\n ) : (\n <LatestArticles\n articles={articles}\n pageSize={pageSize}\n pagination={searchQuery ? undefined : pagination}\n key={searchQuery}\n />\n )}\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { Article } from './articleTypes'\nimport { ArticleCard } from './ArticleCard'\nimport { PaginationNav } from './PaginationNav'\nimport type { ListingPaginationContext } from './pagination'\n\ninterface LatestArticlesProps {\n readonly articles: Article[]\n readonly pageSize: number\n /**\n * Switches from the default client-only \"Load more\" button to real\n * paginated routes: renders every article in `articles` as-is (the caller\n * has already sliced to the current page, e.g. via `paginateArticles`) and\n * a `PaginationNav` instead of the button. Omit to keep the existing\n * `listingPagination: 'load-more'` behavior, unchanged.\n */\n readonly pagination?: ListingPaginationContext\n}\n\nexport function LatestArticles({ articles, pageSize, pagination }: Readonly<LatestArticlesProps>) {\n const [visibleCount, setVisibleCount] = useState(pageSize)\n\n useEffect(() => {\n setVisibleCount(pageSize)\n }, [articles, pageSize])\n\n if (pagination) {\n return (\n <div>\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8\">\n {articles.map((article) => (\n <ArticleCard key={article.slug} article={article} />\n ))}\n </div>\n <PaginationNav\n basePath={pagination.basePath}\n page={pagination.page}\n totalPages={pagination.totalPages}\n />\n </div>\n )\n }\n\n const visible = articles.slice(0, visibleCount)\n const hasMore = visibleCount < articles.length\n\n return (\n <div>\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8\">\n {visible.map((article) => (\n <ArticleCard key={article.slug} article={article} />\n ))}\n </div>\n {hasMore && (\n <div className=\"mt-10 text-center\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((c) => c + pageSize)}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Load more articles\n </button>\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { ArrowRight, Calendar, Clock } from 'lucide-react'\nimport { getInitials } from './AuthorCard'\nimport { emitArticleEvent } from './events'\nimport type { Article } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleCardProps = Readonly<{\n article: Article\n /** Optional (Phase 27F) - when passed with `config.onEvent`, clicking the author link fires an `author_clicked` event. Omitting it changes nothing. */\n config?: ArticlesConfig\n}>\n\nexport function ArticleCard({ article, config }: ArticleCardProps) {\n return (\n <div className=\"rounded-lg border border-border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-shadow duration-300\">\n <div className=\"p-0\">\n <Image\n src={article.featuredImage}\n alt={article.title}\n className=\"w-full h-48 object-cover rounded-t-lg\"\n width={400}\n height={200}\n />\n </div>\n <div className=\"p-6\">\n <div className=\"flex gap-2 mb-3\">\n <span className=\"bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded\">\n {article.category}\n </span>\n </div>\n <h3\n className=\"text-lg font-semibold leading-none tracking-tight mb-2\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n <Link href={`/articles/${article.slug}`} className=\"hover:text-primary transition-colors\">\n {article.title}\n </Link>\n </h3>\n <p className=\"text-muted-foreground mb-4 line-clamp-3\">{article.excerpt}</p>\n {article.authorSlug && (\n <Link\n href={`/articles/authors/${article.authorSlug}`}\n className=\"mb-4 flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors\"\n onClick={() =>\n emitArticleEvent(config?.onEvent, {\n name: 'author_clicked',\n articleSlug: article.slug,\n authorSlug: article.authorSlug as string,\n })\n }\n >\n {article.authorAvatar ? (\n <Image\n src={article.authorAvatar}\n alt=\"\"\n width={24}\n height={24}\n className=\"h-6 w-6 rounded-full object-cover\"\n />\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground\"\n >\n {getInitials(article.author)}\n </span>\n )}\n <span>{article.author}</span>\n </Link>\n )}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground border-t pt-4\">\n <div className=\"flex items-center gap-3\">\n {article.date && (\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-3 w-3\" />\n <span>{article.date}</span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-3 w-3\" />\n <span>{article.readTime}</span>\n </div>\n </div>\n <Link\n href={`/articles/${article.slug}`}\n className=\"inline-flex items-center justify-center h-8 w-8 p-0 rounded-md hover:bg-accent transition-colors\"\n aria-label={`Read ${article.title}`}\n >\n <ArrowRight className=\"h-4 w-4\" />\n </Link>\n </div>\n </div>\n </div>\n )\n}\n","import Image from 'next/image'\nimport Link from 'next/link'\nimport {\n Facebook,\n Github,\n Globe,\n Instagram,\n Linkedin,\n Mail,\n MessageCircle,\n Twitter,\n Youtube,\n type LucideIcon,\n} from 'lucide-react'\nimport type { AuthorProfile } from './articleTypes'\nimport { getAuthorAvatar, getAuthorSocialLinks, getAuthorUrl } from './authorUtils'\n\ntype AuthorCardProps = Readonly<{\n author: AuthorProfile\n linkToPage?: boolean\n showBio?: boolean\n showSocial?: boolean\n}>\n\nexport function getInitials(name: string): string {\n return name\n .split(' ')\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('')\n}\n\nexport function AuthorCard({\n author,\n linkToPage = true,\n showBio = false,\n showSocial = false,\n}: AuthorCardProps) {\n const avatar = getAuthorAvatar(author)\n const name = linkToPage ? (\n <Link href={getAuthorUrl(author)} className=\"font-medium text-foreground hover:text-primary\">\n {author.name}\n </Link>\n ) : (\n <span className=\"font-medium text-foreground\">{author.name}</span>\n )\n\n return (\n <div className=\"flex items-start gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground\">\n {avatar ? (\n <Image\n src={avatar}\n alt={author.name}\n width={48}\n height={48}\n className=\"h-12 w-12 rounded-full object-cover\"\n />\n ) : (\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-muted-foreground\">\n {getInitials(author.name)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n {name}\n {showBio && author.bio && (\n <p className=\"mt-1 line-clamp-2 text-sm text-muted-foreground\">{author.bio}</p>\n )}\n {showSocial && <AuthorSocialLinks author={author} />}\n </div>\n </div>\n )\n}\n\nconst SOCIAL_ICONS: Record<string, LucideIcon> = {\n Website: Globe,\n Facebook: Facebook,\n Twitter: Twitter,\n X: Twitter,\n LinkedIn: Linkedin,\n Instagram: Instagram,\n YouTube: Youtube,\n GitHub: Github,\n Newsletter: Mail,\n}\n\nfunction getSocialIcon(label: string): LucideIcon {\n return SOCIAL_ICONS[label] ?? MessageCircle\n}\n\nexport function AuthorSocialLinks({ author }: Readonly<{ author: AuthorProfile }>) {\n const links = getAuthorSocialLinks(author)\n if (links.length === 0) return null\n\n return (\n <div className=\"mt-3 flex items-center gap-2\">\n {links.map(({ href, label }) => {\n const Icon = getSocialIcon(label)\n return (\n <Link\n key={`${label}-${href}`}\n href={href}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n aria-label={`${author.name} on ${label}`}\n >\n <Icon className=\"h-4 w-4\" />\n </Link>\n )\n })}\n </div>\n )\n}\n","// Vendor-neutral article event contract (Phase 27F). The package emits\n// these typed events at the right places in existing components/hooks and\n// hands them to `config.onEvent` - it never talks to PostHog/Plunk/any\n// analytics or email vendor directly (see package.json dependencies, which\n// stay clean of them). No PII in any payload: only slugs/IDs/enums, never\n// emails, names-as-identifiers, or free text.\n\nexport type ArticleEventName =\n | 'article_viewed'\n | 'meaningful_read'\n | 'author_clicked'\n | 'cta_viewed'\n | 'cta_clicked'\n | 'shared'\n | 'related_article_clicked'\n | 'path_step_advanced'\n\ninterface ArticleEventBase<Name extends ArticleEventName> {\n name: Name\n /** `Date.now()` at emit time. */\n timestamp: number\n}\n\nexport interface ArticleViewedEvent extends ArticleEventBase<'article_viewed'> {\n articleSlug: string\n category?: string\n seriesSlug?: string\n}\n\n/** Fired once per view after the reader has spent roughly half the article's estimated read time on the page (see `ArticleViewTracker`). */\nexport interface MeaningfulReadEvent extends ArticleEventBase<'meaningful_read'> {\n articleSlug: string\n}\n\nexport interface AuthorClickedEvent extends ArticleEventBase<'author_clicked'> {\n articleSlug: string\n authorSlug: string\n}\n\n/** `ctaId` is `primaryAction.actionId`, an `AuthorProfile.primaryCta` slug, or a `PathDefinition` key - always an app-chosen ID, never label text. */\nexport interface CtaViewedEvent extends ArticleEventBase<'cta_viewed'> {\n ctaId: string\n articleSlug?: string\n}\n\nexport interface CtaClickedEvent extends ArticleEventBase<'cta_clicked'> {\n ctaId: string\n articleSlug?: string\n}\n\nexport interface SharedEvent extends ArticleEventBase<'shared'> {\n articleSlug: string\n /** Share channel key, e.g. `'linkedin'`, `'copy-link'` - never the shared URL/message text. */\n channel: string\n}\n\nexport interface RelatedArticleClickedEvent extends ArticleEventBase<'related_article_clicked'> {\n fromSlug: string\n toSlug: string\n source: 'path' | 'series' | 'category'\n}\n\nexport interface PathStepAdvancedEvent extends ArticleEventBase<'path_step_advanced'> {\n pathKey: string\n fromSlug: string\n toSlug: string\n direction: 'previous' | 'next'\n}\n\nexport type ArticleEvent =\n | ArticleViewedEvent\n | MeaningfulReadEvent\n | AuthorClickedEvent\n | CtaViewedEvent\n | CtaClickedEvent\n | SharedEvent\n | RelatedArticleClickedEvent\n | PathStepAdvancedEvent\n\n/** Register this on `ArticlesConfig.onEvent` to receive every emitted event and translate it to your own analytics stack. */\nexport type ArticleEventHandler = (event: ArticleEvent) => void\n\n// `Omit<ArticleEvent, 'timestamp'>` alone would collapse the discriminated\n// union to its common keys (TypeScript computes `keyof` on a union as the\n// intersection of each member's keys), losing every event-specific field.\n// A distributive conditional type over the naked `T` preserves each\n// member's own shape instead.\ntype DistributiveOmitTimestamp<T> = T extends ArticleEvent ? Omit<T, 'timestamp'> : never\n\n/**\n * Safely invokes `handler` with `event`, stamping `timestamp`. Swallows any\n * error thrown by the consuming app's handler - a broken analytics\n * integration must never break article rendering.\n */\nexport function emitArticleEvent(\n handler: ArticleEventHandler | undefined,\n event: DistributiveOmitTimestamp<ArticleEvent>\n): void {\n if (!handler) return\n try {\n handler({ ...event, timestamp: Date.now() } as ArticleEvent)\n } catch {\n // consuming app's handler errors must never break rendering\n }\n}\n","import Link from 'next/link'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { buildPaginationLinks } from './pagination'\n\ninterface PaginationNavProps {\n readonly basePath: string\n readonly page: number\n readonly totalPages: number\n}\n\n/**\n * Renders real `<a href>` prev/next links between listing pages (used only\n * in `listingPagination: 'pages'` mode), plus `<link rel=\"prev\"/\"next\">`\n * tags for crawlers/tools that still read them.\n *\n * Google stopped using rel=next/prev as an indexing signal in 2019 (Google\n * Search Central's pagination guidance now recommends a unique\n * self-referencing canonical per page - see `generateArticlesIndexPageMetadata`\n * et al in seoUtils.ts - plus real crawlable links between pages, which is\n * what this component's visible Previous/Next links provide). rel=next/prev\n * remains valid HTML and is still read by Bing and some third-party tools,\n * so it's emitted here for free: React 19 hoists `<link>`/`<meta>` elements\n * rendered anywhere in a Server Component tree into `<head>` automatically\n * (not just from layout.js/page.js directly), so no separate Head API call\n * is needed.\n */\nexport function PaginationNav({ basePath, page, totalPages }: PaginationNavProps) {\n if (totalPages <= 1) return null\n\n const { prevUrl, nextUrl } = buildPaginationLinks(basePath, page, totalPages)\n\n return (\n <nav\n className=\"mt-10 flex items-center justify-center gap-4\"\n aria-label=\"Article listing pagination\"\n >\n {prevUrl && <link rel=\"prev\" href={prevUrl} />}\n {nextUrl && <link rel=\"next\" href={nextUrl} />}\n {prevUrl ? (\n <Link\n href={prevUrl}\n className=\"inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n <ChevronLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n Previous\n </Link>\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </span>\n )}\n <span className=\"text-sm text-muted-foreground\">\n Page {page} of {totalPages}\n </span>\n {nextUrl ? (\n <Link\n href={nextUrl}\n className=\"inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Link>\n ) : (\n <span\n aria-hidden=\"true\"\n className=\"inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n )}\n </nav>\n )\n}\n","// Pure pagination math shared by both the server data-prep side (route files\n// slicing articles per page, generateStaticParams) and the client-safe\n// PaginationNav component (building prev/next hrefs). No fs/next dependency\n// so it's safe to import from both `index.ts` and `server.ts` entry points.\nimport type { Article } from './articleTypes'\n\nexport interface PaginatedArticles {\n /** Articles belonging to this page only (already sliced). */\n articles: Article[]\n /** Clamped to the range `[1, totalPages]`. */\n page: number\n totalPages: number\n hasPrevious: boolean\n hasNext: boolean\n}\n\n/** Context threaded from a listing page component down into `LatestArticles`/`PaginationNav` in `'pages'` mode. */\nexport interface ListingPaginationContext {\n page: number\n totalPages: number\n /** Un-paginated route path for this listing, e.g. `/articles` or `/articles/category/campaigns`. */\n basePath: string\n}\n\nexport interface PaginationLinks {\n /** This page's own canonical URL - never points back to page 1 for page > 1. */\n canonicalUrl: string\n prevUrl: string | null\n nextUrl: string | null\n}\n\nexport function getTotalPages(totalCount: number, pageSize: number): number {\n if (totalCount <= 0 || pageSize <= 0) return 1\n return Math.max(1, Math.ceil(totalCount / pageSize))\n}\n\n/** Slices `articles` to the requested page, clamping out-of-range page numbers into `[1, totalPages]`. */\nexport function paginateArticles(\n articles: Article[],\n page: number,\n pageSize: number\n): PaginatedArticles {\n const totalPages = getTotalPages(articles.length, pageSize)\n const requestedPage = Math.trunc(page) || 1\n const clampedPage = Math.min(Math.max(requestedPage, 1), totalPages)\n const start = (clampedPage - 1) * pageSize\n return {\n articles: articles.slice(start, start + pageSize),\n page: clampedPage,\n totalPages,\n hasPrevious: clampedPage > 1,\n hasNext: clampedPage < totalPages,\n }\n}\n\n/** Page 1 is the un-suffixed `basePath` itself; page N>1 is `${basePath}/page/${N}`. */\nexport function buildPageUrl(basePath: string, page: number): string {\n const base = basePath.replace(/\\/$/, '')\n return page > 1 ? `${base}/page/${page}` : base\n}\n\nexport function buildPaginationLinks(\n basePath: string,\n page: number,\n totalPages: number\n): PaginationLinks {\n return {\n canonicalUrl: buildPageUrl(basePath, page),\n prevUrl: page > 1 ? buildPageUrl(basePath, page - 1) : null,\n nextUrl: page < totalPages ? buildPageUrl(basePath, page + 1) : null,\n }\n}\n\n/**\n * Static params for pages 2..totalPages (page 1 has no `/page/1` route - it's\n * served by the un-paginated base route). For nested dynamic segments (e.g.\n * `/articles/category/[category]/page/[page]`), combine this per-category in\n * the consuming app's `generateStaticParams` - see README.\n */\nexport function generateListingPageStaticParams(totalPages: number): { page: string }[] {\n const params: { page: string }[] = []\n for (let page = 2; page <= totalPages; page++) params.push({ page: String(page) })\n return params\n}\n\nexport function parsePageParam(raw: string | undefined | null): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 1\n}\n\nexport function isPageOutOfRange(page: number, totalPages: number): boolean {\n return page < 1 || page > totalPages\n}\n","'use client'\n\nimport { useState, useCallback, useEffect, useRef } from 'react'\nimport { Article, CategoryInfo } from './articleTypes'\n\nexport interface UseArticlesReturn {\n articles: Article[]\n categories: CategoryInfo[]\n searchQuery: string\n loading: boolean\n error: string | null\n handleSearch: (query: string) => void\n}\n\nexport function useArticles(\n initialArticles?: Article[],\n initialCategories?: CategoryInfo[]\n): UseArticlesReturn {\n const [searchQuery, setSearchQuery] = useState('')\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const [articles, setArticles] = useState<Article[]>(initialArticles ?? [])\n const [categories, setCategories] = useState<CategoryInfo[]>(initialCategories ?? [])\n const [loading, setLoading] = useState(initialArticles === undefined)\n const [error, setError] = useState<string | null>(null)\n\n const fetchArticles = useCallback(async (query: string) => {\n try {\n setLoading(true)\n const url = query ? `/api/articles?q=${encodeURIComponent(query)}` : '/api/articles'\n const response = await fetch(url)\n\n if (!response.ok) throw new Error('Failed to fetch articles')\n\n const data = await response.json()\n setArticles(data.articles)\n\n if (!query?.trim()) {\n const catMap = new Map<string, { count: number; featuredImage: string }>()\n for (const article of data.articles as Article[]) {\n for (const cat of article.categories) {\n if (!catMap.has(cat)) {\n catMap.set(cat, { count: 0, featuredImage: article.featuredImage })\n }\n catMap.get(cat)!.count++\n }\n }\n setCategories(\n Array.from(catMap.entries())\n .map(([name, { count, featuredImage }]) => ({\n name,\n slug: name\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, ''),\n count,\n featuredImage,\n }))\n .sort((a, b) => b.count - a.count)\n )\n }\n\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load articles')\n setArticles([])\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n if (initialArticles === undefined) fetchArticles('')\n // Seeded data came from the server render; skip the redundant client fetch on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fetchArticles])\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query)\n if (debounceRef.current) clearTimeout(debounceRef.current)\n if (query === '') {\n fetchArticles('')\n } else if (query.length >= 3) {\n debounceRef.current = setTimeout(() => fetchArticles(query), 1000)\n }\n },\n [fetchArticles]\n )\n\n useEffect(\n () => () => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n },\n []\n )\n\n return { articles, categories, searchQuery, loading, error, handleSearch }\n}\n","'use client'\n\nimport React from 'react'\nimport type { Article, CategoryInfo } from './articleTypes'\nimport { ArticleCategoryGrid } from './ArticleCategoryGrid'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { ArticleSearchBar } from './ArticleSearchBar'\nimport { ArticlesHero } from './ArticlesHero'\nimport { FeaturedArticle } from './FeaturedArticle'\nimport { LatestArticlesSection } from './LatestArticlesSection'\nimport {\n ArticlesConfig,\n ArticlesSection,\n ArticlesTheme,\n DEFAULT_CATEGORIES_PAGE_SIZE,\n DEFAULT_LAYOUT,\n DEFAULT_PAGE_SIZE,\n} from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport { useArticles } from './useArticles'\n\ninterface SectionContext {\n articles: Article[]\n displayedArticles: Article[]\n categories: CategoryInfo[]\n searchQuery: string\n loading: boolean\n error: string | null\n handleSearch: (query: string) => void\n pageSize: number\n categoriesPageSize: number\n pagination?: ListingPaginationContext\n}\n\nfunction buildThemeVars(theme?: ArticlesTheme): React.CSSProperties {\n if (!theme) return {}\n const vars: Record<string, string> = {}\n if (theme.fontFamily) vars['--articles-font-family'] = theme.fontFamily\n if (theme.headerFontFamily) vars['--articles-header-font-family'] = theme.headerFontFamily\n if (theme.headerColor) vars['--articles-header-color'] = theme.headerColor\n if (theme.textColor) vars['--articles-text-color'] = theme.textColor\n if (theme.backgroundColor) vars['--articles-bg-color'] = theme.backgroundColor\n if (theme.linkColor) vars['--articles-link-color'] = theme.linkColor\n if (theme.linkHoverColor) vars['--articles-link-hover-color'] = theme.linkHoverColor\n if (theme.linkTextDecoration) vars['--articles-link-decoration'] = theme.linkTextDecoration\n if (theme.headerFontWeight) vars['--articles-header-font-weight'] = String(theme.headerFontWeight)\n if (theme.headerFontSize) vars['--articles-header-font-size'] = theme.headerFontSize\n if (theme.bodyFontSize) vars['--articles-body-font-size'] = theme.bodyFontSize\n if (theme.lineHeight) vars['--articles-line-height'] = theme.lineHeight\n if (theme.borderRadius) vars['--articles-border-radius'] = theme.borderRadius\n return vars as React.CSSProperties\n}\n\nfunction renderSection(\n section: ArticlesSection,\n ctx: SectionContext,\n config: ArticlesConfig\n): React.ReactNode {\n const {\n articles,\n displayedArticles,\n categories,\n searchQuery,\n loading,\n error,\n handleSearch,\n pageSize,\n categoriesPageSize,\n pagination,\n } = ctx\n\n switch (section) {\n case 'hero':\n return (\n <ArticlesHero\n key=\"hero\"\n title={config.hero?.title}\n description={config.hero?.description}\n />\n )\n\n case 'search':\n return (\n <ArticleSearchBar\n key=\"search\"\n value={searchQuery}\n onChange={handleSearch}\n disabled={loading}\n resultCount={articles.length}\n />\n )\n\n case 'featured':\n return articles.length > 0 && !searchQuery ? (\n <FeaturedArticle\n key=\"featured\"\n article={articles[0]}\n showAuthor={config.showAuthor !== false}\n />\n ) : null\n\n case 'latest':\n if (loading && articles.length === 0) {\n return (\n <section key=\"latest\" id=\"latest\" className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center py-24\">\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4\" />\n <p className=\"text-muted-foreground\">Loading articles...</p>\n </div>\n </div>\n </section>\n )\n }\n if (error) {\n return (\n <section key=\"latest\" id=\"latest\" className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center\">\n <div className=\"bg-red-50 border border-red-200 rounded-lg p-6 max-w-md text-center\">\n <h3 className=\"text-lg font-semibold text-red-800 mb-2\">Error Loading Articles</h3>\n <p className=\"text-red-600 mb-4\">{error}</p>\n <button\n type=\"button\"\n onClick={() => handleSearch('')}\n className=\"inline-flex items-center justify-center px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors\"\n >\n Try again\n </button>\n </div>\n </div>\n </section>\n )\n }\n return (\n <LatestArticlesSection\n key=\"latest\"\n articles={displayedArticles}\n searchQuery={searchQuery}\n onClearSearch={() => handleSearch('')}\n pageSize={pageSize}\n pagination={pagination}\n />\n )\n\n case 'categories':\n return searchQuery ? null : (\n <ArticleCategoryGrid\n key=\"categories\"\n categories={categories}\n pageSize={categoriesPageSize}\n />\n )\n\n // 'newsletter' is intentionally omitted — inject it via the layout prop in the consuming app\n default:\n return null\n }\n}\n\nexport function ArticlesPage({\n config,\n initialArticles,\n initialCategories,\n page,\n totalPages,\n totalCount,\n}: Readonly<{\n config: ArticlesConfig\n initialArticles?: Article[]\n initialCategories?: CategoryInfo[]\n /**\n * Current page number in `listingPagination: 'pages'` mode. Ignored\n * (along with `totalPages`/`totalCount`) unless `config.listingPagination\n * === 'pages'` - the default `'load-more'` behavior never reads these.\n * `initialArticles` should already be this page's slice (see\n * `paginateArticles` from `./server`).\n */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /**\n * True total article count across every page (not just `initialArticles`'\n * length) - used for `CollectionPageSchema.articleCount`. Defaults to\n * `state.articles.length`, matching prior behavior when omitted.\n */\n totalCount?: number\n}>) {\n const state = useArticles(initialArticles, initialCategories)\n const layout = config.layout ?? DEFAULT_LAYOUT\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const categoriesPageSize = config.categoriesPageSize ?? DEFAULT_CATEGORIES_PAGE_SIZE\n const themeVars = buildThemeVars(config.theme)\n\n const isPagesMode =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n const currentPage = page ?? 1\n // The pinned \"featured\" hero only makes sense on page 1 (it always pulls\n // state.articles[0], which on page 2+ would just be that page's first\n // article, not the site's actual latest) - drop it from the layout for\n // page > 1 rather than rendering a misleading featured pick.\n const effectiveLayout =\n isPagesMode && currentPage > 1 ? layout.filter((section) => section !== 'featured') : layout\n\n const hasFeatured = effectiveLayout.includes('featured')\n const articlesWithoutFeatured = hasFeatured ? state.articles.slice(1) : state.articles\n const displayedArticles = state.searchQuery ? state.articles : articlesWithoutFeatured\n\n const pagination: ListingPaginationContext | undefined = isPagesMode\n ? { page: currentPage, totalPages: totalPages as number, basePath: '/articles' }\n : undefined\n\n const ctx: SectionContext = {\n ...state,\n displayedArticles,\n pageSize,\n categoriesPageSize,\n pagination,\n }\n\n const articleCount = totalCount ?? state.articles.length\n\n return (\n <div style={themeVars}>\n {effectiveLayout.map((section) => renderSection(section, ctx, config))}\n {state.articles.length > 0 && (\n <CollectionPageSchema\n title={`Articles | ${config.siteName}`}\n description={`Browse all ${articleCount} articles on ${config.siteName}.`}\n url={`${config.siteUrl}/articles`}\n articleCount={articleCount}\n items={displayedArticles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${config.siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n )}\n </div>\n )\n}\n","import { Fragment } from 'react'\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { Calendar, Clock, User } from 'lucide-react'\nimport type { Article, AuthorProfile } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleDetailHeroProps = Readonly<{\n article: Article\n categoryBasePath?: string\n showDate?: boolean\n showAuthor?: boolean\n authors?: AuthorProfile[]\n /** Optional - when `config.theme.headerFontFamily` is set, applies it to the title. */\n config?: ArticlesConfig\n}>\n\nfunction toSlug(cat: string): string {\n return cat\n .toLowerCase()\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n}\n\nfunction resolveAvatarUrl(author: AuthorProfile, config?: ArticlesConfig): string | null {\n if (!author.avatar) return null\n if (author.avatar.startsWith('http://') || author.avatar.startsWith('https://')) {\n return author.avatar\n }\n if (!config?.siteUrl) return null\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n return `${siteUrl}/articles/authors/${author.slug}/${author.avatar.replace(/^\\/+/, '')}`\n}\n\nexport function ArticleDetailHero({\n article,\n categoryBasePath = '/articles/category',\n showDate = false,\n showAuthor = true,\n authors = [],\n config,\n}: ArticleDetailHeroProps) {\n const showConfiguredAuthors = showAuthor && authors.length > 0\n const showLegacyAuthor = showAuthor && authors.length === 0 && article.author.trim().length > 0\n\n return (\n <section\n style={{\n position: 'relative',\n height: '18rem',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n }}\n >\n <Image\n src={article.featuredImage}\n alt={article.title}\n fill\n sizes=\"100vw\"\n className=\"object-cover\"\n style={{ objectFit: 'cover' }}\n priority\n />\n <div\n style={{\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0,0,0,0.6)',\n }}\n />\n <div\n style={{\n position: 'relative',\n zIndex: 10,\n textAlign: 'center',\n color: 'white',\n padding: '0 1rem',\n maxWidth: '56rem',\n margin: '0 auto',\n width: '100%',\n }}\n >\n {article.categories && article.categories.length > 0 && (\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'center',\n gap: '0.5rem',\n marginBottom: '1rem',\n }}\n >\n {article.categories.slice(0, 4).map((cat) =>\n categoryBasePath ? (\n <Link\n key={cat}\n href={`${categoryBasePath}/${toSlug(cat)}`}\n className=\"inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors\"\n >\n {cat}\n </Link>\n ) : (\n <span\n key={cat}\n className=\"inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full\"\n >\n {cat}\n </span>\n )\n )}\n </div>\n )}\n <h1\n className=\"text-4xl md:text-5xl font-bold mb-4\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n {article.title}\n </h1>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '1.5rem',\n fontSize: '0.875rem',\n color: 'rgba(255,255,255,0.8)',\n }}\n >\n {showDate && article.date && (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <Calendar className=\"h-4 w-4\" />\n <span>{article.date}</span>\n </div>\n )}\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <Clock className=\"h-4 w-4\" />\n <span>{article.readTime}</span>\n </div>\n {showConfiguredAuthors &&\n (() => {\n const singleAvatarUrl =\n authors.length === 1 ? resolveAvatarUrl(authors[0], config) : null\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n {singleAvatarUrl ? (\n <Image\n src={singleAvatarUrl}\n alt=\"\"\n width={20}\n height={20}\n className=\"h-5 w-5 rounded-full object-cover\"\n />\n ) : (\n <User className=\"h-4 w-4\" />\n )}\n <span>\n By{' '}\n {authors.map((author, index) => (\n <Fragment key={author.slug}>\n {index > 0 && ', '}\n <Link\n href={`/articles/authors/${author.slug}`}\n className=\"font-medium text-white underline-offset-4 hover:underline\"\n >\n {author.name}\n </Link>\n </Fragment>\n ))}\n </span>\n </div>\n )\n })()}\n {showLegacyAuthor && (\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <User className=\"h-4 w-4\" />\n <span>By {article.author}</span>\n </div>\n )}\n </div>\n {/*\n Phase 27E: one-sentence author authority statement, sourced from\n the configured author's `promise`. Scoped here (the article detail\n byline) rather than `ArticleCard` - a listing grid repeats the same\n author across many cards, so a second line of text per card adds\n clutter without adding trust; the detail page byline is the one\n high-intent placement where establishing authority right as a\n reader commits to the article actually helps. Only renders for a\n single configured author (ambiguous which author's promise \"wins\"\n with more than one) and only when `promise` is set - omitted\n entirely otherwise, so this never changes existing output.\n */}\n {showConfiguredAuthors && authors.length === 1 && authors[0].promise && (\n <p\n style={{\n marginTop: '0.75rem',\n fontSize: '0.9375rem',\n color: 'rgba(255,255,255,0.85)',\n }}\n >\n {authors[0].promise}\n </p>\n )}\n </div>\n </section>\n )\n}\n","'use client'\n\nimport Image from 'next/image'\nimport Link from 'next/link'\nimport { Breadcrumb } from './Breadcrumb'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { LatestArticles } from './LatestArticles'\nimport {\n breadcrumbsAreEnabled,\n getBreadcrumbsConfig,\n ArticlesConfig,\n DEFAULT_PAGE_SIZE,\n type CategoryBreadcrumbEntry,\n type CustomBreadcrumbItem,\n} from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport type { Article, BreadcrumbItem } from './articleTypes'\n\nfunction getCategoryDescription(\n config: ArticlesConfig,\n slug: string,\n categoryName: string\n): { short: string; long?: string } {\n const raw = config.categoryDescriptions?.[slug]\n if (!raw) return { short: `Browse all articles in ${categoryName}.` }\n if (typeof raw === 'string') return { short: raw }\n return raw\n}\n\ntype CategoryArticlesPageProps = Readonly<{\n category: string\n articles: Article[]\n config: ArticlesConfig\n /** Current page number in `listingPagination: 'pages'` mode. Ignored (with `totalPages`/`totalCount`) unless `config.listingPagination === 'pages'`. `articles` should already be this page's slice. */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /** True total article count across every page. Defaults to `articles.length`. */\n totalCount?: number\n}>\n\nfunction buildCategoryBreadcrumbItems(\n config: ArticlesConfig,\n categoryName: string\n): BreadcrumbItem[] {\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const breadcrumbConfig = getBreadcrumbsConfig(config)\n const trail = breadcrumbConfig.category ?? ['home', 'articles', 'category']\n return trail.flatMap((entry): BreadcrumbItem[] =>\n buildCategoryBreadcrumbEntry(entry, {\n categoryName,\n siteUrl,\n labels: breadcrumbConfig.labels ?? {},\n })\n )\n}\n\nfunction isCustomBreadcrumbItem(entry: unknown): entry is CustomBreadcrumbItem {\n return typeof entry === 'object' && entry !== null && 'name' in entry && 'url' in entry\n}\n\nfunction resolveCustomBreadcrumbItem(item: CustomBreadcrumbItem, siteUrl: string): BreadcrumbItem {\n if (item.url.startsWith('/')) return { name: item.name, url: `${siteUrl}${item.url}` }\n return { name: item.name, url: item.url }\n}\n\nfunction buildCategoryBreadcrumbEntry(\n entry: CategoryBreadcrumbEntry,\n context: Readonly<{\n categoryName: string\n siteUrl: string\n labels: NonNullable<ReturnType<typeof getBreadcrumbsConfig>['labels']>\n }>\n): BreadcrumbItem[] {\n if (isCustomBreadcrumbItem(entry)) {\n return [resolveCustomBreadcrumbItem(entry, context.siteUrl)]\n }\n if (entry === 'home') return [{ name: context.labels.home ?? 'Home', url: context.siteUrl }]\n if (entry === 'articles') {\n return [{ name: context.labels.articles ?? 'Articles', url: `${context.siteUrl}/articles` }]\n }\n return [{ name: context.categoryName }]\n}\n\nexport function CategoryArticlesPage({\n category,\n articles,\n config,\n page,\n totalPages,\n totalCount,\n}: CategoryArticlesPageProps) {\n if (articles.length === 0) return null\n\n const categoryName = articles[0].category\n const heroImage = articles[0].featuredImage\n const description = getCategoryDescription(config, category, categoryName)\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const breadcrumbConfig = getBreadcrumbsConfig(config)\n const breadcrumbItems = buildCategoryBreadcrumbItems(config, categoryName)\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleCount = totalCount ?? articles.length\n const pagination: ListingPaginationContext | undefined =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n ? { page, totalPages, basePath: `/articles/category/${category}` }\n : undefined\n\n return (\n <div>\n <CollectionPageSchema\n title={`${categoryName} | ${config.siteName}`}\n description={description.short}\n url={`${siteUrl}/articles/category/${category}`}\n articleCount={articleCount}\n items={articles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n {breadcrumbsAreEnabled(config) && (\n <Breadcrumb\n items={breadcrumbItems}\n separator={breadcrumbConfig.separator}\n showSchema={breadcrumbConfig.showSchema !== false}\n />\n )}\n {/* Hero */}\n <section\n style={{\n position: 'relative',\n height: '20rem',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n }}\n >\n <Image\n src={heroImage}\n alt={categoryName}\n fill\n sizes=\"100vw\"\n style={{ objectFit: 'cover' }}\n priority\n />\n <div style={{ position: 'absolute', inset: 0, backgroundColor: 'rgba(0,0,0,0.6)' }} />\n <div\n style={{\n position: 'relative',\n zIndex: 10,\n textAlign: 'center',\n color: 'white',\n padding: '0 1rem',\n }}\n >\n <p className=\"text-sm font-semibold uppercase tracking-widest mb-3 opacity-80\">\n Category\n </p>\n <h1 className=\"text-4xl md:text-5xl font-bold mb-3\">{categoryName}</h1>\n {description.long && (\n <p className=\"text-lg opacity-90 max-w-2xl mx-auto mt-2\">{description.long}</p>\n )}\n <p className=\"text-lg opacity-70 mt-2\">\n {articles.length} article{articles.length === 1 ? '' : 's'}\n </p>\n </div>\n </section>\n\n {/* Articles */}\n <section className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n ← All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">{description.short}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n </div>\n )\n}\n","'use client'\n\nimport Link from 'next/link'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { LatestArticles } from './LatestArticles'\nimport { DEFAULT_PAGE_SIZE, type ArticlesConfig } from './articlesConfig'\nimport type { Article } from './articleTypes'\n\ntype SeriesArticlesPageProps = Readonly<{\n /** Machine-safe series identifier - matches `Article.seriesSlug`. */\n seriesSlug: string\n /** Already resolved via `getArticlesBySeries` (seriesOrder-sorted). */\n articles: Article[]\n config: ArticlesConfig\n}>\n\n/**\n * Lightweight series landing page (Phase 27F) - a thinner sibling of\n * `CategoryArticlesPage` for a `seriesSlug`'s ordered reader journey rather\n * than a category's chronological listing. Displays series position badges\n * (1-indexed, following `getArticlesBySeries`' order) so a reader can see\n * where in the journey each article sits; doesn't attempt real ('pages'\n * mode) pagination - series are expected to be short, curated lists rather\n * than open-ended listings, so `LatestArticles` is used purely for its\n * card grid, sized to show every article without a \"Load more\" step.\n */\nexport function SeriesArticlesPage({ seriesSlug, articles, config }: SeriesArticlesPageProps) {\n if (articles.length === 0) return null\n\n const seriesName = articles[0].series ?? seriesSlug\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const seriesUrl = `${siteUrl}/articles/series/${seriesSlug}`\n\n return (\n <div>\n <CollectionPageSchema\n title={`${seriesName} | ${config.siteName}`}\n description={`Follow the ${seriesName} series on ${config.siteName}.`}\n url={seriesUrl}\n articleCount={articles.length}\n items={articles.map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n <section className=\"py-16 bg-muted/50 flex-1\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n ← All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">\n {articles.length} article{articles.length === 1 ? '' : 's'} in this series\n </p>\n </div>\n <h1 className=\"text-3xl font-bold text-foreground mb-8\">{seriesName}</h1>\n <LatestArticles\n articles={articles}\n pageSize={Math.max(articles.length, DEFAULT_PAGE_SIZE)}\n />\n </div>\n </section>\n </div>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport Link from 'next/link'\nimport { CollectionPageSchema } from './ArticleSchemas'\nimport { AuthorDetailHero } from './AuthorDetailHero'\nimport { LatestArticles } from './LatestArticles'\nimport { getAuthorAvatar, getAuthorIdentityUrl, getAuthorSameAs } from './authorUtils'\nimport { emitArticleEvent } from './events'\nimport { CtaViewTracker } from './eventTracking'\nimport { DEFAULT_PAGE_SIZE, getOrganizationId, getPersonId } from './articlesConfig'\nimport type { ArticlesConfig } from './articlesConfig'\nimport type { ListingPaginationContext } from './pagination'\nimport type { Article, AuthorProfile } from './articleTypes'\n\n/**\n * Ordered section keys for the composable author-page render API (Phase\n * 27E). `'custom'` is the one consumer-supplied slot - pass its content via\n * the `customSection` prop. Passing `sections` is fully opt-in: omitting it\n * keeps `AuthorArticlesPage`'s original output (Person/CollectionPage JSON-LD\n * + article list only, no hero) byte-for-byte unchanged.\n */\nexport type AuthorPageSection =\n | 'hero'\n | 'promise'\n | 'servesWho'\n | 'originStory'\n | 'principles'\n | 'proof'\n | 'cta'\n | 'articles'\n | 'custom'\n\ntype AuthorArticlesPageProps = Readonly<{\n author: AuthorProfile\n articles: Article[]\n config: ArticlesConfig\n /** Current page number in `listingPagination: 'pages'` mode. Ignored (with `totalPages`/`totalCount`) unless `config.listingPagination === 'pages'`. `articles` should already be this page's slice. */\n page?: number\n /** Total page count in `'pages'` mode, from `getTotalPages`. */\n totalPages?: number\n /** True total article count across every page. Defaults to `articles.length` (also used as the `Person` schema's `interactionStatistic` count). */\n totalCount?: number\n /**\n * Ordered list of sections to render (Phase 27E composable render API).\n * When omitted, `AuthorArticlesPage` renders exactly as it did before this\n * prop existed - the article list only, no hero and no rich-profile\n * sections, even if `author` has the new optional fields populated.\n * Include `'hero'` to render `AuthorDetailHero` here instead of composing\n * it separately; each other section renders nothing when its backing\n * field (`author.promise`, `author.servesWho`, etc.) is unset.\n */\n sections?: AuthorPageSection[]\n /** Content for the one `'custom'` slot in `sections`. Ignored unless `sections` includes `'custom'`. */\n customSection?: ReactNode\n}>\n\n/**\n * Topics the author demonstrably writes about, taken from the categories of\n * their own published articles - `knowsAbout` takes subject matter, and\n * before 1.3.0 this field was filled with `config.siteName`, which is a\n * publisher name rather than a topic and told a consumer nothing.\n * `AuthorProfile.knowsAbout`, when set, replaces the derived list.\n */\nfunction getKnowsAbout(author: AuthorProfile, articles: readonly Article[]): string[] {\n if (author.knowsAbout?.length) return [...author.knowsAbout]\n return [...new Set(articles.flatMap((article) => article.categories ?? []))].filter(Boolean)\n}\n\nfunction getPersonSchema(\n author: AuthorProfile,\n config: ArticlesConfig,\n articleCount: number,\n knowsAbout: readonly string[]\n) {\n // Phase 27E audit: `promise`/`servesWho`/`principles`/`credentials`/`proof`\n // are intentionally NOT added here. `promise` and `principles` are\n // audience-facing marketing copy, not encyclopedic facts; `servesWho` is an\n // audience segment, not a `knowsAbout` topic; `credentials`/`proof` are\n // unverifiable/self-reported claims. None meet the bar for schema.org\n // structured data, so `description`/`knowsAbout`/`sameAs`/`image` stay\n // sourced exactly as they were before this phase (bio, siteName, approved\n // social links, resolved avatar).\n // Three distinct URLs, deliberately not collapsed into one:\n // - `@id` is the cross-site identity (see `getAuthorIdentityUrl`); it must\n // match what every article's `author` reference emits, and be absolute so\n // it can resolve to the same node from any page.\n // - `url` is the author's own canonical profile.\n // - `mainEntityOfPage` is *this* page, always - it says where the entity is\n // described, so it can never point at another site.\n const pageUrl = `${config.siteUrl.replace(/\\/$/, '')}/articles/authors/${author.slug}`\n const identityUrl = getAuthorIdentityUrl(author, config) ?? pageUrl\n const authorUrl = author.url ?? pageUrl\n return {\n '@context': 'https://schema.org',\n '@type': 'Person',\n '@id': getPersonId(identityUrl),\n name: author.name,\n description: author.bio,\n url: authorUrl,\n ...(getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }),\n ...(getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }),\n ...(knowsAbout.length > 0 && { knowsAbout: [...knowsAbout] }),\n ...(config.organization && { worksFor: { '@id': getOrganizationId(config) } }),\n mainEntityOfPage: pageUrl,\n interactionStatistic: {\n '@type': 'InteractionCounter',\n interactionType: 'https://schema.org/WriteAction',\n userInteractionCount: articleCount,\n },\n }\n}\n\ntype AuthorSectionContext = Readonly<{\n author: AuthorProfile\n articleCount: number\n articles: Article[]\n pageSize: number\n pagination?: ListingPaginationContext\n customSection?: ReactNode\n config?: ArticlesConfig\n}>\n\nfunction renderHeroSection(author: AuthorProfile, articleCount: number): ReactNode {\n return <AuthorDetailHero key=\"hero\" author={author} articleCount={articleCount} />\n}\n\nfunction renderPromiseSection(author: AuthorProfile): ReactNode {\n return author.promise ? (\n <section key=\"promise\" aria-label=\"Author promise\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-3xl px-4 text-center sm:px-6 lg:px-8\">\n <p className=\"text-xl font-medium text-foreground md:text-2xl\">{author.promise}</p>\n </div>\n </section>\n ) : null\n}\n\nfunction renderServesWhoSection(author: AuthorProfile): ReactNode {\n return author.servesWho && author.servesWho.length > 0 ? (\n <section key=\"servesWho\" aria-label=\"Who this author serves\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-4 text-center text-2xl font-bold text-foreground\">Who I help</h2>\n <ul className=\"grid gap-3 sm:grid-cols-2\">\n {author.servesWho.map((who) => (\n <li\n key={who}\n className=\"rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground\"\n >\n {who}\n </li>\n ))}\n </ul>\n </div>\n </section>\n ) : null\n}\n\nfunction renderOriginStorySection(author: AuthorProfile): ReactNode {\n return author.originStory && author.originStory.length > 0 ? (\n <section key=\"originStory\" aria-label=\"Author origin story\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-6 text-2xl font-bold text-foreground\">My story</h2>\n <div className=\"space-y-6\">\n {author.originStory.map((block, blockIndex) => (\n <div key={block.heading ?? blockIndex}>\n {block.heading && (\n <h3 className=\"mb-2 text-lg font-semibold text-foreground\">{block.heading}</h3>\n )}\n {block.paragraphs.map((paragraph, paragraphIndex) => (\n <p\n key={`${block.heading ?? blockIndex}-${paragraph.slice(0, 40)}-${paragraphIndex}`}\n className=\"mb-3 text-muted-foreground\"\n >\n {paragraph}\n </p>\n ))}\n </div>\n ))}\n </div>\n </div>\n </section>\n ) : null\n}\n\nfunction renderPrinciplesSection(author: AuthorProfile): ReactNode {\n return author.principles && author.principles.length > 0 ? (\n <section key=\"principles\" aria-label=\"Author principles\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-3xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-4 text-center text-2xl font-bold text-foreground\">What I believe</h2>\n <ul className=\"space-y-3\">\n {author.principles.map((principle) => (\n <li\n key={principle}\n className=\"rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground\"\n >\n {principle}\n </li>\n ))}\n </ul>\n </div>\n </section>\n ) : null\n}\n\nfunction renderProofSection(author: AuthorProfile): ReactNode {\n return author.proof && author.proof.length > 0 ? (\n <section key=\"proof\" aria-label=\"Author proof\" className=\"bg-background py-12\">\n <div className=\"mx-auto max-w-4xl px-4 sm:px-6 lg:px-8\">\n <h2 className=\"mb-6 text-center text-2xl font-bold text-foreground\">Proof</h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {author.proof.map((item) => (\n <div\n key={`${item.claim}-${item.url ?? item.source ?? ''}`}\n className=\"rounded-lg border border-border bg-card p-4 text-card-foreground\"\n >\n <p className=\"font-medium\">{item.claim}</p>\n {(item.source || item.url) && (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {item.url ? (\n <Link href={item.url} className=\"hover:text-primary hover:underline\">\n {item.source ?? item.url}\n </Link>\n ) : (\n item.source\n )}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n </section>\n ) : null\n}\n\nfunction renderCtaSection(author: AuthorProfile, config?: ArticlesConfig): ReactNode {\n return author.primaryCta ? (\n <CtaViewTracker key=\"cta\" ctaId={`author-cta:${author.slug}`} config={config}>\n <section aria-label=\"Author call to action\" className=\"bg-muted/40 py-12\">\n <div className=\"mx-auto max-w-2xl px-4 text-center sm:px-6 lg:px-8\">\n <Link\n href={author.primaryCta.href}\n onClick={() =>\n emitArticleEvent(config?.onEvent, {\n name: 'cta_clicked',\n ctaId: `author-cta:${author.slug}`,\n })\n }\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground hover:bg-primary/90\"\n >\n {author.primaryCta.label}\n </Link>\n </div>\n </section>\n </CtaViewTracker>\n ) : null\n}\n\nfunction renderArticlesSection(\n author: AuthorProfile,\n articles: Article[],\n pageSize: number,\n pagination?: ListingPaginationContext\n): ReactNode {\n return (\n <section key=\"articles\" aria-label=\"Articles by this author\" className=\"bg-background py-16\">\n <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">Articles by {author.name}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n )\n}\n\nfunction renderAuthorSection(section: AuthorPageSection, ctx: AuthorSectionContext): ReactNode {\n const { author, articleCount, articles, pageSize, pagination, customSection, config } = ctx\n\n switch (section) {\n case 'hero':\n return renderHeroSection(author, articleCount)\n case 'promise':\n return renderPromiseSection(author)\n case 'servesWho':\n return renderServesWhoSection(author)\n case 'originStory':\n return renderOriginStorySection(author)\n case 'principles':\n return renderPrinciplesSection(author)\n case 'proof':\n return renderProofSection(author)\n case 'cta':\n return renderCtaSection(author, config)\n case 'articles':\n return renderArticlesSection(author, articles, pageSize, pagination)\n case 'custom':\n return customSection ?? null\n default:\n return null\n }\n}\n\nexport function AuthorArticlesPage({\n author,\n articles,\n config,\n page,\n totalPages,\n totalCount,\n sections,\n customSection,\n}: AuthorArticlesPageProps) {\n const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE\n const siteUrl = config.siteUrl.replace(/\\/$/, '')\n const articleCount = totalCount ?? articles.length\n const pagination: ListingPaginationContext | undefined =\n config.listingPagination === 'pages' && page !== undefined && totalPages !== undefined\n ? { page, totalPages, basePath: `/articles/authors/${author.slug}` }\n : undefined\n\n const schemas = (\n <>\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{\n __html: JSON.stringify(\n getPersonSchema(author, config, articleCount, getKnowsAbout(author, articles))\n ),\n }}\n />\n <CollectionPageSchema\n title={`${author.name} | ${config.siteName}`}\n description={`Articles by ${author.name} on ${config.siteName}.`}\n url={author.url ?? `${siteUrl}/articles/authors/${author.slug}`}\n articleCount={articleCount}\n items={articles.slice(0, pageSize).map((article, index) => ({\n position: index + 1,\n url: `${siteUrl}/articles/${article.slug}`,\n name: article.title,\n }))}\n />\n </>\n )\n\n if (!sections) {\n return (\n <div>\n {schemas}\n <section className=\"bg-background py-16\">\n <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n <Link href=\"/articles\" className=\"text-sm text-primary hover:underline\">\n All Articles\n </Link>\n <p className=\"text-sm text-muted-foreground\">Articles by {author.name}</p>\n </div>\n <LatestArticles articles={articles} pageSize={pageSize} pagination={pagination} />\n </div>\n </section>\n </div>\n )\n }\n\n const sectionCtx: AuthorSectionContext = {\n author,\n articleCount,\n articles,\n pageSize,\n pagination,\n customSection,\n config,\n }\n\n return (\n <div>\n {schemas}\n {sections.map((section) => renderAuthorSection(section, sectionCtx))}\n </div>\n )\n}\n","import Image from 'next/image'\nimport type { AuthorProfile } from './articleTypes'\nimport { AuthorSocialLinks } from './AuthorCard'\nimport { getAuthorAvatar } from './authorUtils'\n\ntype AuthorDetailHeroProps = Readonly<{\n author: AuthorProfile\n articleCount?: number\n}>\n\nfunction getInitials(name: string): string {\n return name\n .split(' ')\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('')\n}\n\nexport function AuthorDetailHero({ author, articleCount }: AuthorDetailHeroProps) {\n const avatar = getAuthorAvatar(author)\n\n return (\n <section className=\"bg-muted/40 py-16\">\n <div className=\"mx-auto flex max-w-4xl flex-col items-center gap-6 px-4 text-center sm:px-6 lg:px-8\">\n {avatar ? (\n <Image\n src={avatar}\n alt={author.name}\n width={120}\n height={120}\n className=\"h-28 w-28 rounded-full object-cover\"\n priority\n />\n ) : (\n <div className=\"flex h-28 w-28 items-center justify-center rounded-full bg-card text-3xl font-semibold text-muted-foreground shadow-sm\">\n {getInitials(author.name)}\n </div>\n )}\n <div>\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-widest text-muted-foreground\">\n Author\n </p>\n <h1 className=\"text-4xl font-bold text-foreground md:text-5xl\">{author.name}</h1>\n <p className=\"mx-auto mt-4 max-w-2xl text-lg text-muted-foreground\">{author.bio}</p>\n {typeof articleCount === 'number' && (\n <p className=\"mt-3 text-sm text-muted-foreground\">\n {articleCount} article{articleCount === 1 ? '' : 's'}\n </p>\n )}\n </div>\n <AuthorSocialLinks author={author} />\n </div>\n </section>\n )\n}\n","'use client'\n\nimport { useEffect, useRef } from 'react'\nimport type { ReactNode } from 'react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\nimport type { Article } from './articleTypes'\n\ntype ArticleViewTrackerProps = Readonly<{\n article: Pick<Article, 'slug'> & Partial<Pick<Article, 'category' | 'seriesSlug' | 'wordCount'>>\n config?: ArticlesConfig\n}>\n\nconst AVERAGE_WORDS_PER_MINUTE = 200\nconst DEFAULT_MEANINGFUL_READ_MS = 15_000\n\n/**\n * Renders nothing - fires `article_viewed` on mount and `meaningful_read`\n * once, after roughly half the article's estimated read time has elapsed\n * (a deterministic, testable timing approximation rather than scroll-depth\n * tracking, which this package has no reliable cross-app way to measure\n * since it doesn't own the article body's scroll container). Place once on\n * the article detail page alongside `ArticleContent`.\n */\nexport function ArticleViewTracker({ article, config }: ArticleViewTrackerProps) {\n const firedMeaningfulRead = useRef(false)\n\n useEffect(() => {\n emitArticleEvent(config?.onEvent, {\n name: 'article_viewed',\n articleSlug: article.slug,\n category: article.category,\n seriesSlug: article.seriesSlug,\n })\n\n const estimatedMs = article.wordCount\n ? (article.wordCount / AVERAGE_WORDS_PER_MINUTE) * 60_000 * 0.5\n : DEFAULT_MEANINGFUL_READ_MS\n const timer = setTimeout(() => {\n if (firedMeaningfulRead.current) return\n firedMeaningfulRead.current = true\n emitArticleEvent(config?.onEvent, { name: 'meaningful_read', articleSlug: article.slug })\n }, estimatedMs)\n\n return () => clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [article.slug])\n\n return null\n}\n\ntype CtaViewTrackerProps = Readonly<{\n /** Opaque CTA/offer ID - never label text (no PII/marketing copy in event payloads). */\n ctaId: string\n articleSlug?: string\n config?: ArticlesConfig\n children: ReactNode\n}>\n\n/**\n * Wraps any CTA block and fires `cta_viewed` once, the first time at least\n * half of it scrolls into the viewport (`IntersectionObserver`). Falls back\n * to firing immediately when `IntersectionObserver` isn't available (older\n * browsers, non-DOM test environments) rather than never firing.\n */\nexport function CtaViewTracker({ ctaId, articleSlug, config, children }: CtaViewTrackerProps) {\n const ref = useRef<HTMLDivElement | null>(null)\n const fired = useRef(false)\n\n useEffect(() => {\n const node = ref.current\n if (!node) return\n\n function fire() {\n if (fired.current) return\n fired.current = true\n emitArticleEvent(config?.onEvent, { name: 'cta_viewed', ctaId, articleSlug })\n }\n\n if (typeof IntersectionObserver === 'undefined') {\n fire()\n return\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) fire()\n },\n { threshold: 0.5 }\n )\n observer.observe(node)\n return () => observer.disconnect()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ctaId, articleSlug])\n\n return <div ref={ref}>{children}</div>\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { Share2, Copy, Check, Mail, MessageCircle, Users, FileText } from 'lucide-react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\n\ninterface ArticleSocialShareProps {\n readonly title: string\n readonly url: string\n readonly excerpt?: string\n readonly shareMessage?: string\n /** Optional (Phase 27F). Enables `shared` events - requires `articleSlug` too. */\n readonly config?: ArticlesConfig\n /** The article slug being shared. Required to emit `shared`. */\n readonly articleSlug?: string\n}\n\nexport function ArticleSocialShare({\n title,\n url,\n excerpt,\n shareMessage,\n config,\n articleSlug,\n}: ArticleSocialShareProps) {\n const [copied, setCopied] = useState(false)\n\n function trackShare(channel: string) {\n if (!articleSlug) return\n emitArticleEvent(config?.onEvent, { name: 'shared', articleSlug, channel })\n }\n\n const encodedTitle = encodeURIComponent(title)\n const encodedUrl = encodeURIComponent(url)\n const encodedExcerpt = encodeURIComponent(excerpt || '')\n\n const shareLinks = {\n linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,\n facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,\n twitter: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,\n reddit: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,\n email: `mailto:?subject=${encodedTitle}&body=${encodedExcerpt}%0A%0A${encodedUrl}`,\n whatsapp: `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`,\n telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`,\n }\n\n const copyToClipboard = async () => {\n try {\n await navigator.clipboard.writeText(url)\n setCopied(true)\n trackShare('copy-link')\n setTimeout(() => setCopied(false), 2000)\n } catch {\n // clipboard unavailable\n }\n }\n\n const openShareWindow = (shareUrl: string, channel: string) => {\n trackShare(channel)\n globalThis.open(shareUrl, '_blank', 'width=600,height=400,scrollbars=yes,resizable=yes')\n }\n\n const btnClass =\n 'inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground hover:bg-primary/10 hover:border-primary hover:text-primary transition-colors cursor-pointer'\n\n return (\n <div className=\"border-t border-border pt-8 mt-8\">\n <div className=\"flex items-center gap-2 mb-4\">\n <Share2 className=\"h-5 w-5 text-muted-foreground\" />\n <h3 className=\"text-lg font-semibold text-foreground\">Share this article</h3>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.linkedin, 'linkedin')}\n >\n <Users className=\"h-4 w-4\" />\n LinkedIn\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.facebook, 'facebook')}\n >\n <span className=\"text-xs font-bold\">f</span> Facebook\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.twitter, 'twitter')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n Twitter\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.reddit, 'reddit')}\n >\n <FileText className=\"h-4 w-4\" />\n Reddit\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.whatsapp, 'whatsapp')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n WhatsApp\n </button>\n <button\n type=\"button\"\n className={btnClass}\n onClick={() => openShareWindow(shareLinks.telegram, 'telegram')}\n >\n <MessageCircle className=\"h-4 w-4\" />\n Telegram\n </button>\n <a className={btnClass} href={shareLinks.email} onClick={() => trackShare('email')}>\n <Mail className=\"h-4 w-4\" />\n Email\n </a>\n <button type=\"button\" className={btnClass} onClick={copyToClipboard}>\n {copied ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copied ? 'Copied!' : 'Copy Link'}\n </button>\n </div>\n\n {shareMessage && <p className=\"text-sm text-muted-foreground mt-3\">{shareMessage}</p>}\n </div>\n )\n}\n","import Link from 'next/link'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { emitArticleEvent } from './events'\nimport type { ArticlesConfig } from './articlesConfig'\n\ninterface ArticleNavigationProps {\n readonly previous: { slug: string; title: string } | null\n readonly next: { slug: string; title: string } | null\n readonly basePath: string\n /** The article slug this navigation is shown on. Combined with `pathKey`, enables `path_step_advanced` events. */\n readonly fromSlug?: string\n /**\n * Set when this navigation walks a configured `Path` (as opposed to plain\n * chronological/series adjacency) - fires `path_step_advanced` on click.\n * Omit for ordinary previous/next navigation; no event fires without it.\n */\n readonly pathKey?: string\n readonly config?: ArticlesConfig\n}\n\nfunction truncateTitle(title: string, maxLength = 60): string {\n return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title\n}\n\nexport function ArticleNavigation({\n previous,\n next,\n basePath,\n fromSlug,\n pathKey,\n config,\n}: ArticleNavigationProps) {\n if (!previous && !next) return null\n\n function emitStep(toSlug: string, direction: 'previous' | 'next') {\n if (!pathKey || !fromSlug) return\n emitArticleEvent(config?.onEvent, {\n name: 'path_step_advanced',\n pathKey,\n fromSlug,\n toSlug,\n direction,\n })\n }\n\n return (\n <nav className=\"mt-12 pt-8 border-t border-border\" aria-label=\"Article navigation\">\n <div className=\"flex justify-between gap-4\">\n <div className=\"flex-1 min-w-0\">\n {previous && (\n <Link\n href={`${basePath}/${previous.slug}`}\n onClick={() => emitStep(previous.slug, 'previous')}\n className=\"group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors\"\n >\n <ChevronLeft className=\"h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-muted-foreground mb-1\">Previous Article</div>\n <div className=\"font-medium text-foreground truncate\" title={previous.title}>\n {truncateTitle(previous.title)}\n </div>\n </div>\n </Link>\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n {next && (\n <Link\n href={`${basePath}/${next.slug}`}\n onClick={() => emitStep(next.slug, 'next')}\n className=\"group flex items-center justify-end gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors\"\n >\n <div className=\"min-w-0 flex-1 text-right\">\n <div className=\"text-sm text-muted-foreground mb-1\">Next Article</div>\n <div className=\"font-medium text-foreground truncate\" title={next.title}>\n {truncateTitle(next.title)}\n </div>\n </div>\n <ChevronRight className=\"h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0\" />\n </Link>\n )}\n </div>\n </div>\n </nav>\n )\n}\n","import type { Article } from './articleTypes'\nimport type { ArticlesConfig } from './articlesConfig'\nimport { ArticleCard } from './ArticleCard'\nimport { emitArticleEvent } from './events'\nimport type { RelatedContentSource } from './server-articles'\n\ntype RelatedArticlesSectionProps = Readonly<{\n articles: Article[]\n category: string\n /** Overrides the default \"More in {category}\" heading - used by `getRelatedContent` callers when `source` is `'path'`/`'series'`. Omit to keep the default category-based heading. */\n heading?: string\n /** Optional (Phase 27F). Enables `related_article_clicked` events - requires `fromSlug` too. */\n config?: ArticlesConfig\n /** The article slug this related section is shown on. Required to emit `related_article_clicked`. */\n fromSlug?: string\n /** Selection source that produced `articles`, from `getRelatedContent`. Defaults to `'category'`. */\n source?: RelatedContentSource\n}>\n\nexport function RelatedArticlesSection({\n articles,\n category,\n heading,\n config,\n fromSlug,\n source = 'category',\n}: RelatedArticlesSectionProps) {\n if (articles.length === 0) return null\n\n return (\n <section className=\"mt-12 pt-8 border-t border-border\" aria-label=\"Related articles\">\n <h2\n className=\"text-xl font-semibold text-foreground mb-6\"\n style={{ fontFamily: config?.theme?.headerFontFamily }}\n >\n {heading ?? `More in ${category}`}\n </h2>\n <div className=\"grid gap-6 sm:grid-cols-2 lg:grid-cols-3\">\n {articles.map((article) => (\n <div\n key={article.slug}\n onClickCapture={() => {\n if (!fromSlug) return\n emitArticleEvent(config?.onEvent, {\n name: 'related_article_clicked',\n fromSlug,\n toSlug: article.slug,\n source,\n })\n }}\n >\n <ArticleCard article={article} config={config} />\n </div>\n ))}\n </div>\n </section>\n )\n}\n","import Link from 'next/link'\nimport { ArrowLeft } from 'lucide-react'\nimport type { ArticlesConfig } from './articlesConfig'\n\ntype ArticleBackLinkProps = Readonly<{\n config: Pick<ArticlesConfig, 'showBackToArticles'>\n href?: string\n label?: string\n className?: string\n}>\n\nexport function ArticleBackLink({\n config,\n href = '/articles',\n label = 'Back to Articles',\n className,\n}: ArticleBackLinkProps) {\n if (config.showBackToArticles === false) return null\n\n return (\n <Link\n href={href}\n className={\n className ??\n 'inline-flex items-center gap-2 mb-6 -ml-3 px-3 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors'\n }\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {label}\n </Link>\n )\n}\n","import type { TocItem } from './articleTypes'\n\ntype ArticleTOCProps = Readonly<{ toc: TocItem[]; className?: string }>\n\nexport function ArticleTOC({ toc, className }: ArticleTOCProps) {\n if (!toc.length) return null\n return (\n <nav\n aria-label=\"Table of contents\"\n className={`mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className ?? ''}`}\n >\n <p className=\"mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n On this page\n </p>\n <ul className=\"space-y-1 text-sm\">\n {toc.map((item) => (\n <li key={item.id} style={{ paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }}>\n <a\n href={`#${item.id}`}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n {item.text}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n","import type { Article } from './articleTypes'\n\ntype ArticleAnswerProps = Readonly<{\n article: Pick<Article, 'answer'>\n /** Heading shown above the answer. Default: `'The short answer'`. */\n label?: string\n className?: string\n}>\n\n/**\n * Renders `article.answer` as a callout above the article body.\n *\n * The same text is emitted as the Article schema's `abstract` and placed at\n * the top of the article's markdown twin, so the passage an answer engine is\n * most likely to lift is also the one a reader sees first. Returns `null`\n * when the article has no `answer`, so it is safe to render unconditionally.\n */\nexport function ArticleAnswer({\n article,\n label = 'The short answer',\n className,\n}: ArticleAnswerProps) {\n if (!article.answer?.trim()) return null\n return (\n <aside\n className={`mb-8 rounded-lg border-l-4 border-primary bg-muted/40 px-6 py-4 ${className ?? ''}`}\n style={{ borderLeftWidth: '4px' }}\n >\n <p className=\"mb-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground\">\n {label}\n </p>\n <p className=\"text-base leading-relaxed\">{article.answer}</p>\n </aside>\n )\n}\n","'use client'\nimport { useEffect, useState } from 'react'\nimport { ArrowUp } from 'lucide-react'\n\nexport function ScrollToTop() {\n const [visible, setVisible] = useState(false)\n\n useEffect(() => {\n const onScroll = () => setVisible(globalThis.scrollY > 300)\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n return () => globalThis.removeEventListener('scroll', onScroll)\n }, [])\n\n if (!visible) return null\n\n return (\n <button\n aria-label=\"Scroll to top\"\n onClick={() => globalThis.scrollTo({ top: 0, behavior: 'smooth' })}\n className=\"fixed bottom-8 right-8 z-50 rounded-full bg-primary p-2 shadow-md text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <ArrowUp className=\"h-5 w-5\" />\n </button>\n )\n}\n","'use client'\n\nimport { useSession, signIn } from 'next-auth/react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { CommentForm } from './CommentForm'\nimport { CommentThread } from './CommentThread'\nimport type { CommentsConfig } from './articlesConfig'\nimport type { ArticleCommentWithReplies } from './commentTypes'\n\ninterface CommentsSectionProps {\n readonly articleSlug: string\n readonly config: CommentsConfig\n}\n\nexport function CommentsSection({ articleSlug, config }: CommentsSectionProps) {\n const { data: session, status } = useSession()\n const [comments, setComments] = useState<ArticleCommentWithReplies[]>([])\n const [loading, setLoading] = useState(true)\n const [fetchError, setFetchError] = useState<string | null>(null)\n\n const perArticleEnabled = config.perArticleOverride?.[articleSlug] ?? true\n const enabled = config.enabled && perArticleEnabled\n\n const currentUserId = session?.user?.email ?? session?.user?.name ?? undefined\n\n const fetchComments = useCallback(async () => {\n setLoading(true)\n setFetchError(null)\n try {\n const res = await fetch(`/api/articles/${articleSlug}/comments`)\n if (!res.ok) throw new Error('Failed to load comments')\n const data = (await res.json()) as { comments: ArticleCommentWithReplies[] }\n setComments(data.comments)\n } catch {\n setFetchError('Could not load comments. Please try again.')\n } finally {\n setLoading(false)\n }\n }, [articleSlug])\n\n useEffect(() => {\n if (enabled) {\n fetchComments().catch(() => undefined)\n }\n }, [fetchComments, enabled])\n\n if (!enabled) return null\n\n const count = comments.length\n const label = count === 1 ? '1 comment' : `${count} comments`\n\n return (\n <section aria-label=\"Comments\" className=\"mt-12 pt-8 border-t border-border space-y-6\">\n <h2 className=\"text-xl font-semibold text-foreground\">{loading ? 'Comments' : label}</h2>\n\n {status === 'authenticated' ? (\n <CommentForm articleSlug={articleSlug} onSuccess={fetchComments} />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n <button\n onClick={() => signIn()}\n className=\"text-primary underline hover:no-underline focus:outline-none\"\n >\n Sign in\n </button>{' '}\n to join the discussion.\n </p>\n )}\n\n {fetchError && <p className=\"text-sm text-destructive\">{fetchError}</p>}\n\n {loading ? (\n <p className=\"text-sm text-muted-foreground\">Loading comments…</p>\n ) : (\n <CommentThread\n comments={comments}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n onRefresh={fetchComments}\n />\n )}\n </section>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\n\nconst MAX_LENGTH = 2000\nconst WARN_THRESHOLD = 1800\n\nfunction submitLabel(submitting: boolean, parentId?: string): string {\n if (submitting) return 'Posting…'\n return parentId ? 'Reply' : 'Post comment'\n}\n\ninterface CommentFormProps {\n readonly articleSlug: string\n readonly parentId?: string\n readonly onSuccess: () => void\n readonly onCancel?: () => void\n}\n\nexport function CommentForm({ articleSlug, parentId, onSuccess, onCancel }: CommentFormProps) {\n const [body, setBody] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const remaining = MAX_LENGTH - body.length\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!body.trim() || submitting) return\n\n setSubmitting(true)\n setError(null)\n\n try {\n const res = await fetch(`/api/articles/${articleSlug}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ body: body.trim(), parentId: parentId ?? null }),\n })\n\n if (!res.ok) {\n const data = (await res.json()) as { error?: string }\n setError(data.error ?? 'Failed to post comment')\n return\n }\n\n setBody('')\n onSuccess()\n } catch {\n setError('Network error — please try again')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-2\">\n <textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n maxLength={MAX_LENGTH}\n rows={parentId ? 3 : 4}\n placeholder={parentId ? 'Write a reply…' : 'Join the discussion…'}\n disabled={submitting}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 resize-none\"\n aria-label={parentId ? 'Reply text' : 'Comment text'}\n />\n\n {remaining <= MAX_LENGTH - WARN_THRESHOLD && (\n <p className={`text-xs ${remaining < 0 ? 'text-destructive' : 'text-muted-foreground'}`}>\n {remaining} characters remaining\n </p>\n )}\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n\n <div className=\"flex gap-2\">\n <button\n type=\"submit\"\n disabled={submitting || body.trim().length === 0 || body.length > MAX_LENGTH}\n className=\"rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {submitLabel(submitting, parentId)}\n </button>\n\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-md px-4 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n )}\n </div>\n </form>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { CommentForm } from './CommentForm'\nimport type { ArticleComment, ArticleCommentWithReplies } from './commentTypes'\n\nfunction relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const minutes = Math.floor(diff / 60_000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h ago`\n const days = Math.floor(hours / 24)\n if (days < 30) return `${days}d ago`\n const months = Math.floor(days / 30)\n if (months < 12) return `${months}mo ago`\n return `${Math.floor(months / 12)}y ago`\n}\n\ninterface CommentItemProps {\n readonly comment: ArticleCommentWithReplies | ArticleComment\n readonly articleSlug: string\n readonly currentUserId?: string\n readonly isReply?: boolean\n readonly onRefresh: () => void\n}\n\nexport function CommentItem({\n comment,\n articleSlug,\n currentUserId,\n isReply = false,\n onRefresh,\n}: CommentItemProps) {\n const [showReplyForm, setShowReplyForm] = useState(false)\n const [deleting, setDeleting] = useState(false)\n\n const replies = 'replies' in comment ? comment.replies : []\n const canReply = !isReply && !!currentUserId && !comment.isDeleted\n const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted\n\n async function handleDelete() {\n if (deleting) return\n setDeleting(true)\n try {\n await fetch(`/api/articles/${articleSlug}/comments/${comment.id}`, { method: 'DELETE' })\n onRefresh()\n } finally {\n setDeleting(false)\n }\n }\n\n return (\n <div className={`${isReply ? 'ml-8 border-l-2 border-border pl-4' : ''}`}>\n <div className=\"rounded-lg bg-muted/40 p-3 space-y-1\">\n {comment.isDeleted ? (\n <p className=\"text-sm italic text-muted-foreground\">Comment removed</p>\n ) : (\n <>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-foreground\">{comment.authorName}</span>\n <span className=\"text-xs text-muted-foreground\">\n {relativeTime(comment.createdAt)}\n </span>\n </div>\n <p className=\"text-sm text-foreground whitespace-pre-wrap break-words\">\n {comment.body}\n </p>\n <div className=\"flex gap-3 pt-1\">\n {canReply && (\n <button\n onClick={() => setShowReplyForm((v) => !v)}\n className=\"text-xs text-muted-foreground hover:text-foreground\"\n >\n {showReplyForm ? 'Cancel' : 'Reply'}\n </button>\n )}\n {canDelete && (\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"text-xs text-muted-foreground hover:text-destructive disabled:opacity-50\"\n >\n {deleting ? 'Deleting…' : 'Delete'}\n </button>\n )}\n </div>\n </>\n )}\n </div>\n\n {showReplyForm && (\n <div className=\"mt-2 ml-2\">\n <CommentForm\n articleSlug={articleSlug}\n parentId={comment.id}\n onSuccess={() => {\n setShowReplyForm(false)\n onRefresh()\n }}\n onCancel={() => setShowReplyForm(false)}\n />\n </div>\n )}\n\n {replies.length > 0 && (\n <div className=\"mt-2 space-y-2\">\n {replies.map((reply) => (\n <CommentItem\n key={reply.id}\n comment={{ ...reply, replies: [] }}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n isReply\n onRefresh={onRefresh}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { CommentItem } from './CommentItem'\nimport type { ArticleCommentWithReplies } from './commentTypes'\n\ninterface CommentThreadProps {\n readonly comments: ArticleCommentWithReplies[]\n readonly articleSlug: string\n readonly currentUserId?: string\n readonly onRefresh: () => void\n}\n\nexport function CommentThread({\n comments,\n articleSlug,\n currentUserId,\n onRefresh,\n}: CommentThreadProps) {\n if (comments.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground text-center py-6\">\n No comments yet. Be the first to share your thoughts.\n </p>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {comments.map((comment) => (\n <CommentItem\n key={comment.id}\n comment={comment}\n articleSlug={articleSlug}\n currentUserId={currentUserId}\n onRefresh={onRefresh}\n />\n ))}\n </div>\n )\n}\n"],"mappings":"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","names":["Link","Image","import_link","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_link","import_jsx_runtime","Link","import_link","import_image","import_lucide_react","import_jsx_runtime","Link","Image","import_lucide_react","import_react","import_image","import_link","import_lucide_react","import_image","import_link","import_lucide_react","import_jsx_runtime","Link","Image","import_jsx_runtime","Image","Link","import_link","import_lucide_react","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_image","import_link","import_lucide_react","import_jsx_runtime","Image","Link","import_image","import_link","import_jsx_runtime","_a","Image","Link","import_link","import_jsx_runtime","Link","import_link","import_image","import_jsx_runtime","getInitials","Image","import_react","import_jsx_runtime","import_jsx_runtime","_a","Link","import_react","import_lucide_react","import_jsx_runtime","import_link","import_lucide_react","import_jsx_runtime","toSlug","Link","import_jsx_runtime","import_link","import_lucide_react","import_jsx_runtime","Link","import_jsx_runtime","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","e","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|