mcp-scraper 0.57.3 → 0.60.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +549 -107
  4. package/dist/bin/api-server.cjs.map +1 -1
  5. package/dist/bin/api-server.js +2 -2
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  7. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  8. package/dist/bin/mcp-scraper-cli.js +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +36 -8
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +4 -4
  15. package/dist/{chunk-R6CO77BN.js → chunk-2U2ZDDSI.js} +3 -3
  16. package/dist/{chunk-R6CO77BN.js.map → chunk-2U2ZDDSI.js.map} +1 -1
  17. package/dist/{chunk-QSUSTYB3.js → chunk-AIA3MFT2.js} +38 -9
  18. package/dist/chunk-AIA3MFT2.js.map +1 -0
  19. package/dist/chunk-AQA2ZL4X.js +7 -0
  20. package/dist/chunk-AQA2ZL4X.js.map +1 -0
  21. package/dist/{chunk-HU4R6BP2.js → chunk-IMBMDZUO.js} +2 -2
  22. package/dist/{chunk-HU4R6BP2.js.map → chunk-IMBMDZUO.js.map} +1 -1
  23. package/dist/{chunk-5XYBKE3X.js → chunk-KMGRM4FL.js} +2 -2
  24. package/dist/editorial-reading-room/assets/app.js +17 -3
  25. package/dist/{extract-bundle-H7JEELY7.js → extract-bundle-XRRN6NAZ.js} +3 -3
  26. package/dist/{server-EP2SK2ID.js → server-IRKNU6LO.js} +505 -105
  27. package/dist/server-IRKNU6LO.js.map +1 -0
  28. package/dist/{site-extract-repository-CFMSWAVL.js → site-extract-repository-XAA2FVDN.js} +3 -3
  29. package/dist/{worker-S2WXELUC.js → worker-QFSOMF2N.js} +2 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-LHH3GX3W.js +0 -7
  32. package/dist/chunk-LHH3GX3W.js.map +0 -1
  33. package/dist/chunk-QSUSTYB3.js.map +0 -1
  34. package/dist/server-EP2SK2ID.js.map +0 -1
  35. /package/dist/{chunk-5XYBKE3X.js.map → chunk-KMGRM4FL.js.map} +0 -0
  36. /package/dist/{extract-bundle-H7JEELY7.js.map → extract-bundle-XRRN6NAZ.js.map} +0 -0
  37. /package/dist/{site-extract-repository-CFMSWAVL.js.map → site-extract-repository-XAA2FVDN.js.map} +0 -0
  38. /package/dist/{worker-S2WXELUC.js.map → worker-QFSOMF2N.js.map} +0 -0
@@ -12455,8 +12455,8 @@ var init_product_contract_generated = __esm({
12455
12455
  "freeSignupCredits": 0
12456
12456
  },
12457
12457
  "inventory": {
12458
- "totalTools": 251,
12459
- "scraperTools": 150,
12458
+ "totalTools": 252,
12459
+ "scraperTools": 151,
12460
12460
  "memoryTools": 101
12461
12461
  },
12462
12462
  "concurrencyPack": {
@@ -44650,7 +44650,7 @@ var PACKAGE_VERSION;
44650
44650
  var init_version = __esm({
44651
44651
  "src/version.ts"() {
44652
44652
  "use strict";
44653
- PACKAGE_VERSION = "0.57.3";
44653
+ PACKAGE_VERSION = "0.60.0";
44654
44654
  }
44655
44655
  });
44656
44656
 
@@ -46479,7 +46479,7 @@ var init_contracts = __esm({
46479
46479
  });
46480
46480
 
46481
46481
  // src/mcp/mcp-tool-schemas.ts
46482
- var import_zod43, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
46482
+ var import_zod43, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsUpdateEditorialArticleInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
46483
46483
  var init_mcp_tool_schemas = __esm({
46484
46484
  "src/mcp/mcp-tool-schemas.ts"() {
46485
46485
  "use strict";
@@ -46776,6 +46776,7 @@ var init_mcp_tool_schemas = __esm({
46776
46776
  includeZipGroups: import_zod43.z.boolean().default(true).describe("Include ZIP and county groups from the active hosted ZIP dataset.")
46777
46777
  };
46778
46778
  CommonsSearchEntitiesInputSchema = {
46779
+ mode: import_zod43.z.enum(["lexical", "semantic", "hybrid"]).optional().describe("Retrieval mode. hybrid (default) fuses meaning-based candidates with exact term matching; lexical matches terms only, which is what you want when the caller quoted an exact name or phrase; semantic ignores term matching entirely."),
46779
46780
  query: import_zod43.z.string().trim().max(300).optional().describe("Search text matched against title, description, tags, keywords, JSON-LD, source metadata, citations, media, and article body."),
46780
46781
  entityType: import_zod43.z.string().trim().max(120).optional().describe('Optional entity type filter. "Public Article", "PublicArticle", "Article", and "item" normalize to PublicArticle.'),
46781
46782
  tag: import_zod43.z.string().trim().max(80).optional().describe("Single canonical tag filter. Use tags for multiple tags."),
@@ -47190,7 +47191,21 @@ var init_mcp_tool_schemas = __esm({
47190
47191
  baseRevision: import_zod43.z.number().int().positive().optional().describe("Required when revising an existing edition; use the current revision from commons_get_publication."),
47191
47192
  ...CreateEditorialReadingRoomInputSchema
47192
47193
  };
47194
+ CommonsUpdateEditorialArticleInputSchema = {
47195
+ publicationSubdomain: CommonsPublicationSubdomainSchema.describe("Publication owned by the authenticated account."),
47196
+ editionSlug: import_zod43.z.string().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(80).optional().describe("Edition holding the article. Defaults to the publication\u2019s latest edition."),
47197
+ articleSlug: import_zod43.z.string().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(80).describe("Slug of the one article to replace, from commons_get_publication with includeArticles."),
47198
+ idempotencyKey: import_zod43.z.string().trim().min(8).max(200).describe("Unique key for this intended edit. Reuse it only when retrying the same edit."),
47199
+ baseRevision: import_zod43.z.number().int().positive().describe("Current edition revision from commons_get_publication. The edit is refused if the edition moved on."),
47200
+ markdown: import_zod43.z.string().trim().min(1).max(4e5).optional().describe("Replacement Markdown body for this article. Omit to change only the metadata fields below."),
47201
+ title: import_zod43.z.string().trim().min(1).max(180).optional().describe("Replacement article title."),
47202
+ summary: import_zod43.z.string().trim().min(1).max(500).optional().describe("Replacement one or two sentence summary."),
47203
+ category: import_zod43.z.string().trim().min(1).max(80).optional().describe("Replacement section label."),
47204
+ kicker: import_zod43.z.string().trim().min(1).max(140).optional().describe("Replacement framing line above the title."),
47205
+ order: import_zod43.z.number().int().min(1).max(1e3).optional().describe("Replacement reading-order position.")
47206
+ };
47193
47207
  CommonsGetPublicationInputSchema = {
47208
+ includeArticles: import_zod43.z.boolean().optional().describe("Return every stored article\u2019s Markdown source alongside each edition. Large payload; request it only when you intend to edit an article. Owner-only: a publication read by name never returns sources."),
47194
47209
  subdomain: CommonsPublicationSubdomainSchema.optional().describe("Public publication name to inspect. Omit to return the publication owned by the authenticated account."),
47195
47210
  includeEditions: import_zod43.z.boolean().default(true).describe("Include the latest revision of every published edition.")
47196
47211
  };
@@ -50408,7 +50423,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50408
50423
  }, async (input) => formatG2Reviews(await executor.g2Reviews(input), input));
50409
50424
  server.registerTool("commons_search_entities", {
50410
50425
  title: "Transparent Commons Entity Search",
50411
- description: "Search the Transparent Commons public wiki graph using the same fields the public /wiki/ frontend uses: title, description, tags, keywords, JSON-LD, article body, citations, media, source metadata, related entities, trails, and saved account filters. This reads published Commons entities only.",
50426
+ description: "Search the Transparent Commons public wiki graph. Hybrid by default: meaning-based candidates fused with exact matching over title, description, tags, keywords, JSON-LD, body, citations, media, source metadata, related entities, trails, and saved filters, ranked title-first. Set mode:lexical for a quoted exact name or phrase. Reads published Commons entities only; never a personal Memory vault.",
50412
50427
  inputSchema: CommonsSearchEntitiesInputSchema,
50413
50428
  outputSchema: recordOutputSchema("commons_search_entities", CommonsGenericOutputSchema),
50414
50429
  annotations: {
@@ -50486,7 +50501,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50486
50501
  }, async (input) => executor.commonsValidateEntity(input));
50487
50502
  server.registerTool("commons_submit_entity", {
50488
50503
  title: "Transparent Commons Governed Entity Write",
50489
- description: "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. Choose a precise entityType and write a neutral encyclopedia projection with the matching entity structure, not a blog article or raw scrape dump. Use H2 sections and H3/H4/H5 subitems; every heading appears in the public page menu. Omit unsupported sections instead of publishing empty/filler headings. Transparent Commons is an open wiki: any account may edit any entity, and every edit publishes and is recorded on the public contribution ledger. Publishing requires a featured image and body/source evidence. Pass baseRevision when editing so the ledger can show whether the edit was written on top of a newer revision. Requires idempotencyKey.",
50504
+ description: "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. commons_prepare_entity returns the entity profile contract and section guidance to compose against. Transparent Commons is an open wiki: any account may edit any entity, and every edit publishes and is recorded on the public contribution ledger. Publishing requires a featured image and body/source evidence. Pass baseRevision when editing so the ledger can show whether the edit was written on top of a newer revision. Requires idempotencyKey.",
50490
50505
  inputSchema: CommonsSubmitEntityInputSchema,
50491
50506
  outputSchema: recordOutputSchema("commons_submit_entity", CommonsGenericOutputSchema),
50492
50507
  annotations: {
@@ -50512,7 +50527,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50512
50527
  }, async (input) => executor.commonsGetEntityLedger(input));
50513
50528
  server.registerTool("commons_host_image", {
50514
50529
  title: "Host a Transparent Commons Image",
50515
- description: "Download an image from a public https URL, or accept base64 bytes, and store it as a permanent Commons-hosted image. Returns the hosted URL to use on an entity. Commons never serves a third-party image URL: submitting an entity with an external image hosts it automatically, and this tool exists for hosting an image before or independently of a write. JPEG, PNG, GIF, and WebP up to 10 MB; SVG is rejected because the wiki serves images to the public web. Identical bytes are stored once and reused.",
50530
+ description: "Host an image from a public https URL or base64 bytes, returning a permanent Commons URL. Commons never serves a third-party image URL \u2014 commons_submit_entity hosts external images automatically, so use this only to host one before or independently of a write. JPEG, PNG, GIF, WebP up to 10 MB; SVG rejected. Identical bytes are stored once.",
50516
50531
  inputSchema: CommonsHostImageInputSchema,
50517
50532
  outputSchema: recordOutputSchema("commons_host_image", CommonsGenericOutputSchema),
50518
50533
  annotations: {
@@ -50590,9 +50605,16 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50590
50605
  outputSchema: recordOutputSchema("commons_publish_editorial", CommonsGenericOutputSchema),
50591
50606
  annotations: { title: "Publish Transparent Commons Editorial Edition", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
50592
50607
  }, async (input) => executor.commonsPublishEditorial(input));
50608
+ server.registerTool("commons_update_editorial_article", {
50609
+ title: "Edit One Transparent Commons Article",
50610
+ description: "Replace the Markdown of one article in a published edition without resupplying the others: it reads the stored sources, swaps the article you name, and publishes the next revision with the rest byte-identical. Read commons_get_publication with includeArticles first for the current source and revision. commons_publish_editorial is the full-replace path. Requires baseRevision and idempotencyKey.",
50611
+ inputSchema: CommonsUpdateEditorialArticleInputSchema,
50612
+ outputSchema: recordOutputSchema("commons_update_editorial_article", CommonsGenericOutputSchema),
50613
+ annotations: { title: "Edit One Transparent Commons Article", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
50614
+ }, async (input) => executor.commonsUpdateEditorialArticle(input));
50593
50615
  server.registerTool("commons_get_publication", {
50594
50616
  title: "Get Transparent Commons Publication",
50595
- description: "Read a Transparent Commons publication and its latest edition revisions. Omit subdomain to inspect the caller-owned publication; pass a name to inspect a public publication. Returns the permanent public and archive URLs needed for sharing or later edits.",
50617
+ description: "Read a Transparent Commons publication and its latest edition revisions. Omit subdomain to inspect the caller-owned publication; pass a name to inspect a public publication. Returns the permanent public and archive URLs needed for sharing or later edits. Set includeArticles to also return every stored article\u2019s Markdown source and slug, which is what commons_update_editorial_article edits \u2014 the payload is large, so request it only when you intend to edit.",
50596
50618
  inputSchema: CommonsGetPublicationInputSchema,
50597
50619
  outputSchema: recordOutputSchema("commons_get_publication", CommonsGenericOutputSchema),
50598
50620
  annotations: { title: "Get Transparent Commons Publication", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
@@ -50659,7 +50681,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50659
50681
  }, async (input) => executor.localSourcebookCapture(input));
50660
50682
  server.registerTool("local_sourcebook_submission_status", {
50661
50683
  title: "Local Sourcebook Submission Status",
50662
- description: "Read the authenticated caller\u2019s listing draft, enrichment coverage, immutable revision number, publication state, and exact live LocalSourcebook.com profile and reviews URLs. When status is failed, acquisitionError states why in plain language \u2014 read it before retrying, because a refresh repeats the same acquisition and will fail the same way until the cause is fixed. Coverage counters are written only when acquisition succeeds, so a failed listing still shows the pre-run crawl block; zeroed counters do not mean the crawl never ran.",
50684
+ description: "Read the caller\u2019s listing draft, enrichment coverage, revision number, publication state, and live LocalSourcebook.com profile and reviews URLs. When status is failed, acquisitionError says why \u2014 read it before retrying, since a refresh repeats the same acquisition. Coverage counters are written only on success, so zeroed counters on a failed listing do not mean the crawl never ran.",
50663
50685
  inputSchema: LocalSourcebookSubmissionStatusInputSchema,
50664
50686
  outputSchema: recordOutputSchema("local_sourcebook_submission_status", LocalSourcebookOutputSchema),
50665
50687
  annotations: localPlanningToolAnnotations("Local Sourcebook Submission Status")
@@ -51589,6 +51611,12 @@ var init_http_mcp_tool_executor = __esm({
51589
51611
  commonsHostImage(input) {
51590
51612
  return this.call("/commons/images", input);
51591
51613
  }
51614
+ commonsUpdateEditorialArticle(input) {
51615
+ const { idempotencyKey: idempotencyKey3, ...body } = input;
51616
+ return this.call("/commons/publications/articles", { ...body, idempotencyKey: idempotencyKey3 }, this.timeoutMs, "POST", {
51617
+ "Idempotency-Key": `commons-article-${(0, import_node_crypto28.createHash)("sha256").update(idempotencyKey3).digest("hex")}`
51618
+ });
51619
+ }
51592
51620
  commonsSaveFilter(input) {
51593
51621
  return this.call("/commons/filters", input);
51594
51622
  }
@@ -51617,7 +51645,7 @@ var init_http_mcp_tool_executor = __esm({
51617
51645
  });
51618
51646
  }
51619
51647
  commonsGetPublication(input) {
51620
- const query = new URLSearchParams({ includeEditions: String(input.includeEditions ?? true) });
51648
+ const query = new URLSearchParams({ includeEditions: String(input.includeEditions ?? true), includeArticles: String(input.includeArticles ?? false) });
51621
51649
  return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
51622
51650
  }
51623
51651
  async captureSerpSnapshot(input) {
@@ -64052,9 +64080,7 @@ function socialMeta(input) {
64052
64080
  ];
64053
64081
  return tags.join("\n ");
64054
64082
  }
64055
- function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
64056
- const sourceBytes = input.articles.reduce((total, article) => total + Buffer.byteLength(article.markdown), 0);
64057
- if (sourceBytes > 2e6) throw new Error("Editorial reading-room Markdown must be 2,000,000 bytes or fewer in total.");
64083
+ function buildEditorialArticles(input) {
64058
64084
  const seenSlugs = /* @__PURE__ */ new Set();
64059
64085
  const seenOrders = /* @__PURE__ */ new Set();
64060
64086
  const parser = markdownRenderer();
@@ -64081,7 +64107,19 @@ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
64081
64107
  html: rendered
64082
64108
  };
64083
64109
  });
64110
+ return { articles, warnings, totalWordCount };
64111
+ }
64112
+ function renderEditorialArticleModels(input) {
64113
+ return buildEditorialArticles(input).articles;
64114
+ }
64115
+ function renderEditorialReadingRoom(input, now = /* @__PURE__ */ new Date()) {
64116
+ const sourceBytes = input.articles.reduce((total, article) => total + Buffer.byteLength(article.markdown), 0);
64117
+ if (sourceBytes > 2e6) throw new Error("Editorial reading-room Markdown must be 2,000,000 bytes or fewer in total.");
64084
64118
  const generatedAt = now.toISOString();
64119
+ const built = buildEditorialArticles(input);
64120
+ const articles = built.articles;
64121
+ const warnings = built.warnings;
64122
+ const totalWordCount = built.totalWordCount;
64085
64123
  const data = { ...input, generatedAt, articles };
64086
64124
  const template = readAsset("index.html");
64087
64125
  const html = template.replace("<title>Editorial Reading Room</title>", `<title>${escapeHtml6(input.site.title)}</title>`).replace('content="A mobile-first editorial reading room."', `content="${escapeHtml6(input.deck || input.site.heroTitle)}"`).replace("<!--__READING_ROOM_SOCIAL_META__-->", socialMeta(input)).replace("/*__READING_ROOM_STYLES__*/", readAsset("styles.css")).replace("/*__READING_ROOM_DATA__*/", escapedJsonForHtml(data)).replace("/*__READING_ROOM_SCRIPT__*/", readAsset("app.js"));
@@ -64562,6 +64600,141 @@ var init_commons_image_store = __esm({
64562
64600
  }
64563
64601
  });
64564
64602
 
64603
+ // src/api/commons-embeddings.ts
64604
+ function commonsEmbedModel() {
64605
+ return (process.env.JINA_EMBED_MODEL ?? "jina-embeddings-v5-omni-small").trim();
64606
+ }
64607
+ function commonsEmbedDim() {
64608
+ return Number((process.env.JINA_EMBED_DIM ?? "1024").trim());
64609
+ }
64610
+ function commonsSemanticSearchConfigured() {
64611
+ return Boolean(process.env.JINA_API_KEY?.trim() && process.env.MEMORY_DATABASE_URL?.trim());
64612
+ }
64613
+ function vectorSql() {
64614
+ if (_vectorSql) return _vectorSql;
64615
+ const url = process.env.MEMORY_DATABASE_URL?.trim();
64616
+ if (!url) throw new Error("MEMORY_DATABASE_URL is not set; Commons semantic search needs the shared Postgres.");
64617
+ _vectorSql = (0, import_serverless.neon)(url);
64618
+ return _vectorSql;
64619
+ }
64620
+ async function ensureCommonsVectorSchema() {
64621
+ if (vectorSchemaReady) return;
64622
+ const dimension = commonsEmbedDim();
64623
+ await vectorSql().query("CREATE EXTENSION IF NOT EXISTS vector");
64624
+ await vectorSql().query(`
64625
+ CREATE TABLE IF NOT EXISTS commons_index_vectors (
64626
+ document_id TEXT PRIMARY KEY,
64627
+ entity_id TEXT NOT NULL,
64628
+ document_type TEXT NOT NULL,
64629
+ title TEXT NOT NULL,
64630
+ embedding vector(${dimension}) NOT NULL,
64631
+ model TEXT NOT NULL,
64632
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
64633
+ )
64634
+ `);
64635
+ await vectorSql().query("CREATE INDEX IF NOT EXISTS commons_index_vectors_entity ON commons_index_vectors(entity_id)");
64636
+ vectorSchemaReady = true;
64637
+ }
64638
+ async function embedCommonsTexts(texts) {
64639
+ const apiKey = process.env.JINA_API_KEY?.trim();
64640
+ if (!apiKey) throw new Error("JINA_API_KEY is not set; Commons semantic search cannot embed.");
64641
+ if (!texts.length) return [];
64642
+ const response = await fetch("https://api.jina.ai/v1/embeddings", {
64643
+ method: "POST",
64644
+ headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
64645
+ body: JSON.stringify({
64646
+ model: commonsEmbedModel(),
64647
+ dimensions: commonsEmbedDim(),
64648
+ input: texts.map((text2) => ({ text: text2.slice(0, 8e3) }))
64649
+ }),
64650
+ signal: AbortSignal.timeout(6e4)
64651
+ });
64652
+ if (!response.ok) {
64653
+ throw new Error(`Jina embedding request failed with HTTP ${response.status}: ${(await response.text()).slice(0, 200)}`);
64654
+ }
64655
+ const payload = await response.json();
64656
+ const vectors = (payload.data ?? []).map((item) => item.embedding);
64657
+ if (vectors.length !== texts.length) {
64658
+ throw new Error(`Jina returned ${vectors.length} embeddings for ${texts.length} inputs.`);
64659
+ }
64660
+ return vectors;
64661
+ }
64662
+ async function embedQueuedCommonsDocuments(limit = 50) {
64663
+ if (!commonsSemanticSearchConfigured()) return { claimed: 0, embedded: 0, failed: 0, remaining: 0 };
64664
+ await ensureCommonsVectorSchema();
64665
+ const bounded = Math.max(1, Math.min(200, Math.floor(limit)));
64666
+ const queued = await getDb().execute({
64667
+ sql: `SELECT id, entity_id, document_type, title, text FROM commons_index_documents
64668
+ WHERE embedding_status IN ('queued', 'failed') ORDER BY updated_at ASC LIMIT ?`,
64669
+ args: [bounded]
64670
+ });
64671
+ const rows = queued.rows;
64672
+ if (!rows.length) return { claimed: 0, embedded: 0, failed: 0, remaining: await queuedCommonsDocumentCount() };
64673
+ let embedded = 0;
64674
+ let failed = 0;
64675
+ const model = commonsEmbedModel();
64676
+ try {
64677
+ const vectors = await embedCommonsTexts(rows.map((row) => `${row.title}
64678
+
64679
+ ${row.text}`));
64680
+ for (const [index, row] of rows.entries()) {
64681
+ const literal = `[${vectors[index].join(",")}]`;
64682
+ await vectorSql().query(
64683
+ `INSERT INTO commons_index_vectors (document_id, entity_id, document_type, title, embedding, model, updated_at)
64684
+ VALUES ($1, $2, $3, $4, $5::vector, $6, now())
64685
+ ON CONFLICT (document_id) DO UPDATE SET entity_id = EXCLUDED.entity_id, document_type = EXCLUDED.document_type,
64686
+ title = EXCLUDED.title, embedding = EXCLUDED.embedding, model = EXCLUDED.model, updated_at = now()`,
64687
+ [row.id, row.entity_id, row.document_type, row.title, literal, model]
64688
+ );
64689
+ await getDb().execute({
64690
+ sql: `UPDATE commons_index_documents SET embedding_status = 'indexed', embedding_provider = ?, embedding_model = ?,
64691
+ vector_ref = ?, indexed_at = ?, error = NULL WHERE id = ?`,
64692
+ args: [COMMONS_EMBED_PROVIDER, model, row.id, (/* @__PURE__ */ new Date()).toISOString(), row.id]
64693
+ });
64694
+ embedded += 1;
64695
+ }
64696
+ } catch (error) {
64697
+ failed = rows.length - embedded;
64698
+ const message = (error instanceof Error ? error.message : String(error)).slice(0, 500);
64699
+ for (const row of rows.slice(embedded)) {
64700
+ await getDb().execute({
64701
+ sql: `UPDATE commons_index_documents SET embedding_status = 'failed', error = ? WHERE id = ?`,
64702
+ args: [message, row.id]
64703
+ }).catch(() => void 0);
64704
+ }
64705
+ }
64706
+ return { claimed: rows.length, embedded, failed, remaining: await queuedCommonsDocumentCount() };
64707
+ }
64708
+ async function queuedCommonsDocumentCount() {
64709
+ const result = await getDb().execute(`SELECT COUNT(*) AS n FROM commons_index_documents WHERE embedding_status IN ('queued', 'failed')`);
64710
+ return Number(result.rows[0]?.n ?? 0);
64711
+ }
64712
+ async function semanticCommonsEntityScores(query, limit = 40) {
64713
+ const scores = /* @__PURE__ */ new Map();
64714
+ if (!commonsSemanticSearchConfigured() || !query.trim()) return scores;
64715
+ await ensureCommonsVectorSchema();
64716
+ const [vector] = await embedCommonsTexts([query]);
64717
+ if (!vector) return scores;
64718
+ const rows = await vectorSql().query(
64719
+ `SELECT entity_id, MAX(1 - (embedding <=> $1::vector)) AS score
64720
+ FROM commons_index_vectors GROUP BY entity_id ORDER BY score DESC LIMIT $2`,
64721
+ [`[${vector.join(",")}]`, Math.max(1, Math.min(100, limit))]
64722
+ );
64723
+ for (const row of rows) scores.set(String(row.entity_id), Number(row.score));
64724
+ return scores;
64725
+ }
64726
+ var import_serverless, COMMONS_EMBED_PROVIDER, _vectorSql, vectorSchemaReady;
64727
+ var init_commons_embeddings = __esm({
64728
+ "src/api/commons-embeddings.ts"() {
64729
+ "use strict";
64730
+ import_serverless = require("@neondatabase/serverless");
64731
+ init_db();
64732
+ COMMONS_EMBED_PROVIDER = "jina";
64733
+ _vectorSql = null;
64734
+ vectorSchemaReady = false;
64735
+ }
64736
+ });
64737
+
64565
64738
  // src/api/schema-presence.ts
64566
64739
  async function loadSchemaObjects() {
64567
64740
  const res = await getDb().execute(
@@ -65055,17 +65228,35 @@ async function searchCommonsEntities(filters = {}, userId) {
65055
65228
  args.push(mergedFilters.updatedAfter);
65056
65229
  }
65057
65230
  const terms = tokenize(mergedFilters.query);
65058
- for (const term of terms) {
65059
- clauses.push(`(
65060
- search_text LIKE ?
65061
- OR EXISTS (
65062
- SELECT 1 FROM commons_claims
65063
- WHERE commons_claims.subject_entity_id = commons_entities.id
65064
- AND (commons_claims.predicate LIKE ? OR commons_claims.predicate_label LIKE ? OR commons_claims.title LIKE ? OR commons_claims.object_url LIKE ?)
65065
- )
65066
- )`);
65067
- const pattern = `%${escapeLike(term)}%`;
65068
- args.push(pattern, pattern, pattern, pattern, pattern);
65231
+ const searchMode = mergedFilters.mode === "lexical" || mergedFilters.mode === "semantic" ? mergedFilters.mode : "hybrid";
65232
+ const semanticScores = !terms.length || searchMode === "lexical" || !commonsSemanticSearchConfigured() ? /* @__PURE__ */ new Map() : await semanticCommonsEntityScores(String(mergedFilters.query ?? ""), SEARCH_CANDIDATE_LIMIT).catch(() => /* @__PURE__ */ new Map());
65233
+ const semanticIds = [...semanticScores.entries()].filter(([, score]) => score >= 0.6).sort((left, right) => right[1] - left[1]).slice(0, 60).map(([id]) => id);
65234
+ if (terms.length) {
65235
+ const termClauses = [];
65236
+ for (const term of terms) {
65237
+ termClauses.push(`(
65238
+ search_text LIKE ?
65239
+ OR EXISTS (
65240
+ SELECT 1 FROM commons_claims
65241
+ WHERE commons_claims.subject_entity_id = commons_entities.id
65242
+ AND (commons_claims.predicate LIKE ? OR commons_claims.predicate_label LIKE ? OR commons_claims.title LIKE ? OR commons_claims.object_url LIKE ?)
65243
+ )
65244
+ )`);
65245
+ const pattern = `%${escapeLike(term)}%`;
65246
+ args.push(pattern, pattern, pattern, pattern, pattern);
65247
+ }
65248
+ const lexicalClause = `(${termClauses.join(" AND ")})`;
65249
+ if (searchMode === "semantic") {
65250
+ if (!semanticIds.length) return { results: [], filters: mergedFilters, limit, offset };
65251
+ args.length -= terms.length * 5;
65252
+ clauses.push(`id IN (${semanticIds.map(() => "?").join(", ")})`);
65253
+ args.push(...semanticIds);
65254
+ } else if (semanticIds.length) {
65255
+ clauses.push(`(${lexicalClause} OR id IN (${semanticIds.map(() => "?").join(", ")}))`);
65256
+ args.push(...semanticIds);
65257
+ } else {
65258
+ clauses.push(lexicalClause);
65259
+ }
65069
65260
  }
65070
65261
  if (!terms.length) {
65071
65262
  args.push(limit, offset);
@@ -65102,6 +65293,7 @@ async function searchCommonsEntities(filters = {}, userId) {
65102
65293
  `,
65103
65294
  args
65104
65295
  });
65296
+ const mode = searchMode;
65105
65297
  const ranked = candidates.rows.map((row) => {
65106
65298
  const record = row;
65107
65299
  const score = relevanceScore(terms, {
@@ -65112,8 +65304,10 @@ async function searchCommonsEntities(filters = {}, userId) {
65112
65304
  searchText: String(record.search_text ?? ""),
65113
65305
  claimText: String(record.claim_text ?? "")
65114
65306
  });
65115
- return { id: String(record.id), updatedAt: String(record.updated_at ?? ""), title: String(record.title ?? ""), score };
65116
- }).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || right.updatedAt.localeCompare(left.updatedAt) || left.title.localeCompare(right.title));
65307
+ const semantic = semanticScores.get(String(record.id)) ?? 0;
65308
+ const fused = mode === "semantic" ? semantic * 100 : score + semantic * 60;
65309
+ return { id: String(record.id), updatedAt: String(record.updated_at ?? ""), title: String(record.title ?? ""), score: fused, lexical: score, semantic };
65310
+ }).filter((candidate) => mode === "semantic" ? candidate.semantic > 0 : candidate.lexical > 0 || candidate.semantic >= 0.62).sort((left, right) => right.score - left.score || right.updatedAt.localeCompare(left.updatedAt) || left.title.localeCompare(right.title));
65117
65311
  const pageIds = ranked.slice(offset, offset + limit).map((candidate) => candidate.id);
65118
65312
  if (!pageIds.length) return { results: [], filters: mergedFilters, limit, offset };
65119
65313
  const page = await getDb().execute({
@@ -66599,6 +66793,7 @@ function normalizeRelatedEntities(related) {
66599
66793
  }
66600
66794
  function sanitizeSearchFilters(filters) {
66601
66795
  return {
66796
+ mode: filters.mode === "lexical" || filters.mode === "semantic" ? filters.mode : "hybrid",
66602
66797
  query: optionalText2(filters.query, 300) ?? void 0,
66603
66798
  entityType: filters.entityType ? normalizeEntityType(filters.entityType) : void 0,
66604
66799
  tag: optionalText2(filters.tag, 80) ?? void 0,
@@ -66866,6 +67061,7 @@ var init_commons_repository = __esm({
66866
67061
  import_node_crypto39 = require("crypto");
66867
67062
  init_db();
66868
67063
  init_commons_image_store();
67064
+ init_commons_embeddings();
66869
67065
  init_schema_presence();
66870
67066
  init_rates();
66871
67067
  init_commons_linksets();
@@ -67057,6 +67253,101 @@ var init_commons_repository = __esm({
67057
67253
  }
67058
67254
  });
67059
67255
 
67256
+ // src/editorial-reading-room/article-page.ts
67257
+ function escapeHtml7(value) {
67258
+ return String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
67259
+ }
67260
+ function articlePath(origin, slug4) {
67261
+ return `${origin.replace(/\/+$/, "")}/${encodeURIComponent(slug4)}`;
67262
+ }
67263
+ function renderEditorialArticlePage(input) {
67264
+ const articles = renderEditorialArticleModels(input.edition);
67265
+ const index = articles.findIndex((article2) => article2.slug === input.articleSlug);
67266
+ if (index === -1) return null;
67267
+ const article = articles[index];
67268
+ const previous = index > 0 ? articles[index - 1] : null;
67269
+ const next = index < articles.length - 1 ? articles[index + 1] : null;
67270
+ const canonicalUrl = articlePath(input.origin, article.slug);
67271
+ const site = input.edition.site;
67272
+ const description = article.summary || input.edition.deck || site.title;
67273
+ const jsonLd = JSON.stringify({
67274
+ "@context": "https://schema.org",
67275
+ "@type": "Article",
67276
+ "@id": `${canonicalUrl}#article`,
67277
+ headline: article.title,
67278
+ description,
67279
+ articleSection: article.category,
67280
+ wordCount: article.wordCount,
67281
+ url: canonicalUrl,
67282
+ isPartOf: { "@type": "CollectionPage", "@id": `${input.editionUrl}#collection`, name: site.title, url: input.editionUrl },
67283
+ publisher: { "@type": "Organization", name: input.publicationTitle, url: input.origin }
67284
+ }).replace(/</g, "\\u003c");
67285
+ const jumps = article.toc.length ? `<nav class="article-jumps" aria-label="On this page">${article.toc.map((item) => `<a href="#${escapeHtml7(item.id)}">${escapeHtml7(item.title)}</a>`).join("")}</nav>` : "";
67286
+ const neighbour = (item, label) => item ? `<a class="neighbour" href="${escapeHtml7(articlePath(input.origin, item.slug))}"><span>${label}</span><strong>${escapeHtml7(item.title)}</strong></a>` : "";
67287
+ const html = `<!doctype html>
67288
+ <html lang="en">
67289
+ <head>
67290
+ <meta charset="utf-8">
67291
+ <meta name="viewport" content="width=device-width, initial-scale=1">
67292
+ <title>${escapeHtml7(article.title)} \u2014 ${escapeHtml7(input.publicationTitle)}</title>
67293
+ <meta name="description" content="${escapeHtml7(description)}">
67294
+ <link rel="canonical" href="${escapeHtml7(canonicalUrl)}">
67295
+ <meta property="og:type" content="article">
67296
+ <meta property="og:title" content="${escapeHtml7(article.title)}">
67297
+ <meta property="og:description" content="${escapeHtml7(description)}">
67298
+ <meta property="og:url" content="${escapeHtml7(canonicalUrl)}">
67299
+ <meta property="og:site_name" content="${escapeHtml7(input.publicationTitle)}">
67300
+ <meta name="twitter:card" content="summary_large_image">
67301
+ <meta name="twitter:title" content="${escapeHtml7(article.title)}">
67302
+ <meta name="twitter:description" content="${escapeHtml7(description)}">
67303
+ <script type="application/ld+json">${jsonLd}</script>
67304
+ <style>
67305
+ :root{color-scheme:light dark;--ink:#12171f;--muted:#5b6672;--rule:#dfe4ea;--bg:#fbfbfa;--accent:#8a6a2f}
67306
+ *{box-sizing:border-box}
67307
+ body{margin:0;background:var(--bg);color:var(--ink);font:17px/1.65 Georgia,"Times New Roman",serif}
67308
+ a{color:inherit}
67309
+ header.masthead{border-bottom:1px solid var(--rule);padding:18px 20px}
67310
+ header.masthead a{font:600 13px/1 ui-sans-serif,system-ui,sans-serif;letter-spacing:.08em;text-transform:uppercase;text-decoration:none;color:var(--muted)}
67311
+ main{max-width:44rem;margin:0 auto;padding:34px 20px 64px}
67312
+ .kicker{font:600 12px/1 ui-sans-serif,system-ui,sans-serif;letter-spacing:.1em;text-transform:uppercase;color:var(--accent)}
67313
+ h1{font-size:2.1rem;line-height:1.18;margin:12px 0 10px}
67314
+ .summary{color:var(--muted);font-size:1.06rem;margin:0 0 18px}
67315
+ .meta{font:12px/1 ui-sans-serif,system-ui,sans-serif;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:11px 0;margin-bottom:26px}
67316
+ .article-jumps{display:flex;flex-wrap:wrap;gap:10px;margin:0 0 26px}
67317
+ .article-jumps a{font:13px/1 ui-sans-serif,system-ui,sans-serif;color:var(--muted);text-decoration:none;border:1px solid var(--rule);border-radius:999px;padding:6px 11px}
67318
+ article h2{font-size:1.4rem;margin:32px 0 10px}
67319
+ article h3{font-size:1.15rem;margin:24px 0 8px}
67320
+ article p{margin:0 0 17px}
67321
+ .neighbours{display:grid;gap:12px;margin-top:44px;padding-top:22px;border-top:1px solid var(--rule)}
67322
+ .neighbour{display:block;text-decoration:none;border:1px solid var(--rule);border-radius:10px;padding:13px 15px;background:#fff}
67323
+ .neighbour span{display:block;font:11px/1 ui-sans-serif,system-ui,sans-serif;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);margin-bottom:5px}
67324
+ footer{border-top:1px solid var(--rule);padding:20px;text-align:center;color:var(--muted);font:13px/1.5 ui-sans-serif,system-ui,sans-serif}
67325
+ @media (prefers-color-scheme:dark){:root{--ink:#eef1f5;--muted:#9aa4b1;--rule:#2b323b;--bg:#12151a}.neighbour{background:#171b21}}
67326
+ </style>
67327
+ </head>
67328
+ <body>
67329
+ <header class="masthead"><a href="${escapeHtml7(input.origin)}">${escapeHtml7(input.publicationTitle)}</a></header>
67330
+ <main>
67331
+ <p class="kicker">${escapeHtml7(article.kicker || article.category)}</p>
67332
+ <h1>${escapeHtml7(article.title)}</h1>
67333
+ <p class="summary">${escapeHtml7(article.summary)}</p>
67334
+ <p class="meta">${escapeHtml7(article.category)} \xB7 ${article.wordCount} words \xB7 ${article.readingMinutes} min \xB7 <a href="${escapeHtml7(input.editionUrl)}">${escapeHtml7(site.title)}</a></p>
67335
+ ${jumps}
67336
+ <article>${article.html}</article>
67337
+ <div class="neighbours">${neighbour(previous, "Previous")}${neighbour(next, "Next")}</div>
67338
+ </main>
67339
+ <footer>${escapeHtml7(input.publicationTitle)} \xB7 <a href="${escapeHtml7(input.editionUrl)}">All articles in ${escapeHtml7(site.title)}</a></footer>
67340
+ </body>
67341
+ </html>`;
67342
+ return { html, article, canonicalUrl };
67343
+ }
67344
+ var init_article_page = __esm({
67345
+ "src/editorial-reading-room/article-page.ts"() {
67346
+ "use strict";
67347
+ init_render();
67348
+ }
67349
+ });
67350
+
67060
67351
  // src/api/commons-publication-repository.ts
67061
67352
  async function prepareCommonsPublication(input, user) {
67062
67353
  await ensureCommonsSchema();
@@ -67241,6 +67532,92 @@ async function publishCommonsEditorial(input, user) {
67241
67532
  if (!edition) throw new CommonsPublicationError("publication_publish_failed", "The published edition was not persisted.");
67242
67533
  return publicationResult({ ...publication, latestEditionId: editionId, updatedAt: now }, edition, false);
67243
67534
  }
67535
+ async function updateCommonsEditorialArticle(input, user) {
67536
+ await ensureCommonsSchema();
67537
+ const subdomain = normalizePublicationSubdomain(input.publicationSubdomain);
67538
+ const publication = await getCommonsPublicationBySubdomain(subdomain);
67539
+ if (!publication) throw new CommonsPublicationError("publication_not_found", "Claim this publication name before editing an edition.", 404);
67540
+ if (publication.ownerUserId !== Number(user.id)) {
67541
+ throw new CommonsPublicationError("publication_not_owned", "Only the account that claimed this publication can edit it.", 404);
67542
+ }
67543
+ const idempotencyKey3 = requiredIdempotencyKey(input.idempotencyKey);
67544
+ const replay = await getEditionByIdempotency(Number(user.id), idempotencyKey3);
67545
+ if (replay) return publicationResult(publication, replay, true);
67546
+ const latestOverall = publication.latestEditionId ? await getPublicationEditionById(publication.latestEditionId) : null;
67547
+ const editionSlug = normalizeEditionSlug(input.editionSlug || latestOverall?.editionSlug || "");
67548
+ if (!editionSlug) throw new CommonsPublicationError("publication_edition_required", "This publication has no edition yet; publish one before editing an article.", 409);
67549
+ const latest = await getLatestEdition(publication.id, editionSlug);
67550
+ if (!latest) throw new CommonsPublicationError("publication_edition_not_found", `Edition ${editionSlug} does not exist.`, 404);
67551
+ if (input.baseRevision !== latest.revision) {
67552
+ throw new CommonsPublicationError("publication_revision_conflict", `Edition ${editionSlug} is revision ${latest.revision}; read it again before editing.`, 409);
67553
+ }
67554
+ const articles = latest.articles;
67555
+ const index = articles.findIndex((article) => article.slug === input.articleSlug);
67556
+ if (index === -1) {
67557
+ throw new CommonsPublicationError("publication_article_not_found", `Edition ${editionSlug} has no article with slug ${input.articleSlug}. Known slugs: ${articles.map((article) => article.slug).join(", ")}.`, 404);
67558
+ }
67559
+ const current = articles[index];
67560
+ const nextArticles = [...articles];
67561
+ nextArticles[index] = {
67562
+ ...current,
67563
+ ...input.markdown === void 0 ? {} : { markdown: input.markdown },
67564
+ ...input.title === void 0 ? {} : { title: input.title },
67565
+ ...input.summary === void 0 ? {} : { summary: input.summary },
67566
+ ...input.category === void 0 ? {} : { category: input.category },
67567
+ ...input.kicker === void 0 ? {} : { kicker: input.kicker },
67568
+ ...input.order === void 0 ? {} : { order: input.order }
67569
+ };
67570
+ const editionInput = { site: latest.site, deck: latest.deck, articles: nextArticles };
67571
+ const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
67572
+ const rendered = renderEditorialReadingRoom(editionInput);
67573
+ const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
67574
+ const editionId = `tced_${(0, import_node_crypto40.randomUUID)()}`;
67575
+ const revision = latest.revision + 1;
67576
+ const now = (/* @__PURE__ */ new Date()).toISOString();
67577
+ await getDb().batch([
67578
+ {
67579
+ sql: `
67580
+ INSERT INTO commons_publication_editions (
67581
+ id, publication_id, owner_user_id, edition_slug, revision, title, site_json, deck,
67582
+ articles_json, html, filename, sha256, article_count, word_count, bytes, warnings_json,
67583
+ idempotency_key, created_at, published_at
67584
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
67585
+ `,
67586
+ args: [
67587
+ editionId,
67588
+ publication.id,
67589
+ Number(user.id),
67590
+ editionSlug,
67591
+ revision,
67592
+ latest.site.title,
67593
+ JSON.stringify(latest.site),
67594
+ latest.deck,
67595
+ JSON.stringify(nextArticles),
67596
+ html,
67597
+ rendered.filename,
67598
+ (0, import_node_crypto40.createHash)("sha256").update(html).digest("hex"),
67599
+ rendered.articleCount,
67600
+ rendered.wordCount,
67601
+ Buffer.byteLength(html),
67602
+ JSON.stringify(rendered.warnings),
67603
+ idempotencyKey3,
67604
+ now,
67605
+ now
67606
+ ]
67607
+ },
67608
+ {
67609
+ sql: "UPDATE commons_publications SET latest_edition_id = ?, updated_at = ? WHERE id = ? AND owner_user_id = ?",
67610
+ args: [editionId, now, publication.id, Number(user.id)]
67611
+ }
67612
+ ], "write");
67613
+ const edition = await getPublicationEditionById(editionId);
67614
+ if (!edition) throw new CommonsPublicationError("publication_publish_failed", "The edited edition was not persisted.");
67615
+ return {
67616
+ ...publicationResult({ ...publication, latestEditionId: editionId, updatedAt: now }, edition, false),
67617
+ updatedArticle: { slug: input.articleSlug, previousRevision: latest.revision, revision },
67618
+ unchangedArticles: articles.length - 1
67619
+ };
67620
+ }
67244
67621
  async function getCommonsPublicationBySubdomain(subdomainInput) {
67245
67622
  await ensureCommonsSchema();
67246
67623
  const subdomain = normalizePublicationSubdomain(subdomainInput);
@@ -67269,6 +67646,20 @@ async function listCommonsPublicationEditions(publicationId) {
67269
67646
  if (!publication) return [];
67270
67647
  return result.rows.map((row) => rowToEdition(row, publication.subdomain));
67271
67648
  }
67649
+ async function getCommonsPublicationArticleHtml(subdomainInput, articleSlug, editionSlugInput) {
67650
+ const found = await getCommonsPublicationEditionHtml(subdomainInput, editionSlugInput);
67651
+ if (!found) return null;
67652
+ const { publication, edition } = found;
67653
+ const page = renderEditorialArticlePage({
67654
+ edition: { site: edition.site, deck: edition.deck, articles: edition.articles },
67655
+ articleSlug,
67656
+ origin: publicationPublicUrl(publication.subdomain),
67657
+ editionUrl: editionPublicUrl(publication.subdomain, edition.editionSlug),
67658
+ publicationTitle: publication.title
67659
+ });
67660
+ if (!page) return null;
67661
+ return { publication, edition, html: page.html, canonicalUrl: page.canonicalUrl };
67662
+ }
67272
67663
  async function getCommonsPublicationEditionHtml(subdomainInput, editionSlugInput) {
67273
67664
  const publication = await getCommonsPublicationBySubdomain(subdomainInput);
67274
67665
  if (!publication) return null;
@@ -67338,6 +67729,7 @@ function rowToEdition(row, subdomain) {
67338
67729
  revision: Number(row.revision),
67339
67730
  title: String(row.title),
67340
67731
  site: parseJson4(row.site_json, {}),
67732
+ articles: parseJson4(row.articles_json, []),
67341
67733
  deck: String(row.deck),
67342
67734
  filename: String(row.filename),
67343
67735
  sha256: String(row.sha256),
@@ -67416,6 +67808,7 @@ var init_commons_publication_repository = __esm({
67416
67808
  init_db();
67417
67809
  init_commons_repository();
67418
67810
  init_render();
67811
+ init_article_page();
67419
67812
  PUBLICATION_ROOT_DOMAIN = process.env.COMMONS_PUBLICATION_ROOT_DOMAIN || "transparent-commons.cc";
67420
67813
  RESERVED_SUBDOMAINS = /* @__PURE__ */ new Set([
67421
67814
  "admin",
@@ -67455,6 +67848,7 @@ var init_commons_publication_repository = __esm({
67455
67848
  // src/api/commons-routes.ts
67456
67849
  function filtersFromQuery(query) {
67457
67850
  return {
67851
+ mode: query.mode === "lexical" || query.mode === "semantic" ? query.mode : void 0,
67458
67852
  query: query.query || query.q,
67459
67853
  entityType: query.entityType,
67460
67854
  tag: query.tag,
@@ -67510,10 +67904,17 @@ function publicationError(c, error) {
67510
67904
  }
67511
67905
  throw error;
67512
67906
  }
67907
+ function withArticleSources(editions, include) {
67908
+ if (include) return editions;
67909
+ return editions.map((edition) => {
67910
+ const { articles: _articles, ...rest } = edition;
67911
+ return rest;
67912
+ });
67913
+ }
67513
67914
  function xmlEscape(value) {
67514
67915
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
67515
67916
  }
67516
- var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema, HostImageSchema;
67917
+ var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema, UpdateEditorialArticleSchema, HostImageSchema;
67517
67918
  var init_commons_routes = __esm({
67518
67919
  "src/api/commons-routes.ts"() {
67519
67920
  "use strict";
@@ -67522,6 +67923,7 @@ var init_commons_routes = __esm({
67522
67923
  init_api_auth();
67523
67924
  init_commons_repository();
67524
67925
  init_commons_image_store();
67926
+ init_commons_embeddings();
67525
67927
  init_commons_linksets();
67526
67928
  init_listing_write_billing();
67527
67929
  init_commons_publication_repository();
@@ -67529,6 +67931,7 @@ var init_commons_routes = __esm({
67529
67931
  commonsApp = new import_hono31.Hono();
67530
67932
  auth2 = createApiKeyAuth();
67531
67933
  SearchBodySchema = import_zod52.z.object({
67934
+ mode: import_zod52.z.enum(["lexical", "semantic", "hybrid"]).optional(),
67532
67935
  query: import_zod52.z.string().max(300).optional(),
67533
67936
  entityType: import_zod52.z.string().max(120).optional(),
67534
67937
  tag: import_zod52.z.string().max(80).optional(),
@@ -67691,6 +68094,15 @@ var init_commons_routes = __esm({
67691
68094
  ValidatePublicationSchema = import_zod52.z.object(CommonsValidatePublicationInputSchema).strict();
67692
68095
  ClaimPublicationSchema = import_zod52.z.object(CommonsClaimPublicationInputSchema).strict();
67693
68096
  PublishEditorialSchema = import_zod52.z.object(CommonsPublishEditorialInputSchema).strict();
68097
+ UpdateEditorialArticleSchema = import_zod52.z.object(CommonsUpdateEditorialArticleInputSchema).strict();
68098
+ commonsApp.post("/index/embed", auth2, async (c) => {
68099
+ const body = await c.req.json().catch(() => ({}));
68100
+ const result = await embedQueuedCommonsDocuments(Number(body.limit) || 50);
68101
+ return c.json({ ok: true, data: result });
68102
+ });
68103
+ commonsApp.get("/index/status", auth2, async (c) => {
68104
+ return c.json({ ok: true, data: { queued: await queuedCommonsDocumentCount(), semanticSearchConfigured: commonsSemanticSearchConfigured() } });
68105
+ });
67694
68106
  commonsApp.get("/health", async (c) => {
67695
68107
  await ensureCommonsSchema();
67696
68108
  return c.json({ ok: true, data: commonsDatabaseReport() });
@@ -67726,11 +68138,16 @@ var init_commons_routes = __esm({
67726
68138
  if (!parsed.success) return validationError(c, parsed.error);
67727
68139
  return publicationOperation(c, () => publishCommonsEditorial(parsed.data, c.get("user")), 201);
67728
68140
  });
68141
+ commonsApp.post("/publications/articles", auth2, async (c) => {
68142
+ const parsed = UpdateEditorialArticleSchema.safeParse(await c.req.json().catch(() => ({})));
68143
+ if (!parsed.success) return validationError(c, parsed.error);
68144
+ return publicationOperation(c, () => updateCommonsEditorialArticle(parsed.data, c.get("user")), 201);
68145
+ });
67729
68146
  commonsApp.get("/publications/me", auth2, async (c) => {
67730
68147
  const publication = await getCommonsPublicationForOwner(Number(c.get("user").id));
67731
68148
  if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "This account has not claimed a Commons publication yet." }, 404);
67732
68149
  const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
67733
- return c.json({ ok: true, data: { publication, editions } });
68150
+ return c.json({ ok: true, data: { publication, editions: withArticleSources(editions, c.req.query("includeArticles") === "true") } });
67734
68151
  });
67735
68152
  commonsApp.get("/publications/:subdomain/site", async (c) => {
67736
68153
  try {
@@ -67744,6 +68161,28 @@ var init_commons_routes = __esm({
67744
68161
  return publicationError(c, error);
67745
68162
  }
67746
68163
  });
68164
+ commonsApp.get("/publications/:subdomain/articles/:articleSlug/site", async (c) => {
68165
+ try {
68166
+ const result = await getCommonsPublicationArticleHtml(c.req.param("subdomain"), c.req.param("articleSlug"));
68167
+ if (!result) return c.json({ ok: false, error: "article_not_found", message: "No published article matched that publication and slug." }, 404);
68168
+ return c.html(result.html, 200, {
68169
+ "cache-control": "public, max-age=60, s-maxage=300",
68170
+ "x-robots-tag": "index, follow",
68171
+ link: `<${result.canonicalUrl}>; rel="canonical"`
68172
+ });
68173
+ } catch (error) {
68174
+ return publicationError(c, error);
68175
+ }
68176
+ });
68177
+ commonsApp.get("/publications/:subdomain/editions/:editionSlug/articles/:articleSlug/site", async (c) => {
68178
+ try {
68179
+ const result = await getCommonsPublicationArticleHtml(c.req.param("subdomain"), c.req.param("articleSlug"), c.req.param("editionSlug"));
68180
+ if (!result) return c.json({ ok: false, error: "article_not_found", message: "No published article matched that publication, edition, and slug." }, 404);
68181
+ return c.html(result.html, 200, { "cache-control": "public, max-age=60, s-maxage=300", "x-robots-tag": "index, follow" });
68182
+ } catch (error) {
68183
+ return publicationError(c, error);
68184
+ }
68185
+ });
67747
68186
  commonsApp.get("/publications/:subdomain/editions/:editionSlug/site", async (c) => {
67748
68187
  try {
67749
68188
  const result = await getCommonsPublicationEditionHtml(c.req.param("subdomain"), c.req.param("editionSlug"));
@@ -67770,7 +68209,7 @@ var init_commons_routes = __esm({
67770
68209
  const publication = await getCommonsPublicationBySubdomain(c.req.param("subdomain"));
67771
68210
  if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "No Commons publication matched that name." }, 404);
67772
68211
  const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
67773
- return c.json({ ok: true, data: { publication, editions } });
68212
+ return c.json({ ok: true, data: { publication, editions: withArticleSources(editions, false) } });
67774
68213
  } catch (error) {
67775
68214
  return publicationError(c, error);
67776
68215
  }
@@ -71312,7 +71751,7 @@ function pixelSnippet(publicId) {
71312
71751
  function formSnippet(publicId) {
71313
71752
  return `<div data-mcp-form="${publicId}"></div><script async src="https://mcpscraper.dev/analytics/v1/form.js?form=${publicId}"></script>`;
71314
71753
  }
71315
- function escapeHtml7(value) {
71754
+ function escapeHtml8(value) {
71316
71755
  return String(value ?? "").replace(
71317
71756
  /[&<>"']/g,
71318
71757
  (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]
@@ -71324,13 +71763,13 @@ function renderPublicForm(form, placementUrl) {
71324
71763
  const fields = form.fields.map((field) => {
71325
71764
  const required = field.required ? " required" : "";
71326
71765
  const options = (field.options ?? []).map(
71327
- (option) => `<option value="${escapeHtml7(option)}">${escapeHtml7(option)}</option>`
71766
+ (option) => `<option value="${escapeHtml8(option)}">${escapeHtml8(option)}</option>`
71328
71767
  ).join("");
71329
- const control = field.type === "textarea" ? `<textarea id="${escapeHtml7(field.id)}" name="${escapeHtml7(field.id)}"${required}></textarea>` : field.type === "select" ? `<select id="${escapeHtml7(field.id)}" name="${escapeHtml7(field.id)}"${required}><option value="">Choose\u2026</option>${options}</select>` : field.type === "checkbox" ? `<input id="${escapeHtml7(field.id)}" name="${escapeHtml7(field.id)}" type="checkbox" value="true"${required}>` : `<input id="${escapeHtml7(field.id)}" name="${escapeHtml7(field.id)}" type="${escapeHtml7(field.type)}"${required}>`;
71330
- return `<label${field.type === "checkbox" ? ' class="check"' : ""}><span>${escapeHtml7(field.label)}${field.required ? " *" : ""}</span>${control}</label>`;
71768
+ const control = field.type === "textarea" ? `<textarea id="${escapeHtml8(field.id)}" name="${escapeHtml8(field.id)}"${required}></textarea>` : field.type === "select" ? `<select id="${escapeHtml8(field.id)}" name="${escapeHtml8(field.id)}"${required}><option value="">Choose\u2026</option>${options}</select>` : field.type === "checkbox" ? `<input id="${escapeHtml8(field.id)}" name="${escapeHtml8(field.id)}" type="checkbox" value="true"${required}>` : `<input id="${escapeHtml8(field.id)}" name="${escapeHtml8(field.id)}" type="${escapeHtml8(field.type)}"${required}>`;
71769
+ return `<label${field.type === "checkbox" ? ' class="check"' : ""}><span>${escapeHtml8(field.label)}${field.required ? " *" : ""}</span>${control}</label>`;
71331
71770
  }).join("");
71332
71771
  const brand = form.brand;
71333
- return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml7(brand.textColor)};background:${escapeHtml7(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml7(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml7(form.consent_text)}</p>` : ""}<button>${escapeHtml7(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml7(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
71772
+ return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml8(brand.textColor)};background:${escapeHtml8(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml8(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml8(form.consent_text)}</p>` : ""}<button>${escapeHtml8(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml8(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
71334
71773
  }
71335
71774
  var import_hono32, import_factory6, import_zod54, import_node_crypto43, import_papaparse4, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
71336
71775
  var init_analytics_routes = __esm({
@@ -72816,7 +73255,7 @@ var init_scheduled_run_view_links = __esm({
72816
73255
  });
72817
73256
 
72818
73257
  // src/personal-authority/render.ts
72819
- function escapeHtml8(value) {
73258
+ function escapeHtml9(value) {
72820
73259
  return value.replace(/[&<>"']/g, (character) => ({
72821
73260
  "&": "&amp;",
72822
73261
  "<": "&lt;",
@@ -72827,13 +73266,13 @@ function escapeHtml8(value) {
72827
73266
  }
72828
73267
  function safeHref(value) {
72829
73268
  const trimmed = value.trim();
72830
- return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml8(trimmed) : "#";
73269
+ return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml9(trimmed) : "#";
72831
73270
  }
72832
73271
  function initials(name) {
72833
73272
  return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase()).join("") || "PA";
72834
73273
  }
72835
73274
  function action(action3, secondary = false) {
72836
- return `<a class="button${secondary ? " button--secondary" : ""}" href="${safeHref(action3.href)}">${escapeHtml8(action3.label)} <span aria-hidden="true">\u2197</span></a>`;
73275
+ return `<a class="button${secondary ? " button--secondary" : ""}" href="${safeHref(action3.href)}">${escapeHtml9(action3.label)} <span aria-hidden="true">\u2197</span></a>`;
72837
73276
  }
72838
73277
  function section(enabled, html) {
72839
73278
  return enabled ? html : "";
@@ -72842,7 +73281,7 @@ function renderPersonalAuthorityV1(input, config, generatedAt) {
72842
73281
  const flags = config.featureFlags;
72843
73282
  const brand = config.brandName || input.name;
72844
73283
  const generatedAtIso = generatedAt.toISOString();
72845
- const social = flags.socialLinks && input.socialLinks.length > 0 ? `<nav class="social" aria-label="Professional profiles">${input.socialLinks.map((link2) => `<a href="${safeHref(link2.href)}">${escapeHtml8(link2.label)}</a>`).join("")}</nav>` : "";
73284
+ const social = flags.socialLinks && input.socialLinks.length > 0 ? `<nav class="social" aria-label="Professional profiles">${input.socialLinks.map((link2) => `<a href="${safeHref(link2.href)}">${escapeHtml9(link2.label)}</a>`).join("")}</nav>` : "";
72846
73285
  const navigation = flags.navigation ? `<button class="menu" type="button" aria-expanded="false" aria-controls="nav">Menu</button><nav id="nav" class="nav" aria-label="Primary navigation"><a href="#about">About</a><a href="#services">Work</a><a href="#proof">Proof</a><a href="#contact">Contact</a></nav>` : "";
72847
73286
  const authority = input.authority;
72848
73287
  const consultation = input.consultation;
@@ -72854,21 +73293,21 @@ function renderPersonalAuthorityV1(input, config, generatedAt) {
72854
73293
  <html lang="en" data-theme="${config.theme}">
72855
73294
  <head>
72856
73295
  <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
72857
- <meta name="description" content="${escapeHtml8(input.summary)}"><title>${escapeHtml8(input.name)} \u2014 ${escapeHtml8(input.professionalTitle)}</title>
73296
+ <meta name="description" content="${escapeHtml9(input.summary)}"><title>${escapeHtml9(input.name)} \u2014 ${escapeHtml9(input.professionalTitle)}</title>
72858
73297
  <style>
72859
73298
  :root{--blue:#2f66eb;--blue-soft:#dbe6ff;--ink:#050708;--paper:#f7f8fb;--white:#fff;--gray:#9ca5b5;--line:#27303b;--serif:Georgia,'Times New Roman',serif;--sans:Arial,Helvetica,sans-serif}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--ink);color:var(--white);font-family:var(--sans);line-height:1.55}a{color:inherit}.skip{position:absolute;left:-9999px}.skip:focus{left:16px;top:16px;z-index:9;background:#fff;color:#000;padding:10px}.header{height:88px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(20px,4vw,72px);border-bottom:1px solid var(--line);position:relative;z-index:3;background:rgba(5,7,8,.96)}.brand{display:flex;align-items:center;gap:12px;text-decoration:none;font-family:var(--serif);font-size:24px}.mark{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;background:var(--blue);font-family:var(--sans);font-weight:700}.nav{display:flex;align-items:center;gap:28px}.nav a{text-decoration:none;color:#c8ced8;font-size:14px}.nav a:hover{color:#fff}.menu{display:none;background:transparent;color:#fff;border:1px solid #5b6471;padding:10px 14px}.hero{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.25fr);min-height:760px;border-bottom:3px solid var(--blue)}.portrait{display:grid;place-items:end center;background:radial-gradient(circle at 50% 38%,#273243 0,#11161d 38%,#060708 70%);overflow:hidden}.portrait-card{width:min(82%,560px);aspect-ratio:4/5;display:grid;place-items:center;align-self:end;border:1px solid #263142;border-bottom:0;background:linear-gradient(145deg,#252d38,#0b0d10 58%);font-family:var(--serif);font-size:clamp(80px,13vw,180px);color:#d9e4ff;text-shadow:0 0 42px rgba(47,102,235,.5)}.hero-copy{padding:clamp(44px,7vw,110px) clamp(24px,6vw,100px);display:flex;flex-direction:column;justify-content:center;background:linear-gradient(135deg,#080a0d,#101318)}h1,h2,h3,p{margin-top:0}.hero h1{font-family:var(--serif);font-size:clamp(58px,8vw,116px);line-height:.92;font-weight:400;color:var(--blue);margin-bottom:24px}.role{font-family:var(--serif);font-size:clamp(28px,3.8vw,52px);line-height:1.05;border-left:5px solid var(--blue);padding-left:22px;max-width:820px}.summary{color:#c6cbd3;font-size:clamp(17px,1.5vw,22px);max-width:760px}.actions{display:flex;gap:12px;flex-wrap:wrap;margin:24px 0}.button{display:inline-flex;align-items:center;justify-content:space-between;gap:28px;min-height:58px;padding:0 26px;background:var(--blue);border:1px solid var(--blue);text-decoration:none;font-weight:700}.button--secondary{background:transparent;border-color:#5c6572}.social{display:flex;gap:10px;flex-wrap:wrap}.social a{border:1px solid #39414d;border-radius:999px;padding:8px 13px;text-decoration:none;color:#d8dce4;font-size:13px}.light{background:var(--paper);color:#111722;padding:clamp(70px,10vw,150px) clamp(20px,6vw,90px)}.light h2,.section h2{font-family:var(--serif);font-size:clamp(42px,6vw,82px);line-height:1.02;font-weight:400;max-width:1080px}.light p{max-width:920px;color:#4e596a;font-size:18px}.section{padding:clamp(70px,9vw,130px) clamp(20px,6vw,90px);border-bottom:1px solid var(--line)}.section-head{max-width:920px;margin-bottom:46px}.eyebrow{text-transform:uppercase;letter-spacing:.18em;font-size:12px;color:#aab2bf}.consult{display:grid;grid-template-columns:1.3fr .7fr;gap:50px;background:linear-gradient(130deg,#060708,#111721)}.accent-card{min-height:360px;border:1px solid #2a3545;background:linear-gradient(145deg,rgba(47,102,235,.22),transparent 55%);display:grid;place-items:center;font-family:var(--serif);font-size:96px;color:var(--blue)}.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.card{border:1px solid #323a46;padding:26px;min-height:220px;background:#0b0e12}.card h3{font-family:var(--serif);font-size:30px}.card p{color:#b9c0ca}.learning{background:#020303}.topic-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:#29313c;border:1px solid #29313c}.topic{background:#050708;padding:30px}.topic strong{display:block;color:var(--blue);text-transform:uppercase;font-size:11px;letter-spacing:.15em}.topic h3{font-family:var(--serif);font-size:30px;margin:8px 0}.topic p{color:#aeb6c1}.proof{background:linear-gradient(180deg,#0a0d11,#050607)}.quotes{display:flex;gap:14px;overflow:auto;scroll-snap-type:x mandatory;padding-bottom:12px}.quote{min-width:min(420px,86vw);scroll-snap-align:start;border:1px solid #343d49;padding:30px;background:#0c1015}.quote q{font-family:var(--serif);font-size:30px;line-height:1.25}.quote span{display:block;margin-top:24px;color:#aeb5c0;font-size:12px;text-transform:uppercase;letter-spacing:.12em}.cta{text-align:center;border-top:3px solid var(--blue);background:radial-gradient(circle at 50% 0,rgba(47,102,235,.25),transparent 50%)}.cta h2{margin-left:auto;margin-right:auto}.footer{display:grid;grid-template-columns:1fr auto;gap:40px;padding:46px clamp(20px,6vw,90px);color:#abb3bf}.footer strong{color:#fff;font-family:var(--serif);font-size:26px}.generated{font-size:11px;color:#737d8b;margin-top:10px}html[data-theme='slate']{--blue:#7f95b8;--blue-soft:#dfe6ef}html[data-theme='monochrome']{--blue:#fff;--blue-soft:#e7e7e7}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
72860
73299
  @media(max-width:760px){.header{height:78px}.menu{display:block}.nav{display:none;position:absolute;left:0;right:0;top:77px;padding:18px 20px;background:#090b0e;border-bottom:1px solid #303844;flex-direction:column;align-items:stretch}.nav.open{display:flex}.hero{grid-template-columns:1fr;min-height:0}.portrait{min-height:390px}.portrait-card{width:72%;max-height:360px}.hero-copy{padding:48px 20px 66px}.hero h1{font-size:64px}.role{font-size:34px}.actions{display:grid}.button{width:100%}.consult{grid-template-columns:1fr}.accent-card{min-height:220px}.grid,.topic-grid{grid-template-columns:1fr}.light,.section{padding:64px 20px}.light h2,.section h2{font-size:46px}.footer{grid-template-columns:1fr}}
72861
73300
  </style></head>
72862
73301
  <body><a class="skip" href="#main">Skip to main content</a>
72863
- <header class="header"><a class="brand" href="#top"><span class="mark">${escapeHtml8(initials(input.name).slice(0, 1))}</span><strong>${escapeHtml8(brand)}</strong></a>${navigation}</header>
72864
- <main id="main"><section class="hero" id="top"><div class="portrait" aria-label="Portrait placeholder"><div class="portrait-card" aria-hidden="true">${escapeHtml8(initials(input.name))}</div></div><div class="hero-copy"><h1>${escapeHtml8(input.name)}</h1><p class="role">${escapeHtml8(input.professionalTitle)}</p><p class="summary">${escapeHtml8(input.summary)}</p><div class="actions">${action(input.primaryAction)}${input.secondaryAction ? action(input.secondaryAction, true) : ""}</div>${social}</div></section>
72865
- ${section(flags.authority && Boolean(authority), `<section class="light" id="about"><h2>${escapeHtml8(authority?.heading ?? "")}</h2>${authority?.paragraphs.map((paragraph) => `<p>${escapeHtml8(paragraph)}</p>`).join("") ?? ""}</section>`)}
72866
- ${section(flags.consultation && Boolean(consultation), `<section class="section consult"><div><p class="eyebrow">${escapeHtml8(consultation?.eyebrow ?? "Start a conversation")}</p><h2>${escapeHtml8(consultation?.heading ?? "")}</h2><p class="summary">${escapeHtml8(consultation?.summary ?? "")}</p>${consultation ? action(consultation.action) : ""}</div><div class="accent-card" aria-label="Image placeholder">${escapeHtml8(initials(input.name))}</div></section>`)}
72867
- ${section(flags.learning && Boolean(learning), `<section class="section learning"><div class="section-head"><h2>${escapeHtml8(learning?.heading ?? "")}</h2><p class="summary">${escapeHtml8(learning?.summary ?? "")}</p></div><div class="topic-grid">${learning?.topics.map((topic) => `<article class="topic"><strong>${escapeHtml8(topic.title)}</strong><h3>${escapeHtml8(topic.headline)}</h3><p>${escapeHtml8(topic.summary)}</p>${topic.href ? `<a href="${safeHref(topic.href)}">Explore \u2192</a>` : ""}</article>`).join("") ?? ""}</div></section>`)}
72868
- ${section(flags.services && Boolean(services), `<section class="section" id="services"><div class="section-head"><h2>${escapeHtml8(services?.heading ?? "")}</h2><p class="summary">${escapeHtml8(services?.summary ?? "")}</p></div><div class="grid">${services?.offers.map((offer) => `<article class="card"><h3>${escapeHtml8(offer.title)}</h3><p>${escapeHtml8(offer.description)}</p>${offer.action ? action(offer.action, true) : ""}</article>`).join("") ?? ""}</div></section>`)}
72869
- ${section(flags.proof && Boolean(proof), `<section class="section proof" id="proof"><div class="section-head"><h2>${escapeHtml8(proof?.heading ?? "")}</h2><p class="summary">${escapeHtml8(proof?.summary ?? "")}</p></div><div class="quotes">${proof?.testimonials.map((item) => `<article class="quote"><q>${escapeHtml8(item.quote)}</q><span>${escapeHtml8(item.person)}${item.role ? ` / ${escapeHtml8(item.role)}` : ""}</span></article>`).join("") ?? ""}</div></section>`)}
72870
- ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id="contact"><p class="eyebrow">${escapeHtml8(finalCta?.eyebrow ?? "Ready when you are")}</p><h2>${escapeHtml8(finalCta?.heading ?? "")}</h2><p class="summary">${escapeHtml8(finalCta?.summary ?? "")}</p>${finalCta ? action(finalCta.action) : ""}</section>`)}
72871
- </main><footer class="footer"><div><strong>${escapeHtml8(input.name)}</strong><div>${escapeHtml8(input.professionalTitle)}</div>${config.showGeneratedAt ? `<div class="generated">Generated ${escapeHtml8(generatedAtIso)}</div>` : ""}</div><div>${input.location ? `<div>${escapeHtml8(input.location)}</div>` : ""}${input.email ? `<a href="mailto:${escapeHtml8(input.email)}">${escapeHtml8(input.email)}</a>` : ""}${input.phone ? `<div>${escapeHtml8(input.phone)}</div>` : ""}</div></footer>
73302
+ <header class="header"><a class="brand" href="#top"><span class="mark">${escapeHtml9(initials(input.name).slice(0, 1))}</span><strong>${escapeHtml9(brand)}</strong></a>${navigation}</header>
73303
+ <main id="main"><section class="hero" id="top"><div class="portrait" aria-label="Portrait placeholder"><div class="portrait-card" aria-hidden="true">${escapeHtml9(initials(input.name))}</div></div><div class="hero-copy"><h1>${escapeHtml9(input.name)}</h1><p class="role">${escapeHtml9(input.professionalTitle)}</p><p class="summary">${escapeHtml9(input.summary)}</p><div class="actions">${action(input.primaryAction)}${input.secondaryAction ? action(input.secondaryAction, true) : ""}</div>${social}</div></section>
73304
+ ${section(flags.authority && Boolean(authority), `<section class="light" id="about"><h2>${escapeHtml9(authority?.heading ?? "")}</h2>${authority?.paragraphs.map((paragraph) => `<p>${escapeHtml9(paragraph)}</p>`).join("") ?? ""}</section>`)}
73305
+ ${section(flags.consultation && Boolean(consultation), `<section class="section consult"><div><p class="eyebrow">${escapeHtml9(consultation?.eyebrow ?? "Start a conversation")}</p><h2>${escapeHtml9(consultation?.heading ?? "")}</h2><p class="summary">${escapeHtml9(consultation?.summary ?? "")}</p>${consultation ? action(consultation.action) : ""}</div><div class="accent-card" aria-label="Image placeholder">${escapeHtml9(initials(input.name))}</div></section>`)}
73306
+ ${section(flags.learning && Boolean(learning), `<section class="section learning"><div class="section-head"><h2>${escapeHtml9(learning?.heading ?? "")}</h2><p class="summary">${escapeHtml9(learning?.summary ?? "")}</p></div><div class="topic-grid">${learning?.topics.map((topic) => `<article class="topic"><strong>${escapeHtml9(topic.title)}</strong><h3>${escapeHtml9(topic.headline)}</h3><p>${escapeHtml9(topic.summary)}</p>${topic.href ? `<a href="${safeHref(topic.href)}">Explore \u2192</a>` : ""}</article>`).join("") ?? ""}</div></section>`)}
73307
+ ${section(flags.services && Boolean(services), `<section class="section" id="services"><div class="section-head"><h2>${escapeHtml9(services?.heading ?? "")}</h2><p class="summary">${escapeHtml9(services?.summary ?? "")}</p></div><div class="grid">${services?.offers.map((offer) => `<article class="card"><h3>${escapeHtml9(offer.title)}</h3><p>${escapeHtml9(offer.description)}</p>${offer.action ? action(offer.action, true) : ""}</article>`).join("") ?? ""}</div></section>`)}
73308
+ ${section(flags.proof && Boolean(proof), `<section class="section proof" id="proof"><div class="section-head"><h2>${escapeHtml9(proof?.heading ?? "")}</h2><p class="summary">${escapeHtml9(proof?.summary ?? "")}</p></div><div class="quotes">${proof?.testimonials.map((item) => `<article class="quote"><q>${escapeHtml9(item.quote)}</q><span>${escapeHtml9(item.person)}${item.role ? ` / ${escapeHtml9(item.role)}` : ""}</span></article>`).join("") ?? ""}</div></section>`)}
73309
+ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id="contact"><p class="eyebrow">${escapeHtml9(finalCta?.eyebrow ?? "Ready when you are")}</p><h2>${escapeHtml9(finalCta?.heading ?? "")}</h2><p class="summary">${escapeHtml9(finalCta?.summary ?? "")}</p>${finalCta ? action(finalCta.action) : ""}</section>`)}
73310
+ </main><footer class="footer"><div><strong>${escapeHtml9(input.name)}</strong><div>${escapeHtml9(input.professionalTitle)}</div>${config.showGeneratedAt ? `<div class="generated">Generated ${escapeHtml9(generatedAtIso)}</div>` : ""}</div><div>${input.location ? `<div>${escapeHtml9(input.location)}</div>` : ""}${input.email ? `<a href="mailto:${escapeHtml9(input.email)}">${escapeHtml9(input.email)}</a>` : ""}${input.phone ? `<div>${escapeHtml9(input.phone)}</div>` : ""}</div></footer>
72872
73311
  <script>document.querySelector('.menu')?.addEventListener('click',function(){var nav=document.querySelector('.nav');var open=this.getAttribute('aria-expanded')!=='true';this.setAttribute('aria-expanded',String(open));nav?.classList.toggle('open',open)});document.querySelectorAll('.nav a').forEach(function(link){link.addEventListener('click',function(){document.querySelector('.nav')?.classList.remove('open');document.querySelector('.menu')?.setAttribute('aria-expanded','false')})});</script></body></html>`;
72873
73312
  const bytes = Buffer.byteLength(html, "utf8");
72874
73313
  return {
@@ -72888,7 +73327,7 @@ var init_render2 = __esm({
72888
73327
  });
72889
73328
 
72890
73329
  // src/personal-authority/render-v2.ts
72891
- function escapeHtml9(value) {
73330
+ function escapeHtml10(value) {
72892
73331
  return value.replace(
72893
73332
  /[&<>"']/g,
72894
73333
  (character) => ({
@@ -72902,13 +73341,13 @@ function escapeHtml9(value) {
72902
73341
  }
72903
73342
  function safeHref2(value) {
72904
73343
  const trimmed = value.trim();
72905
- return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml9(trimmed) : "#";
73344
+ return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml10(trimmed) : "#";
72906
73345
  }
72907
73346
  function initials2(name) {
72908
73347
  return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase()).join("") || "PA";
72909
73348
  }
72910
73349
  function action2(action3, secondary = false) {
72911
- return `<a class="button${secondary ? " button--secondary" : ""}" href="${safeHref2(action3.href)}">${escapeHtml9(action3.label)} <span aria-hidden="true">\u2197</span></a>`;
73350
+ return `<a class="button${secondary ? " button--secondary" : ""}" href="${safeHref2(action3.href)}">${escapeHtml10(action3.label)} <span aria-hidden="true">\u2197</span></a>`;
72912
73351
  }
72913
73352
  function section2(enabled, html) {
72914
73353
  return enabled ? html : "";
@@ -72952,7 +73391,7 @@ function renderPersonalAuthorityV2(input, config, generatedAt) {
72952
73391
  const surfaceContrast = bestContrastTextColor(colors.surface);
72953
73392
  const accentChannels = hexChannels(colors.accent);
72954
73393
  const generatedAtIso = generatedAt.toISOString();
72955
- const social = flags.socialLinks && input.socialLinks.length > 0 ? `<nav class="social" aria-label="Professional profiles">${input.socialLinks.map((link2) => `<a href="${safeHref2(link2.href)}">${escapeHtml9(link2.label)}</a>`).join("")}</nav>` : "";
73394
+ const social = flags.socialLinks && input.socialLinks.length > 0 ? `<nav class="social" aria-label="Professional profiles">${input.socialLinks.map((link2) => `<a href="${safeHref2(link2.href)}">${escapeHtml10(link2.label)}</a>`).join("")}</nav>` : "";
72956
73395
  const navigation = flags.navigation ? `<button class="menu" type="button" aria-expanded="false" aria-controls="nav">Menu</button><nav id="nav" class="nav" aria-label="Primary navigation"><a href="#about">About</a><a href="#services">Work</a><a href="#proof">Proof</a><a href="#contact">Contact</a></nav>` : "";
72957
73396
  const authority = input.authority;
72958
73397
  const consultation = input.consultation;
@@ -72964,22 +73403,22 @@ function renderPersonalAuthorityV2(input, config, generatedAt) {
72964
73403
  <html lang="en" data-theme="${config.theme}">
72965
73404
  <head>
72966
73405
  <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
72967
- <meta name="description" content="${escapeHtml9(input.summary)}"><title>${escapeHtml9(input.name)} - ${escapeHtml9(input.professionalTitle)}</title>
73406
+ <meta name="description" content="${escapeHtml10(input.summary)}"><title>${escapeHtml10(input.name)} - ${escapeHtml10(input.professionalTitle)}</title>
72968
73407
  <style>
72969
73408
  :root{--accent:${colors.accent};--accent-rgb:${accentChannels};--accent-contrast:${accentContrast};--background:${colors.background};--surface:${colors.surface};--surface-contrast:${surfaceContrast};--text:${colors.text};--muted:color-mix(in srgb,var(--text) 68%,var(--background));--surface-muted:color-mix(in srgb,var(--surface-contrast) 72%,var(--surface));--line:color-mix(in srgb,var(--text) 16%,var(--background));--display:${displayFont};--body:${bodyFont}}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--background);color:var(--text);font-family:var(--body);line-height:1.55}a{color:inherit}.skip{position:absolute;left:-9999px}.skip:focus{left:16px;top:16px;z-index:9;background:var(--accent);color:var(--accent-contrast);padding:10px 14px;border-radius:999px}.header{height:80px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(20px,4vw,72px);position:relative;z-index:3;background:var(--surface);color:var(--surface-contrast);border-radius:0 0 24px 24px}.brand{display:flex;align-items:center;gap:12px;text-decoration:none;font-family:var(--display);font-size:24px}.mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:var(--accent);color:var(--accent-contrast);font-family:var(--body);font-weight:700}.nav{display:flex;align-items:center;gap:28px}.nav a{text-decoration:none;color:var(--surface-muted);font-size:14px}.nav a:hover{color:var(--surface-contrast)}.nav a:focus-visible,.button:focus-visible,.social a:focus-visible,.menu:focus-visible{outline:3px solid var(--accent);outline-offset:4px}.menu{display:none;background:transparent;color:var(--surface-contrast);border:1px solid color-mix(in srgb,var(--surface-contrast) 28%,var(--surface));border-radius:999px;padding:10px 14px}.hero{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.25fr);min-height:min(760px,calc(100dvh - 80px));border-bottom:1px solid var(--line);background:var(--background)}.portrait{margin:clamp(16px,3vw,36px) clamp(12px,2vw,24px) clamp(16px,3vw,36px) clamp(16px,3vw,36px);display:grid;place-items:end center;background:var(--surface);border-radius:28px;overflow:hidden}.portrait-card{width:min(82%,560px);aspect-ratio:4/5;display:grid;place-items:center;align-self:end;border:1px solid color-mix(in srgb,var(--surface-contrast) 18%,var(--surface));border-bottom:0;background:linear-gradient(145deg,color-mix(in srgb,var(--surface-contrast) 12%,var(--surface)),var(--surface) 66%);font-family:var(--display);font-size:clamp(80px,13vw,180px);color:var(--surface-contrast)}.hero-copy{padding:clamp(44px,7vw,110px) clamp(24px,6vw,100px);display:flex;flex-direction:column;justify-content:center;background:var(--background)}h1,h2,h3,p{margin-top:0}.hero h1{font-family:var(--display);font-size:clamp(58px,8vw,116px);line-height:.96;font-weight:500;letter-spacing:-.04em;color:var(--text);margin-bottom:24px;text-wrap:balance}.role{font-family:var(--display);font-size:clamp(28px,3.8vw,52px);line-height:1.08;border-left:5px solid var(--accent);padding-left:22px;max-width:820px;text-wrap:balance}.summary{color:var(--muted);font-size:clamp(17px,1.5vw,22px);max-width:65ch}.actions{display:flex;gap:12px;flex-wrap:wrap;margin:24px 0}.button{display:inline-flex;align-items:center;justify-content:space-between;gap:28px;min-height:58px;padding:0 26px;background:var(--accent);color:var(--accent-contrast);border:1px solid var(--accent);border-radius:999px;text-decoration:none;font-weight:700;transition:transform .2s ease,filter .2s ease}.button:hover{filter:brightness(1.06)}.button:active{transform:translateY(1px)}.button--secondary{background:transparent;color:var(--text);border-color:var(--line)}.social{display:flex;gap:10px;flex-wrap:wrap}.social a{border:1px solid var(--line);border-radius:999px;padding:8px 13px;text-decoration:none;color:var(--muted);font-size:13px}.light{background:var(--background);color:var(--text);padding:clamp(70px,10vw,150px) clamp(20px,6vw,90px)}.light h2,.section h2{font-family:var(--display);font-size:clamp(42px,6vw,82px);line-height:1.05;font-weight:500;letter-spacing:-.035em;max-width:1080px;text-wrap:balance}.light p{max-width:65ch;color:var(--muted);font-size:18px}.section{padding:clamp(70px,9vw,130px) clamp(20px,6vw,90px);border-bottom:1px solid var(--line)}.section-head{max-width:920px;margin-bottom:46px}.eyebrow{text-transform:uppercase;letter-spacing:.18em;font-size:12px;color:var(--accent);font-weight:700}.consult{margin:clamp(20px,4vw,64px);display:grid;grid-template-columns:1.3fr .7fr;gap:50px;background:var(--surface);color:var(--surface-contrast);border:0;border-radius:28px}.consult .summary{color:var(--surface-muted)}.accent-card{min-height:360px;border:1px solid color-mix(in srgb,var(--surface-contrast) 18%,var(--surface));border-radius:24px;background:color-mix(in srgb,var(--accent) 14%,var(--surface));display:grid;place-items:center;font-family:var(--display);font-size:96px;color:var(--surface-contrast)}.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}.card{padding:30px;min-height:240px;border-radius:28px;background:var(--surface);color:var(--surface-contrast)}.card h3{font-family:var(--display);font-size:30px}.card p{color:var(--surface-muted)}.card .button--secondary{color:var(--surface-contrast);border-color:color-mix(in srgb,var(--surface-contrast) 28%,var(--surface))}.learning{background:var(--background)}.topic-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.topic{background:color-mix(in srgb,var(--text) 5%,var(--background));color:var(--text);border:1px solid var(--line);border-radius:28px;padding:30px}.topic strong{display:block;color:var(--accent);text-transform:uppercase;font-size:11px;letter-spacing:.15em}.topic h3{font-family:var(--display);font-size:30px;margin:8px 0}.topic p{color:var(--muted)}.topic a{color:var(--text);font-weight:700}.proof{background:color-mix(in srgb,var(--text) 5%,var(--background))}.quotes{display:flex;gap:14px;overflow:auto;scroll-snap-type:x mandatory;padding-bottom:12px}.quote{min-width:min(420px,86vw);scroll-snap-align:start;border:1px solid var(--line);border-radius:24px;padding:30px;background:var(--background)}.quote q{font-family:var(--display);font-size:30px;line-height:1.25}.quote span{display:block;margin-top:24px;color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.12em}.cta{margin:clamp(20px,4vw,64px);text-align:center;border:0;border-radius:28px;background:var(--surface);color:var(--surface-contrast)}.cta .summary{color:var(--surface-muted);margin-left:auto;margin-right:auto}.cta h2{margin-left:auto;margin-right:auto}.footer{display:grid;grid-template-columns:1fr auto;gap:40px;padding:46px clamp(20px,6vw,90px);color:var(--muted);background:var(--background)}.footer strong{color:var(--text);font-family:var(--display);font-size:26px}.generated{font-size:11px;color:var(--muted);margin-top:10px}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
72970
73409
  @media(max-width:760px){.header{height:78px;border-radius:0 0 18px 18px}.menu{display:block}.nav{display:none;position:absolute;left:0;right:0;top:77px;padding:18px 20px;background:var(--surface);border-radius:0 0 18px 18px;flex-direction:column;align-items:stretch}.nav.open{display:flex}.hero{grid-template-columns:1fr;min-height:0}.portrait{min-height:390px;margin:16px;border-radius:22px}.portrait-card{width:72%;max-height:360px}.hero-copy{padding:36px 20px 66px}.hero h1{font-size:58px}.role{font-size:34px}.actions{display:grid}.button{width:100%}.consult{grid-template-columns:1fr;margin:16px;border-radius:22px}.cta{margin:16px;border-radius:22px}.accent-card{min-height:220px}.grid,.topic-grid{grid-template-columns:1fr}.card,.topic{border-radius:22px}.light,.section{padding:64px 20px}.consult,.cta{padding:52px 20px}.light h2,.section h2{font-size:46px}.footer{grid-template-columns:1fr}}
72971
73410
  @media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.button{transition:none}}
72972
73411
  </style></head>
72973
73412
  <body><a class="skip" href="#main">Skip to main content</a>
72974
- <header class="header"><a class="brand" href="#top"><span class="mark">${escapeHtml9(initials2(input.name).slice(0, 1))}</span><strong>${escapeHtml9(brand)}</strong></a>${navigation}</header>
72975
- <main id="main"><section class="hero" id="top"><div class="portrait" aria-label="Portrait placeholder"><div class="portrait-card" aria-hidden="true">${escapeHtml9(initials2(input.name))}</div></div><div class="hero-copy"><h1>${escapeHtml9(input.name)}</h1><p class="role">${escapeHtml9(input.professionalTitle)}</p><p class="summary">${escapeHtml9(input.summary)}</p><div class="actions">${action2(input.primaryAction)}${input.secondaryAction ? action2(input.secondaryAction, true) : ""}</div>${social}</div></section>
72976
- ${section2(flags.authority && Boolean(authority), `<section class="light" id="about"><h2>${escapeHtml9(authority?.heading ?? "")}</h2>${authority?.paragraphs.map((paragraph) => `<p>${escapeHtml9(paragraph)}</p>`).join("") ?? ""}</section>`)}
72977
- ${section2(flags.consultation && Boolean(consultation), `<section class="section consult"><div><p class="eyebrow">${escapeHtml9(consultation?.eyebrow ?? "Start a conversation")}</p><h2>${escapeHtml9(consultation?.heading ?? "")}</h2><p class="summary">${escapeHtml9(consultation?.summary ?? "")}</p>${consultation ? action2(consultation.action) : ""}</div><div class="accent-card" aria-label="Image placeholder">${escapeHtml9(initials2(input.name))}</div></section>`)}
72978
- ${section2(flags.learning && Boolean(learning), `<section class="section learning"><div class="section-head"><h2>${escapeHtml9(learning?.heading ?? "")}</h2><p class="summary">${escapeHtml9(learning?.summary ?? "")}</p></div><div class="topic-grid">${learning?.topics.map((topic) => `<article class="topic"><strong>${escapeHtml9(topic.title)}</strong><h3>${escapeHtml9(topic.headline)}</h3><p>${escapeHtml9(topic.summary)}</p>${topic.href ? `<a href="${safeHref2(topic.href)}">Explore \u2192</a>` : ""}</article>`).join("") ?? ""}</div></section>`)}
72979
- ${section2(flags.services && Boolean(services), `<section class="section" id="services"><div class="section-head"><h2>${escapeHtml9(services?.heading ?? "")}</h2><p class="summary">${escapeHtml9(services?.summary ?? "")}</p></div><div class="grid">${services?.offers.map((offer) => `<article class="card"><h3>${escapeHtml9(offer.title)}</h3><p>${escapeHtml9(offer.description)}</p>${offer.action ? action2(offer.action, true) : ""}</article>`).join("") ?? ""}</div></section>`)}
72980
- ${section2(flags.proof && Boolean(proof), `<section class="section proof" id="proof"><div class="section-head"><h2>${escapeHtml9(proof?.heading ?? "")}</h2><p class="summary">${escapeHtml9(proof?.summary ?? "")}</p></div><div class="quotes">${proof?.testimonials.map((item) => `<article class="quote"><q>${escapeHtml9(item.quote)}</q><span>${escapeHtml9(item.person)}${item.role ? ` / ${escapeHtml9(item.role)}` : ""}</span></article>`).join("") ?? ""}</div></section>`)}
72981
- ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id="contact"><p class="eyebrow">${escapeHtml9(finalCta?.eyebrow ?? "Ready when you are")}</p><h2>${escapeHtml9(finalCta?.heading ?? "")}</h2><p class="summary">${escapeHtml9(finalCta?.summary ?? "")}</p>${finalCta ? action2(finalCta.action) : ""}</section>`)}
72982
- </main><footer class="footer"><div><strong>${escapeHtml9(input.name)}</strong><div>${escapeHtml9(input.professionalTitle)}</div>${config.showGeneratedAt ? `<div class="generated">Generated ${escapeHtml9(generatedAtIso)}</div>` : ""}</div><div>${input.location ? `<div>${escapeHtml9(input.location)}</div>` : ""}${input.email ? `<a href="mailto:${escapeHtml9(input.email)}">${escapeHtml9(input.email)}</a>` : ""}${input.phone ? `<div>${escapeHtml9(input.phone)}</div>` : ""}</div></footer>
73413
+ <header class="header"><a class="brand" href="#top"><span class="mark">${escapeHtml10(initials2(input.name).slice(0, 1))}</span><strong>${escapeHtml10(brand)}</strong></a>${navigation}</header>
73414
+ <main id="main"><section class="hero" id="top"><div class="portrait" aria-label="Portrait placeholder"><div class="portrait-card" aria-hidden="true">${escapeHtml10(initials2(input.name))}</div></div><div class="hero-copy"><h1>${escapeHtml10(input.name)}</h1><p class="role">${escapeHtml10(input.professionalTitle)}</p><p class="summary">${escapeHtml10(input.summary)}</p><div class="actions">${action2(input.primaryAction)}${input.secondaryAction ? action2(input.secondaryAction, true) : ""}</div>${social}</div></section>
73415
+ ${section2(flags.authority && Boolean(authority), `<section class="light" id="about"><h2>${escapeHtml10(authority?.heading ?? "")}</h2>${authority?.paragraphs.map((paragraph) => `<p>${escapeHtml10(paragraph)}</p>`).join("") ?? ""}</section>`)}
73416
+ ${section2(flags.consultation && Boolean(consultation), `<section class="section consult"><div><p class="eyebrow">${escapeHtml10(consultation?.eyebrow ?? "Start a conversation")}</p><h2>${escapeHtml10(consultation?.heading ?? "")}</h2><p class="summary">${escapeHtml10(consultation?.summary ?? "")}</p>${consultation ? action2(consultation.action) : ""}</div><div class="accent-card" aria-label="Image placeholder">${escapeHtml10(initials2(input.name))}</div></section>`)}
73417
+ ${section2(flags.learning && Boolean(learning), `<section class="section learning"><div class="section-head"><h2>${escapeHtml10(learning?.heading ?? "")}</h2><p class="summary">${escapeHtml10(learning?.summary ?? "")}</p></div><div class="topic-grid">${learning?.topics.map((topic) => `<article class="topic"><strong>${escapeHtml10(topic.title)}</strong><h3>${escapeHtml10(topic.headline)}</h3><p>${escapeHtml10(topic.summary)}</p>${topic.href ? `<a href="${safeHref2(topic.href)}">Explore \u2192</a>` : ""}</article>`).join("") ?? ""}</div></section>`)}
73418
+ ${section2(flags.services && Boolean(services), `<section class="section" id="services"><div class="section-head"><h2>${escapeHtml10(services?.heading ?? "")}</h2><p class="summary">${escapeHtml10(services?.summary ?? "")}</p></div><div class="grid">${services?.offers.map((offer) => `<article class="card"><h3>${escapeHtml10(offer.title)}</h3><p>${escapeHtml10(offer.description)}</p>${offer.action ? action2(offer.action, true) : ""}</article>`).join("") ?? ""}</div></section>`)}
73419
+ ${section2(flags.proof && Boolean(proof), `<section class="section proof" id="proof"><div class="section-head"><h2>${escapeHtml10(proof?.heading ?? "")}</h2><p class="summary">${escapeHtml10(proof?.summary ?? "")}</p></div><div class="quotes">${proof?.testimonials.map((item) => `<article class="quote"><q>${escapeHtml10(item.quote)}</q><span>${escapeHtml10(item.person)}${item.role ? ` / ${escapeHtml10(item.role)}` : ""}</span></article>`).join("") ?? ""}</div></section>`)}
73420
+ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id="contact"><p class="eyebrow">${escapeHtml10(finalCta?.eyebrow ?? "Ready when you are")}</p><h2>${escapeHtml10(finalCta?.heading ?? "")}</h2><p class="summary">${escapeHtml10(finalCta?.summary ?? "")}</p>${finalCta ? action2(finalCta.action) : ""}</section>`)}
73421
+ </main><footer class="footer"><div><strong>${escapeHtml10(input.name)}</strong><div>${escapeHtml10(input.professionalTitle)}</div>${config.showGeneratedAt ? `<div class="generated">Generated ${escapeHtml10(generatedAtIso)}</div>` : ""}</div><div>${input.location ? `<div>${escapeHtml10(input.location)}</div>` : ""}${input.email ? `<a href="mailto:${escapeHtml10(input.email)}">${escapeHtml10(input.email)}</a>` : ""}${input.phone ? `<div>${escapeHtml10(input.phone)}</div>` : ""}</div></footer>
72983
73422
  <script>document.querySelector('.menu')?.addEventListener('click',function(){var nav=document.querySelector('.nav');var open=this.getAttribute('aria-expanded')!=='true';this.setAttribute('aria-expanded',String(open));nav?.classList.toggle('open',open)});document.querySelectorAll('.nav a').forEach(function(link){link.addEventListener('click',function(){document.querySelector('.nav')?.classList.remove('open');document.querySelector('.menu')?.setAttribute('aria-expanded','false')})});</script></body></html>`;
72984
73423
  const bytes = Buffer.byteLength(html, "utf8");
72985
73424
  return {
@@ -73005,7 +73444,7 @@ var init_render_v2 = __esm({
73005
73444
  });
73006
73445
 
73007
73446
  // src/newsroom-publisher/render.ts
73008
- function escapeHtml10(value) {
73447
+ function escapeHtml11(value) {
73009
73448
  return value.replace(/[&<>"']/g, (character) => ({
73010
73449
  "&": "&amp;",
73011
73450
  "<": "&lt;",
@@ -73016,30 +73455,30 @@ function escapeHtml10(value) {
73016
73455
  }
73017
73456
  function safeHref3(value) {
73018
73457
  const trimmed = value.trim();
73019
- return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml10(trimmed) : "#";
73458
+ return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml11(trimmed) : "#";
73020
73459
  }
73021
73460
  function formatDate(value) {
73022
73461
  const date = new Date(value);
73023
- return Number.isNaN(date.getTime()) ? escapeHtml10(value) : new Intl.DateTimeFormat("en", { month: "short", day: "numeric", year: "numeric" }).format(date);
73462
+ return Number.isNaN(date.getTime()) ? escapeHtml11(value) : new Intl.DateTimeFormat("en", { month: "short", day: "numeric", year: "numeric" }).format(date);
73024
73463
  }
73025
73464
  function initials3(value) {
73026
73465
  return value.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
73027
73466
  }
73028
73467
  function storyMedia(story2, className = "") {
73029
73468
  if (story2.imageUrl) {
73030
- return `<div class="story-media ${className}"><img src="${safeHref3(story2.imageUrl)}" alt="${escapeHtml10(story2.imageAlt || story2.title)}" loading="lazy"></div>`;
73469
+ return `<div class="story-media ${className}"><img src="${safeHref3(story2.imageUrl)}" alt="${escapeHtml11(story2.imageAlt || story2.title)}" loading="lazy"></div>`;
73031
73470
  }
73032
73471
  const tone = [...story2.title].reduce((total, character) => total + character.charCodeAt(0), 0) % 5;
73033
- return `<div class="story-media story-media--placeholder story-media--tone-${tone} ${className}" role="img" aria-label="Editorial image placeholder for ${escapeHtml10(story2.title)}"><i></i><b></b><span>${escapeHtml10(story2.category)}</span></div>`;
73472
+ return `<div class="story-media story-media--placeholder story-media--tone-${tone} ${className}" role="img" aria-label="Editorial image placeholder for ${escapeHtml11(story2.title)}"><i></i><b></b><span>${escapeHtml11(story2.category)}</span></div>`;
73034
73473
  }
73035
73474
  function byline(story2, showBylines) {
73036
- const author = story2.author.href ? `<a href="${safeHref3(story2.author.href)}">${escapeHtml10(story2.author.name)}</a>` : escapeHtml10(story2.author.name);
73037
- return `<div class="byline">${showBylines ? `<span>By ${author}</span>` : ""}<time datetime="${escapeHtml10(story2.publishedAt)}">${formatDate(story2.publishedAt)}</time>${story2.sponsored ? "<b>Sponsored</b>" : ""}</div>`;
73475
+ const author = story2.author.href ? `<a href="${safeHref3(story2.author.href)}">${escapeHtml11(story2.author.name)}</a>` : escapeHtml11(story2.author.name);
73476
+ return `<div class="byline">${showBylines ? `<span>By ${author}</span>` : ""}<time datetime="${escapeHtml11(story2.publishedAt)}">${formatDate(story2.publishedAt)}</time>${story2.sponsored ? "<b>Sponsored</b>" : ""}</div>`;
73038
73477
  }
73039
73478
  function storyCard(story2, showBylines, variant = "") {
73040
73479
  return `<article class="story-card ${variant}">
73041
73480
  <a class="story-card__media" href="${safeHref3(story2.href)}">${storyMedia(story2)}</a>
73042
- <div class="story-card__body"><a class="kicker" href="${safeHref3(story2.href)}">${escapeHtml10(story2.category)}</a><h3><a href="${safeHref3(story2.href)}">${escapeHtml10(story2.title)}</a></h3>${story2.dek ? `<p>${escapeHtml10(story2.dek)}</p>` : ""}${byline(story2, showBylines)}</div>
73481
+ <div class="story-card__body"><a class="kicker" href="${safeHref3(story2.href)}">${escapeHtml11(story2.category)}</a><h3><a href="${safeHref3(story2.href)}">${escapeHtml11(story2.title)}</a></h3>${story2.dek ? `<p>${escapeHtml11(story2.dek)}</p>` : ""}${byline(story2, showBylines)}</div>
73043
73482
  </article>`;
73044
73483
  }
73045
73484
  function renderNewsroomPublisherV1(input, config, generatedAt) {
@@ -73056,22 +73495,22 @@ function renderNewsroomPublisherV1(input, config, generatedAt) {
73056
73495
  input.primaryPages.contact
73057
73496
  ];
73058
73497
  const trustPages = Object.values(input.trustPages);
73059
- const ticker = flags.breakingTicker && input.ticker ? `<div class="ticker"><div><b>${escapeHtml10(input.ticker.label)}</b><a href="${safeHref3(input.ticker.href || "#latest")}">${escapeHtml10(input.ticker.headline)}</a><span>${formatDate(generatedAtIso)}</span></div></div>` : "";
73060
- const masthead = `<nav class="utility-nav" aria-label="Publication pages"><div><span>${escapeHtml10(input.publication.location || "Independent newsroom")}</span>${primaryPages.slice(1).map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml10(link2.label)}</a>`).join("")}</div></nav><header class="masthead">
73061
- <a class="wordmark" href="${safeHref3(input.publication.url)}"><span>${escapeHtml10(brand)}</span><small>${escapeHtml10(input.publication.tagline)}</small></a>
73498
+ const ticker = flags.breakingTicker && input.ticker ? `<div class="ticker"><div><b>${escapeHtml11(input.ticker.label)}</b><a href="${safeHref3(input.ticker.href || "#latest")}">${escapeHtml11(input.ticker.headline)}</a><span>${formatDate(generatedAtIso)}</span></div></div>` : "";
73499
+ const masthead = `<nav class="utility-nav" aria-label="Publication pages"><div><span>${escapeHtml11(input.publication.location || "Independent newsroom")}</span>${primaryPages.slice(1).map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml11(link2.label)}</a>`).join("")}</div></nav><header class="masthead">
73500
+ <a class="wordmark" href="${safeHref3(input.publication.url)}"><span>${escapeHtml11(brand)}</span><small>${escapeHtml11(input.publication.tagline)}</small></a>
73062
73501
  <div class="masthead__date"><span>Independent journalism</span><time datetime="${generatedAtIso}">${formatDate(generatedAtIso)}</time></div>
73063
73502
  <div class="masthead__tools"><button type="button" aria-label="Search">Search</button><button type="button" aria-label="Open menu">Menu</button></div>
73064
73503
  </header>`;
73065
- const navigation = flags.navigation ? `<nav class="desk-nav" aria-label="Publication categories"><div><a class="desk-nav__home" href="#top">Home</a>${input.categories.map((category) => `<a href="${safeHref3(category.href)}">${escapeHtml10(category.label)}</a>`).join("")}<a class="desk-nav__latest" href="#latest">Latest</a></div></nav>` : "";
73504
+ const navigation = flags.navigation ? `<nav class="desk-nav" aria-label="Publication categories"><div><a class="desk-nav__home" href="#top">Home</a>${input.categories.map((category) => `<a href="${safeHref3(category.href)}">${escapeHtml11(category.label)}</a>`).join("")}<a class="desk-nav__latest" href="#latest">Latest</a></div></nav>` : "";
73066
73505
  const leadGrid = flags.leadGrid ? `<section class="lead-grid" aria-label="Top stories">
73067
73506
  <div class="lead-grid__label"><span>Today\u2019s edition</span><b>Top stories</b></div>
73068
- <article class="lead-story"><a class="lead-story__media" href="${safeHref3(lead.href)}">${storyMedia(lead, "story-media--lead")}</a><div class="lead-story__body"><a class="kicker" href="${safeHref3(lead.href)}">${escapeHtml10(lead.category)}</a><h1><a href="${safeHref3(lead.href)}">${escapeHtml10(lead.title)}</a></h1>${lead.dek ? `<p>${escapeHtml10(lead.dek)}</p>` : ""}${byline(lead, config.showBylines)}<a class="read-story" href="${safeHref3(lead.href)}">Read the full report <span>\u2197</span></a></div></article>
73507
+ <article class="lead-story"><a class="lead-story__media" href="${safeHref3(lead.href)}">${storyMedia(lead, "story-media--lead")}</a><div class="lead-story__body"><a class="kicker" href="${safeHref3(lead.href)}">${escapeHtml11(lead.category)}</a><h1><a href="${safeHref3(lead.href)}">${escapeHtml11(lead.title)}</a></h1>${lead.dek ? `<p>${escapeHtml11(lead.dek)}</p>` : ""}${byline(lead, config.showBylines)}<a class="read-story" href="${safeHref3(lead.href)}">Read the full report <span>\u2197</span></a></div></article>
73069
73508
  <div class="lead-support">${supporting.slice(0, 4).map((story2, index) => `<div class="lead-support__item"><span>${String(index + 1).padStart(2, "0")}</span>${storyCard(story2, config.showBylines, "story-card--support")}</div>`).join("")}</div>
73070
73509
  </section>` : "";
73071
73510
  const latest = flags.latestNews && input.latest.length > 0 ? `<section class="latest" id="latest">
73072
73511
  <div class="section-title"><div><span>Live desk</span><h2>Latest news</h2></div><a href="#top">Back to top \u2191</a></div>
73073
- <div class="latest__layout"><div class="latest__feed">${input.latest.map((story2, index) => `<article class="feed-story"><span>${String(index + 1).padStart(2, "0")}</span><div><a class="kicker" href="${safeHref3(story2.href)}">${escapeHtml10(story2.category)}</a><h3><a href="${safeHref3(story2.href)}">${escapeHtml10(story2.title)}</a></h3>${story2.dek ? `<p>${escapeHtml10(story2.dek)}</p>` : ""}${byline(story2, config.showBylines)}</div>${storyMedia(story2, "story-media--feed")}</article>`).join("")}</div>
73074
- <aside class="editor-note"><span>From the newsroom</span><h3>Reporting with context, sources, and accountable bylines.</h3><p>${escapeHtml10(input.editorial.disclosure)}</p>${trustPages.slice(0, 4).map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml10(link2.label)} \u2197</a>`).join("")}</aside></div>
73512
+ <div class="latest__layout"><div class="latest__feed">${input.latest.map((story2, index) => `<article class="feed-story"><span>${String(index + 1).padStart(2, "0")}</span><div><a class="kicker" href="${safeHref3(story2.href)}">${escapeHtml11(story2.category)}</a><h3><a href="${safeHref3(story2.href)}">${escapeHtml11(story2.title)}</a></h3>${story2.dek ? `<p>${escapeHtml11(story2.dek)}</p>` : ""}${byline(story2, config.showBylines)}</div>${storyMedia(story2, "story-media--feed")}</article>`).join("")}</div>
73513
+ <aside class="editor-note"><span>From the newsroom</span><h3>Reporting with context, sources, and accountable bylines.</h3><p>${escapeHtml11(input.editorial.disclosure)}</p>${trustPages.slice(0, 4).map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml11(link2.label)} \u2197</a>`).join("")}</aside></div>
73075
73514
  </section>` : "";
73076
73515
  const sectionLayouts = ["panorama", "mosaic", "tiles"];
73077
73516
  const sections = flags.categorySections ? input.sections.map((section3, sectionIndex) => {
@@ -73082,16 +73521,16 @@ function renderNewsroomPublisherV1(input, config, generatedAt) {
73082
73521
  layout === "panorama" && index === 0 ? "story-card--panorama" : layout === "mosaic" && index === 0 ? "story-card--mosaic-lead" : ""
73083
73522
  )).join("");
73084
73523
  return `<section class="desk-section desk-section--${sectionIndex % 2 ? "tint" : "plain"} desk-section--layout-${layout}">
73085
- <div class="section-title"><div><span>Coverage desk \xB7 ${String(sectionIndex + 1).padStart(2, "0")}</span><h2>${escapeHtml10(section3.name)}</h2></div>${section3.href ? `<a href="${safeHref3(section3.href)}">View all ${escapeHtml10(section3.name)} \u2192</a>` : ""}</div>
73524
+ <div class="section-title"><div><span>Coverage desk \xB7 ${String(sectionIndex + 1).padStart(2, "0")}</span><h2>${escapeHtml11(section3.name)}</h2></div>${section3.href ? `<a href="${safeHref3(section3.href)}">View all ${escapeHtml11(section3.name)} \u2192</a>` : ""}</div>
73086
73525
  <div class="desk-section__grid desk-section__grid--${layout}">${cards}</div>
73087
73526
  </section>`;
73088
73527
  }).join("") : "";
73089
- const newsletter = flags.newsletter && input.newsletter ? `<section class="newsletter"><div><span>${escapeHtml10(input.newsletter.eyebrow || "Reader briefing")}</span><h2>${escapeHtml10(input.newsletter.heading)}</h2><p>${escapeHtml10(input.newsletter.summary)}</p></div><a href="${safeHref3(input.newsletter.action.href)}">${escapeHtml10(input.newsletter.action.label)} \u2192</a></section>` : "";
73090
- const pressRoom = flags.pressRoom && input.pressRoom ? `<section class="press-room" id="press-room"><div class="press-room__intro"><span>Separate from editorial coverage</span><h2>${escapeHtml10(input.pressRoom.heading)}</h2><p>${escapeHtml10(input.pressRoom.summary)}</p><a href="${safeHref3(input.pressRoom.action.href)}">${escapeHtml10(input.pressRoom.action.label)} \u2192</a></div><div class="release-list">${input.pressRoom.releases.map((release) => `<article><time datetime="${escapeHtml10(release.publishedAt)}">${formatDate(release.publishedAt)}</time><span>${escapeHtml10(release.originatingCity)}</span><h3><a href="${safeHref3(release.href)}">${escapeHtml10(release.headline)}</a></h3></article>`).join("") || "<p>No releases are currently published.</p>"}</div></section>` : "";
73091
- const trustCenter = `<section class="trust-center" id="trust"><div class="trust-center__intro"><span>Public record</span><h2>How this newsroom works.</h2><p>Ownership, editorial practice, technology use, commercial relationships, and reader rights should be easy to find\u2014and written to be understood.</p></div><div class="trust-center__grid">${trustPages.map((link2, index) => `<a href="${safeHref3(link2.href)}"><span>${String(index + 1).padStart(2, "0")}</span><strong>${escapeHtml10(link2.label)}</strong><small>Read \u2192</small></a>`).join("")}</div></section>`;
73092
- const trustFooter = flags.trustFooter ? `<footer class="site-footer"><div class="editor"><span class="editor__avatar">${escapeHtml10(initials3(input.editorial.managingEditor.name))}</span><div><small>${escapeHtml10(input.editorial.managingEditor.role)}</small><h2>${input.editorial.managingEditor.href ? `<a href="${safeHref3(input.editorial.managingEditor.href)}">${escapeHtml10(input.editorial.managingEditor.name)}</a>` : escapeHtml10(input.editorial.managingEditor.name)}</h2><p>${escapeHtml10(input.editorial.managingEditor.bio)}</p></div></div><div class="footer-links"><h3>${escapeHtml10(brand)}</h3><p>${escapeHtml10(input.publication.tagline)}</p><nav>${primaryPages.map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml10(link2.label)}</a>`).join("")}</nav>${config.showGeneratedAt ? `<time datetime="${generatedAtIso}">Updated ${formatDate(generatedAtIso)}</time>` : ""}</div></footer>` : "";
73528
+ const newsletter = flags.newsletter && input.newsletter ? `<section class="newsletter"><div><span>${escapeHtml11(input.newsletter.eyebrow || "Reader briefing")}</span><h2>${escapeHtml11(input.newsletter.heading)}</h2><p>${escapeHtml11(input.newsletter.summary)}</p></div><a href="${safeHref3(input.newsletter.action.href)}">${escapeHtml11(input.newsletter.action.label)} \u2192</a></section>` : "";
73529
+ const pressRoom = flags.pressRoom && input.pressRoom ? `<section class="press-room" id="press-room"><div class="press-room__intro"><span>Separate from editorial coverage</span><h2>${escapeHtml11(input.pressRoom.heading)}</h2><p>${escapeHtml11(input.pressRoom.summary)}</p><a href="${safeHref3(input.pressRoom.action.href)}">${escapeHtml11(input.pressRoom.action.label)} \u2192</a></div><div class="release-list">${input.pressRoom.releases.map((release) => `<article><time datetime="${escapeHtml11(release.publishedAt)}">${formatDate(release.publishedAt)}</time><span>${escapeHtml11(release.originatingCity)}</span><h3><a href="${safeHref3(release.href)}">${escapeHtml11(release.headline)}</a></h3></article>`).join("") || "<p>No releases are currently published.</p>"}</div></section>` : "";
73530
+ const trustCenter = `<section class="trust-center" id="trust"><div class="trust-center__intro"><span>Public record</span><h2>How this newsroom works.</h2><p>Ownership, editorial practice, technology use, commercial relationships, and reader rights should be easy to find\u2014and written to be understood.</p></div><div class="trust-center__grid">${trustPages.map((link2, index) => `<a href="${safeHref3(link2.href)}"><span>${String(index + 1).padStart(2, "0")}</span><strong>${escapeHtml11(link2.label)}</strong><small>Read \u2192</small></a>`).join("")}</div></section>`;
73531
+ const trustFooter = flags.trustFooter ? `<footer class="site-footer"><div class="editor"><span class="editor__avatar">${escapeHtml11(initials3(input.editorial.managingEditor.name))}</span><div><small>${escapeHtml11(input.editorial.managingEditor.role)}</small><h2>${input.editorial.managingEditor.href ? `<a href="${safeHref3(input.editorial.managingEditor.href)}">${escapeHtml11(input.editorial.managingEditor.name)}</a>` : escapeHtml11(input.editorial.managingEditor.name)}</h2><p>${escapeHtml11(input.editorial.managingEditor.bio)}</p></div></div><div class="footer-links"><h3>${escapeHtml11(brand)}</h3><p>${escapeHtml11(input.publication.tagline)}</p><nav>${primaryPages.map((link2) => `<a href="${safeHref3(link2.href)}">${escapeHtml11(link2.label)}</a>`).join("")}</nav>${config.showGeneratedAt ? `<time datetime="${generatedAtIso}">Updated ${formatDate(generatedAtIso)}</time>` : ""}</div></footer>` : "";
73093
73532
  const html = `<!doctype html>
73094
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><title>${escapeHtml10(brand)} \xB7 News Publisher</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@75..125,300..900&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
73533
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><title>${escapeHtml11(brand)} \xB7 News Publisher</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@75..125,300..900&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
73095
73534
  <style>
73096
73535
  :root{--ink:#0b0b0c;--soft-ink:#222326;--muted:#696b70;--paper:#fff;--cream:#f4f0e8;--wash:#f5f5f3;--line:#d8d8d4;--signal:#275cf3;--acid:#d9ff43;--dark:#0b0b0c;--serif:"Instrument Serif",Georgia,serif;--sans:"Archivo",Arial,sans-serif;--mono:"JetBrains Mono",monospace;font-family:var(--sans);color:var(--ink);background:var(--paper)}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);line-height:1.45}body[data-theme=journal]{--cream:#eee7da;--signal:#9d2d25;--acid:#f0c84f}body[data-theme=midnight]{--paper:#e8e9ec;--cream:#dfe1e6;--wash:#d8dae0;--line:#b9bdc6;--signal:#224bcc}a{color:inherit;text-decoration:none}a,button{transition:color .18s ease,background .18s ease,opacity .18s ease,transform .18s ease}button{font:inherit}.utility-nav{color:#d2d2d2;background:var(--dark);border-bottom:1px solid #292929}.utility-nav>div{max-width:1180px;height:35px;margin:auto;display:flex;align-items:center;justify-content:flex-end;gap:20px;padding:0 18px;overflow:auto}.utility-nav span{margin-right:auto;color:#8d8d8d}.utility-nav a,.utility-nav span{font:600 8px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}.utility-nav a:hover{color:#fff}.ticker{color:var(--ink);background:var(--acid);padding:0 18px}.ticker>div{max-width:1180px;height:36px;margin:auto;display:flex;align-items:center;gap:16px;font:600 10px/1 var(--mono)}.ticker b{padding:5px 7px;color:#fff;background:var(--ink);font-size:8px;letter-spacing:.1em;text-transform:uppercase}.ticker a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ticker a:hover{text-decoration:underline}.ticker span{margin-left:auto;opacity:.58;font-size:8px;text-transform:uppercase}.masthead{max-width:1180px;height:126px;margin:auto;display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:42px;padding:0 18px}.wordmark{display:grid;justify-self:start}.wordmark span{font:400 clamp(40px,5vw,68px)/.82 var(--serif);letter-spacing:-.055em}.wordmark small{max-width:430px;margin-top:11px;color:var(--muted);font:600 8px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase}.masthead__date{display:grid;gap:3px;text-align:right}.masthead__date span,.masthead__date time{font:600 8px/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase}.masthead__date span{color:var(--signal)}.masthead__date time{color:var(--muted)}.masthead__tools{display:flex;border:1px solid var(--ink)}.masthead__tools button{height:38px;padding:0 13px;border:0;background:transparent;font:700 9px/1 var(--mono);text-transform:uppercase;cursor:pointer}.masthead__tools button+button{border-left:1px solid var(--ink)}.masthead__tools button:hover{color:#fff;background:var(--ink)}.desk-nav{position:sticky;top:0;z-index:20;color:var(--ink);background:rgba(255,255,255,.96);border-top:1px solid var(--ink);border-bottom:1px solid var(--ink);backdrop-filter:blur(10px)}.desk-nav>div{max-width:1180px;height:43px;margin:auto;display:flex;align-items:center;overflow-x:auto;padding:0 18px}.desk-nav a{height:42px;display:grid;place-items:center;padding:0 17px;border-right:1px solid var(--line);font:650 9px/1 var(--mono);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}.desk-nav a:first-child{border-left:1px solid var(--line)}.desk-nav a:hover,.desk-nav__home{color:#fff!important;background:var(--ink)}.desk-nav__latest{margin-left:auto;color:var(--signal);border-left:1px solid var(--line)}main{max-width:1180px;margin:auto;padding:30px 18px 90px}.lead-grid{margin:0 -18px;padding:0 18px 42px;color:#fff;background:var(--dark);border-bottom:1px solid var(--ink)}.lead-grid__label{height:50px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #303030}.lead-grid__label span,.lead-grid__label b{font:600 8px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}.lead-grid__label span{color:var(--acid)}.lead-grid__label b{color:#8f8f8f}.lead-story{display:grid;grid-template-columns:minmax(0,1.28fr) minmax(340px,.72fr);min-height:500px;border-bottom:1px solid #303030}.lead-story__media{display:block;margin-left:calc((1180px - 100vw)/2);min-width:0}.story-media{position:relative;aspect-ratio:16/10;overflow:hidden;background:#e2e2df}.story-media img{width:100%;height:100%;display:block;object-fit:cover;filter:saturate(.88) contrast(1.03);transition:transform .55s cubic-bezier(.2,.7,.2,1)}a:hover .story-media img{transform:scale(1.025)}.story-media--lead{height:100%;min-height:500px;aspect-ratio:auto}.story-media--placeholder{isolation:isolate;display:block;background:#d9d7d1}.story-media--placeholder:after{position:absolute;z-index:1;inset:0;background:linear-gradient(145deg,transparent 35%,rgba(0,0,0,.14));content:""}.story-media--placeholder i,.story-media--placeholder b{position:absolute;display:block}.story-media--placeholder i{width:72%;aspect-ratio:1;right:-20%;top:-32%;border:clamp(22px,5vw,72px) solid currentColor;border-radius:50%;opacity:.78}.story-media--placeholder b{width:42%;height:115%;left:15%;bottom:-45%;background:currentColor;transform:rotate(32deg);opacity:.88}.story-media--placeholder span{position:absolute;z-index:2;left:18px;bottom:15px;padding:6px 8px;color:#fff;background:var(--ink);font:600 8px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}.story-media--tone-0{color:#123fce;background:#a5c8ff}.story-media--tone-1{color:#ef4d25;background:#ffd9bd}.story-media--tone-2{color:#907000;background:#f3df36}.story-media--tone-3{color:#1a7857;background:#b8e8cc}.story-media--tone-4{color:#7d3de0;background:#dfc5ff}.lead-story__body{display:flex;flex-direction:column;justify-content:center;padding:45px 0 45px 40px}.kicker{display:inline-flex;width:max-content;color:var(--signal);font:700 8px/1 var(--mono);letter-spacing:.11em;text-transform:uppercase}.lead-grid .kicker{color:var(--acid)}.lead-story h1{max-width:540px;margin:13px 0 18px;font:400 clamp(48px,5vw,76px)/.92 var(--serif);letter-spacing:-.052em}.lead-story p{max-width:490px;margin:0;color:#bdbdbd;font-size:15px;line-height:1.55}.byline{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:16px;color:var(--muted);font:600 8px/1.4 var(--mono);letter-spacing:.05em;text-transform:uppercase}.byline span{color:var(--ink);font-weight:700}.lead-grid .byline{color:#848484}.lead-grid .byline span{color:#e7e7e7}.byline b{padding:4px 6px;color:var(--ink);background:var(--acid)}.read-story{width:max-content;margin-top:34px;padding-bottom:6px;border-bottom:1px solid #777;font:650 10px/1 var(--mono);text-transform:uppercase}.read-story span{color:var(--acid)}.read-story:hover{border-color:#fff}.lead-support{display:grid;grid-template-columns:repeat(4,1fr);gap:0}.lead-support__item{display:grid;grid-template-columns:25px minmax(0,1fr);gap:12px;padding:22px 18px 0 0;border-right:1px solid #303030}.lead-support__item+.lead-support__item{padding-left:18px}.lead-support__item:last-child{border-right:0}.lead-support__item>span{color:#666;font:600 9px/1 var(--mono)}.story-card{min-width:0}.story-card h3{margin:7px 0;font:400 26px/1.02 var(--serif);letter-spacing:-.025em}.story-card p{margin:9px 0;color:var(--muted);font-size:12px}.story-card__body{padding-top:11px}.story-card--support .story-card__media{display:none}.story-card--support .story-card__body{padding:0}.story-card--support h3{margin-top:8px;color:#fff;font-size:21px}.story-card--support p,.story-card--support .byline span{display:none}.story-card--support .byline{color:#777}.section-title{display:flex;align-items:end;justify-content:space-between;gap:30px;margin-bottom:24px;padding-bottom:11px;border-bottom:2px solid var(--ink)}.section-title span,.newsletter span,.press-room__intro>span,.trust-center__intro>span{display:block;color:var(--signal);font:700 8px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}.section-title h2,.newsletter h2,.press-room h2,.trust-center h2,.site-footer h2{margin:7px 0 0;font:400 clamp(38px,5vw,58px)/.95 var(--serif);letter-spacing:-.04em}.section-title>a{padding-bottom:4px;border-bottom:1px solid var(--ink);font:650 9px/1 var(--mono);text-transform:uppercase}.latest{padding:64px 0}.latest__layout{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:55px}.latest__feed{border-top:1px solid var(--ink)}.feed-story{display:grid;grid-template-columns:34px minmax(0,1fr) 180px;gap:18px;align-items:start;padding:19px 0;border-bottom:1px solid var(--line)}.feed-story>span{color:#a0a0a0;font:600 9px/1 var(--mono)}.feed-story h3{margin:7px 0;font:400 29px/1.02 var(--serif);letter-spacing:-.025em}.feed-story p{margin:8px 0;color:var(--muted);font-size:12px}.story-media--feed{aspect-ratio:4/3}.editor-note{position:sticky;top:68px;align-self:start;padding:25px;color:#fff;background:var(--dark);border-top:5px solid var(--acid)}.editor-note>span{color:var(--acid);font:600 8px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}.editor-note h3{margin:13px 0;font:400 31px/.98 var(--serif)}.editor-note p{color:#aaa;font-size:12px}.editor-note a{display:block;padding:11px 0;border-top:1px solid #333;font:600 9px/1 var(--mono);text-transform:uppercase}.editor-note a:hover{color:var(--acid)}.desk-section{margin:0 -18px;padding:58px 18px;border-top:1px solid var(--ink)}.desk-section--tint{background:var(--cream)}.desk-section__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:26px 14px}.story-card--wide{grid-column:span 2;grid-row:span 2}.story-card--wide h3{max-width:530px;font-size:40px}.story-card--wide p{max-width:540px;font-size:13px}.desk-section__grid>.story-card:not(.story-card--wide) .story-media{aspect-ratio:1}.newsletter{position:relative;margin:64px 0;display:grid;grid-template-columns:1fr auto;align-items:end;gap:50px;overflow:hidden;padding:clamp(38px,6vw,72px);color:var(--ink);background:var(--acid)}.newsletter:after{position:absolute;width:260px;height:260px;right:18%;top:-160px;border:55px solid var(--ink);border-radius:50%;content:"";opacity:.08}.newsletter span{color:var(--ink)}.newsletter h2{max-width:760px;font-size:clamp(44px,6vw,72px)}.newsletter p{max-width:680px;margin-bottom:0;color:#303030}.newsletter>a,.press-room__intro>a{position:relative;z-index:1;display:inline-flex;padding:14px 18px;color:#fff;background:var(--ink);border:1px solid var(--ink);font:700 9px/1 var(--mono);text-transform:uppercase}.newsletter>a:hover,.press-room__intro>a:hover{color:var(--ink);background:transparent}.press-room{display:grid;grid-template-columns:.7fr 1.3fr;gap:56px;padding:62px 0;border-top:6px solid var(--ink)}.press-room__intro p{max-width:390px;color:var(--muted)}.release-list{border-top:1px solid var(--ink)}.release-list article{display:grid;grid-template-columns:105px 105px 1fr;gap:15px;align-items:start;padding:20px 0;border-bottom:1px solid var(--line)}.release-list time,.release-list span{color:var(--muted);font:500 8px/1.4 var(--mono);text-transform:uppercase}.release-list h3{margin:0;font:400 22px/1.04 var(--serif)}.trust-center{margin:20px -18px -90px;padding:70px max(18px,calc((100vw - 1144px)/2)) 82px;color:#fff;background:var(--dark)}.trust-center__intro{max-width:810px;margin-bottom:38px}.trust-center__intro>span{color:var(--acid)}.trust-center__intro p{max-width:670px;color:#aaa}.trust-center__grid{display:grid;grid-template-columns:repeat(2,1fr);border-top:1px solid #444}.trust-center__grid a{display:grid;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:15px;min-height:70px;padding:0 18px 0 0;border-bottom:1px solid #333}.trust-center__grid a:nth-child(odd){margin-right:28px}.trust-center__grid a:hover{color:var(--acid)}.trust-center__grid span,.trust-center__grid small{color:#696969;font:600 8px/1 var(--mono)}.trust-center__grid strong{font:400 20px/1 var(--serif)}.site-footer{display:grid;grid-template-columns:1.15fr .85fr;gap:80px;padding:70px max(24px,calc((100vw - 1144px)/2));color:var(--ink);background:var(--cream);border-top:1px solid var(--ink)}.editor{display:grid;grid-template-columns:78px 1fr;gap:22px}.editor__avatar{width:78px;height:78px;display:grid;place-items:center;color:#fff;background:var(--signal);font:400 27px/1 var(--serif)}.editor small{color:var(--signal);font:700 8px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase}.editor h2{font-size:38px}.editor p,.footer-links p{max-width:620px;color:var(--muted)}.footer-links h3{margin-top:0;font:400 29px/1 var(--serif)}.footer-links nav{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--ink)}.footer-links nav a{padding:9px 0;border-bottom:1px solid var(--line);font:600 8px/1 var(--mono);text-transform:uppercase}.footer-links time{display:block;margin-top:24px;color:var(--muted);font:500 8px/1 var(--mono)}@media(max-width:900px){.masthead{grid-template-columns:1fr auto;height:110px}.masthead__date{display:none}.lead-story{grid-template-columns:1fr}.lead-story__media{margin:0}.lead-story__body{padding:35px 0}.lead-support{grid-template-columns:1fr 1fr}.lead-support__item:nth-child(2){border-right:0}.lead-support__item:nth-child(n+3){border-top:1px solid #303030}.latest__layout,.press-room,.site-footer{grid-template-columns:1fr}.editor-note{position:static}.desk-section__grid{grid-template-columns:1fr 1fr}.story-card--wide{grid-column:1/-1}.site-footer{gap:40px}.feed-story{grid-template-columns:30px 1fr 150px}}@media(max-width:620px){.utility-nav span,.utility-nav a:nth-of-type(-n+2),.ticker span{display:none}.ticker>div{height:34px}.masthead{height:92px;gap:14px}.wordmark span{font-size:38px}.wordmark small{max-width:220px;font-size:6px}.masthead__tools button:first-child{display:none}.desk-nav>div{padding:0}.desk-nav a{padding:0 13px}.lead-grid{padding-bottom:20px}.lead-grid__label{height:40px}.story-media--lead{min-height:320px}.lead-story{min-height:0}.lead-story h1{font-size:48px}.lead-story__body{padding:27px 0 34px}.lead-support{grid-template-columns:1fr}.lead-support__item,.lead-support__item+.lead-support__item{padding:17px 0;border-right:0;border-top:1px solid #303030}.story-card--support h3{font-size:24px}.section-title{align-items:start}.section-title>a{display:none}.latest{padding:48px 0}.feed-story{grid-template-columns:25px 1fr}.story-media--feed{grid-column:2}.feed-story h3{font-size:26px}.desk-section__grid{grid-template-columns:1fr}.story-card--wide{grid-column:auto;grid-row:auto}.story-card--wide h3{font-size:34px}.newsletter{grid-template-columns:1fr}.newsletter h2{font-size:48px}.release-list article{grid-template-columns:1fr}.trust-center__grid{grid-template-columns:1fr}.trust-center__grid a:nth-child(odd){margin-right:0}.site-footer{padding:50px 20px}.editor{grid-template-columns:1fr}.footer-links nav{grid-template-columns:1fr}}
73097
73536
  /* Tampa-inspired publication palette */
@@ -73108,7 +73547,7 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
73108
73547
  @media(max-width:620px){main{width:100%}.story-media--lead{height:360px;min-height:0}.lead-story__body,.lead-support,.latest,.feed-story{width:100%;max-width:calc(100vw - 36px)}.feed-story>div{min-width:0}.lead-story h1,.story-card h3,.feed-story h3,.story-card h3 a,.feed-story h3 a{white-space:normal;word-break:break-word;overflow-wrap:anywhere}.lead-story h1{max-width:100%;font-size:40px;line-height:.94}.lead-support__item{width:100%;max-width:100%;grid-template-columns:24px minmax(0,1fr);overflow:hidden}.story-card--support,.story-card--support .story-card__body,.story-card--support h3{width:100%;max-width:calc(100vw - 92px)}.feed-story>div,.feed-story h3,.feed-story p{max-width:calc(100vw - 100px)}}
73109
73548
  @media(max-width:900px){.story-card--panorama{grid-template-columns:1fr}.desk-section__grid--mosaic{grid-template-columns:repeat(2,1fr)}.desk-section__grid--tiles{grid-template-columns:repeat(2,1fr)}}
73110
73549
  @media(max-width:620px){.desk-section__grid--panorama,.desk-section__grid--mosaic,.desk-section__grid--tiles{grid-template-columns:1fr}.story-card--panorama{display:block}.story-card--panorama h3,.story-card--mosaic-lead h3{font-size:34px}.story-card--mosaic-lead{grid-column:auto;grid-row:auto}.desk-section__grid--tiles>.story-card:nth-child(3n+2){margin-top:0}}
73111
- </style></head><body data-theme="${escapeHtml10(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
73550
+ </style></head><body data-theme="${escapeHtml11(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
73112
73551
  const bytes = Buffer.byteLength(html);
73113
73552
  const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
73114
73553
  const filename = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
@@ -73123,7 +73562,7 @@ var init_render3 = __esm({
73123
73562
  });
73124
73563
 
73125
73564
  // src/blog-article/render.ts
73126
- function escapeHtml11(value) {
73565
+ function escapeHtml12(value) {
73127
73566
  return value.replace(
73128
73567
  /[&<>"']/g,
73129
73568
  (character) => ({
@@ -73137,11 +73576,11 @@ function escapeHtml11(value) {
73137
73576
  }
73138
73577
  function safeHref4(value) {
73139
73578
  const trimmed = value.trim();
73140
- return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml11(trimmed) : "#";
73579
+ return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml12(trimmed) : "#";
73141
73580
  }
73142
73581
  function formatDate2(value) {
73143
73582
  const date = new Date(value);
73144
- return Number.isNaN(date.getTime()) ? escapeHtml11(value) : new Intl.DateTimeFormat("en", {
73583
+ return Number.isNaN(date.getTime()) ? escapeHtml12(value) : new Intl.DateTimeFormat("en", {
73145
73584
  month: "long",
73146
73585
  day: "numeric",
73147
73586
  year: "numeric"
@@ -73155,15 +73594,15 @@ function jsonForScript(value) {
73155
73594
  }
73156
73595
  function portrait(input) {
73157
73596
  if (input.author.portraitUrl) {
73158
- return `<img class="author-portrait" src="${safeHref4(input.author.portraitUrl)}" alt="${escapeHtml11(input.author.portraitAlt || input.author.name)}">`;
73597
+ return `<img class="author-portrait" src="${safeHref4(input.author.portraitUrl)}" alt="${escapeHtml12(input.author.portraitAlt || input.author.name)}">`;
73159
73598
  }
73160
- return `<span class="author-portrait author-initials" aria-label="${escapeHtml11(input.author.name)}">${escapeHtml11(initials4(input.author.name))}</span>`;
73599
+ return `<span class="author-portrait author-initials" aria-label="${escapeHtml12(input.author.name)}">${escapeHtml12(initials4(input.author.name))}</span>`;
73161
73600
  }
73162
73601
  function sidebarArt(input) {
73163
73602
  if (input.sidebarMedia?.imageUrl) {
73164
- return `<img src="${safeHref4(input.sidebarMedia.imageUrl)}" alt="${escapeHtml11(input.sidebarMedia.imageAlt)}">`;
73603
+ return `<img src="${safeHref4(input.sidebarMedia.imageUrl)}" alt="${escapeHtml12(input.sidebarMedia.imageAlt)}">`;
73165
73604
  }
73166
- return `<svg viewBox="0 0 560 330" role="img" aria-label="${escapeHtml11(input.sidebarMedia?.imageAlt || "Editorial article illustration")}"><defs><linearGradient id="ba-sky" x1="0" x2="1"><stop stop-color="#dbeafe"/><stop offset="1" stop-color="#d1fae5"/></linearGradient></defs><rect width="560" height="330" fill="url(#ba-sky)"/><circle cx="455" cy="72" r="88" fill="#93c5fd" opacity=".35"/><path d="M76 180 280 42l204 138v118H76Z" fill="#fff"/><path d="M52 185 280 28l228 157" fill="none" stroke="#10213d" stroke-width="18" stroke-linecap="round" stroke-linejoin="round"/><rect x="232" y="180" width="96" height="118" rx="8" fill="#bfdbfe"/><circle cx="385" cy="178" r="40" fill="#f8d5b8"/><path d="M335 330v-70c0-39 22-68 50-68s50 29 50 68v70" fill="#2563eb"/><circle cx="458" cy="196" r="34" fill="#8b5e3c"/><path d="M418 330v-64c0-34 18-60 40-60s40 26 40 60v64" fill="#45d5aa"/></svg>`;
73605
+ return `<svg viewBox="0 0 560 330" role="img" aria-label="${escapeHtml12(input.sidebarMedia?.imageAlt || "Editorial article illustration")}"><defs><linearGradient id="ba-sky" x1="0" x2="1"><stop stop-color="#dbeafe"/><stop offset="1" stop-color="#d1fae5"/></linearGradient></defs><rect width="560" height="330" fill="url(#ba-sky)"/><circle cx="455" cy="72" r="88" fill="#93c5fd" opacity=".35"/><path d="M76 180 280 42l204 138v118H76Z" fill="#fff"/><path d="M52 185 280 28l228 157" fill="none" stroke="#10213d" stroke-width="18" stroke-linecap="round" stroke-linejoin="round"/><rect x="232" y="180" width="96" height="118" rx="8" fill="#bfdbfe"/><circle cx="385" cy="178" r="40" fill="#f8d5b8"/><path d="M335 330v-70c0-39 22-68 50-68s50 29 50 68v70" fill="#2563eb"/><circle cx="458" cy="196" r="34" fill="#8b5e3c"/><path d="M418 330v-64c0-34 18-60 40-60s40 26 40 60v64" fill="#45d5aa"/></svg>`;
73167
73606
  }
73168
73607
  function citations(input) {
73169
73608
  const published = new Date(input.publishedAt);
@@ -73192,23 +73631,23 @@ function renderBlogArticleV1(input, config, generatedAt) {
73192
73631
  ...flags.faq && input.faq.length ? [{ id: "faq", title: "Frequently asked questions" }] : []
73193
73632
  ];
73194
73633
  const contributorText = [
73195
- `Written by <a href="${safeHref4(input.author.href || "#author")}">${escapeHtml11(input.author.name)}</a>`,
73196
- input.editor ? `Edited by <a href="${safeHref4(input.editor.href || "#")}">${escapeHtml11(input.editor.name)}</a>` : "",
73197
- input.reviewer ? `Reviewed by <a href="${safeHref4(input.reviewer.href || "#")}">${escapeHtml11(input.reviewer.name)}${input.reviewer.credential ? `, ${escapeHtml11(input.reviewer.credential)}` : ""}</a>` : ""
73634
+ `Written by <a href="${safeHref4(input.author.href || "#author")}">${escapeHtml12(input.author.name)}</a>`,
73635
+ input.editor ? `Edited by <a href="${safeHref4(input.editor.href || "#")}">${escapeHtml12(input.editor.name)}</a>` : "",
73636
+ input.reviewer ? `Reviewed by <a href="${safeHref4(input.reviewer.href || "#")}">${escapeHtml12(input.reviewer.name)}${input.reviewer.credential ? `, ${escapeHtml12(input.reviewer.credential)}` : ""}</a>` : ""
73198
73637
  ].filter(Boolean).join(" \xB7 ");
73199
73638
  const sections = input.sections.map(
73200
- (section3) => `<section id="${escapeHtml11(section3.id)}" class="article-section"><header>${section3.eyebrow ? `<span class="eyebrow">${escapeHtml11(section3.eyebrow)}</span>` : ""}<h2>${escapeHtml11(section3.title)}</h2></header>${section3.paragraphs.map((paragraph) => `<p>${escapeHtml11(paragraph)}</p>`).join("")}${section3.bullets?.length ? `<ul>${section3.bullets.map((item) => `<li>${escapeHtml11(item)}</li>`).join("")}</ul>` : ""}${section3.callout ? `<aside class="callout"><span>Worth noting</span><h3>${escapeHtml11(section3.callout.heading)}</h3><p>${escapeHtml11(section3.callout.body)}</p></aside>` : ""}</section>`
73639
+ (section3) => `<section id="${escapeHtml12(section3.id)}" class="article-section"><header>${section3.eyebrow ? `<span class="eyebrow">${escapeHtml12(section3.eyebrow)}</span>` : ""}<h2>${escapeHtml12(section3.title)}</h2></header>${section3.paragraphs.map((paragraph) => `<p>${escapeHtml12(paragraph)}</p>`).join("")}${section3.bullets?.length ? `<ul>${section3.bullets.map((item) => `<li>${escapeHtml12(item)}</li>`).join("")}</ul>` : ""}${section3.callout ? `<aside class="callout"><span>Worth noting</span><h3>${escapeHtml12(section3.callout.heading)}</h3><p>${escapeHtml12(section3.callout.body)}</p></aside>` : ""}</section>`
73201
73640
  ).join("");
73202
- const faq = flags.faq && input.faq.length ? `<section id="faq" class="faq"><span class="eyebrow">Common questions</span><h2>Frequently asked questions</h2><div>${input.faq.map((item, index) => `<article><h3><button type="button" aria-expanded="false" aria-controls="faq-${index}"><span>${escapeHtml11(item.question)}</span><b aria-hidden="true">+</b></button></h3><p id="faq-${index}" hidden>${escapeHtml11(item.answer)}</p></article>`).join("")}</div></section>` : "";
73203
- const media = flags.sidebarMedia && input.sidebarMedia ? `<figure class="sidebar-media" data-article-media>${sidebarArt(input)}${input.sidebarMedia.caption ? `<figcaption>${escapeHtml11(input.sidebarMedia.caption)}</figcaption>` : ""}</figure>` : "";
73204
- const disclosure = flags.disclosure && input.disclosure ? `<section class="disclosure"><button type="button" aria-expanded="false" aria-controls="disclosure-details"><span>${escapeHtml11(input.disclosure.summary)}</span><b aria-hidden="true">\u2304</b></button><p id="disclosure-details" hidden>${escapeHtml11(input.disclosure.details)}</p></section>` : "";
73205
- const toc = flags.stickyToc ? `<aside class="toc" data-sticky-toc><header><strong>Table of contents</strong><button type="button" aria-expanded="true" aria-controls="toc-links" aria-label="Collapse table of contents">\u2303</button></header><nav id="toc-links" aria-label="Table of contents">${tocItems.map((item, index) => `<a href="#${escapeHtml11(item.id)}"${index === 0 ? ' aria-current="location"' : ""}><i></i><span>${escapeHtml11(item.title)}</span></a>`).join("")}</nav><button class="back-top" type="button">Back to top \u2191</button></aside>` : "";
73641
+ const faq = flags.faq && input.faq.length ? `<section id="faq" class="faq"><span class="eyebrow">Common questions</span><h2>Frequently asked questions</h2><div>${input.faq.map((item, index) => `<article><h3><button type="button" aria-expanded="false" aria-controls="faq-${index}"><span>${escapeHtml12(item.question)}</span><b aria-hidden="true">+</b></button></h3><p id="faq-${index}" hidden>${escapeHtml12(item.answer)}</p></article>`).join("")}</div></section>` : "";
73642
+ const media = flags.sidebarMedia && input.sidebarMedia ? `<figure class="sidebar-media" data-article-media>${sidebarArt(input)}${input.sidebarMedia.caption ? `<figcaption>${escapeHtml12(input.sidebarMedia.caption)}</figcaption>` : ""}</figure>` : "";
73643
+ const disclosure = flags.disclosure && input.disclosure ? `<section class="disclosure"><button type="button" aria-expanded="false" aria-controls="disclosure-details"><span>${escapeHtml12(input.disclosure.summary)}</span><b aria-hidden="true">\u2304</b></button><p id="disclosure-details" hidden>${escapeHtml12(input.disclosure.details)}</p></section>` : "";
73644
+ const toc = flags.stickyToc ? `<aside class="toc" data-sticky-toc><header><strong>Table of contents</strong><button type="button" aria-expanded="true" aria-controls="toc-links" aria-label="Collapse table of contents">\u2303</button></header><nav id="toc-links" aria-label="Table of contents">${tocItems.map((item, index) => `<a href="#${escapeHtml12(item.id)}"${index === 0 ? ' aria-current="location"' : ""}><i></i><span>${escapeHtml12(item.title)}</span></a>`).join("")}</nav><button class="back-top" type="button">Back to top \u2191</button></aside>` : "";
73206
73645
  const actions = flags.citation || flags.share ? `<div class="article-actions">${flags.citation ? '<button type="button" data-cite aria-haspopup="dialog">\u270E <span>Cite us</span></button>' : ""}${flags.share ? '<button type="button" data-share>\u21E7 <span>Share this article</span></button>' : ""}</div><p class="action-status" aria-live="polite"></p>` : "";
73207
- const author = flags.authorCard ? `<section id="author" class="author-card">${actions}<div class="author-grid">${portrait(input)}<div><span>Written by</span><h2>${input.author.href ? `<a href="${safeHref4(input.author.href)}">${escapeHtml11(input.author.name)}</a>` : escapeHtml11(input.author.name)}</h2><p>${escapeHtml11(input.author.role)}</p>${input.author.socialLinks.length || input.author.email ? `<nav aria-label="Author links">${input.author.socialLinks.map((link2) => `<a href="${safeHref4(link2.href)}">${escapeHtml11(link2.label)}</a>`).join("")}${input.author.email ? `<a href="mailto:${escapeHtml11(input.author.email)}">Email</a>` : ""}</nav>` : ""}</div>${input.author.href ? `<a class="author-more" href="${safeHref4(input.author.href)}">Read more from ${escapeHtml11(input.author.name.split(/\s+/)[0] || input.author.name)}</a>` : ""}</div><p class="author-bio">${escapeHtml11(input.author.bio)}</p></section>` : actions;
73208
- const citationDialog = flags.citation ? `<dialog id="citation-dialog"><div><button class="dialog-close" type="button" aria-label="Close citation guide">\xD7</button><h2>Quick citation guide</h2><p>Select a citation to automatically copy it to your clipboard.</p><section>${Object.keys(citationValues).map((style) => `<button type="button" data-citation="${style}"><strong>${style}:</strong> ${escapeHtml11(citationValues[style])}</button>`).join("")}</section><p class="citation-status" aria-live="polite"></p></div></dialog>` : "";
73209
- const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><meta name="description" content="${escapeHtml11(input.dek || input.keyTakeaways[0])}"><title>${escapeHtml11(input.title)} \xB7 ${escapeHtml11(brand)}</title><style>
73646
+ const author = flags.authorCard ? `<section id="author" class="author-card">${actions}<div class="author-grid">${portrait(input)}<div><span>Written by</span><h2>${input.author.href ? `<a href="${safeHref4(input.author.href)}">${escapeHtml12(input.author.name)}</a>` : escapeHtml12(input.author.name)}</h2><p>${escapeHtml12(input.author.role)}</p>${input.author.socialLinks.length || input.author.email ? `<nav aria-label="Author links">${input.author.socialLinks.map((link2) => `<a href="${safeHref4(link2.href)}">${escapeHtml12(link2.label)}</a>`).join("")}${input.author.email ? `<a href="mailto:${escapeHtml12(input.author.email)}">Email</a>` : ""}</nav>` : ""}</div>${input.author.href ? `<a class="author-more" href="${safeHref4(input.author.href)}">Read more from ${escapeHtml12(input.author.name.split(/\s+/)[0] || input.author.name)}</a>` : ""}</div><p class="author-bio">${escapeHtml12(input.author.bio)}</p></section>` : actions;
73647
+ const citationDialog = flags.citation ? `<dialog id="citation-dialog"><div><button class="dialog-close" type="button" aria-label="Close citation guide">\xD7</button><h2>Quick citation guide</h2><p>Select a citation to automatically copy it to your clipboard.</p><section>${Object.keys(citationValues).map((style) => `<button type="button" data-citation="${style}"><strong>${style}:</strong> ${escapeHtml12(citationValues[style])}</button>`).join("")}</section><p class="citation-status" aria-live="polite"></p></div></dialog>` : "";
73648
+ const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><meta name="description" content="${escapeHtml12(input.dek || input.keyTakeaways[0])}"><title>${escapeHtml12(input.title)} \xB7 ${escapeHtml12(brand)}</title><style>
73210
73649
  :root{--ink:#10213d;--ink2:#1c355a;--blue:#2563eb;--blue2:#1749b6;--mint:#45d5aa;--paper:#f7f5f0;--line:#dce4ef;--card:#fff;--body:#475569;--serif:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif;--sans:Inter,Avenir Next,ui-sans-serif,system-ui,sans-serif}body[data-theme="slate"]{--ink:#1e293b;--ink2:#334155;--blue:#475569;--blue2:#1e293b;--mint:#94a3b8;--paper:#f8fafc}body[data-theme="forest"]{--ink:#12352e;--ink2:#23584c;--blue:#147d64;--blue2:#0c5b48;--mint:#79d8bd;--paper:#f4f8f3}*{box-sizing:border-box}html{scroll-behavior:smooth;background:var(--paper)}body{margin:0;color:#252a33;background:var(--paper);font-family:var(--sans);line-height:1.6}a{color:inherit}button{font:inherit}button:focus-visible,a:focus-visible{outline:3px solid var(--mint);outline-offset:3px}.progress{position:fixed;z-index:50;top:0;left:0;height:4px;width:0;background:var(--mint)}.hero{position:relative;overflow:hidden;color:#fff;background:var(--ink)}.hero:after{position:absolute;right:-110px;top:-150px;width:420px;height:420px;border:1px solid #ffffff1a;border-radius:50%;content:""}.hero-inner{position:relative;z-index:1;max-width:1240px;margin:auto;padding:58px 42px 126px}.breadcrumb{display:flex;gap:8px;margin-bottom:34px;color:#dbeafecc;font-size:14px}.eyebrow{display:block;color:var(--blue);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase}.hero .eyebrow{color:var(--mint)}h1,h2,h3,p{margin-top:0}.hero h1{max-width:1000px;margin:14px 0 24px;font:700 clamp(44px,6vw,78px)/.98 var(--serif);letter-spacing:-.035em}.hero .dek{max-width:780px;color:#dbeafe;font-size:20px}.contributors{margin-top:30px;color:#eff6ff;font-size:14px}.contributors a{font-weight:800}.meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#dbeafecc}.layout{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,760px) 290px;gap:64px;max-width:1240px;margin:-78px auto 0;padding:0 42px 80px}.main{min-width:0}.takeaways{padding:38px 44px;border-radius:24px;background:#fff;box-shadow:0 24px 70px #10213d24}.takeaways h2{margin:5px 0 20px;font:700 32px/1.1 var(--serif);color:var(--ink)}.takeaways ul{display:grid;gap:16px;margin:0;padding:0;list-style:none}.takeaways li{position:relative;padding-left:26px;color:var(--body);font-size:17px}.takeaways li:before{position:absolute;left:2px;top:.7em;width:8px;height:8px;border-radius:50%;background:var(--blue);content:""}.intro{padding:40px 0 4px}.intro p,.article-section>p{margin-bottom:22px;color:var(--body);font:400 18px/1.75 var(--sans)}.article-section{scroll-margin-top:28px;padding-top:45px}.article-section h2,.faq h2{margin:6px 0 20px;color:var(--ink);font:700 38px/1.08 var(--serif);letter-spacing:-.02em}.article-section ul{display:grid;gap:10px;margin:22px 0;padding-left:24px;color:var(--body);font-size:17px}.article-section li::marker{color:var(--blue)}.callout{margin:34px 0;padding:24px 28px;border-left:4px solid var(--blue);border-radius:0 16px 16px 0;background:#fff}.callout span{color:var(--blue);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.callout h3{margin:5px 0 8px;color:var(--ink);font:700 24px/1.15 var(--serif)}.callout p{margin:0;color:var(--body)}.rail{padding-top:0}.sidebar-media{overflow:hidden;margin:0 0 18px;border:1px solid #dbeafe;border-radius:16px;background:#fff}.sidebar-media img,.sidebar-media svg{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.sidebar-media figcaption{padding:10px 13px;color:#64748b;font-size:11px}.disclosure{margin-bottom:28px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe}.disclosure button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:14px;padding:15px 0;border:0;color:#64748b;background:transparent;text-align:left;font-size:12px;cursor:pointer}.disclosure button b{color:var(--blue);font-size:18px;transition:transform .2s}.disclosure button[aria-expanded="true"] b{transform:rotate(180deg)}.disclosure p{padding:0 0 16px;margin:0;color:#64748b;font-size:12px}.toc{position:sticky;top:30px;padding:22px;border:1px solid var(--line);border-radius:16px;background:#fff;box-shadow:0 2px 5px #10213d12}.toc header{display:flex;align-items:center;justify-content:space-between}.toc header strong{color:var(--ink);font:700 21px/1 var(--serif)}.toc header button{width:34px;height:34px;border:0;border-radius:50%;color:var(--blue);background:transparent;cursor:pointer}.toc nav{display:grid;margin-top:14px}.toc nav a{display:flex;align-items:flex-start;gap:11px;padding:8px 0;color:#64748b;text-decoration:none;font-size:13px;line-height:1.35}.toc nav i{width:8px;height:8px;margin-top:5px;border:2px solid #bfdbfe;border-radius:50%}.toc nav a[aria-current="location"]{color:var(--ink);font-weight:800}.toc nav a[aria-current="location"] i{border-color:var(--blue);background:var(--blue)}.back-top{width:100%;margin-top:18px;padding:16px 0 0;border:0;border-top:1px solid var(--line);color:var(--blue);background:transparent;text-align:right;font-size:13px;font-weight:800;cursor:pointer}.faq{scroll-margin-top:28px;padding-top:64px}.faq>div{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.faq article+article{border-top:1px solid var(--line)}.faq h3{margin:0}.faq h3 button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:18px;padding:19px 0;border:0;color:var(--ink);background:transparent;text-align:left;font-weight:800;cursor:pointer}.faq h3 b{color:var(--blue);font-size:22px}.faq article>p{padding:0 0 20px;margin:0;color:var(--body);font-size:16px}.author-card{margin-top:64px;padding:32px 36px;border-radius:18px;background:#fff}.article-actions{display:flex;flex-wrap:wrap;justify-content:space-between;gap:14px}.article-actions button{display:inline-flex;min-height:46px;align-items:center;justify-content:center;gap:8px;padding:8px 18px;border:2px solid var(--blue);border-radius:999px;color:var(--ink);background:#fff;font-weight:800;cursor:pointer}.article-actions button:hover{color:#fff;background:var(--blue)}.action-status{min-height:20px;margin:8px 0 0;color:var(--blue);text-align:right;font-size:13px;font-weight:700}.author-grid{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:center;margin-top:24px}.author-portrait{width:88px;height:88px;border-radius:50%;object-fit:cover}.author-initials{display:grid;place-items:center;color:var(--ink);background:#fecdd3;font-size:22px;font-weight:900}.author-grid span{color:#64748b;font-size:13px}.author-grid h2{margin:2px 0;color:var(--ink);font:700 27px/1 var(--serif)}.author-grid h2 a{text-decoration:none}.author-grid p{margin:8px 0;color:#64748b;font-size:14px}.author-grid nav{display:flex;flex-wrap:wrap;gap:14px}.author-grid nav a{color:#64748b;font-size:12px}.author-more{display:inline-flex;min-height:48px;align-items:center;justify-content:center;padding:10px 24px;border:2px solid var(--blue);color:var(--blue);text-align:center;text-decoration:none;font-size:13px;font-weight:800}.author-bio{margin:24px 0 0!important;padding-top:20px;border-top:1px solid var(--line);color:var(--body);font-size:16px}dialog{width:min(720px,calc(100% - 30px));max-height:calc(100dvh - 30px);padding:0;border:0;border-radius:18px;color:var(--ink);background:#fff;box-shadow:0 24px 70px #10213d38}dialog::backdrop{background:#0f172a7a;backdrop-filter:blur(2px)}dialog>div{position:relative;padding:30px}dialog h2{margin:0 50px 2px 0;font:700 30px/1.1 var(--serif)}dialog>div>p{color:#64748b}.dialog-close{position:absolute;top:13px;right:13px;width:44px;height:44px;border:0;border-radius:50%;color:var(--blue);background:transparent;font-size:29px;cursor:pointer}dialog section{border-top:1px solid var(--line)}dialog section button{display:block;width:100%;padding:17px 5px;border:0;border-bottom:1px solid var(--line);color:var(--body);background:#fff;text-align:left;line-height:1.65;cursor:pointer}.citation-status{min-height:22px;margin:14px 0 0!important;color:var(--blue)!important;font-size:13px;font-weight:800}.generated{max-width:1156px;margin:-52px auto 50px;padding:0 42px;color:#94a3b8;font-size:11px}@media(max-width:900px){.layout{display:block}.rail{display:none}.hero-inner{padding-bottom:116px}}@media(max-width:620px){.hero-inner{padding:36px 20px 104px}.hero h1{font-size:46px}.hero .dek{font-size:17px}.layout{margin-top:-70px;padding:0 16px 54px}.takeaways{padding:28px 24px}.article-section h2,.faq h2{font-size:32px}.author-card{padding:24px}.article-actions{display:grid;justify-content:start}.author-grid{grid-template-columns:1fr;gap:12px}.author-more{width:100%}dialog>div{padding:24px}.generated{padding:0 20px}}
73211
- </style></head><body data-theme="${escapeHtml11(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml11(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml11(input.eyebrow)}</span><h1>${escapeHtml11(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml11(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml11(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml11(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml11(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
73650
+ </style></head><body data-theme="${escapeHtml12(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml12(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml12(input.eyebrow)}</span><h1>${escapeHtml12(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml12(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml12(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml12(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml12(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
73212
73651
  const bytes = Buffer.byteLength(html);
73213
73652
  const sha2563 = (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex");
73214
73653
  return {
@@ -76108,7 +76547,7 @@ function sql() {
76108
76547
  if (_sql) return _sql;
76109
76548
  const url = process.env.MEMORY_DATABASE_URL?.trim();
76110
76549
  if (!url) throw new Error("MEMORY_DATABASE_URL is not set (paa-crawler needs mcp-memory Neon URL for reads)");
76111
- _sql = (0, import_serverless.neon)(url);
76550
+ _sql = (0, import_serverless2.neon)(url);
76112
76551
  return _sql;
76113
76552
  }
76114
76553
  function physicalVault(owner, vault) {
@@ -76301,11 +76740,11 @@ async function dbUsage(identity, plan) {
76301
76740
  unlimited: isUnlimited(plan)
76302
76741
  };
76303
76742
  }
76304
- var import_serverless, _sql, EMBED_BYTES_PER_CHUNK, FREE_COST_CAP_USD;
76743
+ var import_serverless2, _sql, EMBED_BYTES_PER_CHUNK, FREE_COST_CAP_USD;
76305
76744
  var init_memory_db = __esm({
76306
76745
  "src/api/memory-db.ts"() {
76307
76746
  "use strict";
76308
- import_serverless = require("@neondatabase/serverless");
76747
+ import_serverless2 = require("@neondatabase/serverless");
76309
76748
  init_rates();
76310
76749
  init_memory_universe();
76311
76750
  _sql = null;
@@ -79326,6 +79765,7 @@ var init_server = __esm({
79326
79765
  init_scheduled_artifact_internal_routes();
79327
79766
  init_site_audit_worker();
79328
79767
  init_local_sourcebook_worker();
79768
+ init_commons_embeddings();
79329
79769
  init_local_sourcebook_dispatch();
79330
79770
  import_factory8 = require("hono/factory");
79331
79771
  import_cookie2 = require("hono/cookie");
@@ -82109,7 +82549,7 @@ var init_server = __esm({
82109
82549
  const budget = { maxJobs: 10, deadlineMs: startedAt + CRON_TICK_DRAIN_BUDGET_MS };
82110
82550
  const origin = `${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`;
82111
82551
  const unfinished = [];
82112
- const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery, workflowDispatchResult, scheduledRunReconciliation, retentionSweep] = await Promise.all([
82552
+ const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, commonsEmbeddingDrain, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery, workflowDispatchResult, scheduledRunReconciliation, retentionSweep] = await Promise.all([
82113
82553
  settleWithinTickBudget("drainQueue", unfinished, drainQueue2(budget), []),
82114
82554
  settleWithinTickBudget("monthlyRefreshSweep", unfinished, runMonthlyRefreshSweep(), { status: "skipped_deadline" }),
82115
82555
  settleWithinTickBudget("reapBrowserSessions", unfinished, reapIdleBrowserSessions(120), { reaped: 0 }),
@@ -82126,6 +82566,7 @@ var init_server = __esm({
82126
82566
  settleWithinTickBudget("siteExtractReconcile", unfinished, reconcileSiteExtractSettlements(10), { checked: 0, settled: 0, failed: 0 }),
82127
82567
  settleWithinTickBudget("paaRedispatch", unfinished, redispatchPendingPaaHarvests(10), { checked: 0, dispatched: 0, failed: 0 }),
82128
82568
  settleWithinTickBudget("paaReconcile", unfinished, reconcilePaaHarvestSettlements(10), { checked: 0, staleFailed: 0, settled: 0, failed: 0 }),
82569
+ settleWithinTickBudget("commonsEmbeddingDrain", unfinished, embedQueuedCommonsDocuments(50), { claimed: 0, embedded: 0, failed: 0, remaining: 0 }),
82129
82570
  settleWithinTickBudget("localSourcebookRedispatch", unfinished, redispatchQueuedLocalSourcebookAcquisitions(5), { checked: 0, dispatched: 0, failed: 0 }),
82130
82571
  settleWithinTickBudget("analyticsRollup", unfinished, refreshAnalyticsDailyRollupsIfDue(), { status: "failed", rows: 0 }),
82131
82572
  settleWithinTickBudget("analyticsForms", unfinished, deliverPendingAnalyticsForms(10), { claimed: 0, delivered: 0, deferred: 0 }),
@@ -82156,6 +82597,7 @@ var init_server = __esm({
82156
82597
  siteExtractReconciliation,
82157
82598
  paaHarvestRedispatch,
82158
82599
  paaHarvestReconciliation,
82600
+ commonsEmbeddingDrain,
82159
82601
  localSourcebookRedispatch,
82160
82602
  analyticsRollup,
82161
82603
  analyticsFormDelivery,