@reddoorla/maintenance 0.76.0 → 0.79.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 (125) hide show
  1. package/README.md +34 -7
  2. package/dist/{blux-RIJBRVCF.js → blux-2P4FGCUL.js} +5 -5
  3. package/dist/{blux-RIJBRVCF.js.map → blux-2P4FGCUL.js.map} +1 -1
  4. package/dist/{bump-deps-OQR3IXLN.js → bump-deps-XB2LJTVN.js} +3 -3
  5. package/dist/{chunk-RHPDQW6D.js → chunk-2SLED3NB.js} +1 -1
  6. package/dist/{chunk-RHPDQW6D.js.map → chunk-2SLED3NB.js.map} +1 -1
  7. package/dist/{chunk-3U3IGVAQ.js → chunk-3LWKIKZ2.js} +47 -9
  8. package/dist/chunk-3LWKIKZ2.js.map +1 -0
  9. package/dist/{chunk-HIN3P4ZN.js → chunk-4SOCPK7A.js} +10 -1
  10. package/dist/chunk-4SOCPK7A.js.map +1 -0
  11. package/dist/{chunk-CASCWQQX.js → chunk-73UFRZKY.js} +34 -19
  12. package/dist/chunk-73UFRZKY.js.map +1 -0
  13. package/dist/chunk-A3WDWNN5.js +70 -0
  14. package/dist/chunk-A3WDWNN5.js.map +1 -0
  15. package/dist/{chunk-D3DQFC32.js → chunk-ATJ6ZA2V.js} +2 -2
  16. package/dist/{chunk-ZO7WNY57.js → chunk-B5ZUQZFL.js} +2 -2
  17. package/dist/{chunk-PH3AGF4Z.js → chunk-DC5H7KEQ.js} +2 -2
  18. package/dist/{chunk-DYKG53IR.js → chunk-FAQIMC26.js} +9 -9
  19. package/dist/{chunk-4VAUSRSS.js → chunk-FVVGDIGX.js} +2 -2
  20. package/dist/{chunk-7MJBTV2R.js → chunk-FX7RAMGC.js} +2 -2
  21. package/dist/{chunk-PKVZMFEB.js → chunk-K5JZ5KLO.js} +2 -2
  22. package/dist/{chunk-CZAMYVOP.js → chunk-KPQXB5CR.js} +2 -2
  23. package/dist/{chunk-BTEBRTW4.js → chunk-LS6QPANO.js} +2 -2
  24. package/dist/{chunk-2X322ZES.js → chunk-MGJ72BA4.js} +2 -2
  25. package/dist/chunk-MNDIED4M.js +137 -0
  26. package/dist/chunk-MNDIED4M.js.map +1 -0
  27. package/dist/{chunk-ZYBOT5PM.js → chunk-NT4WIP7I.js} +2 -2
  28. package/dist/{chunk-JNXCDUYB.js → chunk-Q276MZ7E.js} +3 -4
  29. package/dist/chunk-Q276MZ7E.js.map +1 -0
  30. package/dist/{chunk-GYNRZCM6.js → chunk-QG6OSL4M.js} +2 -2
  31. package/dist/{chunk-IRRZG66U.js → chunk-QMXQUL5Z.js} +34 -18
  32. package/dist/chunk-QMXQUL5Z.js.map +1 -0
  33. package/dist/{chunk-YEGHUODU.js → chunk-VSHC4HOI.js} +96 -1
  34. package/dist/chunk-VSHC4HOI.js.map +1 -0
  35. package/dist/{chunk-JTRTG5CU.js → chunk-YIQ7IWE3.js} +2 -2
  36. package/dist/{chunk-XKOZUVK2.js → chunk-ZTPPCHDB.js} +1 -1
  37. package/dist/chunk-ZTPPCHDB.js.map +1 -0
  38. package/dist/cli/bin.js +32 -21
  39. package/dist/cli/bin.js.map +1 -1
  40. package/dist/cli/commands/audit.d.ts +1 -1
  41. package/dist/cli/commands/audit.js +7 -5
  42. package/dist/cli/commands/audit.js.map +1 -1
  43. package/dist/client-X5WUJWDE.js +11 -0
  44. package/dist/configs/eslint.js +10 -0
  45. package/dist/configs/eslint.js.map +1 -1
  46. package/dist/{convert-to-pnpm-OVMASATZ.js → convert-to-pnpm-FZHSLUQB.js} +3 -3
  47. package/dist/{db-3QW2XEXA.js → db-JSIZYT23.js} +3 -3
  48. package/dist/{digest-75FWSC74.js → digest-NQAQI34P.js} +11 -11
  49. package/dist/forms/index.js +6 -62
  50. package/dist/forms/index.js.map +1 -1
  51. package/dist/{github-signals-TF3VSNIN.js → github-signals-4LWQGD5V.js} +5 -5
  52. package/dist/{health-endpoint-JZGYCMTV.js → health-endpoint-LFVE26QS.js} +3 -3
  53. package/dist/index.d.ts +11 -3
  54. package/dist/index.js +32 -24
  55. package/dist/index.js.map +1 -1
  56. package/dist/{init-2KOISBCZ.js → init-Y5O33BUL.js} +14 -12
  57. package/dist/{init-2KOISBCZ.js.map → init-Y5O33BUL.js.map} +1 -1
  58. package/dist/{launch-K4EHKTXC.js → launch-3SYLX263.js} +8 -5
  59. package/dist/launch-3SYLX263.js.map +1 -0
  60. package/dist/migrate-LJUFGXQZ.js +7 -0
  61. package/dist/{onboard-PX36YDZA.js → onboard-RDGHVCNG.js} +3 -3
  62. package/dist/{orchestrate-CPR6KVS2.js → orchestrate-QOQXOJLI.js} +5 -5
  63. package/dist/{preflight-QRRMPI7K.js → preflight-2PWVVIRR.js} +6 -6
  64. package/dist/protection-audit-X3BHT2MB.js +140 -0
  65. package/dist/protection-audit-X3BHT2MB.js.map +1 -0
  66. package/dist/recipes/sync-configs.d.ts +1 -1
  67. package/dist/recipes/sync-configs.js +3 -3
  68. package/dist/{renovate-dispatch-6II2JVOX.js → renovate-dispatch-2VYCEH42.js} +2 -2
  69. package/dist/renovate-dispatch-2VYCEH42.js.map +1 -0
  70. package/dist/{report-SLGBDJ6O.js → report-IU4CLJS5.js} +3 -3
  71. package/dist/{self-updating-2YSVNMPP.js → self-updating-ALMUWJNX.js} +5 -4
  72. package/dist/{self-updating-2YSVNMPP.js.map → self-updating-ALMUWJNX.js.map} +1 -1
  73. package/dist/{selftest-RVASRO3Z.js → selftest-PJFABZDM.js} +5 -5
  74. package/dist/{smoke-suite-CSP23NZM.js → smoke-suite-SUEJFBYP.js} +3 -3
  75. package/dist/{submissions-MDKWJYMM.js → submissions-3I3GAQAX.js} +13 -5
  76. package/dist/submissions-3I3GAQAX.js.map +1 -0
  77. package/dist/{submissions-BNRGY3JQ.js → submissions-SURN7BV5.js} +3 -3
  78. package/dist/{svelte-codemods-V54KOBWU.js → svelte-codemods-IWO7X3F3.js} +3 -3
  79. package/dist/{sync-configs-UBCJRASZ.js → sync-configs-7XGHFC6P.js} +4 -4
  80. package/dist/{types-0jreMCF5.d.ts → types-DnEL3NRn.d.ts} +1 -1
  81. package/dist/{upgrade-WR6ICPAG.js → upgrade-AM4S7JF3.js} +3 -3
  82. package/package.json +1 -1
  83. package/dist/chunk-3U3IGVAQ.js.map +0 -1
  84. package/dist/chunk-CASCWQQX.js.map +0 -1
  85. package/dist/chunk-HIN3P4ZN.js.map +0 -1
  86. package/dist/chunk-IRRZG66U.js.map +0 -1
  87. package/dist/chunk-JNXCDUYB.js.map +0 -1
  88. package/dist/chunk-XKOZUVK2.js.map +0 -1
  89. package/dist/chunk-YEGHUODU.js.map +0 -1
  90. package/dist/client-NVVQK3HH.js +0 -11
  91. package/dist/launch-K4EHKTXC.js.map +0 -1
  92. package/dist/migrate-35DNNF3H.js +0 -7
  93. package/dist/renovate-dispatch-6II2JVOX.js.map +0 -1
  94. package/dist/submissions-MDKWJYMM.js.map +0 -1
  95. /package/dist/{bump-deps-OQR3IXLN.js.map → bump-deps-XB2LJTVN.js.map} +0 -0
  96. /package/dist/{chunk-D3DQFC32.js.map → chunk-ATJ6ZA2V.js.map} +0 -0
  97. /package/dist/{chunk-ZO7WNY57.js.map → chunk-B5ZUQZFL.js.map} +0 -0
  98. /package/dist/{chunk-PH3AGF4Z.js.map → chunk-DC5H7KEQ.js.map} +0 -0
  99. /package/dist/{chunk-DYKG53IR.js.map → chunk-FAQIMC26.js.map} +0 -0
  100. /package/dist/{chunk-4VAUSRSS.js.map → chunk-FVVGDIGX.js.map} +0 -0
  101. /package/dist/{chunk-7MJBTV2R.js.map → chunk-FX7RAMGC.js.map} +0 -0
  102. /package/dist/{chunk-PKVZMFEB.js.map → chunk-K5JZ5KLO.js.map} +0 -0
  103. /package/dist/{chunk-CZAMYVOP.js.map → chunk-KPQXB5CR.js.map} +0 -0
  104. /package/dist/{chunk-BTEBRTW4.js.map → chunk-LS6QPANO.js.map} +0 -0
  105. /package/dist/{chunk-2X322ZES.js.map → chunk-MGJ72BA4.js.map} +0 -0
  106. /package/dist/{chunk-ZYBOT5PM.js.map → chunk-NT4WIP7I.js.map} +0 -0
  107. /package/dist/{chunk-GYNRZCM6.js.map → chunk-QG6OSL4M.js.map} +0 -0
  108. /package/dist/{chunk-JTRTG5CU.js.map → chunk-YIQ7IWE3.js.map} +0 -0
  109. /package/dist/{client-NVVQK3HH.js.map → client-X5WUJWDE.js.map} +0 -0
  110. /package/dist/{convert-to-pnpm-OVMASATZ.js.map → convert-to-pnpm-FZHSLUQB.js.map} +0 -0
  111. /package/dist/{db-3QW2XEXA.js.map → db-JSIZYT23.js.map} +0 -0
  112. /package/dist/{digest-75FWSC74.js.map → digest-NQAQI34P.js.map} +0 -0
  113. /package/dist/{github-signals-TF3VSNIN.js.map → github-signals-4LWQGD5V.js.map} +0 -0
  114. /package/dist/{health-endpoint-JZGYCMTV.js.map → health-endpoint-LFVE26QS.js.map} +0 -0
  115. /package/dist/{migrate-35DNNF3H.js.map → migrate-LJUFGXQZ.js.map} +0 -0
  116. /package/dist/{onboard-PX36YDZA.js.map → onboard-RDGHVCNG.js.map} +0 -0
  117. /package/dist/{orchestrate-CPR6KVS2.js.map → orchestrate-QOQXOJLI.js.map} +0 -0
  118. /package/dist/{preflight-QRRMPI7K.js.map → preflight-2PWVVIRR.js.map} +0 -0
  119. /package/dist/{report-SLGBDJ6O.js.map → report-IU4CLJS5.js.map} +0 -0
  120. /package/dist/{selftest-RVASRO3Z.js.map → selftest-PJFABZDM.js.map} +0 -0
  121. /package/dist/{smoke-suite-CSP23NZM.js.map → smoke-suite-SUEJFBYP.js.map} +0 -0
  122. /package/dist/{submissions-BNRGY3JQ.js.map → submissions-SURN7BV5.js.map} +0 -0
  123. /package/dist/{svelte-codemods-V54KOBWU.js.map → svelte-codemods-IWO7X3F3.js.map} +0 -0
  124. /package/dist/{sync-configs-UBCJRASZ.js.map → sync-configs-7XGHFC6P.js.map} +0 -0
  125. /package/dist/{upgrade-WR6ICPAG.js.map → upgrade-AM4S7JF3.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/commands/blux.ts","../src/blux/parse.ts","../src/blux/archetype.ts","../src/blux/normalize.ts","../src/blux/collections.ts","../src/blux/assemble.ts","../src/blux/emit/custom-types.ts","../src/blux/emit/coerce-html.ts","../src/blux/emit/slices.ts","../src/blux/emit/flatten.ts","../src/blux/emit/migration-plan.ts","../src/blux/emit/theme.ts","../src/blux/emit/review.ts","../src/blux/emit/block-styles.ts","../src/blux/validate.ts","../src/blux/grid/parse-grid.ts","../src/blux/grid/token.ts","../src/blux/grid/leaf.ts","../src/blux/grid/classify-band.ts","../src/blux/grid/feed-grid.ts","../src/blux/products.ts","../src/blux/emit/grid-slice.ts","../src/blux/emit/grid-plan.ts","../src/blux/emit/presentation.ts","../src/blux/emit/convert.ts","../src/blux/catalog/spec.ts","../src/blux/catalog/catalog-page-type.ts","../src/blux/catalog/sanitize.ts","../src/blux/catalog/cells.ts","../src/blux/catalog/feeds.ts","../src/blux/catalog/classify.ts","../src/blux/catalog/entities.ts","../src/blux/catalog/rewrite-doc-urls.ts","../src/blux/catalog/emit.ts","../src/blux/catalog/resolve-fixture.ts","../src/blux/emit/site-config.ts","../src/blux/catalog/chrome.ts","../src/blux/emit/validate-layout.ts","../src/blux/emit/rewrite-manifest.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname, basename } from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { assembleIR } from \"../../blux/assemble.js\";\nimport { buildMigrationPlan } from \"../../blux/emit/migration-plan.js\";\nimport { emitThemeCssFile } from \"../../blux/emit/theme.js\";\nimport { buildReviewManifest } from \"../../blux/emit/review.js\";\nimport { blockStylesByIndex, blockClassDefaults } from \"../../blux/emit/block-styles.js\";\nimport { validateCoverage } from \"../../blux/validate.js\";\nimport { parseGridBands, extractMapConfig, makeIsMapMount } from \"../../blux/grid/index.js\";\nimport { feedAssetBase, extFor, isFeedBand } from \"../../blux/grid/feed-grid.js\";\nimport { materializeProducts, type ProductRecord } from \"../../blux/products.js\";\nimport { convertExport, convertSite, sitePages } from \"../../blux/emit/convert.js\";\nimport { bandOrCollection, buildCatalogPlan } from \"../../blux/catalog/index.js\";\nimport type { CatalogSpec } from \"../../blux/catalog/index.js\";\nimport { resolveFixture } from \"../../blux/catalog/resolve-fixture.js\";\nimport { rewriteDocUrls, rewriteValueUrls } from \"../../blux/catalog/rewrite-doc-urls.js\";\nimport { buildChrome } from \"../../blux/catalog/chrome.js\";\nimport { buildSiteConfig, socialHrefResolverFromHtml } from \"../../blux/emit/site-config.js\";\nimport { validateLayout, formatLayoutReport } from \"../../blux/emit/validate-layout.js\";\nimport { rewriteManifestUrls } from \"../../blux/emit/rewrite-manifest.js\";\nimport type { SitePresentation } from \"../../blux/emit/presentation.js\";\nimport { richText, assetRef } from \"../../blux/emit/plan.js\";\nimport type { MigrationPlan, PlanAsset, PlanDocument } from \"../../blux/emit/plan.js\";\nimport { frozenPageCustomType, FROZEN_PAGE_TYPE } from \"../../blux/freeze/frozen-page-type.js\";\nimport type { FrozenManifest } from \"../../blux/freeze/types.js\";\nimport type { Diagnostic } from \"../../blux/ir.js\";\n\nexport type BluxCommandOptions = {\n /** Output directory for emit (default: <exportDir>/blux-out). */\n out?: string;\n /** Base URL of the converted site for the review manifest. */\n convertedBase?: string;\n /** Base URL of the original Blux site (default: https://<site.json domain>). */\n bluxBase?: string;\n /** Reconstruct + HEAD-probe CDN URLs for used assets the HTML scrape missed (network). */\n probe?: boolean;\n /** validate: the converted site's rendered HTML — a file path or http(s) URL. */\n against?: string;\n /** freeze: site slug for the emitted template/manifest (default: slugified dir name). */\n site?: string;\n /** freeze: path to the site's extra-slots declaration (see\n * blux/freeze/types.ts `ExtraSlotsFile`) — editable content the byte-faithful\n * template carries no token for, e.g. a video poster or a panel the render\n * rebuilds from a baked image. Optional; omitted means no extra slots. */\n extraSlots?: string;\n /** Test seam for --probe; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n cwd?: string;\n verbose?: boolean;\n};\n\n/** `blux <action> [dir]` — emit: Blux export dir → migration plan + custom-type\n * schemas + theme CSS + review manifest, all deterministic and offline.\n * migrate: a previously emitted plan → live Prismic (creds-gated; the runner\n * is imported lazily so emit runs never touch @prismicio).\n * migrate-catalog: a catalog plan → live Prismic in TWO phases — assets\n * first (returns the cdn→Prismic url map), rewriteDocUrls over the plan\n * documents, then the rewritten documents (assets hit the reuse branch).\n * validate: offline layout-fidelity gate (parse+classify index.html, diff the\n * emitted manifest vs the source answer key) — exits non-zero on drift.\n * --against <file|url> additionally runs content coverage of a rendered page.\n * grid: parse rendered index.html → grid-tree.json (layout tree).\n * convert: parse+classify index.html + assemble IR from site.json → the grid\n * migration-plan.json (page doc) + blux-presentation.json (render manifest) +\n * theme.css (+ map-config.json when a map is present), all offline, no creds. */\nexport async function runBluxCommand(\n action: string,\n dir: string | undefined,\n opts: BluxCommandOptions,\n): Promise<{ output: string; code: number }> {\n if (action === \"emit\") {\n if (!dir) return { output: \"blux emit needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return {\n output: `could not read site.json in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const out = opts.out ?? join(dir, \"blux-out\");\n const htmlPaths = (\n await glob([\"**/*.html\"], { cwd: dir, absolute: true, ignore: [\"blux-out/**\"] })\n ).sort();\n const htmls = await Promise.all(htmlPaths.map((p) => readFile(p, \"utf-8\")));\n\n const ir = assembleIR({ siteJson, htmls });\n\n let probeLine = \"\";\n if (opts.probe) {\n const { probeAssetUrls } = await import(\"../../blux/emit/probe.js\");\n // derive \"used\" from the plan's own __asset_id markers — the single\n // source of truth for which assets documents actually reference\n const used = new Set<string>();\n const collect = (v: unknown): void => {\n if (!v || typeof v !== \"object\") return;\n if (\"__asset_id\" in v) used.add((v as { __asset_id: string }).__asset_id);\n else if (Array.isArray(v)) v.forEach(collect);\n else Object.values(v).forEach(collect);\n };\n buildMigrationPlan(ir).documents.forEach((d) => collect(d.data));\n\n const targets = ir.assets.filter((a) => used.has(a.id) && !a.sourceUrl);\n const probed = await probeAssetUrls(targets, ir.meta.bluxSiteId, opts.fetchImpl ?? fetch);\n let hits = 0;\n for (const a of ir.assets) {\n const url = probed.get(a.id);\n if (url) {\n a.sourceUrl = url;\n hits++;\n }\n }\n ir.diagnostics = ir.diagnostics.filter(\n (d) => !(d.kind === \"unresolved-asset\" && probed.get(d.where)),\n );\n probeLine = `probe resolved ${hits}/${targets.length} used assets`;\n }\n\n const plan = buildMigrationPlan(ir);\n const manifest = buildReviewManifest(ir, {\n convertedBase: opts.convertedBase ?? \"http://localhost:5173\",\n bluxBase: opts.bluxBase ?? `https://${ir.meta.domain}`,\n });\n\n await mkdir(join(out, \"customtypes\"), { recursive: true });\n await writeFile(join(out, \"ir.json\"), JSON.stringify(ir, null, 2));\n await writeFile(join(out, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n await writeFile(join(out, \"theme.css\"), emitThemeCssFile(ir.theme));\n await writeFile(join(out, \"review-manifest.json\"), JSON.stringify(manifest, null, 2));\n await writeFile(\n join(out, \"styles-manifest.json\"),\n JSON.stringify(plan.stylesManifest, null, 2),\n );\n for (const ct of plan.customTypes) {\n await writeFile(join(out, \"customtypes\", `${ct.id}.json`), JSON.stringify(ct.json, null, 2));\n }\n\n const resolved = ir.assets.filter((a) => a.sourceUrl !== null).length;\n const diagnostics = [...ir.diagnostics, ...plan.diagnostics];\n const lines = [\n `site: ${ir.meta.name} (${ir.meta.domain})`,\n ...(probeLine ? [probeLine] : []),\n `pages: ${ir.pages.length} | custom types: ${plan.customTypes.length} | documents: ${plan.documents.length} | assets: ${resolved}/${ir.assets.length} resolved`,\n `diagnostics: ${diagnostics.length}`,\n ...diagnostics.map((d) => ` - [${d.kind}] ${d.where}: ${d.message}`),\n `wrote ${out}`,\n ];\n return { output: lines.join(\"\\n\"), code: 0 };\n }\n\n if (action === \"migrate\") {\n if (!dir) {\n return {\n output: \"blux migrate needs an emitted output directory (or a plan .json path).\",\n code: 1,\n };\n }\n if (!process.env.PRISMIC_REPOSITORY_NAME || !process.env.PRISMIC_WRITE_TOKEN) {\n return {\n output: \"Set PRISMIC_REPOSITORY_NAME and PRISMIC_WRITE_TOKEN to run a live migration.\",\n code: 1,\n };\n }\n const planPath = dir.endsWith(\".json\") ? dir : join(dir, \"migration-plan.json\");\n const plan = JSON.parse(await readFile(planPath, \"utf-8\")) as MigrationPlan;\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n const pushed = await pushCustomTypes(plan.customTypes);\n // stream progress to stderr — a throttled run over many assets/docs takes\n // minutes and silence reads as a hang; stdout stays the result summary\n const r = await runMigration(plan, (line) => process.stderr.write(`${line}\\n`));\n const missing = r.missingAssets.length\n ? `\\nWARNING missing assets: ${r.missingAssets.join(\", \")}`\n : \"\";\n // Rewrite the render manifest's media urls from the CDN url the export\n // carries → the durable Prismic url we just uploaded to. Skipped silently\n // when no manifest sits beside the plan (e.g. an archetype-only emit).\n let manifestNote = \"\";\n const manifestPath = join(dirname(planPath), \"blux-presentation.json\");\n let manifestRaw: string | null = null;\n try {\n manifestRaw = await readFile(manifestPath, \"utf-8\");\n } catch {\n /* no manifest beside the plan (e.g. archetype-only emit) — nothing to\n rewrite; not an error. A rewrite failure on a manifest that DOES\n exist must surface, not be swallowed (it silently strands the render\n on Blux CDN urls). */\n }\n if (manifestRaw !== null) {\n const manifest = JSON.parse(manifestRaw) as SitePresentation;\n const rewritten = rewriteManifestUrls(manifest, r.assetUrlByCdn);\n await writeFile(manifestPath, JSON.stringify(rewritten, null, 2) + \"\\n\");\n manifestNote = \"\\nmanifest media rewritten to Prismic urls\";\n }\n return {\n output:\n `custom types pushed: ${pushed.join(\", \") || \"none\"}\\n` +\n `assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | ` +\n `documents: ${r.docsCreated} created, ${r.docsUpdated} updated → ` +\n `${process.env.PRISMIC_REPOSITORY_NAME} (publish the migration release in the dashboard)` +\n missing +\n manifestNote,\n code: 0,\n };\n }\n\n if (action === \"migrate-catalog\") {\n if (!dir) return { output: \"blux migrate-catalog needs the catalog --out dir.\", code: 1 };\n let plan: MigrationPlan;\n try {\n plan = JSON.parse(await readFile(join(dir, \"migration-plan.json\"), \"utf-8\")) as MigrationPlan;\n } catch (err) {\n return {\n output: `could not read migration-plan.json in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n // Progress streams to stderr as it happens (a throttled two-phase run over\n // many assets/docs takes minutes and silence reads as a hang) and is\n // collected ONLY for the failure path, where it IS the diagnostic — a\n // success over hundreds of docs must not replay the whole log. Warnings\n // ride warn(): into the success output AND (via log) onto stderr the\n // moment they happen.\n const lines: string[] = [];\n const log = (line: string): void => {\n lines.push(line);\n process.stderr.write(`${line}\\n`);\n };\n const warnings: string[] = [];\n const warn = (line: string): void => {\n warnings.push(line);\n log(line);\n };\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n try {\n const pushed = plan.customTypes.length ? await pushCustomTypes(plan.customTypes) : [];\n // Phase 1: assets only — populates the media library and returns the\n // complete cdn→Prismic url map. runMigration uploads assets first but\n // only returns the map at the end, so a single call can never rewrite\n // documents before posting them; hence the two-phase shape.\n const assetsPass = await runMigration({ ...plan, documents: [] }, log);\n // Rewrite the serialized-string surfaces resolveDocData never touches\n // (BluxBlock payloads, widget_html, embed_html, background wrappers).\n const r = rewriteDocUrls(plan.documents, assetsPass.assetUrlByCdn);\n if (r.unmatched.length) {\n warn(\n `WARNING: ${r.unmatched.length} CDN url(s) survived the rewrite: ${r.unmatched\n .slice(0, 5)\n .join(\", \")}`,\n );\n }\n // Chrome (nav + footer logos) rides site-config.json beside the plan, not\n // in plan.documents — rewrite it with the SAME asset map so it too leaves\n // the CDN. Absent on older/non-catalog plans (ENOENT) — then nothing to do.\n let chromeRewritten = 0;\n const chromeUnmatched: string[] = [];\n const siteConfigPath = join(dir, \"site-config.json\");\n try {\n const parsed: unknown = JSON.parse(await readFile(siteConfigPath, \"utf-8\"));\n const sc = rewriteValueUrls(parsed, assetsPass.assetUrlByCdn);\n await writeFile(siteConfigPath, JSON.stringify(sc.value, null, 2) + \"\\n\");\n chromeRewritten = sc.rewritten;\n chromeUnmatched.push(...sc.unmatched);\n if (sc.unmatched.length) {\n warn(\n `WARNING: ${sc.unmatched.length} chrome CDN url(s) survived the rewrite: ${sc.unmatched\n .slice(0, 5)\n .join(\", \")}`,\n );\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n // Phase 2: documents — every asset re-lists into the by-filename reuse\n // branch (uploaded in phase 1), so nothing re-uploads.\n const docsPass = await runMigration({ ...plan, documents: r.documents }, log);\n if (docsPass.missingAssets.length) {\n warn(`WARNING missing assets: ${docsPass.missingAssets.join(\", \")}`);\n }\n return {\n output: [\n `custom types pushed: ${pushed.join(\", \") || \"none\"}`,\n ...warnings,\n `migrate-catalog: assets ${assetsPass.assetsUploaded} uploaded/${assetsPass.assetsReused} reused; ` +\n `urls rewritten ${r.rewritten} (${r.unmatched.length} unmatched); ` +\n `chrome urls rewritten ${chromeRewritten} (${chromeUnmatched.length} unmatched); ` +\n `docs ${docsPass.docsCreated} created/${docsPass.docsUpdated} updated ` +\n `(publish the migration release in the dashboard)`,\n ].join(\"\\n\"),\n // No warning blocks phase 2 — the docs land in an UNPUBLISHED migration\n // release, so a non-zero exit reaches the operator before anything goes\n // live.\n code: r.unmatched.length || chromeUnmatched.length || docsPass.missingAssets.length ? 1 : 0,\n };\n } catch (err) {\n return {\n output: [...lines, `migrate-catalog failed: ${(err as Error).message}`].join(\"\\n\"),\n code: 1,\n };\n }\n }\n\n if (action === \"validate\") {\n if (!dir) return { output: \"blux validate needs a Blux export directory.\", code: 1 };\n let exportHtml: string;\n try {\n exportHtml = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return { output: `could not read index.html in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n // Resolve the optional --against render FIRST so a bad target hard-fails\n // before we spend the convert pipeline (and so its error message wins).\n let rendered: string | null = null;\n if (opts.against) {\n try {\n if (/^https?:\\/\\//.test(opts.against)) {\n const res = await (opts.fetchImpl ?? fetch)(opts.against);\n if (!res.ok) {\n return {\n output: `could not fetch --against ${opts.against}: HTTP ${res.status}`,\n code: 1,\n };\n }\n rendered = await res.text();\n } else {\n rendered = await readFile(opts.against, \"utf-8\");\n }\n } catch (err) {\n return {\n output: `could not read --against ${opts.against}: ${(err as Error).message}`,\n code: 1,\n };\n }\n }\n\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read site.json in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n const { specs, presentation } = convertExport({ html: exportHtml, siteJson });\n const layout = validateLayout(specs, presentation);\n const lines = [formatLayoutReport(layout)];\n\n // Content coverage is informational only — it names export text the render\n // dropped, but layout fidelity alone gates the exit code below. A coverage\n // gap never flips a faithful layout to a non-zero exit.\n if (rendered !== null) {\n const report = validateCoverage(exportHtml, rendered);\n lines.push(\n \"\",\n `content coverage: ${report.covered}/${report.total} runs (${report.coveragePct}%)`,\n ...(report.missing.length\n ? [\n \"missing runs — export text absent from the render:\",\n ...report.missing.map((m) => ` - ${m}`),\n ]\n : [\"all export text runs present in the render\"]),\n );\n }\n\n return { output: lines.join(\"\\n\"), code: layout.faithful ? 0 : 1 };\n }\n\n if (action === \"grid\") {\n if (!dir) return { output: \"blux grid needs a Blux export directory.\", code: 1 };\n let html: string;\n try {\n html = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return {\n output: `could not read index.html in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const bands = parseGridBands(html);\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"grid-tree.json\"), JSON.stringify(bands, null, 2));\n const mapConfig = extractMapConfig(html);\n if (mapConfig) {\n await writeFile(\n join(outDir, \"map-config.json\"),\n JSON.stringify(mapConfig, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n return {\n output:\n `Parsed ${bands.length} bands → ${join(outDir, \"grid-tree.json\")}` +\n (mapConfig ? \", map config extracted\" : \"\"),\n code: 0,\n };\n }\n\n if (action === \"convert\") {\n if (!dir) return { output: \"blux convert needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read export in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n // Every site page renders to its own index.html: the homepage at the\n // export root, the rest at <path>/index.html. A page dir the export\n // doesn't contain (an unexported draft) is skipped — convertSite records\n // the missing-page-html diagnostic.\n const htmlByUid = new Map<string, string>();\n for (const p of sitePages(siteJson)) {\n const file = p.path ? join(dir, p.path, \"index.html\") : join(dir, \"index.html\");\n try {\n htmlByUid.set(p.uid, await readFile(file, \"utf-8\"));\n } catch {\n /* missing page dir — diagnosed by convertSite */\n }\n }\n if (!htmlByUid.size) {\n return { output: `could not read any page html in ${dir}`, code: 1 };\n }\n const { pages, ir, plan, presentation } = convertSite({ siteJson, htmlByUid });\n\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n await writeFile(\n join(outDir, \"blux-presentation.json\"),\n JSON.stringify(presentation, null, 2) + \"\\n\",\n );\n // theme.css via the single-source-of-truth assembler — this convert output\n // now carries the runtime :root mirror too (was missing it before).\n await writeFile(join(outDir, \"theme.css\"), emitThemeCssFile(ir.theme));\n // Map configs are per page now (informational — the presentation manifest\n // co-locates each map on its band).\n const mapConfigs = Object.fromEntries(\n pages.filter((p) => p.mapConfig).map((p) => [p.uid, p.mapConfig]),\n );\n if (Object.keys(mapConfigs).length) {\n await writeFile(join(outDir, \"map-config.json\"), JSON.stringify(mapConfigs, null, 2) + \"\\n\");\n }\n // Site chrome (nav dropdowns + footer socials/copyright) → site-config.json,\n // consumed by the render's Nav/Footer. The nav logo is chrome, not on any\n // page grid, so it isn't in the scraped urlMap — resolve it the scraped url\n // first, else reconstruct the CDN url (base + uuid + ext, like feed media).\n {\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaDict = (siteJson as { media?: Record<string, { type?: string }> }).media ?? {};\n const extByMime: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n };\n const resolveLogo = (uuid: string): string | null => {\n const scraped = sourceUrlById.get(uuid);\n if (scraped) return scraped;\n const ext = extByMime[String(mediaDict[uuid]?.type ?? \"\")];\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n // Footer social profile urls aren't in the export (Blux injects them at\n // render time), but they ride the scraped live footer — recover them by\n // host from the same page html the grid was built from.\n const resolveSocialHref = socialHrefResolverFromHtml([...htmlByUid.values()]);\n const siteConfig = buildSiteConfig(siteJson, resolveLogo, resolveSocialHref);\n await writeFile(join(outDir, \"site-config.json\"), JSON.stringify(siteConfig, null, 2) + \"\\n\");\n }\n // Product catalog → products.json. A Blux \"products\" feed drives a detail\n // page per record (/products/<slug>) from a template the static export\n // drops; rebuild the catalog deterministically with cleaned categories +\n // resolved images. Image urls reconstruct like feed media (base + uuid +\n // ext) — the scraped url map only covers page assets, not feed records.\n {\n const feeds =\n (siteJson as { feeds?: Record<string, { publish?: unknown; items?: unknown }> }).feeds ??\n {};\n const productFeed = Object.values(feeds).find((f) => f?.publish === \"products\");\n if (productFeed && Array.isArray(productFeed.items)) {\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaMeta =\n (siteJson as { media?: Record<string, { type?: string; name?: string }> }).media ?? {};\n const resolveImage = (uuid: string): string | null => {\n const ext = extFor(mediaMeta[uuid]?.type, mediaMeta[uuid]?.name);\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n const products = materializeProducts(productFeed.items as ProductRecord[], resolveImage);\n await writeFile(join(outDir, \"products.json\"), JSON.stringify(products, null, 2) + \"\\n\");\n }\n }\n // The favicon never rides the migration plan (plan assets get uploaded to\n // Prismic media — the wrong destination), so convert downloads it directly\n // beside the other outputs. This is convert's ONLY network touch and it\n // uses the same injectable fetch seam as --probe, so tests stay offline\n // and convertExport itself stays pure. A fetch failure never fails the\n // command — the {assetId, url} pair is preserved as favicon.json so the\n // download can be re-run by hand.\n let faviconLine = \"\";\n if (ir.meta.favicon?.sourceUrl) {\n const { assetId, sourceUrl } = ir.meta.favicon;\n try {\n const res = await (opts.fetchImpl ?? fetch)(sourceUrl);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n await writeFile(join(outDir, \"favicon.png\"), new Uint8Array(await res.arrayBuffer()));\n faviconLine = `\\nfavicon → ${join(outDir, \"favicon.png\")}`;\n } catch (err) {\n await writeFile(\n join(outDir, \"favicon.json\"),\n JSON.stringify({ assetId, url: sourceUrl }, null, 2) + \"\\n\",\n );\n faviconLine =\n `\\nfavicon fetch failed (${(err as Error).message}) — ` +\n `url preserved in ${join(outDir, \"favicon.json\")}`;\n }\n }\n const layoutByUid: Record<string, ReturnType<typeof validateLayout>> = {};\n const reportLines: string[] = [];\n for (const p of pages) {\n const pagePresentation = presentation.pages[p.uid];\n if (!pagePresentation) continue;\n const layout = validateLayout(p.specs, pagePresentation);\n layoutByUid[p.uid] = layout;\n reportLines.push(`[${p.uid}] ${formatLayoutReport(layout)}`);\n }\n await writeFile(\n join(outDir, \"layout-report.json\"),\n JSON.stringify({ pages: layoutByUid }, null, 2) + \"\\n\",\n );\n const totalBands = pages.reduce((n, p) => n + p.bands.length, 0);\n const missing = ir.diagnostics.filter((d) => d.kind === \"missing-page-html\");\n return {\n output:\n `Converted ${pages.length} pages / ${totalBands} bands → ${outDir} ` +\n `(${plan.documents.length} page documents` +\n (Object.keys(mapConfigs).length\n ? `, map config on ${Object.keys(mapConfigs).join(\", \")}`\n : \"\") +\n \")\" +\n (missing.length ? `\\nskipped (no html): ${missing.map((d) => d.where).join(\", \")}` : \"\") +\n faviconLine +\n \"\\n\" +\n reportLines.join(\"\\n\"),\n code: 0,\n };\n }\n\n if (action === \"catalog\") {\n if (!dir) return { output: \"blux catalog needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read export in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n // Read every site page's index.html (homepage at the export root, the rest\n // at <path>/index.html) exactly like convert, then route every band through\n // the breadth classifier (classifyBand → rich CatalogSpec) and emit a\n // plan-only, sidecar-free migration plan (full field data in the page doc).\n // Feed bands intercept FIRST (spec §7 rule 1): the positional convert-path\n // join `content.pages[p].items[band.index]` names the band's site.json\n // item; one with `sources[]` becomes a blux_collection query-spec slice.\n const feeds =\n (\n siteJson as {\n feeds?: Record<\n string,\n { name?: string; items?: unknown[]; fields?: unknown } | undefined\n >;\n }\n ).feeds ?? {};\n // Per-page content also carries the SEO fields (title lives on sitePages;\n // `description` + the social/og image `media` ride here) — same positional\n // `content.pages[pageIndex]` join the items use, so they thread through\n // without touching the parse/normalize layer.\n const pageContentByIndex = (\n siteJson as {\n content?: {\n pages?: { items?: unknown[]; description?: unknown; media?: { media?: unknown } }[];\n };\n }\n )?.content?.pages;\n // Classify-time diagnostics (positional-join misalignments, unknown/\n // skipped feed sources) ride the plan alongside emit-time ones.\n const classifyDiagnostics: Diagnostic[] = [];\n const pages: {\n uid: string;\n title: string;\n specs: CatalogSpec[];\n description?: string;\n socialImageId?: string;\n }[] = [];\n // Every successfully-read page html feeds the IR assembly below: the asset\n // index is scraped from the rendered pages (site.json's media dict has no\n // urls), so a media without a CDN data-base can still resolve.\n const htmls: string[] = [];\n // Band-visual capture (Task 9): the `.blocksNcontainer` class defaults are\n // site-wide (compute once); per-band inline styles are per page.\n const blockDefaults = blockClassDefaults(siteJson);\n for (const [pageIndex, p] of sitePages(siteJson).entries()) {\n const file = p.path ? join(dir, p.path, \"index.html\") : join(dir, \"index.html\");\n let html: string;\n try {\n html = await readFile(file, \"utf-8\");\n } catch {\n continue; // missing page dir (unexported draft) — skip\n }\n htmls.push(html);\n // Decision B (plan 4b): when the page html carries the Blux map script,\n // inject the mount predicate so the map band routes to a BluxSection\n // widget (config inlined at emit); pages without one classify as before.\n const mapConfig = extractMapConfig(html);\n const blockStyles = blockStylesByIndex(siteJson, pageIndex);\n const catalogOpts = mapConfig\n ? {\n isMapMount: makeIsMapMount(mapConfig),\n mapConfig,\n diagnostics: classifyDiagnostics,\n pageUid: p.uid,\n styles: blockStyles,\n defaults: blockDefaults,\n }\n : {\n diagnostics: classifyDiagnostics,\n pageUid: p.uid,\n styles: blockStyles,\n defaults: blockDefaults,\n };\n const pageContent = pageContentByIndex?.[pageIndex];\n const pageItems = pageContent?.items;\n const bands = parseGridBands(html);\n const specs: CatalogSpec[] = bands.map((b) =>\n bandOrCollection(b, pageItems?.[b.index], feeds, catalogOpts),\n );\n // The positional join runs band→item; a feed-bearing item whose index\n // has NO band (williamsonHomes homepage: items[1] sources Projects but\n // parseGridBands yields 0,2-8) is never consumed by the loop above —\n // diagnose it, never silent. Diagnostic-only: with no band there is no\n // legitimate place to emit the collection.\n const bandIdx = new Set(bands.map((b) => b.index));\n for (const [i, it] of (pageItems ?? []).entries()) {\n if (!isFeedBand(it) || bandIdx.has(i)) continue;\n const firstSource = String(it.sources[0]);\n const feedName = String(feeds[firstSource]?.name ?? firstSource);\n classifyDiagnostics.push({\n kind: \"feed-band-misalign\",\n where: `${p.uid}:${i}`,\n message: `feed item at index ${i} (\"${feedName}\") has no matching band — collection not emitted`,\n });\n }\n // Per-page SEO (Blux `content.pages[i]`): meta description + the social/\n // og image asset uuid (a nested `media.media` string, like feed record\n // media). Both optional — buildCatalogPlan omits the meta field when absent\n // (\"\" description → no meta_description).\n const description =\n typeof pageContent?.description === \"string\" ? pageContent.description : undefined;\n const socialMedia = pageContent?.media?.media;\n const socialImageId =\n typeof socialMedia === \"string\" && socialMedia ? socialMedia : undefined;\n pages.push({\n uid: p.uid,\n title: p.title,\n specs,\n ...(description ? { description } : {}),\n ...(socialImageId ? { socialImageId } : {}),\n });\n }\n if (!pages.length) {\n return { output: `could not read any page html in ${dir}`, code: 1 };\n }\n // The real IR asset index (Plan 4d Task 1): assembleIR scrapes the CDN\n // urls out of the page htmls, so a media without a CDN data-base falls\n // back to its AssetRef.sourceUrl instead of an unresolved-asset\n // diagnostic. IR diagnostics (unresolved assets, page normalization)\n // merge with the classify-time set — both ride the written plan. The\n // `url` field is unused by buildCatalogPlan (it derives urls via\n // mediaUrl) but the CatalogAssetIndex type requires it.\n const ir = assembleIR({ siteJson, htmls });\n const plan = buildCatalogPlan(\n pages,\n {\n assets: ir.assets.map((a) => ({\n id: a.id,\n url: a.sourceUrl ?? \"\",\n alt: a.alt,\n sourceUrl: a.sourceUrl,\n })),\n diagnostics: [...classifyDiagnostics, ...ir.diagnostics],\n },\n feeds,\n );\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n // Site chrome (Task 4): nav via the frozen buildSiteConfig extraction,\n // footer with FULL columns (the-pointe's leasing contacts with tel/mailto\n // links — site-config's socials+text reduction is too thin). Chrome media\n // (nav/footer logos) resolve the plan's own asset url first — a media the\n // page grid also uses keeps its parser-captured base (e.g. a shared\n // parent-site siteID), so tier-1-first avoids a wrong-siteID tier-3\n // reconstruction. Then the IR's scraped sourceUrl, else reconstruct the\n // CDN url (base + uuid + ext) — static exports download chrome images\n // locally, so the html scrape routinely misses them (the convert action's\n // resolveLogo fallback).\n //\n // The Blux CDN is being retired, so a chrome-ONLY logo (one the page grid\n // never uses, hence absent from plan.assets) is registered here as its own\n // plan asset: it uploads in migrate-catalog's asset phase, and that action's\n // site-config.json rewrite swaps its CDN url to the Prismic one. A logo the\n // grid also uses is already a plan asset (the tier-1 branch), so we never\n // double-register it. Chrome is built BEFORE the plan is written so these\n // assets ride the emitted migration-plan.json.\n //\n // We register a scraped (tier-2) cloudfront sourceUrl — a real url from the\n // export html — AND a tier-3 RECONSTRUCTION (`${chromeBase}${uuid}.${ext}`)\n // WHEN chromeBase is CORROBORATED by a real IMAGE served from it. the-pointe's\n // own nav/footer logos are exactly this case: referenced by id, unscraped,\n // served from the site's base alongside 45 feed images. Two things make a\n // corroborated reconstruction about as reliable as one of those feed images:\n // (1) the base is proven to resolve (an image already loads from it), and\n // (2) the ext is the logo's LITERAL filename ext — the byte Blux serves, the\n // same way a feed image uses its literal data-ext (NOT a mime-normalized\n // guess, which would turn a `.jpeg` object into a 404-ing `.jpg`). The\n // residual risk is only a referenced-but-deleted own asset — a loud, accepted\n // 404 (runMigration throws), the same failure any feed image would hit if\n // deleted. Corroboration requires an IMAGE (not just any plan.asset): a\n // backstop file asset (a PDF, often on a different host) is not evidence a\n // logo resolves, and `siteID` is provenance not serving base — so a served\n // image is the only sound signal. An UNcorroborated reconstruction is a pure\n // guess: left on the CDN, flagged by migrate-catalog's site-config rewrite as\n // an unmatched url (WARNING + non-zero exit) for manual follow-up — never\n // silent, never a mid-migrate abort.\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const planUrlById = new Map(plan.assets.map((a) => [a.id, a.url] as const));\n const chromeBase = feedAssetBase(htmls, ir.meta.bluxSiteId);\n const isImageUrl = (u: string) => /\\.(png|jpe?g|gif|webp|svg|avif|ico)$/i.test(u);\n const chromeBaseCorroborated = plan.assets.some(\n (a) => a.url.startsWith(chromeBase) && isImageUrl(a.url),\n );\n const chromeOnlyAssets = new Map<string, { id: string; url: string; alt: string }>();\n const chrome = buildChrome(siteJson, (uuid) => {\n const fromPlan = planUrlById.get(uuid);\n if (fromPlan !== undefined) return fromPlan;\n const a = assetById.get(uuid);\n if (!a) return null;\n if (a.sourceUrl) {\n // tier-2: a real scraped url. Evict only a Blux CDN one — an external\n // (non-cloudfront) logo url needs no migration.\n if (a.sourceUrl.includes(\"cloudfront.net\") && !chromeOnlyAssets.has(uuid))\n chromeOnlyAssets.set(uuid, { id: uuid, url: a.sourceUrl, alt: a.alt ?? \"\" });\n return a.sourceUrl;\n }\n // tier-3: reconstruct against the site's own base with the LITERAL filename\n // ext (feed-image-equivalent — see note above), mime-derived only as a\n // fallback. Register it only when that base is corroborated by an image.\n const ext = a.name.match(/\\.([a-z0-9]{2,4})$/i)?.[1]?.toLowerCase() ?? extFor(a.mime, a.name);\n if (!ext) return null;\n const url = `${chromeBase}${uuid}.${ext}`;\n if (chromeBaseCorroborated && !chromeOnlyAssets.has(uuid))\n chromeOnlyAssets.set(uuid, { id: uuid, url, alt: a.alt ?? \"\" });\n return url;\n });\n // These chrome logos join the plan so migrate-catalog uploads them and its\n // site-config.json rewrite can swap their urls off the retiring CDN.\n plan.assets.push(...chromeOnlyAssets.values());\n await writeFile(join(outDir, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n // Task 5: an OFFLINE render fixture for the starter's fidelity-gate route\n // (Task 8). resolveFixture resolves the plan's markers into the Prismic-\n // HYDRATED shapes the production SliceZone consumes (richtext → node arrays,\n // asset → {url,alt,dimensions}) — the live migrate path resolves the same\n // markers into Migration API shapes instead. It runs on the plan BEFORE any\n // migrate url-rewrite, so image urls are still the Blux CDN (correct for the\n // offline gate, which allowlists CDN 404s).\n await writeFile(\n join(outDir, \"render-fixture.json\"),\n JSON.stringify(resolveFixture(plan), null, 2),\n );\n await writeFile(join(outDir, \"site-config.json\"), JSON.stringify(chrome, null, 2) + \"\\n\");\n // theme.css: the single-source-of-truth theme file (build @theme + runtime\n // :root mirror + roles + buttons) — identical across emit/catalog/convert.\n await writeFile(join(outDir, \"theme.css\"), emitThemeCssFile(ir.theme));\n const totalBands = pages.reduce((n, p) => n + p.specs.length, 0);\n return {\n output:\n `Cataloged ${pages.length} pages / ${totalBands} bands → ` +\n `${join(outDir, \"migration-plan.json\")} (${plan.documents.length} page documents)`,\n code: 0,\n };\n }\n\n if (action === \"freeze\") {\n if (!dir) return { output: \"blux freeze needs a Blux export directory.\", code: 1 };\n const { freezeSite, emitFrozen } = await import(\"../../blux/freeze/index.js\");\n const { emitFavicon } = await import(\"../../blux/freeze/favicon.js\");\n const site = (opts.site ?? basename(dir)).replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n const outDir = opts.out ?? join(dir, \"frozen-out\");\n // A malformed declaration must stop the freeze, not ship: these become live\n // CMS fields. Both the read and the parse are deliberately un-caught.\n let extraSlots: unknown;\n if (opts.extraSlots) {\n extraSlots = JSON.parse(await readFile(opts.extraSlots, \"utf-8\"));\n }\n const result = await freezeSite({\n indexHtmlPath: join(dir, \"index.html\"),\n site,\n ...(extraSlots === undefined ? {} : { extraSlots }),\n });\n const paths = await emitFrozen(outDir, result);\n // Fail-soft: a freeze without a favicon is still a good freeze (the icon\n // can be re-fetched while the CDN lives; see favicon.ts).\n let favicon: string;\n try {\n const exportHtml = await readFile(join(dir, \"index.html\"), \"utf-8\");\n const fav = await emitFavicon(exportHtml, dir, outDir);\n favicon = fav ? `, favicon.png` : \"\";\n } catch (err) {\n favicon = ` (favicon FAILED: ${(err as Error).message})`;\n }\n const imgs = result.manifest.slots.filter((s) => s.kind === \"image\").length;\n const txts = result.manifest.slots.filter((s) => s.kind === \"text\").length;\n const map = result.mapConfig ? `, map (${result.mapConfig.layers.length} layers)` : \"\";\n return {\n output:\n `froze ${site}: ${imgs} image slots, ${txts} text slots, ` +\n `${result.manifest.fontLinks.length} font links${map} → ` +\n `${paths.template}, ${paths.manifest}${favicon}`,\n code: 0,\n };\n }\n\n if (action === \"migrate-frozen\") {\n if (!dir) return { output: \"blux migrate-frozen needs the frozen --out dir.\", code: 1 };\n // The freeze artifact `<site>.slots.json` lives in the --out dir (opts.out ??\n // dir; see the `freeze` action). --site pins it; otherwise the sole\n // *.slots.json in the dir (freeze emits exactly one per site).\n const outDir = opts.out ?? dir;\n let manifestPath: string;\n if (opts.site) {\n const site = opts.site.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n manifestPath = join(outDir, `${site}.slots.json`);\n } else {\n const found = (await glob([\"*.slots.json\"], { cwd: outDir, absolute: true })).sort();\n if (!found.length)\n return { output: `no *.slots.json in ${outDir} — run 'blux freeze' first.`, code: 1 };\n manifestPath = found[0]!;\n }\n let manifest: FrozenManifest;\n try {\n manifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as FrozenManifest;\n } catch (err) {\n return { output: `could not read ${manifestPath}: ${(err as Error).message}`, code: 1 };\n }\n\n // Build the migration plan from the manifest. Image slots (+ the optional\n // meta image) become plan ASSETS keyed by a stable synthetic id; the\n // frozen_page doc references each via an `assetRef` marker — the SAME\n // Image-field representation catalog docs use (resolveDocData turns\n // `{__asset_id}` into `{id}`, the depth-1-safe Migration API shape; the\n // slots group is a top-level repeatable group, so this resolves cleanly).\n // Text slots become single-paragraph Rich Text markers. The freeze stores a\n // text node's RAW source (entities already encoded, e.g. \"&amp;\"/\"&nbsp;\",\n // never a bare \"<\"), so it is a valid HTML fragment — pass it straight into\n // the <p> for htmlAsRichText. Re-escaping would double-encode (\"&amp;\" →\n // \"&amp;amp;\" → literal \"&amp;\" on the page). Assets dedupe by url so an\n // image reused across slots uploads once.\n const assetIdByUrl = new Map<string, string>();\n const assets: PlanAsset[] = [];\n const assetFor = (url: string): string => {\n let id = assetIdByUrl.get(url);\n if (!id) {\n id = `frozen-asset-${assets.length}`;\n assetIdByUrl.set(url, id);\n assets.push({ id, url, alt: \"\" });\n }\n return id;\n };\n // An Image field only accepts image assets; non-image media (video/pdf) must\n // ride a Text `media_url` instead. Register + upload every media asset the\n // same way, but flag the non-image ones to patch after phase 1 (once their\n // Prismic url is known).\n const isImageUrl = (u: string): boolean => /\\.(jpe?g|png|gif|webp|svg|avif)(\\?|$)/i.test(u);\n const mediaSlots = new Map<string, string>(); // non-image slot key -> cdn url\n const slotRows: Record<string, unknown>[] = manifest.slots.map((s) => {\n if (s.kind === \"image\" && s.url) {\n if (!isImageUrl(s.url)) mediaSlots.set(s.key, s.url);\n return { key: s.key, kind: s.kind, image: assetRef(assetFor(s.url)) };\n }\n return { key: s.key, kind: s.kind, text: richText(`<p>${s.text ?? \"\"}</p>`) };\n });\n const data: Record<string, unknown> = {\n title: manifest.title,\n meta_title: manifest.metaTitle ?? \"\",\n meta_description: \"\",\n slots: slotRows,\n };\n if (manifest.metaImageUrl) data.meta_image = assetRef(assetFor(manifest.metaImageUrl));\n const doc: PlanDocument = { type: FROZEN_PAGE_TYPE, uid: manifest.uid, data };\n const plan: MigrationPlan = {\n customTypes: [frozenPageCustomType()],\n documents: [doc],\n assets,\n stylesManifest: [],\n diagnostics: [],\n };\n const imageSlots = manifest.slots.filter((s) => s.kind === \"image\" && s.url).length;\n\n // Progress streams to stderr (a throttled two-phase run reads as a hang\n // otherwise); collected only for the failure path where it IS the diagnostic.\n const lines: string[] = [];\n const log = (line: string): void => {\n lines.push(line);\n process.stderr.write(`${line}\\n`);\n };\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n try {\n const pushed = await pushCustomTypes(plan.customTypes);\n // Phase 1: assets only — populates the media library and returns the\n // cdn→Prismic url map (mirrors migrate-catalog's two-phase shape).\n const assetsPass = await runMigration({ ...plan, documents: [] }, log);\n // Every image slot's CDN url must resolve to a Prismic asset — surface any\n // that didn't rather than silently shipping a doc with a dead reference.\n const unresolved = assets.filter((a) => !assetsPass.assetUrlByCdn.has(a.url));\n if (unresolved.length) {\n return {\n output:\n `frozen migrate aborted: ${unresolved.length} image url(s) did not resolve to a ` +\n `Prismic asset (first: ${unresolved[0]!.url}).`,\n code: 1,\n };\n }\n // Non-image media (video/pdf) can't sit in an Image field — now that\n // phase 1 has resolved their Prismic urls, swap those slots off the image\n // assetRef and onto a `media_url` text field before posting the doc.\n for (const row of slotRows) {\n const cdn = mediaSlots.get(row.key as string);\n if (cdn) {\n delete row.image;\n row.media_url = assetsPass.assetUrlByCdn.get(cdn) ?? \"\";\n }\n }\n // Phase 2: the doc. Assets re-list into the reuse branch (uploaded in phase\n // 1), and resolveDocData rewrites each `assetRef` marker to `{id}` — the\n // uploaded Prismic asset, never the CDN url. Lands in an UNPUBLISHED\n // migration release (the runner never publishes).\n const docsPass = await runMigration(plan, log);\n if (docsPass.missingAssets.length) {\n return {\n output:\n `frozen migrate aborted: ${docsPass.missingAssets.length} asset ref(s) went ` +\n `unresolved in the doc (first: ${docsPass.missingAssets[0]}).`,\n code: 1,\n };\n }\n const docWord = docsPass.docsUpdated ? \"updated\" : \"created\";\n return {\n output:\n `frozen_page pushed: ${pushed.join(\", \") || \"none\"}; ` +\n `assets ${assetsPass.assetsUploaded} uploaded/${assetsPass.assetsReused} reused; ` +\n `${imageSlots} image slot(s) rewritten to Prismic; ` +\n `doc ${manifest.uid} ${docWord} → ${process.env.PRISMIC_REPOSITORY_NAME} ` +\n `(publish the migration release in the dashboard)`,\n code: 0,\n };\n } catch (err) {\n return {\n output: `frozen migrate failed: ${(err as Error).message}\\n${lines.join(\"\\n\")}`,\n code: 1,\n };\n }\n }\n\n return {\n output: `unknown blux action '${action}'. Use: emit, migrate, migrate-catalog, migrate-frozen, validate, grid, convert, catalog, freeze.`,\n code: 1,\n };\n}\n","/** Style config for a block's title/body. `class: \"disable\"` (or the bare\n * string \"disable\") hides that element on the rendered site. */\nexport type BluxTextStyle = string | { class?: string; [key: string]: unknown };\n\nexport type BluxBlock = {\n title?: string;\n _title?: BluxTextStyle;\n body?: string;\n _body?: BluxTextStyle;\n media?: { media?: string };\n backgroundMedia?: { media?: string };\n class?: string;\n ratio?: string;\n loadEffect?: string;\n items?: BluxBlock[];\n styles?: Record<string, unknown>;\n};\n\nconst hasDisable = (cls: unknown) =>\n typeof cls === \"string\" && cls.split(/\\s+/).includes(\"disable\");\n\n/** Display text of a Blux title/body pair, or undefined when the element is\n * hidden. Blux stores the text itself in `title`/`body`; the underscore twin\n * (`_title`/`_body`) is style config whose `class: \"disable\"` hides the\n * element, so its text must not be migrated. */\nexport function visibleText(text: unknown, style: BluxTextStyle | undefined): string | undefined {\n const s = typeof text === \"number\" && Number.isFinite(text) ? String(text) : text;\n if (typeof s !== \"string\" || s.trim() === \"\") return undefined;\n if (hasDisable(style)) return undefined;\n if (typeof style === \"object\" && style !== null && hasDisable(style.class)) return undefined;\n return s.trim();\n}\nexport type BluxPage = {\n title?: string;\n description?: string;\n /** The page's routing slug. Often empty/absent — the slug then derives from\n * the title. The FIRST page is the homepage regardless (Blux is positional). */\n url?: string;\n items?: BluxBlock[];\n};\nexport type BluxFeed = {\n name?: string;\n source?: string;\n publish?: string;\n fields?: { title?: string; field?: string; type?: string }[];\n items?: Record<string, unknown>[];\n};\nexport type BluxMedia = { name?: string; type?: string; size?: unknown; siteID?: string };\nexport type BluxRaw = {\n meta: { name: string; domain: string; bluxSiteId: string };\n pages: BluxPage[];\n feeds: Record<string, BluxFeed>;\n media: Record<string, BluxMedia>;\n styles: {\n colors?: Record<string, string>;\n text?: Record<string, unknown>;\n buttons?: Record<string, unknown>;\n };\n nav: { title?: string; url?: string }[];\n settings: {\n fonts?: { heading?: string; body?: string };\n /** The site favicon, declared as a bare media uuid ({\"media\": \"<uuid>\"}). */\n favicon?: { media?: string };\n };\n};\n\nfunction asObject(v: unknown): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) {\n throw new Error(\"Invalid site.json: expected an object\");\n }\n return v as Record<string, unknown>;\n}\n\nexport function parseBluxSite(input: unknown): BluxRaw {\n const j = asObject(input);\n const content = (j.content ?? {}) as { pages?: BluxPage[] };\n const styles = (j.styles ?? {}) as BluxRaw[\"styles\"];\n const nav = ((j.navigation as { items?: unknown }[] | undefined)?.[0]?.items ?? []) as {\n title?: string;\n url?: string;\n }[];\n return {\n meta: {\n name: String(j.name ?? \"\"),\n domain: String(j.domain ?? \"\"),\n bluxSiteId: String(j.id ?? \"\"),\n },\n pages: Array.isArray(content.pages) ? content.pages : [],\n feeds: (j.feeds ?? {}) as Record<string, BluxFeed>,\n media: (j.media ?? {}) as Record<string, BluxMedia>,\n styles,\n nav,\n settings: (j.settings ?? {}) as BluxRaw[\"settings\"],\n };\n}\n","import { visibleText, type BluxBlock } from \"./parse.js\";\n\nconst nonEmpty = (v: unknown): boolean =>\n v != null &&\n v !== \"\" &&\n !(Array.isArray(v) && v.length === 0) &&\n !(typeof v === \"object\" && !Array.isArray(v) && Object.keys(v as object).length === 0);\n\nexport type ArchetypeResult = {\n sliceType: \"hero\" | \"media_text\" | \"rich_text\" | \"grid\" | \"slider\" | \"collection_list\";\n variation: string;\n confidence: number;\n};\n\nexport function archetype(b: BluxBlock): ArchetypeResult {\n const heading = visibleText(b.title, b._title) !== undefined;\n const text = visibleText(b.body, b._body) !== undefined;\n const media = nonEmpty(b.media?.media);\n const bg = nonEmpty(b.backgroundMedia?.media);\n const kids = Array.isArray(b.items) && b.items.length > 0;\n const cls = nonEmpty(b.class) ? String(b.class) : null;\n\n // Slides keep their grouping even under a background — exploding a\n // carousel into siblings loses more than dropping its backdrop does.\n if (kids && cls === \"slides\")\n return { sliceType: \"slider\", variation: \"default\", confidence: 0.85 };\n // A background image/video makes a hero even with no visible copy — Blux\n // uses text-less full-bleed banners (e.g. a hero video with a disabled label).\n if (bg)\n return { sliceType: \"hero\", variation: \"default\", confidence: heading || text ? 0.9 : 0.7 };\n if (kids)\n return { sliceType: \"grid\", variation: \"default\", confidence: cls === \"grid\" ? 0.9 : 0.7 };\n // Any visible copy next to media is a media_text; media alone still is,\n // just with less certainty.\n if (media && heading && text)\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.9 };\n if (media && (heading || text))\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.75 };\n if (media) return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.6 };\n if (heading && text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.85 };\n if (heading || text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.6 };\n return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.2 };\n}\n","import { visibleText, type BluxBlock, type BluxRaw, type BluxTextStyle } from \"./parse.js\";\nimport { archetype } from \"./archetype.js\";\nimport type {\n ButtonStyleIR,\n FontLoad,\n PageIR,\n SectionIR,\n TextStyleIR,\n ThemeIR,\n Diagnostic,\n} from \"./ir.js\";\n\n/** The styles.text role (\"text5\") a block's _title/_body class points at. */\nfunction textRole(style: BluxTextStyle | undefined): string | undefined {\n const cls = typeof style === \"object\" && style !== null ? style.class : style;\n if (typeof cls !== \"string\") return undefined;\n return cls.split(/\\s+/).find((c) => /^text\\d+$/.test(c));\n}\n\nconst str = (x: unknown): string =>\n typeof x === \"string\" ? x : typeof x === \"number\" ? String(x) : \"\";\n\n/** A cleaned CSS value, or \"\" when the export left a malformed one. Blux emits\n * degenerate placeholders — \"\" and \"px\" for unset lengths, \"0.px\" for a zeroed\n * one — which would poison a Tailwind custom property (an invalid `var()` value\n * collapses the whole declaration). Those are always single tokens, so the\n * numeric-prefix guard runs only on a lone length; multi-value shorthands\n * (\"10px 40px 10px 40px\"), colors, \"0\", and keywords all pass through. */\nexport function cleanCssValue(x: unknown): string {\n const s = str(x).trim();\n if (s === \"\" || s === \"px\") return \"\";\n if (!/\\s/.test(s) && /px$/.test(s) && !/^-?(\\d+(\\.\\d+)?|\\.\\d+)px$/.test(s)) return \"\";\n return s;\n}\n\n/** Per-element inline style overrides on a _title/_body element (color,\n * font-size, margin, …) minus the `class` token, cleaned. undefined when none\n * survive — e.g. a hero title's `{ class: \"text0\", color: \"#fff\" }` white\n * override that a role reference alone would lose. */\nfunction inlineStyle(style: BluxTextStyle | undefined): Record<string, string> | undefined {\n if (typeof style !== \"object\" || style === null) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(style)) {\n if (k === \"class\") continue;\n const cleaned = cleanCssValue(v);\n if (cleaned) out[k] = cleaned;\n }\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Parse `settings.fonts.google` (\"Scope+One:regular|Montserrat:300,500,regular\")\n * into families + numeric weights (\"regular\" → \"400\"). */\nfunction parseGoogleFonts(google: string): FontLoad[] {\n if (!google.trim()) return [];\n return google\n .split(\"|\")\n .map((spec) => {\n const [rawFamily = \"\", rawWeights = \"\"] = spec.split(\":\");\n const family = rawFamily.replace(/\\+/g, \" \").trim();\n const weights = rawWeights\n .split(\",\")\n .map((w) => w.trim())\n .filter(Boolean)\n .map((w) => (w === \"regular\" ? \"400\" : w));\n return { family, weights: weights.length ? weights : [\"400\"] };\n })\n .filter((f) => f.family);\n}\n\n/** The real font-family for a text style. A Typekit `font-ident`\n * (`T:Family:variant:obfuscated`) carries the true family in segment 2 while\n * `font-family` holds the obfuscated id (e.g. `ysxc`); Google idents (`G:…`) and\n * missing idents already have the true family in `font-family`. */\nfunction fontFamilyFromStyle(m: Record<string, unknown>): string {\n const ident = str(m[\"font-ident\"]);\n if (ident.startsWith(\"T:\")) {\n const fam = ident.split(\":\")[1]?.trim();\n if (fam) return fam;\n }\n return str(m[\"font-family\"]).replace(/['\"]/g, \"\");\n}\n\n/** Typekit fonts to preload, parsed from the comma-separated `settings.fonts.string`\n * idents (`T:Montserrat:n6:ysxc`). `fonts.google` omits Typekit faces, so the page's\n * Montserrat 600 would otherwise never be requested. Variant `nN`/`iN` → weight N×100. */\nfunction typekitFontLoads(fontString: string): FontLoad[] {\n const byFamily = new Map<string, string[]>();\n for (const ident of fontString.split(\",\").map((s) => s.trim())) {\n if (!ident.startsWith(\"T:\")) continue;\n const [, family = \"\", variant = \"\"] = ident.split(\":\");\n const fam = family.trim();\n if (!fam) continue;\n const v = variant.trim();\n const digit = /^[ni](\\d)$/.exec(v);\n const weight = digit ? `${Number(digit[1]) * 100}` : v === \"regular\" ? \"400\" : v;\n if (!weight) continue;\n const ws = byFamily.get(fam) ?? [];\n if (!ws.includes(weight)) ws.push(weight);\n byFamily.set(fam, ws);\n }\n return [...byFamily].map(([family, weights]) => ({ family, weights }));\n}\n\n/** Union `extra` font-loads into `base`, preserving `base` order and folding new\n * weights into an existing family (so Montserrat gains 600 instead of duplicating). */\nfunction mergeFontLoads(base: FontLoad[], extra: FontLoad[]): FontLoad[] {\n const out = base.map((f) => ({ family: f.family, weights: [...f.weights] }));\n for (const e of extra) {\n const existing = out.find((f) => f.family === e.family);\n if (existing) {\n for (const w of e.weights) if (!existing.weights.includes(w)) existing.weights.push(w);\n } else {\n out.push({ family: e.family, weights: [...e.weights] });\n }\n }\n return out;\n}\n\nconst CONFIDENCE_MIN = 0.5;\n\nfunction sectionFromBlock(b: BluxBlock, pageUid: string, diagnostics: Diagnostic[]): SectionIR {\n const a = archetype(b);\n if (a.confidence < CONFIDENCE_MIN) {\n diagnostics.push({\n kind: \"low-confidence-block\",\n where: pageUid,\n message: `block mapped to ${a.sliceType} at ${a.confidence}`,\n });\n }\n const heading = visibleText(b.title, b._title);\n const body = visibleText(b.body, b._body);\n const headingRole = heading !== undefined ? textRole(b._title) : undefined;\n const bodyRole = body !== undefined ? textRole(b._body) : undefined;\n const headingStyle = heading !== undefined ? inlineStyle(b._title) : undefined;\n const bodyStyle = body !== undefined ? inlineStyle(b._body) : undefined;\n // Route every value through cleanCssValue (as inlineStyle does) so numeric\n // block styles (a JSON `\"z-index\": 10`) are kept, not silently dropped.\n const block: Record<string, string> = {};\n for (const [k, v] of Object.entries(b.styles ?? {})) {\n const cleaned = cleanCssValue(v);\n if (cleaned) block[k] = cleaned;\n }\n const presentation = {\n ...(headingRole ? { headingRole } : {}),\n ...(bodyRole ? { bodyRole } : {}),\n ...(headingStyle ? { headingStyle } : {}),\n ...(bodyStyle ? { bodyStyle } : {}),\n ...(Object.keys(block).length ? { block } : {}),\n };\n const section: SectionIR = {\n sliceType: a.sliceType,\n variation: a.variation,\n confidence: a.confidence,\n fields: {\n ...(heading !== undefined ? { heading } : {}),\n ...(body !== undefined ? { body } : {}),\n ...(b.media?.media ? { media: b.media.media } : {}),\n ...(b.backgroundMedia?.media ? { backgroundMedia: b.backgroundMedia.media } : {}),\n ...(b.ratio ? { ratio: String(b.ratio) } : {}),\n ...(b.loadEffect ? { anim: String(b.loadEffect) } : {}),\n },\n ...(Object.keys(presentation).length ? { presentation } : {}),\n };\n if (Array.isArray(b.items) && b.items.length > 0) {\n section.children = b.items.map((child) => sectionFromBlock(child, pageUid, diagnostics));\n }\n return section;\n}\n\nfunction slugify(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\") || \"page\"\n );\n}\n\nexport function normalizePages(raw: BluxRaw): { pages: PageIR[]; diagnostics: Diagnostic[] } {\n const diagnostics: Diagnostic[] = [];\n const seen = new Set<string>();\n const pages = raw.pages.map((p, i) => {\n // The routing slug: the source `url` when set, else the title slug. The\n // FIRST page is the homepage (Blux is positional) — its uid is pinned to\n // \"home\", the render's root-route contract (getByUID(\"page\", \"home\")),\n // and its path is \"\" (the export renders it at the root index.html).\n const urlSlug = String(p.url ?? \"\").trim();\n const path = i === 0 ? \"\" : urlSlug ? slugify(urlSlug) : slugify(String(p.title ?? \"\"));\n let uid = i === 0 ? \"home\" : path;\n if (seen.has(uid)) {\n // Two pages slugging identically (e.g. a title-duplicate draft with no\n // url of its own) would silently overwrite each other's documents.\n const base = uid;\n for (let n = 2; seen.has(uid); n++) uid = `${base}-${n}`;\n diagnostics.push({\n kind: \"duplicate-page-uid\",\n where: base,\n message: `page ${i} (\"${String(p.title ?? \"\")}\") slugs to \"${base}\" already used — renamed to \"${uid}\"`,\n });\n }\n seen.add(uid);\n return {\n uid,\n title: String(p.title ?? \"\"),\n description: String(p.description ?? \"\"),\n path,\n sections: (p.items ?? []).map((b) => sectionFromBlock(b, uid, diagnostics)),\n };\n });\n return { pages, diagnostics };\n}\n\nexport function normalizeTheme(raw: BluxRaw): ThemeIR {\n const colors = Object.entries(raw.styles.colors ?? {}).map(([role, value]) => ({\n role,\n value: String(value),\n }));\n // styles.text is a position-stable array whose slots are { _label,\n // \".textN\": { css props incl font-ident } }. A deleted style leaves a\n // { removed: true } tombstone with no \".textN\" key — the role name comes\n // from that inner key (not the array index), so tombstones drop out and\n // roles never renumber if the array is ever compacted.\n const textStyles: TextStyleIR[] = [];\n for (const v of Object.values(raw.styles.text ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.text\\d+$/.test(k));\n if (!innerKey) continue;\n const m = (entry[innerKey] ?? {}) as Record<string, unknown>;\n const transform = cleanCssValue(m[\"text-transform\"]);\n const tracking = cleanCssValue(m[\"letter-spacing\"]);\n // The style's block margin carries Blux's stack rhythm (e.g. \"10px 0\" on\n // Grid Titles / Caption Body). An explicit \"0\" matches the render default,\n // so only real values ride the IR.\n const margin = cleanCssValue(m[\"margin\"]);\n const mobileSize = cleanCssValue(m[\"__media_mobile_font-size\"]);\n const mobileLineHeight = cleanCssValue(m[\"__media_mobile_line-height\"]);\n textStyles.push({\n role: innerKey.slice(1), // \".text11\" -> \"text11\"\n label: str(entry._label),\n fontFamily: fontFamilyFromStyle(m),\n size: cleanCssValue(m[\"font-size\"]) || \"16px\",\n weight:\n typeof m[\"font-weight\"] === \"number\" ? m[\"font-weight\"] : str(m[\"font-weight\"]) || 400,\n lineHeight: cleanCssValue(m[\"line-height\"]) || \"1.5\",\n ...(transform && transform !== \"none\" ? { transform } : {}),\n ...(tracking ? { letterSpacing: tracking } : {}),\n ...(margin && margin !== \"0\" ? { margin } : {}),\n ...(mobileSize ? { mobileSize } : {}),\n ...(mobileLineHeight ? { mobileLineHeight } : {}),\n });\n }\n // Button skins: styles.buttons mirrors the text-styles shape (an entry per\n // role with the values one level down under the \".buttonsN\" key, tombstones\n // as { removed: true }). Values pass through in DECLARATION ORDER — the\n // skins rely on it (a `border` shorthand then `border-top/right/left: 0`\n // overrides nets a bottom-only rule) — dropping empties and the internal\n // `font-ident` marker.\n const cleanCssMap = (v: unknown): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const [k, val] of Object.entries((v ?? {}) as Record<string, unknown>)) {\n if (k === \"font-ident\") continue;\n const s = cleanCssValue(typeof val === \"number\" ? String(val) : val);\n if (s) out[k] = s;\n }\n return out;\n };\n const buttonStyles: ButtonStyleIR[] = [];\n for (const v of Object.values(raw.styles.buttons ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.buttons\\d+$/.test(k));\n if (!innerKey) continue;\n const css = cleanCssMap(entry[innerKey]);\n if (Object.keys(css).length === 0) continue;\n const hover = cleanCssMap(entry[`${innerKey}:hover`]);\n const active = cleanCssMap(entry[`${innerKey}:active`]);\n buttonStyles.push({\n role: innerKey.slice(1), // \".buttons2\" -> \"buttons2\"\n label: str(entry._label),\n css,\n ...(Object.keys(hover).length ? { hover } : {}),\n ...(Object.keys(active).length ? { active } : {}),\n });\n }\n // Fonts: explicit settings win; otherwise Blux's own default roles —\n // text0 \"Title (Default)\" and text1 \"Body (Default)\".\n const fonts = (raw.settings.fonts ?? {}) as Record<string, unknown>;\n const roleFont = (r: string) => textStyles.find((t) => t.role === r)?.fontFamily ?? \"\";\n return {\n colors,\n fonts: {\n heading: str(fonts.heading) || roleFont(\"text0\"),\n body: str(fonts.body) || roleFont(\"text1\"),\n },\n fontLoad: mergeFontLoads(\n parseGoogleFonts(str(fonts.google)),\n typekitFontLoads(str(fonts.string)),\n ),\n textStyles,\n buttonStyles,\n };\n}\n","import type { BluxFeed, BluxRaw } from \"./parse.js\";\nimport type { CollectionIR, FieldDef, RecordIR } from \"./ir.js\";\n\nfunction singularSlug(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n return slug.replace(/s$/, \"\") || \"item\";\n}\n\nconst RICHTEXT_KEYS = new Set([\"body\", \"description\"]);\n\nfunction fieldType(key: string, value: unknown): FieldDef[\"type\"] {\n if (RICHTEXT_KEYS.has(key)) return \"richtext\";\n if (value && typeof value === \"object\" && \"media\" in (value as object)) return \"image\";\n if (Array.isArray(value)) return \"group\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n if (key === \"date\") return \"date\";\n if (/^(url|link)/.test(key)) return \"link\";\n return \"text\";\n}\n\n/** Underscore-prefixed keys are per-element style config (same convention as\n * page blocks' _title/_body), not content — never model or migrate them. */\nconst isStyleKey = (key: string) => key.startsWith(\"_\");\n\nfunction deriveFields(feed: BluxFeed): FieldDef[] {\n const seen = new Map<string, FieldDef[\"type\"]>();\n // Declared custom fields first (Blux feed.fields), then observed item keys.\n for (const d of feed.fields ?? []) {\n if (d.field) seen.set(d.field, \"text\");\n }\n for (const item of feed.items ?? []) {\n for (const [key, value] of Object.entries(item)) {\n if (isStyleKey(key)) continue;\n if (!seen.has(key) || seen.get(key) === \"text\") seen.set(key, fieldType(key, value));\n }\n }\n return [...seen.entries()].map(([key, type]) => ({ key, type }));\n}\n\nfunction recordUid(values: Record<string, unknown>, i: number): string {\n const title = typeof values.title === \"string\" ? values.title : \"\";\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return slug || `item-${i}`;\n}\n\nexport function modelCollections(raw: BluxRaw): CollectionIR[] {\n const out: CollectionIR[] = [];\n for (const feed of Object.values(raw.feeds)) {\n const label = String(feed.name ?? \"\");\n const items = feed.items ?? [];\n const records: RecordIR[] = items.map((item, i) => {\n const mediaRefs: string[] = [];\n for (const value of Object.values(item)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n mediaRefs.push((value as { media: string }).media);\n }\n }\n const values = Object.fromEntries(Object.entries(item).filter(([key]) => !isStyleKey(key)));\n return { uid: recordUid(item, i), values, mediaRefs };\n });\n out.push({\n apiId: singularSlug(label),\n label,\n publishRoute: feed.publish ? String(feed.publish) : null,\n fields: deriveFields(feed),\n records,\n });\n }\n return out;\n}\n","import { parseBluxSite } from \"./parse.js\";\nimport { normalizePages, normalizeTheme } from \"./normalize.js\";\nimport { modelCollections } from \"./collections.js\";\nimport { collectAssetUrls } from \"./assets.js\";\nimport type { AssetRef, Diagnostic, SiteIR } from \"./ir.js\";\n\nexport function assembleIR(input: { siteJson: unknown; htmls: string[] }): SiteIR {\n const raw = parseBluxSite(input.siteJson);\n const { pages, diagnostics: pageDiags } = normalizePages(raw);\n const theme = normalizeTheme(raw);\n const collections = modelCollections(raw);\n const urlMap = collectAssetUrls(input.htmls);\n\n const diagnostics: Diagnostic[] = [...pageDiags];\n const assets: AssetRef[] = Object.entries(raw.media).map(([id, m]) => {\n const sourceUrl = urlMap.get(id) ?? null;\n if (!sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: id,\n message: `no CDN url for ${m.name ?? id}`,\n });\n }\n return {\n id,\n sourceUrl,\n name: String(m.name ?? \"\"),\n mime: String(m.type ?? \"\"),\n alt: String(m.name ?? \"\"),\n };\n });\n\n // The favicon is declared only in settings (settings.favicon.media) and its\n // uuid is routinely ABSENT from the media dict — so resolve it straight from\n // the scraped urlMap (the <link rel=\"icon\"> tags are part of the HTML scrape)\n // rather than through the assets list above. It lives on meta, not in\n // `assets`, because migration-plan assets get uploaded to Prismic media —\n // the wrong destination for a favicon (convert downloads it beside the plan).\n const faviconId = raw.settings.favicon?.media;\n const favicon = faviconId\n ? { assetId: faviconId, sourceUrl: urlMap.get(faviconId) ?? null }\n : undefined;\n if (favicon && !favicon.sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: favicon.assetId,\n message: `no CDN url for favicon ${favicon.assetId}`,\n });\n }\n\n return {\n meta: { ...raw.meta, ...(favicon ? { favicon } : {}) },\n theme,\n pages,\n collections,\n assets,\n diagnostics,\n };\n}\n","import type { CollectionIR, FieldDef } from \"../ir.js\";\nimport type { PlanCustomType } from \"./plan.js\";\n\nconst FIELD_CONFIG: Record<\n FieldDef[\"type\"],\n () => { type: string; config: Record<string, unknown> }\n> = {\n text: () => ({ type: \"Text\", config: {} }),\n richtext: () => ({\n type: \"StructuredText\",\n config: { multi: \"paragraph,strong,em,hyperlink,list-item,o-list-item\" },\n }),\n image: () => ({ type: \"Image\", config: { constraint: {}, thumbnails: [] } }),\n group: () => ({ type: \"Group\", config: { fields: { value: { type: \"Text\", config: {} } } } }),\n date: () => ({ type: \"Date\", config: {} }),\n boolean: () => ({ type: \"Boolean\", config: {} }),\n number: () => ({ type: \"Number\", config: {} }),\n link: () => ({ type: \"Link\", config: { allowTargetBlank: true } }),\n};\n\nexport function buildCustomType(c: CollectionIR): PlanCustomType {\n const Main: Record<string, unknown> = {};\n for (const f of c.fields) {\n const spec = FIELD_CONFIG[f.type]();\n Main[f.key] = { ...spec, config: { ...spec.config, label: f.key } };\n }\n return {\n id: c.apiId,\n label: c.label,\n repeatable: true,\n json: { id: c.apiId, label: c.label, repeatable: true, status: true, json: { Main } },\n };\n}\n","/** HTML-level rich-text coercion so emitted plans validate against the slice\n * models' StructuredText restrictions (heading slots are `single` and\n * heading-restricted; body slots allow no headings — see Plan 4's field-type\n * table). Blux markup is simple generated HTML, so tag rewriting is reliable. */\n\nconst BLOCK_RE = /<(h[1-6]|p|div)(\\s[^>]*)?>[\\s\\S]*?<\\/\\1>/i;\n\n/** Coerce a heading-slot HTML fragment to a single block whose tag is in\n * `allowed` (e.g. [\"h2\",\"h3\"]): keep an allowed tag, clamp other headings to\n * the nearest allowed level, promote paragraphs/bare text to the LOWEST\n * allowed heading. Only the first block survives (the fields are `single`). */\nexport function coerceHeadingHtml(html: string, allowed: string[]): string {\n const m = html.match(BLOCK_RE);\n const block = m ? m[0] : html;\n const tagMatch = block.match(/^<(h[1-6]|p|div)(\\s[^>]*)?>/i);\n const tag = tagMatch?.[1]?.toLowerCase();\n if (tag && allowed.includes(tag)) return block;\n\n const levels = allowed.filter((t) => /^h[1-6]$/.test(t)).map((t) => Number(t[1]));\n const target =\n tag && tag.startsWith(\"h\")\n ? `h${levels.reduce((b, l) => (Math.abs(l - Number(tag[1])) < Math.abs(b - Number(tag[1])) ? l : b))}`\n : `h${Math.max(...levels)}`;\n\n if (!tag) return `<${target}>${block}</${target}>`;\n return block\n .replace(new RegExp(`^<${tag}`, \"i\"), `<${target}`)\n .replace(new RegExp(`</${tag}>$`, \"i\"), `</${target}>`);\n}\n\n/** Demote all headings in a body-slot fragment to paragraphs (body fields\n * allow no heading blocks). Attributes and inline markup pass through. */\nexport function demoteHeadingsHtml(html: string): string {\n return html.replace(/<(\\/?)h[1-6](\\s[^>]*)?>/gi, \"<$1p$2>\");\n}\n","import type { SectionIR } from \"../ir.js\";\nimport { richText, assetRef, type PlanSlice } from \"./plan.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\n\n/** Allowed heading tags per slice heading slot — MUST mirror the\n * StructuredText configs in reddoor-starter/src/lib/slices/<Slice>/model.json. */\nconst HEADING_TAGS = {\n hero: [\"h1\", \"h2\"],\n media_text: [\"h2\", \"h3\"],\n section_grid: [\"h2\", \"h3\"],\n section_grid_item: [\"h3\", \"h4\"],\n} satisfies Record<string, string[]>;\n\nfunction rt(html?: string) {\n return html ? richText(html) : undefined;\n}\nfunction rtHeading(html: string | undefined, slot: keyof typeof HEADING_TAGS) {\n return html ? richText(coerceHeadingHtml(html, HEADING_TAGS[slot])) : undefined;\n}\nfunction rtBody(html?: string) {\n return html ? richText(demoteHeadingsHtml(html)) : undefined;\n}\nfunction img(id?: string) {\n return id ? assetRef(id) : undefined;\n}\nfunction compact(o: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));\n}\n\nexport function sectionToSlice(s: SectionIR): PlanSlice {\n const f = s.fields;\n switch (s.sliceType) {\n case \"hero\":\n return {\n slice_type: \"hero\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"hero\"),\n body: rtBody(f.body),\n background_image: img(f.backgroundMedia ?? f.media),\n }),\n items: [],\n };\n case \"media_text\":\n return {\n slice_type: \"media_text\",\n variation: s.variation === \"imageLeft\" ? \"imageLeft\" : \"imageRight\",\n primary: compact({\n heading: rtHeading(f.heading, \"media_text\"),\n body: rtBody(f.body),\n media: img(f.media),\n }),\n items: [],\n };\n case \"collection_list\":\n return {\n slice_type: \"collection_list\",\n variation: s.variation === \"list\" ? \"list\" : \"grid\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n collection_type: s.collectionRef?.apiId ?? \"\",\n max_items: 24,\n }),\n items: [],\n };\n case \"grid\":\n case \"slider\":\n return {\n slice_type: \"section_grid\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n columns: f.columns ?? 3,\n }),\n items: (s.children ?? [])\n .map((c) =>\n compact({\n item_heading: rtHeading(c.fields.heading, \"section_grid_item\"),\n item_body: rtBody(c.fields.body),\n item_media: img(c.fields.media),\n }),\n )\n // a child whose only content was hidden text has nothing to show\n .filter((item) => Object.keys(item).length > 0),\n };\n case \"rich_text\":\n default:\n // rich_text content allows every block type — no coercion needed\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n primary: compact({ content: rt(f.heading ? `${f.heading}${f.body ?? \"\"}` : f.body) }),\n items: [],\n };\n }\n}\n","import type { SectionIR } from \"../ir.js\";\n\nconst isContainer = (s: SectionIR) => s.sliceType === \"grid\" || s.sliceType === \"slider\";\nconst isFlatLeaf = (s: SectionIR) =>\n (s.sliceType === \"media_text\" || s.sliceType === \"rich_text\") && !(s.children ?? []).length;\n\n/** Depth-first flatten: Prismic slices cannot nest, so a container survives\n * as a section_grid-with-items only when every child is representable as a\n * flat item (childless media_text/rich_text). Anything richer — nested\n * containers, heroes with backgrounds — explodes into sequential sibling\n * sections, and the container's OWN content (heading/body/media) survives\n * as a leading media_text or rich_text section. Proven need: thePointe's\n * depth-4 tree kept only 7/53 images under one-level flattening. */\nexport function flattenSections(sections: SectionIR[]): SectionIR[] {\n const out: SectionIR[] = [];\n for (const s of sections) {\n const children = s.children ?? [];\n\n if (s.sliceType === \"hero\" && s.fields.media && s.fields.backgroundMedia) {\n // the hero slice only models the background image — surface the\n // foreground image (a logo/overlay in Blux) as a sibling media_text\n const { media, ...heroFields } = s.fields;\n const { children: _heroKids, ...heroSelf } = s;\n out.push({ ...heroSelf, fields: heroFields });\n out.push({\n sliceType: \"media_text\",\n variation: \"imageRight\",\n confidence: s.confidence,\n fields: { media },\n });\n out.push(...flattenSections(children));\n continue;\n }\n\n // a container may keep its items form only when it has no content of its\n // own beyond a heading — section_grid's primary cannot carry media/body\n const keepable = isContainer(s) && !s.fields.media && !s.fields.body;\n if (!children.length) {\n if (isContainer(s) && !keepable) {\n out.push({ ...s, sliceType: s.fields.media ? \"media_text\" : \"rich_text\" });\n } else {\n out.push(s);\n }\n } else if (keepable && children.every(isFlatLeaf)) {\n out.push(s);\n } else if (isContainer(s)) {\n // the container's own content leads its exploded children: with media\n // it is a media_text-shaped section that merely carries a subtree,\n // otherwise its heading/body become a rich_text grouping label\n const { children: _kids, ...self } = s;\n if (self.fields.media) {\n out.push({ ...self, sliceType: \"media_text\" });\n } else if (self.fields.heading || self.fields.body) {\n out.push({ ...self, sliceType: \"rich_text\" });\n }\n out.push(...flattenSections(children));\n } else {\n // normalize attaches `children` to ANY block with items — a hero or\n // media_text can carry a subtree. Its own slice mapping ignores\n // children, so hoist them as following siblings instead of losing them.\n const { children: _hoisted, ...self } = s;\n out.push(self);\n out.push(...flattenSections(children));\n }\n }\n return out;\n}\n","import type { SiteIR, RecordIR, PageIR, AssetRef, Diagnostic } from \"../ir.js\";\nimport {\n richText,\n assetRef,\n type MigrationPlan,\n type PlanDocument,\n type SliceStyleEntry,\n} from \"./plan.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sectionToSlice } from \"./slices.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\nimport { flattenSections } from \"./flatten.js\";\n\n/** Slice fields modeled as Prismic Image fields — only image assets may land here. */\nconst IMAGE_FIELDS = new Set([\"background_image\", \"media\", \"item_media\"]);\n\nconst RICHTEXT = new Set([\"body\", \"description\"]);\n\n/** A page with no title and no sections emits no document (and no review pair). */\nexport function isEmptyPage(p: PageIR): boolean {\n return !p.sections.length && !p.title.trim();\n}\n\n/** True when the asset may occupy a Prismic Image field. A known non-image\n * mime is disqualifying; a MISSING mime (exports often omit `type`) falls\n * back to the filename extension and keeps the asset unless it is clearly\n * not an image. */\nconst NON_IMAGE_EXT = /\\.(mp4|mov|webm|avi|mp3|wav|pdf|zip)$/i;\nfunction isImageAsset(a: AssetRef | undefined): boolean {\n if (!a) return true; // unknown asset id: leave it; migrate reports the miss\n if (a.mime) return a.mime.startsWith(\"image/\");\n return !NON_IMAGE_EXT.test(a.name);\n}\n\nfunction recordData(rec: RecordIR): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rec.values)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n data[key] = assetRef((value as { media: string }).media);\n } else if (RICHTEXT.has(key) && typeof value === \"string\") {\n // record rich-text models allow no heading blocks (see buildCustomType)\n data[key] = richText(demoteHeadingsHtml(value));\n } else {\n data[key] = value;\n }\n }\n return data;\n}\n\nexport function buildMigrationPlan(ir: SiteIR): MigrationPlan {\n const diagnostics: Diagnostic[] = [];\n const customTypes = ir.collections.map(buildCustomType);\n const assetById = new Map(ir.assets.map((a) => [a.id, a]));\n\n /** Drop non-image assets referenced by an Image field, with a diagnostic. */\n const dropNonImages = (rec: Record<string, unknown>, keys: Set<string> | null, where: string) => {\n for (const [key, val] of Object.entries(rec)) {\n if ((keys && !keys.has(key)) || !val || typeof val !== \"object\" || !(\"__asset_id\" in val))\n continue;\n const asset = assetById.get((val as { __asset_id: string }).__asset_id);\n if (!isImageAsset(asset)) {\n diagnostics.push({\n kind: \"non-image-in-image-field\",\n where: `${where}.${key}`,\n message: `${asset?.mime || \"unknown mime\"} asset dropped from image field`,\n });\n delete rec[key];\n }\n }\n };\n\n const documents: PlanDocument[] = [];\n const stylesManifest: MigrationPlan[\"stylesManifest\"] = [];\n for (const page of ir.pages) {\n if (isEmptyPage(page)) {\n diagnostics.push({\n kind: \"empty-page\",\n where: page.uid,\n message: \"page has no title and no sections; skipped\",\n });\n continue;\n }\n const slices: ReturnType<typeof sectionToSlice>[] = [];\n const styleEntries: SliceStyleEntry[] = [];\n for (const section of flattenSections(page.sections)) {\n const slice = sectionToSlice(section);\n for (const rec of [slice.primary, ...slice.items]) {\n dropNonImages(rec, IMAGE_FIELDS, `${page.uid}/${slice.slice_type}`);\n }\n // Structural defaults aren't content — a slice with nothing else to\n // show (e.g. a block whose only content was hidden text, or whose\n // sole video was dropped from an image field) is invisible; skip it.\n const STRUCTURAL = new Set([\"columns\", \"collection_type\", \"max_items\"]);\n const hasContent =\n Object.keys(slice.primary).some((k) => !STRUCTURAL.has(k)) || slice.items.length > 0;\n if (!hasContent) {\n diagnostics.push({\n kind: \"empty-slice\",\n where: `${page.uid}/${slice.slice_type}`,\n message: \"slice has no content after filtering; dropped\",\n });\n continue;\n }\n // Presentation hints ride beside the slice, keyed by its final index.\n // Grid items mirror sectionToSlice's emptiness rule so they stay aligned.\n const itemPresentations = (section.children ?? [])\n .filter((c) => c.fields.heading || c.fields.body || c.fields.media)\n .map((c) => c.presentation ?? null);\n const entry: SliceStyleEntry = {\n index: slices.length,\n sliceType: slice.slice_type,\n ...(section.presentation ? { presentation: section.presentation } : {}),\n ...(slice.slice_type === \"section_grid\" && itemPresentations.some(Boolean)\n ? { items: itemPresentations }\n : {}),\n };\n if (entry.presentation || entry.items) styleEntries.push(entry);\n slices.push(slice);\n }\n stylesManifest.push({ pageUid: page.uid, slices: styleEntries });\n documents.push({\n type: \"page\",\n uid: page.uid,\n // the page type's title is StructuredText(single heading1)\n data: {\n title: richText(coerceHeadingHtml(page.title || page.uid, [\"h1\"])),\n slices,\n },\n });\n }\n for (const c of ir.collections) {\n for (const rec of c.records) {\n const data = recordData(rec);\n // record asset markers only ever occupy Image fields — check them all\n dropNonImages(data, null, `${c.apiId}/${rec.uid}`);\n documents.push({ type: c.apiId, uid: rec.uid, data });\n }\n }\n\n const assets = ir.assets\n .filter((a) => a.sourceUrl !== null)\n .map((a) => ({ id: a.id, url: a.sourceUrl as string, alt: a.alt }));\n\n return { customTypes, documents, assets, stylesManifest, diagnostics };\n}\n","import type { ThemeIR } from \"../ir.js\";\n\n/** The shared custom-property lines (colors, fonts, text tokens) that both the\n * build-time `@theme{}` block and the runtime `:root{}` mirror emit. Extracted\n * so the two blocks can never drift. */\nfunction themeVarLines(theme: ThemeIR): string[] {\n const lines: string[] = [];\n for (const c of theme.colors) lines.push(` --color-${c.role}: ${c.value};`);\n lines.push(` --font-heading: ${theme.fonts.heading || \"sans-serif\"};`);\n lines.push(` --font-body: ${theme.fonts.body || \"sans-serif\"};`);\n for (const t of theme.textStyles) {\n if (t.label) lines.push(` /* ${t.role} — ${t.label} */`);\n lines.push(` --text-${t.role}: ${t.size};`);\n lines.push(` --text-${t.role}--line-height: ${t.lineHeight};`);\n lines.push(` --text-${t.role}--font-weight: ${t.weight};`);\n if (t.fontFamily) lines.push(` --text-${t.role}--font-family: ${t.fontFamily};`);\n if (t.transform) lines.push(` --text-${t.role}--text-transform: ${t.transform};`);\n if (t.letterSpacing) lines.push(` --text-${t.role}--letter-spacing: ${t.letterSpacing};`);\n if (t.margin) lines.push(` --text-${t.role}--margin: ${t.margin};`);\n if (t.mobileSize) lines.push(` --text-${t.role}--mobile-font-size: ${t.mobileSize};`);\n if (t.mobileLineHeight)\n lines.push(` --text-${t.role}--mobile-line-height: ${t.mobileLineHeight};`);\n }\n return lines;\n}\n\n/** ThemeIR → a Tailwind v4 `@theme` block of CSS custom properties. Deterministic.\n * A leading comment lists the exact web-font weights the export loads, so the\n * design pass can install them without measuring the rendered site. */\nexport function emitThemeCss(theme: ThemeIR): string {\n const lines: string[] = [];\n if (theme.fontLoad.length) {\n const spec = theme.fontLoad.map((f) => `${f.family} ${f.weights.join(\",\")}`).join(\"; \");\n lines.push(`/* Fonts to load — ${spec} */`);\n }\n lines.push(\"@theme {\");\n lines.push(...themeVarLines(theme));\n lines.push(\"}\");\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** A runtime-resolvable mirror of the @theme token block. Tailwind's `@theme`\n * is BUILD-TIME — injected raw at runtime (the /dev/blux-pointe gate), the\n * browser ignores it and the `.txt-role-*` rules' `var(--text-*)` never\n * resolve. The same tokens inside a real `:root{}` resolve without Tailwind;\n * identical values to what @theme compiles to :root, so it is harmless when the\n * file is also Tailwind-built. */\nexport function emitRootVarsCss(theme: ThemeIR): string {\n return [\":root {\", ...themeVarLines(theme), \"}\"].join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the `.txt-role-textN` utility layer: one rule per text style that\n * maps the role's `@theme` vars (emitted by {@link emitThemeCss}) onto the\n * h1–h6/p it wraps. A slice applies `txt-role-textN` to a block and its\n * headings/paragraphs pick up that role's type — the same mechanism the\n * per-site `gen-blux-theme.mjs` script used to hand-generate, now owned by\n * `blux emit` so a converted site consumes it with zero hand-tuning.\n *\n * letter-spacing and text-transform fall back through `var(…, default)` so a\n * role that omits them is inert for that property. font-family is set only when\n * the role declares one — a family-less role (a body default) keeps the natural\n * cascade (headings stay heading font, paragraphs stay body font) rather than\n * being forced onto either. margin falls back to 0: Blux's vertical rhythm\n * between stacked blocks is the text styles' own margins (e.g. \"10px 0\" on\n * Grid Titles), which collapse in normal flow — a role without one stays\n * flush, exactly like the original. Returns \"\" when the theme has no text\n * styles. */\nexport function emitRolesCss(theme: ThemeIR): string {\n if (!theme.textStyles.length) return \"\";\n const lines: string[] = [\n \"/* Role utilities — one .txt-role-textN per Blux text style, mapping its\",\n \" @theme vars onto the heading/paragraph it wraps. Generated by blux emit. */\",\n ];\n for (const t of theme.textStyles) {\n const r = t.role;\n lines.push(`.txt-role-${r} :is(h1, h2, h3, h4, h5, h6, p) {`);\n if (t.fontFamily) lines.push(` font-family: var(--text-${r}--font-family);`);\n lines.push(\n ` font-size: var(--text-${r});`,\n ` font-weight: var(--text-${r}--font-weight);`,\n ` line-height: var(--text-${r}--line-height);`,\n ` letter-spacing: var(--text-${r}--letter-spacing, normal);`,\n ` text-transform: var(--text-${r}--text-transform, none);`,\n ` margin: var(--text-${r}--margin, 0);`,\n `}`,\n );\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the button-skin layer: one `.buttonsN` rule per declared Blux\n * button style (plus its :hover/:active variants), and the `.ib` inline-block\n * base the raw anchors rely on (`class=\"ib middle buttonsN\"` — an inline `a`\n * would ignore the skin's vertical padding). Properties emit in the export's\n * own declaration order: the skins rely on it (a `border` shorthand followed\n * by `border-top/right/left: 0` overrides nets a bottom-only rule). Returns \"\"\n * when the theme declares no button styles. */\nexport function emitButtonsCss(theme: ThemeIR): string {\n if (!theme.buttonStyles.length) return \"\";\n const lines: string[] = [\n \"/* Button skins — one .buttonsN per Blux button style; raw anchors carry\",\n \" `ib middle buttonsN` verbatim. Generated by blux emit. */\",\n \".ib {\",\n \" display: inline-block;\",\n \"}\",\n \"/* Text links (`ib middle links`) are underlined by the Blux platform CSS;\",\n \" an inline-block box does not inherit an ancestor's text-decoration, so\",\n \" the affordance must be declared on the anchor itself. */\",\n \".links {\",\n \" text-decoration: underline;\",\n \"}\",\n ];\n const rule = (selector: string, css: Record<string, string>) => {\n lines.push(`${selector} {`);\n for (const [k, v] of Object.entries(css)) lines.push(` ${k}: ${v};`);\n lines.push(\"}\");\n };\n for (const b of theme.buttonStyles) {\n if (b.label) lines.push(`/* ${b.role} — ${b.label} */`);\n rule(`.${b.role}`, b.css);\n if (b.hover) rule(`.${b.role}:hover`, b.hover);\n if (b.active) rule(`.${b.role}:active`, b.active);\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** The complete theme.css a CLI action writes: the build-time `@theme` block,\n * its runtime `:root` mirror (so a raw-injected theme.css resolves\n * `var(--text-*)` — the /dev/blux-pointe gate does exactly that), then the role\n * utilities and button skins. The single source of truth for theme.css\n * assembly — every action (emit/catalog/convert) writes exactly this, so no\n * writer can drift or omit the `:root` mirror. */\nexport function emitThemeCssFile(theme: ThemeIR): string {\n const rolesCss = emitRolesCss(theme);\n const buttonsCss = emitButtonsCss(theme);\n return (\n emitThemeCss(theme) +\n \"\\n\" +\n emitRootVarsCss(theme) +\n (rolesCss ? \"\\n\" + rolesCss : \"\") +\n (buttonsCss ? \"\\n\" + buttonsCss : \"\")\n );\n}\n","import type { SiteIR, Diagnostic } from \"../ir.js\";\nimport { isEmptyPage } from \"./migration-plan.js\";\n\nexport type ReviewPair = { uid: string; converted: string; original: string };\nexport type ReviewManifest = { pairs: ReviewPair[]; diagnostics: Diagnostic[] };\n\nexport function buildReviewManifest(\n ir: SiteIR,\n opts: { convertedBase: string; bluxBase: string },\n): ReviewManifest {\n // empty pages emit no document (see buildMigrationPlan) — nothing to review\n const pairs = ir.pages\n .filter((p) => !isEmptyPage(p))\n .map((p) => ({\n uid: p.uid,\n converted: `${opts.convertedBase}/${p.uid}`,\n // The home page maps to the site root; other pages to /uid.\n original: p.uid === \"home\" ? `${opts.bluxBase}/` : `${opts.bluxBase}/${p.uid}`,\n }));\n return { pairs, diagnostics: ir.diagnostics };\n}\n","import { cleanCssValue } from \"../normalize.js\";\n\n/** Per-band block styles (background-color, min-height, text-align, …) sourced\n * from site.json's top-level page blocks, cleaned exactly as normalize.ts does.\n * Keyed by the block's render-order index, which equals the grid `Band.index`\n * (the `page-block-N` number) for a contiguously-rendered page.\n *\n * site.json's page blocks live at `content.pages[0].items` (see\n * `parseBluxSite` in parse.ts, which reads `j.content.pages`, and `BluxPage`,\n * whose blocks field is `items`) — not `pages[0].blocks` as a naive reading\n * of the export's top level might suggest. */\nexport function blockStylesByIndex(\n siteJson: unknown,\n pageIndex = 0,\n): Map<number, Record<string, string>> {\n const out = new Map<number, Record<string, string>>();\n const blocks = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content?.pages?.[\n pageIndex\n ]?.items;\n if (!Array.isArray(blocks)) return out;\n blocks.forEach((b, i) => {\n const styles = (b as { styles?: Record<string, unknown> })?.styles ?? {};\n const cleaned: Record<string, string> = {};\n for (const [k, v] of Object.entries(styles)) {\n const c = cleanCssValue(v);\n if (c) cleaned[k] = c;\n }\n if (Object.keys(cleaned).length) out.set(i, cleaned);\n });\n return out;\n}\n\n/** A block class's `.blocksNcontainer` defaults: the content padding (with its\n * `__media_mobile_padding` responsive override) and max-width that apply when a\n * block's own styles omit them. */\nexport type BlockDefaults = {\n padding?: string;\n mobilePadding?: string;\n maxWidth?: string;\n};\n\nconst CONTAINER_RE = /^\\.(blocks\\d+)container$/;\n\n/** Per-block-class container defaults keyed by the bare class (\"blocks0\",\n * \"blocks2\", …), sourced from site.json's `styles.blocks` — a position-stable\n * array of `{ _label, \".blocksN\": …, \".blocksNcontainer\": … }` slots. Only the\n * `.blocksNcontainer` entries matter here; the head `<style>` is never parsed\n * (styles.blocks is its structured source). Values are cleaned exactly as\n * `blockStylesByIndex` cleans them, so the export's malformed tombstones\n * (`padding: \"px\"`, empty strings) drop instead of leaking. */\nexport function blockClassDefaults(siteJson: unknown): Map<string, BlockDefaults> {\n const out = new Map<string, BlockDefaults>();\n const blocks = (siteJson as { styles?: { blocks?: unknown[] } })?.styles?.blocks;\n if (!Array.isArray(blocks)) return out;\n for (const slot of blocks) {\n if (slot === null || typeof slot !== \"object\") continue;\n for (const [selector, css] of Object.entries(slot as Record<string, unknown>)) {\n const blockClass = CONTAINER_RE.exec(selector)?.[1];\n if (!blockClass || css === null || typeof css !== \"object\") continue;\n const rec = css as Record<string, unknown>;\n const padding = cleanCssValue(rec[\"padding\"]);\n const mobilePadding = cleanCssValue(rec[\"__media_mobile_padding\"]);\n const maxWidth = cleanCssValue(rec[\"max-width\"]);\n const defaults: BlockDefaults = {\n ...(padding ? { padding } : {}),\n ...(mobilePadding ? { mobilePadding } : {}),\n ...(maxWidth ? { maxWidth } : {}),\n };\n if (Object.keys(defaults).length) out.set(blockClass, defaults);\n }\n }\n return out;\n}\n","/** Content-coverage validation: does a converted site actually render every\n * piece of text the Blux export shows? The export's rendered `index.html` is\n * the answer key; we extract its visible text runs and check each appears in\n * the converted site's rendered HTML. A missing run is a content gap the\n * deterministic transform left behind (e.g. a hero title that never mapped\n * onto a slice field) — surfaced without spending a token eyeballing pages. */\n\nconst DROP_ELEMENTS = /<(script|style|head|noscript|svg|template)[\\s\\S]*?<\\/\\1>/gi;\nconst COMMENTS = /<!--[\\s\\S]*?-->/g;\n\n/** Decode the entity forms an export mixes in so they match the raw characters\n * a Prismic render emits. Numeric first, then the common named ones. */\nfunction decodeEntities(s: string): string {\n return (\n s\n .replace(/&#(\\d+);/g, (_, n) => codePoint(Number(n)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => codePoint(parseInt(h, 16)))\n .replace(/&nbsp;/gi, \" \")\n .replace(/&(?:amp);/gi, \"&\")\n .replace(/&(?:lt);/gi, \"<\")\n .replace(/&(?:gt);/gi, \">\")\n .replace(/&(?:quot);/gi, '\"')\n .replace(/&(?:apos|#39);/gi, \"'\")\n // any remaining named entity (&mdash; &rsquo; &copy; …) becomes a space,\n // not the literal token \"mdash\"/\"rsquo\" — a phantom word would break the\n // substring match and report otherwise-present text as a false gap\n .replace(/&[a-z][a-z0-9]*;/gi, \" \")\n );\n}\n\nfunction codePoint(n: number): string {\n try {\n return String.fromCodePoint(n);\n } catch {\n return \" \";\n }\n}\n\n/** Fold text to a matchable form: decode, lowercase, and reduce every run of\n * non-alphanumeric characters (punctuation, smart quotes, em-dashes, symbols)\n * to a single space. Coverage then compares words, not typography — so\n * \"CBRE & Co. — Leasing\" and \"cbre co leasing\" are the same content. */\nexport function normalizeText(s: string): string {\n return decodeEntities(s)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim();\n}\n\n/** A normalized run counts as content only if it carries a real word — at least\n * three alphanumeric characters together. Filters whitespace, lone punctuation,\n * and single/double-letter fragments left by inline tags. */\nfunction hasWord(run: string): boolean {\n return /[a-z0-9]{3,}/.test(run);\n}\n\n/** Strip the non-content elements and all tags from an HTML string, leaving\n * one normalized text blob (used for the render side of a coverage check). */\nexport function flattenText(html: string): string {\n const text = html\n .replace(DROP_ELEMENTS, \" \")\n .replace(COMMENTS, \" \")\n .replace(/<[^>]+>/g, \" \");\n return normalizeText(text);\n}\n\n/** Split an HTML string into de-duplicated, normalized visible text runs — the\n * text between tags, once script/style/head are removed. Inline tags fragment a\n * sentence into several runs; each must still appear in the render to count. */\nexport function extractTextRuns(html: string): string[] {\n const stripped = html.replace(DROP_ELEMENTS, \" \").replace(COMMENTS, \" \");\n const seen = new Set<string>();\n for (const piece of stripped.split(/<[^>]+>/)) {\n const run = normalizeText(piece);\n if (hasWord(run)) seen.add(run);\n }\n return [...seen];\n}\n\nexport type CoverageReport = {\n total: number;\n covered: number;\n missing: string[];\n coveragePct: number;\n};\n\n/** Compare the export's rendered HTML (answer key) against the converted site's\n * rendered HTML. Every text run in the export should appear somewhere in the\n * render; the ones that don't are the content the transform dropped. */\nexport function validateCoverage(exportHtml: string, renderedHtml: string): CoverageReport {\n const runs = extractTextRuns(exportHtml);\n // Pad with spaces and match a space-bounded run so a short label (\"art\")\n // isn't counted as covered because it sits inside a longer word\n // (\"apartments\") — that would silently hide a real content gap.\n const blob = ` ${flattenText(renderedHtml)} `;\n const missing = runs.filter((run) => !blob.includes(` ${run} `));\n const covered = runs.length - missing.length;\n const coveragePct = runs.length === 0 ? 100 : Math.round((covered / runs.length) * 100);\n return { total: runs.length, covered, missing, coveragePct };\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement, Node as HTMLNode } from \"node-html-parser\";\nimport type { Band, Cell, GridToken, Media, Node } from \"./types.js\";\nimport { parseGridToken } from \"./token.js\";\nimport {\n textRoleFromClass,\n headingLevel,\n mediaFromElement,\n stripAssetExt,\n blockPlainText,\n readBgSizing,\n textLeafStyle,\n cssProp,\n} from \"./leaf.js\";\n\nconst DEFAULT_TOKEN: GridToken = { cols: 1, raw: \"grid-1\" };\n\nconst isElement = (n: HTMLNode): n is HTMLElement =>\n (n as HTMLElement).tagName !== undefined && (n as HTMLElement).tagName !== null;\n\nconst hasClass = (el: HTMLElement, c: string) => el.classNames.split(/\\s+/).includes(c);\n\n/** A hidden feed-template prototype: the `display:none` `cagriditem` (id\n * `…-template`) that Blux clones client-side, once per feed record, to render a\n * feed grid at runtime. Its content is Handlebars tokens (`{{media}}`,\n * `{{title}}`) — never real content — so it must be dropped from the static\n * parse; keeping it renders the literal `{{…}}` text and an empty tile. The\n * visible feed tiles are materialized separately (from the feed records), not\n * from this element. */\nconst isFeedTemplate = (el: HTMLElement): boolean => {\n const id = el.getAttribute(\"id\") ?? \"\";\n const style = el.getAttribute(\"style\") ?? \"\";\n const hidden = /display\\s*:\\s*none/i.test(style);\n return hidden && (/-template$/.test(id) || /\\{\\{[a-z_]+\\}\\}/i.test(el.innerHTML));\n};\n\n/** A foreground media holder: a `block-media-holder`, a bare\n * `camediaload[data-media]`, or a `<video>`. */\nconst isMediaHolder = (el: HTMLElement): boolean =>\n hasClass(el, \"block-media-holder\") ||\n (hasClass(el, \"camediaload\") && !!el.getAttribute(\"data-media\")) ||\n el.tagName === \"VIDEO\";\n\nconst isLeafElement = (el: HTMLElement): boolean =>\n hasClass(el, \"block-title\") ||\n hasClass(el, \"block-body\") ||\n hasClass(el, \"block-subtitle\") ||\n isMediaHolder(el);\n\n/** A leaf `<a>`: a CTA button / text link with no structural descendants. Such\n * an anchor is not a wrapper — peeling through it (its inner text/spans are not\n * structural) would drop the link entirely — so it is treated as a structural\n * leaf. A linked media/grid wrapper (`<a>` containing a camediaload/grid) is NOT\n * a leaf anchor: it keeps peeling so the inner media parses normally. */\nconst isLeafAnchor = (el: HTMLElement): boolean =>\n el.tagName === \"A\" && collectStructuralChildren(el).length === 0;\n\n/** Is this element a structural boundary (a leaf, a grid row, or a token-bearing\n * cell/holder), as opposed to a pure wrapper div we should peel through? */\nconst isStructural = (el: HTMLElement): boolean =>\n isLeafElement(el) ||\n el.hasAttribute(\"data-exec\") || // Blux custom-code embed (e.g. map mount)\n hasClass(el, \"cagrid\") ||\n isLeafAnchor(el) ||\n parseGridToken(el.classNames) !== null;\n\n/** The \"card\" styling inherited from the wrapper div(s) peeled to reach a\n * structural child — a Blux card's inline `background-color` (on the `.blocksN`\n * fill) and the `padding` its `.blocksNcontainer` insets the content by, both of\n * which the plain peel drops. The nearest wrapper wins for each. `nested` marks\n * that the walk has passed a grid-cell boundary (block-grid-container /\n * block-subcontent): only cell-level container padding is captured — the\n * BAND-level container's padding is the band's own content padding, already\n * handled via the band style/blockClass defaults, and capturing it here would\n * inset the content twice. */\ntype CardStyle = {\n background?: string;\n padding?: string;\n /** A nested block-in-cell's inline `min-height` (e.g. the-tower band 1's\n * 80vh gradient panel): the cell's block pins its own box height, which the\n * plain peel would drop — the cell then renders at content height. Captured\n * only inside a cell (like padding): a band-level container's min-height is\n * the band's full-height chrome, handled at the band level. */\n minHeight?: string;\n /** A `block-background-layer` sibling's paint — the abs-fill div a nested\n * block uses for gradient (or plain) fills that `inlineBg` never sees\n * (they're not a wrapper `background-color`). Emitted as the `background`\n * shorthand so gradients survive. Image layers (camediaload) stay out of\n * scope — those are band-background territory. */\n layerBackground?: string;\n /** A peeled `valignmiddle` wrapper: the original vertically centers this\n * cell's content against its row siblings (band 6/12's side captions sit\n * centered on their photos; band 3's stats card centers in its column) —\n * and, paired with `minHeight`, centers content within the block's own box.\n * Rides the node style as the `_valign` presentation hint. */\n valign?: boolean;\n /** The cell belongs to a `cagridFlexHeight` grid: the original stretches\n * each cell's direct block to the full row height (`.cagriditem>div\n * {height:100%}`), so a painted block fills its whole column — not just its\n * content box. Rides painted container nodes as the `_fill: column` hint. */\n fill?: boolean;\n nested?: boolean;\n};\n/** A structural child plus any card styling peeled off its wrapper(s). */\ntype StructuralChild = { el: HTMLElement; card?: CardStyle };\n\n/** A grid-cell boundary: once the peel passes one of these, wrappers below are\n * cell-level (their padding is real content inset, not band chrome). */\nconst isCellBoundary = (el: HTMLElement): boolean =>\n hasClass(el, \"block-grid-container\") || hasClass(el, \"block-subcontent\");\n\n/** Inline `background-color` off an element's style attribute, ignoring the\n * transparent default (which is not a deviation worth carrying). */\nfunction inlineBg(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"background-color\")?.trim();\n if (!c) return undefined;\n const low = c.toLowerCase().replace(/\\s+/g, \"\");\n if (low === \"transparent\" || low === \"rgba(0,0,0,0)\") return undefined;\n return c;\n}\n\n/** Inline `padding` shorthand off an element's style attribute, ignoring an\n * all-zero value (no inset worth carrying). */\nfunction inlinePadding(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"padding\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem)?\\s*)+$/i.test(c)) return undefined;\n return c;\n}\n\n/** Inline `min-height` off an element's style attribute, ignoring zero/auto\n * (no sizing worth carrying). Blux repeats the value on the item AND its\n * inner `height: 1px` container — same value, either capture works. */\nfunction inlineMinHeight(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"min-height\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem|vh|vw)?|auto)$/i.test(c)) return undefined;\n return c;\n}\n\n/** The paint of a `block-background-layer` child, if `el` has one: the\n * abs-fill div that fills a nested block-in-cell (gradient or plain color —\n * carried in the `background` shorthand, which `inlineBg`'s background-color\n * read never sees). `none`/`transparent` carry no paint. */\nfunction layerBackground(el: HTMLElement): string | undefined {\n for (const child of el.childNodes) {\n if (!isElement(child) || !hasClass(child, \"block-background-layer\")) continue;\n const s = child.getAttribute(\"style\") ?? \"\";\n const bg = (cssProp(s, \"background\") ?? cssProp(s, \"background-color\"))?.trim();\n if (bg && !/^(none|transparent)$/i.test(bg)) return bg;\n }\n return undefined;\n}\n\n/** The child elements that carry structure, peeling pure wrapper divs. A peeled\n * card wrapper's inline background-color and (cell-level) content padding ride\n * along to the structural node it wraps (the nearest wrapper wins for each) so\n * a card's fill and inset survive the peel.\n *\n * Two wrapper shapes are PROMOTED to structural instead of peeled, so they\n * parse to their own stack and the styling/containment attaches exactly once:\n * - a multi-child `block-subcontent`: a Blux grid CELL groups its blocks —\n * the original contains their margins per cell (a block-content clearfix\n * blocks the collapse), so flattening the boundary away merges rhythm that\n * the original keeps separate;\n * - a padded wrapper around ≥2 structural children: threading the padding\n * onto each child would inset every one of them (duplication) — the group\n * is the thing that's padded. */\nexport function collectStructuralChildren(\n el: HTMLElement,\n inherited: CardStyle = {},\n): StructuralChild[] {\n const out: StructuralChild[] = [];\n // A nested block's background-layer sibling paints the whole block: fold it\n // onto the card its content siblings inherit. Band-level layers stay out —\n // a band's background is SectionBand territory.\n const layerBg = inherited.nested === true ? layerBackground(el) : undefined;\n const base: CardStyle =\n layerBg !== undefined ? { ...inherited, layerBackground: layerBg } : inherited;\n for (const child of el.childNodes) {\n if (!isElement(child)) continue;\n // The layer itself is pure paint (abs-fill, never content) — consumed above.\n if (hasClass(child, \"block-background-layer\")) continue;\n // A hidden feed-template prototype ({{…}} tokens, cloned by JS per record)\n // is not real content — drop it so the band doesn't render literal braces.\n if (isFeedTemplate(child)) continue;\n const nested = base.nested === true || isCellBoundary(child);\n const background = inlineBg(child) ?? base.background;\n // Cell-level wrappers only — a band-level container's padding (and\n // min-height: the full-height band chrome) is the band's own concern\n // (see CardStyle.nested).\n const padding = (nested ? inlinePadding(child) : undefined) ?? base.padding;\n const minHeight = (nested ? inlineMinHeight(child) : undefined) ?? base.minHeight;\n const valign = (nested && hasClass(child, \"valignmiddle\")) || base.valign === true;\n const card: CardStyle = {\n ...(background !== undefined ? { background } : {}),\n ...(padding !== undefined ? { padding } : {}),\n ...(minHeight !== undefined ? { minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(valign ? { valign } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n // A boxed wrapper (padding, min-height, or a paint layer) around ≥2\n // structural children is PROMOTED so the box applies exactly once —\n // threading it onto each child would inset/size/paint every one of them.\n const group =\n !isStructural(child) &&\n ((hasClass(child, \"block-subcontent\") && parseGridToken(child.classNames) === null) ||\n (nested &&\n (inlinePadding(child) !== undefined ||\n inlineMinHeight(child) !== undefined ||\n layerBackground(child) !== undefined))) &&\n collectStructuralChildren(child).length >= 2;\n if (isStructural(child)) {\n // A structural child's card carries ONLY what the peeled wrappers above\n // it accumulated — never the child's own inline style: leaves self-carry\n // it (textLeafStyle, readImgSizing) and boxing it here would apply it\n // twice (band-3 stat labels' 8px inset, once threaded contexts reach\n // inside token-bearing holders).\n const inheritedCard: CardStyle = {\n ...(base.background !== undefined ? { background: base.background } : {}),\n ...(base.padding !== undefined ? { padding: base.padding } : {}),\n ...(base.minHeight !== undefined ? { minHeight: base.minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(base.valign === true ? { valign: true } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n out.push({\n el: child,\n ...(Object.keys(inheritedCard).length > 0 ? { card: inheritedCard } : {}),\n });\n } else if (group) {\n // The promoted wrapper IS the box — its own values ride the group card.\n // Its own paint layer sits one level down where the pre-scan (which only\n // reads el's direct children) won't see it again — fold it on here. A\n // promoted cell boundary keeps its inner walk's capture instead (its\n // own pre-scan runs nested).\n const ownLayer = !isCellBoundary(child) ? layerBackground(child) : undefined;\n const groupCard: CardStyle =\n ownLayer !== undefined ? { ...card, layerBackground: ownLayer } : card;\n out.push({\n el: child,\n ...(Object.keys(groupCard).length > 0 ? { card: groupCard } : {}),\n });\n } else {\n out.push(...collectStructuralChildren(child, card));\n }\n }\n return out;\n}\n\n/** Does a parsed leaf node carry real text (not just empty markup)? Gates caption\n * capture so an empty `.block-*` inside a media holder adds nothing. Routes\n * heading/body through `blockPlainText` (same as the subtitle path) so an\n * entity- or `&nbsp;`-only block reads as empty, not as text. */\nfunction nodeHasText(n: Node): boolean {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) !== \"\";\n if (n.kind === \"subtitle\") return n.text.trim() !== \"\";\n return false;\n}\n\n/** Is a caption element (or an ancestor up to and including the media holder)\n * marked `class:\"disable\"`? Blux omits disabled blocks from the rendered HTML,\n * but guard anyway so hidden copy is never leaked into a caption. */\nfunction isDisabledWithin(caption: HTMLElement, holder: HTMLElement): boolean {\n let a: HTMLElement | null | undefined = caption;\n while (a) {\n if (hasClass(a, \"disable\")) return true;\n if (a === holder) return false;\n a = a.parentNode as HTMLElement | null | undefined;\n }\n return false;\n}\n\n/** Parse one element into a grid Node. Leaves dispatch by role; everything else\n * becomes a row / stack / single / raw via parseContainer. */\nexport function parseNode(el: HTMLElement, nested = false): Node {\n if (hasClass(el, \"block-title\") && /^H[1-6]$/.test(el.tagName ?? \"\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"heading\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n level: headingLevel(el),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-body\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"body\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-subtitle\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n // Route through blockPlainText (not raw `.text`): a `<br>` in a display\n // subtitle survives as a newline while insignificant source whitespace\n // collapses — `.text` alone can't tell a hard break from source formatting.\n return {\n kind: \"subtitle\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n text: blockPlainText(el.innerHTML),\n };\n }\n if (isMediaHolder(el)) {\n const media = mediaFromElement(el);\n if (media) {\n // Blux slider tiles nest the slide's CAPTION inside the media holder (the\n // holder is `data-bgmedia` and the copy overlays it). The holder is an\n // opaque media leaf, so those captions would be dropped. When one carries\n // block-title/body/subtitle text, emit the media PLUS the caption(s) as a\n // stack so the copy survives. Pure-media holders — the vast majority —\n // stay a bare media node, byte-identical (no `.block-*` descendant → no\n // extra work). This does NOT change the peel boundary: the holder is still\n // a structural leaf; only its own internal text is recovered here.\n const captions = el\n .querySelectorAll(\".block-title, .block-body, .block-subtitle\")\n .filter((c) => !isDisabledWithin(c, el))\n .map((c) => parseNode(c))\n .filter(nodeHasText);\n if (captions.length) {\n return { kind: \"stack\", children: [{ kind: \"media\", media }, ...captions] };\n }\n return { kind: \"media\", media };\n }\n }\n if (el.hasAttribute(\"data-exec\")) {\n // Custom-code embed (map, third-party widget). Keep the whole subtree —\n // including id=\"burbank_map\" and any inline initMap/KmlLayer scripts — so\n // extract-map can read it and Grid.svelte can render it verbatim.\n return { kind: \"raw\", html: el.outerHTML };\n }\n if (el.tagName === \"A\") {\n // A leaf CTA button / text link (isLeafAnchor). Preserve the whole anchor —\n // href + label — verbatim so the render layer keeps the clickable link.\n return { kind: \"raw\", html: el.outerHTML };\n }\n return parseContainer(el, nested);\n}\n\n/** Attach a peeled card wrapper's styling to a container node's `style`\n * (row/stack only — a Blux card wraps a grid or a stack of blocks, never a bare\n * leaf). The background is the card's fill; the padding is the content inset its\n * `.blocksNcontainer` applies. Padding rides only when a background marks this a\n * real card, so a plain band container's padding (already handled via the band's\n * blockClass defaults) is not double-captured onto a nested node. */\n/** Does a container node's style carry visible paint or sizing — the cues that\n * make a `cagridFlexHeight` column-fill hint worth emitting? */\nconst nodePainted = (style: Record<string, string>): boolean =>\n \"background\" in style || \"background-color\" in style || \"min-height\" in style;\n\nfunction withCardStyle(node: Node, card?: CardStyle): Node {\n if (\n !card ||\n (card.background === undefined &&\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign &&\n !card.fill)\n )\n return node;\n // background-color first, `background` shorthand second: when a block has\n // both a wrapper fill and a paint layer, the fuller layer paint wins (CSS\n // last-declaration order in the emitted style attribute).\n if (node.kind === \"row\" || node.kind === \"stack\") {\n const style: Record<string, string> = { ...(node.style ?? {}) };\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n // The FlexHeight column fill only matters when there's paint/sizing to\n // extend — an unpainted stack stretching is visually identity.\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n if (Object.keys(style).length === 0) return node;\n return { ...node, style };\n }\n // A MEDIA leaf under a min-height wrapper keeps its leaf shape: the frame\n // height folds into media.minHeight (slider slides repeat the same value on\n // holder and wrapper) instead of boxing the slide in a synthetic stack —\n // which would demote Carousel classification (carouselSlides matches bare\n // media) and turn working sliders into static galleries.\n if (node.kind === \"media\" && card.padding === undefined && !card.valign) {\n if (card.minHeight !== undefined && node.media.minHeight === undefined)\n return { ...node, media: { ...node.media, minHeight: card.minHeight } };\n return node;\n }\n // A boxed wrapper around a bare non-media leaf (padding, min-height, valign,\n // or a paint layer — e.g. band 11's `20px 0 30px` cell container wrapping a\n // single heading, or a flush gradient card holding one): the leaf has no\n // container-style slot, so a synthetic one-child stack carries the box. A\n // wrapper background-color ALONE still drops, as before — those (the\n // carousel captions) are handled by their own render path, and the Grid\n // tree must not invent a box for them.\n if (\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign\n )\n return node;\n const style: Record<string, string> = {};\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n return { kind: \"stack\", children: [node], style };\n}\n\n/** Parse a wrapper/cell/band-body element: a row when it is a grid or holds\n * ≥2 token-bearing children, else a stack / single / raw. */\nexport function parseContainer(el: HTMLElement, nested = false): Node {\n // A container that IS a cell/grid element starts its walk inside the cell\n // context, so its own inner wrappers' padding is captured (CardStyle.nested).\n // A PROMOTED wrapper (a boxed group inside a cell) threads `nested` in from\n // its call site — restarting outside the cell context would silently drop\n // the padding/valign its inner wrappers still carry. A cagridFlexHeight grid\n // additionally stretches each cell's direct block to the full row height —\n // its cells' cards carry `fill` so painted blocks emit the `_fill: column`\n // hint (the fill never threads past the cell: the cell's own parseContainer\n // restarts the walk without it).\n const kids = collectStructuralChildren(\n el,\n isCellBoundary(el) || hasClass(el, \"cagrid\")\n ? { nested: true, ...(hasClass(el, \"cagridFlexHeight\") ? { fill: true } : {}) }\n : nested\n ? { nested: true }\n : {},\n );\n // Parse each structural child up front, then drop any that collapse to an\n // EMPTY raw — an empty `.caslider`/wrapper (no static slides, JS-hydrated)\n // yields `raw:\"\"`, which would otherwise survive as a phantom sibling (e.g.\n // turning a lone poster image into `[media, empty-block]`). A non-empty raw\n // (a `[data-exec]` embed, a leaf `<a>`) always has real html, so it is kept.\n // A structural child may carry `card` styling (background + padding) peeled off\n // a card wrapper — it rides onto that child's container node via withCardStyle.\n const parsed = kids\n .map((k) => ({\n token: parseGridToken(k.el.classNames),\n node: withCardStyle(parseNode(k.el, k.card?.nested === true), k.card),\n }))\n .filter((p) => !(p.node.kind === \"raw\" && p.node.html.trim() === \"\"));\n const isGrid = hasClass(el, \"cagrid\");\n const tokenCount = parsed.filter((p) => p.token).length;\n // A LONE width-constrained cell (grid-2-r60 etc.) still needs its row: the\n // token IS the content column's width (band 9/11's 60% column) — flattening\n // it to a stack silently renders the content full-width. cols 1 / \"any\"\n // carry no width constraint, so a lone one still flattens.\n const hasWidthToken = parsed.some((p) => p.token && p.token.cols !== 1 && p.token.cols !== \"any\");\n\n if ((isGrid || tokenCount >= 2 || hasWidthToken) && parsed.length > 0) {\n const cells: Cell[] = parsed.map((p) => ({ token: p.token ?? DEFAULT_TOKEN, node: p.node }));\n if (hasClass(el, \"caslider\")) {\n // A source slider row. `data-columns` = slides visible at a time; only a\n // positive integer is meaningful (conditional build keeps `columns`\n // absent, not undefined, under exactOptionalPropertyTypes).\n const cols = Number(el.getAttribute(\"data-columns\"));\n const slider = Number.isInteger(cols) && cols > 0 ? { columns: cols } : {};\n return { kind: \"row\", cells, slider };\n }\n return { kind: \"row\", cells };\n }\n const [only] = parsed;\n if (parsed.length === 1 && only) return only.node;\n if (parsed.length === 0) return { kind: \"raw\", html: rawInnerHtml(el) };\n return { kind: \"stack\", children: parsed.map((p) => p.node) };\n}\n\n/** The raw-fallback innerHTML, blanked when it is a JS-hydrated feed grid's\n * leftover `{{…}}` template (a container with no structural content of its\n * own). Real content is already extracted as nodes before this fallback is\n * reached, so Handlebars tokens here are never real content — dumping them\n * would render literal `{{media}}` text. A genuinely-unrecognized container\n * (no tokens) still surfaces verbatim as before. */\nfunction rawInnerHtml(el: HTMLElement): string {\n const html = el.innerHTML;\n return /\\{\\{[a-z_]+\\}\\}/i.test(html) ? \"\" : html;\n}\n\nconst BAND_ID_RE = /^page-block-(\\d+)$/;\n// The band wrapper's blocksN class — kept off the HTML because site.json\n// items[].class is unreliable (null for 7/16 the-pointe blocks).\nconst BLOCK_CLASS_RE = /\\bblocks\\d+\\b/;\n\n/** Read the band-level background media off a `camediaload` band wrapper. */\nfunction bandBackground(el: HTMLElement): Media | undefined {\n if (!hasClass(el, \"camediaload\")) return undefined;\n const rawId = el.getAttribute(\"data-media\");\n if (!rawId) return undefined;\n const ext = el.getAttribute(\"data-ext\") ?? undefined;\n const base = el.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n // A band background carries its own render sizing (background-size/position);\n // a corner-anchored `auto` accent must not be centered + full-bleed.\n ...readBgSizing(el),\n };\n}\n\n/** Parse the rendered Blux index.html into the page's top-level band tree. */\nexport function parseGridBands(html: string): Band[] {\n const root = parse(html);\n const content = root.querySelector(\"#page-content\");\n if (!content) return [];\n const bands: Band[] = [];\n for (const child of content.childNodes) {\n if (!isElement(child)) continue;\n const m = BAND_ID_RE.exec(child.getAttribute(\"id\") ?? \"\");\n if (!m) continue;\n const idStr = m[1];\n if (idStr === undefined) continue;\n const background = bandBackground(child);\n const blockClass = BLOCK_CLASS_RE.exec(child.classNames)?.[0];\n bands.push({\n index: Number(idStr),\n ...(blockClass ? { blockClass } : {}),\n ...(background ? { background } : {}),\n root: parseContainer(child),\n });\n }\n return bands;\n}\n","import type { GridToken } from \"./types.js\";\n\nconst TOKEN_RE = /\\bgrid-(\\d+|any)(?:-(r|s)(\\d+))?\\b/;\n\n/** Parse the `grid-*` layout token out of an element's class string.\n * `grid-2` -> equal 2-col; `grid-2-r60` -> 60% of a 2-col row; `grid-1-s40` ->\n * 1-col with 40px inter-cell spacing (the `s` suffix is the grid's gap, matching\n * `data-spacing`, NOT a width — width comes from the column count). Returns null\n * when the class has no grid token (e.g. `grid-container`, no column count). */\nexport function parseGridToken(className: string): GridToken | null {\n const m = TOKEN_RE.exec(className);\n if (!m) return null;\n const cols = m[1] === \"any\" ? \"any\" : Number(m[1]);\n const token: GridToken = { cols, raw: m[0] };\n if (m[2] === \"r\") token.ratio = Number(m[3]);\n if (m[2] === \"s\") token.spacing = Number(m[3]);\n return token;\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement } from \"node-html-parser\";\nimport type { Media, VideoPlayback } from \"./types.js\";\n\nconst ROLE_RE = /\\btext\\d+\\b/;\n\n/** The Blux text role (`text5`, `text11`, …) carried on a block-title/body/subtitle\n * element's class in the rendered HTML, or undefined when none is present. */\nexport function textRoleFromClass(className: string): string | undefined {\n return ROLE_RE.exec(className)?.[0];\n}\n\n/** The heading level (1..6) for an h1..h6 element. */\nexport function headingLevel(el: HTMLElement): number {\n const m = /^H([1-6])$/.exec(el.tagName ?? \"\");\n return m ? Number(m[1]) : 2;\n}\n\n/** Plain text of a block's inner HTML for a title field: a hard line break\n * (`<br>`, with or without attributes/self-close) becomes a newline; every other\n * tag drops and HTML entities decode (`Bar &amp; Grill` → `Bar & Grill`); all\n * source-formatting whitespace collapses to single spaces. Robust to\n * pretty-printed exports — insignificant newlines in the markup are NOT mistaken\n * for hard breaks — by routing `<br>` through a sentinel that survives the\n * whitespace collapse. */\nexport function blockPlainText(html: string): string {\n const BR = \"\\uE000\";\n // node-html-parser `.text` strips tags AND decodes entities; the <br>→BR swap\n // runs first so hard breaks survive as the sentinel through the collapse below.\n const text = parse(html.replace(/<br\\b[^>]*>/gi, BR)).text;\n return text\n .replace(/\\s+/g, \" \")\n .replace(/ *\\uE000 */g, \"\\n\")\n .trim();\n}\n\n/** The last path segment of a CDN url, sans extension (the Blux asset uuid). */\nfunction uuidFromUrl(url: string): { id: string; ext?: string } {\n const base = url.split(/[?#]/)[0] ?? \"\";\n const file = base.split(\"/\").pop() ?? \"\";\n const dot = file.lastIndexOf(\".\");\n return dot > 0 ? { id: file.slice(0, dot), ext: file.slice(dot + 1) } : { id: file };\n}\n\n/** Blux `data-media` is sometimes `uuid.ext` and sometimes a bare `uuid`; the\n * existing asset pipeline keys on the bare uuid (extension stripped), matching the\n * video path. Strip a trailing `.<ext>` when `data-ext` names it. */\nexport function stripAssetExt(rawId: string, ext?: string): string {\n return ext && rawId.endsWith(`.${ext}`) ? rawId.slice(0, -(ext.length + 1)) : rawId;\n}\n\n/** Read a single CSS declaration's value out of an inline `style` string. */\nexport function cssProp(style: string, prop: string): string | undefined {\n const m = new RegExp(`(?:^|;|\\\\s)${prop}\\\\s*:\\\\s*([^;]+)`, \"i\").exec(style);\n return m?.[1]?.trim();\n}\n\nconst MARGIN_UTIL_RE = /\\bmargin-(\\d+)(r|l|t|b)\\b/g;\nconst MARGIN_SIDES = { r: \"right\", l: \"left\", t: \"top\", b: \"bottom\" } as const;\n\n/** Decode Blux margin utility classes: `margin-20r` → `margin-right: 20%`.\n * Only `margin-N(r|l|t|b)` decodes — the `pd_*` padding utilities are always\n * duplicated inline in the export, so inline capture already covers them. */\nexport function utilityStylesFromClass(className: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const m of className.matchAll(MARGIN_UTIL_RE)) {\n const [, n, side] = m;\n if (n && side) out[`margin-${MARGIN_SIDES[side as keyof typeof MARGIN_SIDES]}`] = `${n}%`;\n }\n return out;\n}\n\n/** A text leaf's style deviations: the allowlisted inline declarations (`color`,\n * `padding`, `margin*` — everything else is theme noise) merged with the margin\n * utilities decoded off its class. An inline declaration wins over a class\n * utility on conflict. Null when the leaf carries neither, so callers can keep\n * the `style` key absent (exactOptionalPropertyTypes). */\nexport function textLeafStyle(el: HTMLElement): Record<string, string> | null {\n const out = utilityStylesFromClass(el.classNames ?? \"\");\n for (const decl of (el.getAttribute(\"style\") ?? \"\").split(\";\")) {\n const colon = decl.indexOf(\":\");\n if (colon < 0) continue;\n const prop = decl.slice(0, colon).trim().toLowerCase();\n const value = decl.slice(colon + 1).trim();\n if (!value) continue;\n if (prop === \"color\" || prop === \"padding\" || prop.startsWith(\"margin\")) out[prop] = value;\n }\n return Object.keys(out).length ? out : null;\n}\n\nconst CENTERED = new Set([\"center\", \"center center\", \"50% 50%\", \"50%\"]);\n\n/** Background sizing off a BAND-background wrapper's inline style: its\n * `background-size` → `fit` (a background's `auto`/`contain` is meaningful — a\n * native-size decorative accent, not a full-bleed `cover`) and its\n * `background-position` → `position` (e.g. \"right bottom\"). Unlike `readImgSizing`\n * (foreground), `auto` is kept; `cover` and a centered position are the render\n * defaults, so they are left absent to keep the manifest to deviations only. */\nexport function readBgSizing(el: HTMLElement): Pick<Media, \"fit\" | \"position\"> {\n const style = el.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"fit\" | \"position\"> = {};\n const size = cssProp(style, \"background-size\")?.toLowerCase();\n if (size === \"auto\" || size === \"contain\") out.fit = size;\n const pos = cssProp(style, \"background-position\");\n if (pos && !CENTERED.has(pos.toLowerCase())) out.position = pos;\n return out;\n}\n\n/** Intrinsic render sizing off a foreground image holder: the inline pixel\n * `width`, the `.mediaRatio` `data-og-ratio` (→ `aspect`), and the\n * `background-size` (→ `fit`, only when contain/cover — a background's `auto`\n * is not foreground sizing). Each field is present only when the source has it,\n * so a plain holder still yields a bare `Media`. */\nfunction readImgSizing(holder: HTMLElement): Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> {\n const style = holder.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> = {};\n // Only a pixel width is a faithful intrinsic size. A `%`/`vw`/`em`/`calc()`\n // width is relative to context and must NOT be mistaken for px (which the\n // render layer would then apply literally) — skip it, leaving `width` absent.\n const w = cssProp(style, \"width\");\n const wpx = w ? /^(\\d+(?:\\.\\d+)?)px$/i.exec(w) : null;\n if (wpx?.[1]) out.width = Math.round(parseFloat(wpx[1]));\n const ogr = holder.querySelector(\".mediaRatio\")?.getAttribute(\"data-og-ratio\");\n if (ogr) {\n const n = Number(ogr);\n if (Number.isFinite(n)) out.aspect = Math.round(n * 1000) / 1000;\n }\n const fit = cssProp(style, \"background-size\")?.toLowerCase();\n if (fit === \"contain\" || fit === \"cover\") out.fit = fit;\n // The holder's inline min-height (e.g. \"80vh\" on a slider slide) is the\n // height the export reserves for a cover-rendered frame — keep it.\n const mh = cssProp(style, \"min-height\");\n if (mh) out.minHeight = mh;\n return out;\n}\n\n/** Playback semantics from a `<video>`'s boolean attributes — only those PRESENT\n * are set (an absent field = attribute absent). Undefined when none is present. */\nfunction readVideoPlayback(video: HTMLElement): VideoPlayback | undefined {\n const flags = [\"controls\", \"playsinline\", \"autoplay\", \"loop\", \"muted\"] as const;\n const pb: VideoPlayback = {};\n for (const f of flags) if (video.hasAttribute(f)) pb[f] = true;\n return Object.keys(pb).length ? pb : undefined;\n}\n\n/** Intrinsic aspect for a foreground `<video>`, reserved on a nearby\n * `.ib[data-og-ratio]` holder OR a `.mediaRatio` (inline `padding-bottom:NN%`).\n * Values are percent-suffixed strings (e.g. \"56.25%\") — strip the `%` (raw\n * `Number()` NaNs), then reuse the `aspect` = height-%-of-width convention.\n * Fail-safe: no parseable ratio → undefined (video keeps its bare shape). */\nfunction readVideoAspect(video: HTMLElement): number | undefined {\n let raw: string | undefined;\n let anc: HTMLElement | null | undefined = video.parentNode;\n for (let i = 0; i < 3 && anc && !raw; i++) {\n raw = anc.getAttribute?.(\"data-og-ratio\") ?? undefined;\n if (!raw) {\n const mr = anc.querySelector?.(\".mediaRatio\");\n raw = mr ? cssProp(mr.getAttribute(\"style\") ?? \"\", \"padding-bottom\") : undefined;\n }\n anc = anc.parentNode as HTMLElement | null | undefined;\n }\n if (!raw) return undefined;\n const n = parseFloat(raw.replace(/%\\s*$/, \"\"));\n return Number.isFinite(n) ? Math.round(n * 1000) / 1000 : undefined;\n}\n\n/** Resolve the media an element carries: a `.camediaload` descendant (image, via\n * `data-media`) or a `<video>` (via its src uuid). Returns null when there is none. */\nexport function mediaFromElement(el: HTMLElement): Media | null {\n if (el.tagName === \"VIDEO\") {\n const src = el.getAttribute(\"src\") ?? \"\";\n const { id, ext } = uuidFromUrl(src);\n if (!id) return null;\n // The full CDN url sits on `<video src>`; capture its prefix as `base` (the\n // same field an image carries from `data-base`) so `mediaCdnUrl` rebuilds it\n // OFFLINE. Without this a video resolves only via the IR sourceUrl, i.e. it\n // depends on site.json listing the asset — breaking convert's offline\n // invariant even though the url is right here in the markup.\n const clean = src.split(/[?#]/)[0] ?? \"\";\n const slash = clean.lastIndexOf(\"/\");\n const base = slash >= 0 ? clean.slice(0, slash + 1) : undefined;\n const aspect = readVideoAspect(el);\n const playback = readVideoPlayback(el);\n return {\n kind: \"video\",\n assetId: id,\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...(aspect !== undefined ? { aspect } : {}),\n ...(playback ? { playback } : {}),\n };\n }\n const img =\n el.classList.contains(\"camediaload\") && el.getAttribute(\"data-media\")\n ? el\n : el.querySelector(\".camediaload[data-media]\");\n if (img) {\n const rawId = img.getAttribute(\"data-media\");\n if (rawId) {\n const ext = img.getAttribute(\"data-ext\") ?? undefined;\n const base = img.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...readImgSizing(img),\n };\n }\n }\n const video = el.querySelector(\"video\");\n if (video) return mediaFromElement(video);\n return null;\n}\n","import type { Band, Cell, Media, Node, Widget } from \"./types.js\";\nimport type { CarouselSlide, CarouselSpec, SliceSpec } from \"./slice-spec.js\";\nimport { blockPlainText } from \"./leaf.js\";\n\n/** Depth-first collect of every `media` node's `Media` in a subtree. */\nexport function collectMedia(node: Node): Media[] {\n switch (node.kind) {\n case \"media\":\n return [node.media];\n case \"row\":\n return node.cells.flatMap((c) => collectMedia(c.node));\n case \"stack\":\n return node.children.flatMap(collectMedia);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of text nodes (heading/body/subtitle). */\nexport function collectText(node: Node): Node[] {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectText(c.node));\n case \"stack\":\n return node.children.flatMap(collectText);\n case \"media\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every widget in a subtree. */\nexport function collectWidgets(node: Node): Widget[] {\n switch (node.kind) {\n case \"widget\":\n return [node.widget];\n case \"row\":\n return node.cells.flatMap((c) => collectWidgets(c.node));\n case \"stack\":\n return node.children.flatMap(collectWidgets);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every `raw` node in a subtree. */\nfunction collectRaws(node: Node): Node[] {\n switch (node.kind) {\n case \"raw\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectRaws(c.node));\n case \"stack\":\n return node.children.flatMap(collectRaws);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n return [];\n }\n}\n\n/** The root row NODE, or null when the root is not a single row. A `stack`\n * whose only child is a row also counts (Blux wraps rows in holders). */\nexport function topRowNode(node: Node): Extract<Node, { kind: \"row\" }> | null {\n if (node.kind === \"row\") return node;\n if (node.kind === \"stack\" && node.children.length === 1) {\n const [only] = node.children;\n if (only && only.kind === \"row\") return only;\n }\n return null;\n}\n\n/** The cells of the root row, or null when the root is not a single row. */\nexport function topRow(node: Node): Cell[] | null {\n return topRowNode(node)?.cells ?? null;\n}\n\n/** A `raw` node carrying no rendered text or nested block — the shape a\n * client-injected mount (e.g. the map container) parses to. */\nexport function isEmptyRaw(node: Node): boolean {\n if (node.kind !== \"raw\") return false;\n const text = node.html.replace(/<[^>]*>/g, \"\").trim();\n return text.length === 0;\n}\n\n/** Options for the classifier. `isMapMount` is injected by plan 4\n * (`extract-map.ts`); by default nothing is recognized as a map. */\nexport type ClassifyOptions = {\n isMapMount?: (node: Node) => boolean;\n};\n\n/** The band's slice-zone base carried onto every spec (conditional spread keeps\n * `blockClass`/`background` absent, not `undefined`, under\n * exactOptionalPropertyTypes). */\nfunction base(band: Band): { index: number; blockClass?: string; background?: Media } {\n return {\n index: band.index,\n ...(band.blockClass ? { blockClass: band.blockClass } : {}),\n ...(band.background ? { background: band.background } : {}),\n };\n}\n\n/** Plain text of a heading/subtitle/body node. Hard line breaks (Blux `<br>`)\n * survive as newlines so the render layer can split a display title back into\n * lines; all other tags and source whitespace collapse to single spaces. */\nfunction nodeText(node: Node): string {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n // Raw markup — `<br>` → newline, other tags + source formatting → spaces.\n return blockPlainText(node.html);\n case \"subtitle\":\n // Already normalized at parse via blockPlainText (entities decoded, hard\n // breaks as newlines, source whitespace collapsed) — pass it through.\n return node.text.trim();\n case \"row\":\n case \"stack\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return \"\";\n }\n}\n\n/** The single media of a pure-media cell, or null if the cell isn't pure media. */\n/** See through synthetic style boxes — a one-child stack carrying a peeled\n * wrapper's padding/fill — when pattern-matching. The box is presentation, not\n * structure: a cell must not flip from SplitFeature/Gallery to Grid just\n * because its container gained a captured inset. */\nfunction unboxed(node: Node): Node {\n let n = node;\n while (n.kind === \"stack\" && n.children.length === 1) {\n const only = n.children[0];\n if (!only) break;\n n = only;\n }\n return n;\n}\n\nfunction pureCellMedia(cell: Cell): Media | null {\n const n = unboxed(cell.node);\n return n.kind === \"media\" ? n.media : null;\n}\n\n/** TitleBand/Hero presentation metadata: the heading's textN role + h-level and\n * the subtitle's role. Carried alongside the plain-string text (page-doc) so the\n * render applies the right display font/tag — band 15's script accent heading\n * (`h2.text11`) must not render like a plain `text5` title. */\nfunction textRoleMeta(\n heading: Node | undefined,\n subtitle: Node | undefined,\n): { headingRole?: string; headingLevel?: number; subtitleRole?: string } {\n return {\n ...(heading?.kind === \"heading\" && heading.role ? { headingRole: heading.role } : {}),\n ...(heading?.kind === \"heading\" ? { headingLevel: heading.level } : {}),\n ...(subtitle?.kind === \"subtitle\" && subtitle.role ? { subtitleRole: subtitle.role } : {}),\n };\n}\n\n/** A cell's effective column share as a percentage, from its grid token.\n *\n * Width comes from the explicit `ratio` (`grid-2-r60` → 60) or, absent that,\n * an equal split of the column count. `spacing` (the `s` suffix) is a gap, not\n * a width, so it never factors in here. The `\"any\"` → 50 fallback assumes a\n * 2-cell row — the only shape that reaches here today (SplitFeature requires\n * exactly two cells); a bare-`any` cell in a wider row would need real division. */\nfunction cellRatio(cell: Cell): number {\n const t = cell.token;\n if (typeof t.ratio === \"number\") return t.ratio;\n if (t.cols === \"any\") return 50;\n return Math.round(100 / t.cols);\n}\n\nconst isTextNode = (n: Node): boolean =>\n n.kind === \"heading\" || n.kind === \"body\" || n.kind === \"subtitle\";\n\n/** A text node with no rendered text — an empty hero body block (`<p></p>`) or\n * a whitespace-only heading/subtitle. Not a real caption line. */\nconst isBlankText = (n: Node): boolean => {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) === \"\";\n if (n.kind === \"subtitle\") return n.text.trim() === \"\";\n return false;\n};\n\n/** The carousel slides of a slider row, or null when any cell isn't a media\n * slide. A slide is a bare `media` cell, or a stack whose FIRST child is media\n * followed only by text nodes — the band-8 gallery slide `stack[media,\n * heading]` and the full-page hero slide `stack[media, heading, body]` (title +\n * location). The heading is the caption; the first non-blank body/subtitle\n * after it is the `subcaption` (the hero's location line). A slide with a\n * non-text tail (a nested row/media) is richer than a captioned slide, so the\n * band stays a faithful Grid. ≥2 qualifying slides required. */\nfunction carouselSlides(cells: Cell[]): CarouselSlide[] | null {\n const out: CarouselSlide[] = [];\n for (const c of cells) {\n const n = c.node;\n if (n.kind === \"media\") {\n out.push({ media: n.media });\n continue;\n }\n if (n.kind === \"stack\" && n.children.length >= 2) {\n const [m, ...rest] = n.children;\n const h = rest.find((r) => r.kind === \"heading\");\n if (m?.kind === \"media\" && h?.kind === \"heading\" && rest.every(isTextNode)) {\n // The secondary line: the first body/subtitle after the title that\n // carries real text (the hero's location; skip empty body blocks).\n const sub = rest.find(\n (r) => r !== h && (r.kind === \"body\" || r.kind === \"subtitle\") && !isBlankText(r),\n );\n const subcaption =\n sub?.kind === \"body\"\n ? { html: sub.html, ...(sub.role ? { role: sub.role } : {}) }\n : sub?.kind === \"subtitle\"\n ? { html: sub.text, ...(sub.role ? { role: sub.role } : {}) }\n : undefined;\n out.push({\n media: m.media,\n caption: { html: h.html, level: h.level, ...(h.role ? { role: h.role } : {}) },\n ...(subcaption ? { subcaption } : {}),\n });\n continue;\n }\n }\n return null;\n }\n return out.length >= 2 ? out : null;\n}\n\n/** If every cell of a row is exactly one media node, return them in order. */\nfunction galleryMedia(cells: Cell[]): Media[] | null {\n const out: Media[] = [];\n for (const c of cells) {\n const m = pureCellMedia(c);\n if (!m) return null;\n out.push(m);\n }\n return out.length >= 2 ? out : null;\n}\n\n/** Return a copy of the tree with every node matching `isMapMount` replaced by a\n * `widget:map` node. Pure — does not mutate the input. Top-down: a matched\n * container is replaced whole, so its children are never visited. */\nfunction rewriteMapMounts(node: Node, isMapMount: (n: Node) => boolean): Node {\n if (isMapMount(node)) return { kind: \"widget\", widget: { type: \"map\" } };\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({\n token: c.token,\n node: rewriteMapMounts(c.node, isMapMount),\n })),\n // Preserve the row's own markers when rebuilding: dropping the slider\n // would silently demote a Carousel to Grid, and dropping the style would\n // lose a card background — for every band whenever a map config exists.\n ...(node.slider ? { slider: node.slider } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"stack\":\n return {\n kind: \"stack\",\n children: node.children.map((n) => rewriteMapMounts(n, isMapMount)),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return node;\n }\n}\n\n/** The single significant child of a container (ignoring empty raw), or the node\n * itself. Used to detect a band whose dominant content is one widget. */\nfunction soleSignificant(node: Node): Node {\n let kids: Node[];\n switch (node.kind) {\n case \"row\":\n kids = node.cells.map((c) => c.node);\n break;\n case \"stack\":\n kids = node.children;\n break;\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n kids = [node];\n break;\n }\n const significant = kids.filter((n) => !isEmptyRaw(n));\n return significant.length === 1 && significant[0] ? significant[0] : node;\n}\n\n/** Classify one band into a SliceSpec. Conservative: only unambiguous shapes\n * become pattern slices; everything else is a render-faithful Grid fallback. */\nexport function classifyBand(band: Band, opts: ClassifyOptions = {}): SliceSpec {\n // Widget rewrite runs FIRST, so the pattern branches and the Grid fallback\n // all see `widget` nodes in place of injected mounts.\n const root = opts.isMapMount ? rewriteMapMounts(band.root, opts.isMapMount) : band.root;\n const widgets = collectWidgets(root);\n const media = collectMedia(root);\n const text = collectText(root);\n const rowNode = topRowNode(root);\n const row = rowNode ? rowNode.cells : null;\n // A raw node with real content is text we cannot account for — every\n // promotion must refuse to fire over it (only the Grid fallback keeps it).\n const hasSignificantRaw = collectRaws(root).some((n) => !isEmptyRaw(n));\n\n // Top-level widget promotion (before the structural patterns).\n const sole = soleSignificant(root);\n if (sole.kind === \"widget\" && sole.widget.type === \"map\") {\n return { slice: \"LocationMap\", ...base(band) };\n }\n if (\n media.length === 1 &&\n media[0]?.kind === \"video\" &&\n text.length === 0 &&\n widgets.length === 0 &&\n row === null &&\n !hasSignificantRaw\n ) {\n const v = media[0];\n return { slice: \"VideoFeature\", ...base(band), media: v };\n }\n\n const headings = text.filter((n) => n.kind === \"heading\");\n const subtitles = text.filter((n) => n.kind === \"subtitle\");\n const bodies = text.filter((n) => n.kind === \"body\");\n\n // Text-only bands (no media, no row, no widgets, no significant raw — any\n // co-located content must survive via the Grid fallback, not be swallowed).\n if (media.length === 0 && row === null && widgets.length === 0 && !hasSignificantRaw) {\n // TitleBand: exactly one heading + at most one subtitle, nothing else —\n // TitleBandSpec has nowhere to carry surplus text.\n if (headings.length === 1 && subtitles.length <= 1 && bodies.length === 0 && !band.background) {\n const first = headings[0];\n const sub = subtitles[0];\n return {\n slice: \"TitleBand\",\n ...base(band),\n heading: first ? nodeText(first) : \"\",\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...textRoleMeta(first, sub),\n };\n }\n // RichText: body node(s) only — a subtitle would be dropped from the html.\n if (headings.length === 0 && subtitles.length === 0 && bodies.length > 0 && !band.background) {\n return {\n slice: \"RichText\",\n ...base(band),\n html: bodies.map((b) => (b.kind === \"body\" ? b.html : \"\")).join(\"\\n\"),\n };\n }\n }\n\n // Full-bleed hero: a background image with overlay text and no grid row. At\n // most one of each overlay text kind — HeroSpec keeps one heading/subtitle/\n // body, so surplus would be silently dropped.\n if (\n band.background &&\n headings.length === 1 &&\n subtitles.length <= 1 &&\n bodies.length <= 1 &&\n row === null &&\n media.length === 0 &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const h = headings[0];\n const sub = subtitles[0];\n const bod = bodies[0];\n return {\n slice: \"Hero\",\n ...base(band),\n ...(h ? { heading: nodeText(h) } : {}),\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...(bod && bod.kind === \"body\" ? { body: bod.html } : {}),\n ...textRoleMeta(h, sub),\n };\n }\n\n // Carousel: a source slider row (.caslider) whose every cell is a media\n // slide, optionally captioned (stack[media, heading]). Anything richer\n // falls through to the faithful Grid fallback.\n if (rowNode?.slider) {\n const slides = carouselSlides(rowNode.cells);\n if (slides) {\n const spec: CarouselSpec = { slice: \"Carousel\", ...base(band), slides };\n if (rowNode.slider.columns !== undefined) spec.columns = rowNode.slider.columns;\n return spec;\n }\n }\n\n // Gallery: a row whose cells are all single media.\n if (row) {\n const gm = galleryMedia(row);\n if (gm) return { slice: \"Gallery\", ...base(band), media: gm };\n }\n\n // MediaFull: one media, no text, no top row, no widgets, no significant raw —\n // a row sibling, a co-located widget (e.g. a map mount), or raw prose would\n // be silently dropped, so those stay Grid.\n if (\n media.length === 1 &&\n text.length === 0 &&\n row === null &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const m = media[0];\n if (m) return { slice: \"MediaFull\", ...base(band), media: m };\n }\n\n // SplitFeature: exactly two cells, one pure media, one text-bearing.\n if (row && row.length === 2) {\n const [c0, c1] = row;\n if (c0 && c1) {\n const m0 = pureCellMedia(c0);\n const m1 = pureCellMedia(c1);\n const t0 = collectText(c0.node).length > 0;\n const t1 = collectText(c1.node).length > 0;\n if (m0 && !m1 && t1) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m0,\n mediaSide: \"left\",\n ratio: cellRatio(c0),\n text: c1.node,\n };\n }\n if (m1 && !m0 && t0) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m1,\n mediaSide: \"right\",\n ratio: cellRatio(c1),\n text: c0.node,\n };\n }\n }\n }\n\n return { slice: \"Grid\", ...base(band), root };\n}\n\nexport function classifyBands(bands: Band[], opts: ClassifyOptions = {}): SliceSpec[] {\n return bands.map((b) => classifyBand(b, opts));\n}\n","// Feed-grid materialization. Blux feed grids (gallery/portfolio) render their\n// tiles CLIENT-SIDE from feed records — the static export ships only a\n// display:none {{…}} template (dropped by the parser). This module rebuilds\n// the visible tiles DETERMINISTICALLY from the feed data at convert time, so\n// the faithful render shows the real content instead of an empty band.\n//\n// A feed band's site.json item carries `sources` (a feed id, or `__media` for\n// the media library) and `sourceConfig` (a tag filter, a sort, per-tile style\n// config). We resolve the matching records, expand each into a tile\n// (image + optional title/body), and return a Grid node tree — so the band\n// classifies and renders as any other Grid, with no new render surface.\nimport type { Cell, Media, Node } from \"./types.js\";\nimport { CDN_HOSTS } from \"../assets.js\";\n\n/** A media-library entry (`site.json.media[uuid]`) or a feed record. Both are\n * loose bags; we read only the fields the tile needs. */\nexport type FeedRecord = Record<string, unknown>;\n\n/** A resolved tile: an image (already a Media) and/or display text. `title`\n * and `body` are RENDER-READY HTML — feed records store them as HTML (entities\n * pre-encoded, `<br>` markup), and `__media` plain text (name/description) is\n * escaped at resolve time — so the render places them verbatim, never\n * re-escaping (which would double-encode `&amp;` or show a literal `<br>`). */\nexport type FeedTile = { media?: Media; title?: string; body?: string };\n\n/** Everything the materializer needs from the export, injected so the module\n * stays pure/offline and testable. */\nexport type FeedResolvers = {\n /** Feed records by feed id (site.json feeds → their JSON arrays). */\n feeds: Map<string, FeedRecord[]>;\n /** The media library: uuid → its `{ name, type, tags }` entry. */\n media: Map<string, FeedRecord>;\n /** Build a Media (with a resolved url base) for an asset uuid, given its mime\n * type and/or filename (either can supply the extension) — the render\n * resolver turns Media→url the same way it does for parsed media, so feed\n * images flow through one url path. */\n mediaFor: (uuid: string, type: string | undefined, name?: string) => Media | null;\n};\n\n/** A term matches a tag when they're equal OR differ only by a trailing `s`\n * (singular/plural) — Blux's server-side feed resolver stems this way, so a\n * `projects` filter also selects `project`-tagged media (7 real gallery tiles\n * that an exact match drops). Conservative: only a single trailing `s`, so it\n * never over-selects unrelated tags. */\nconst termMatchesTag = (term: string, tag: string): boolean =>\n term === tag ||\n (term.endsWith(\"s\") && term.slice(0, -1) === tag) ||\n (tag.endsWith(\"s\") && tag.slice(0, -1) === term);\n\n/** Parse a Blux tag filter expression into a predicate over a tag set. The\n * DSL: `&&` joins AND terms, `||` joins OR groups; a record matches when ANY\n * OR group has ALL its terms present (singular/plural-insensitive, see\n * `termMatchesTag`). Leading/empty terms (`&&metal&&sofa`) are ignored.\n * Case-insensitive. An empty/absent expression matches all. */\nexport function tagFilter(expr: string | undefined): (tags: string[]) => boolean {\n const groups = (expr ?? \"\")\n .split(\"||\")\n .map((g) =>\n g\n .split(\"&&\")\n .map((t) => t.trim().toLowerCase())\n .filter(Boolean),\n )\n .filter((g) => g.length > 0);\n if (!groups.length) return () => true;\n return (tags) => {\n const set = tags.map((t) => t.toLowerCase());\n return groups.some((g) => g.every((term) => set.some((tag) => termMatchesTag(term, tag))));\n };\n}\n\n/** Records sorted by a Blux `sort` key. `title` → by title with\n * `localeCompare` (matching Blux's own client sort, which uses\n * `((a.sort||\"\")+\"\").localeCompare(b.sort)` for non-numeric sort values);\n * `fdate`/`date` → by the record's date descending (newest first, Blux's\n * default), the undated last; anything else → source order preserved.\n * Stable. */\nfunction sortRecords(records: FeedRecord[], sort: string | undefined): FeedRecord[] {\n if (sort === \"title\") {\n return [...records].sort((a, b) =>\n String(a[\"title\"] ?? \"\").localeCompare(String(b[\"title\"] ?? \"\")),\n );\n }\n if (sort === \"fdate\" || sort === \"date\") {\n return [...records].sort((a, b) =>\n String(b[\"date\"] ?? \"\").localeCompare(String(a[\"date\"] ?? \"\")),\n );\n }\n return records;\n}\n\n/** A validated \"W:H\" crop ratio (e.g. \"4:3\") from a sourceConfig value, or\n * undefined when absent/malformed. Both terms must be positive numbers. */\nexport function cropRatioOf(v: unknown): string | undefined {\n if (typeof v !== \"string\") return undefined;\n const m = /^\\s*(\\d+(?:\\.\\d+)?)\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(v);\n if (!m) return undefined;\n const w = Number(m[1]);\n const h = Number(m[2]);\n return w > 0 && h > 0 ? `${w}:${h}` : undefined;\n}\n\nconst isDisabled = (r: FeedRecord): boolean => r[\"disabled\"] === true || r[\"disable\"] === true;\nconst styleDisabled = (cfg: unknown): boolean =>\n !!cfg && typeof cfg === \"object\" && (cfg as { class?: string })[\"class\"] === \"disable\";\n\n/** Resolve a feed band's source into ordered tiles. `sources[0]` is either\n * `__media` (the media library, tiles = tag-matched images) or a feed id\n * (tiles = its records, filtered + sorted, expanded via the template config).\n * Returns null when the source is unknown or yields nothing. */\nexport function resolveFeedTiles(\n bandDef: {\n sources?: unknown;\n sourceConfig?: Record<string, unknown>;\n },\n resolvers: FeedResolvers,\n): FeedTile[] | null {\n const sources = Array.isArray(bandDef.sources) ? bandDef.sources.map(String) : [];\n const source = sources[0];\n if (!source) return null;\n const cfg = bandDef.sourceConfig ?? {};\n const filterExpr = (cfg[\"filters\"] as { tag?: string } | undefined)?.tag;\n const match = tagFilter(filterExpr);\n const sort = cfg[\"sort\"] as string | undefined;\n const bodyOff = styleDisabled(cfg[\"_body\"]);\n const titleOff = styleDisabled(cfg[\"_title\"]);\n // The grid's tile crop ratio (\"4:3\"): the render frames each tile image in a\n // fixed-aspect cover box (uniform gallery tiles, like the original).\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n const framed = (m: Media | null): Media | null =>\n m && ratio ? { ...m, cropRatio: ratio, fit: \"cover\" } : m;\n\n if (source === \"__media\") {\n // Media-library grid: every tag-matched image, sorted by the config (the\n // gallery/portfolio grids are `fdate` — newest first). Library entries DO\n // carry display text: `name` is a caption (a real title, not a filename)\n // and `description` the body — Blux binds both into the tile overlay\n // (unless _title/_body is disabled). These are PLAIN text, so escape them.\n const matched: FeedRecord[] = [];\n for (const [uuid, entry] of resolvers.media) {\n const type = String(entry[\"type\"] ?? \"\");\n if (!type.startsWith(\"image/\")) continue;\n if (!match((entry[\"tags\"] as string[] | undefined) ?? [])) continue;\n matched.push({ ...entry, __uuid: uuid });\n }\n const tiles = sortRecords(matched, sort)\n .map((entry): FeedTile => {\n const media = framed(\n resolvers.mediaFor(\n String(entry[\"__uuid\"]),\n entry[\"type\"] as string | undefined,\n entry[\"name\"] as string | undefined,\n ),\n );\n const tile: FeedTile = {};\n if (media) tile.media = media;\n if (!titleOff && entry[\"name\"]) tile.title = escapeHtml(String(entry[\"name\"]));\n if (!bodyOff && entry[\"description\"]) tile.body = plainToHtml(String(entry[\"description\"]));\n return tile;\n })\n .filter((t) => t.media || t.title || t.body);\n return tiles.length ? tiles : null;\n }\n\n const records = resolvers.feeds.get(source);\n if (!records) return null;\n const enabled = records.filter((r) => !isDisabled(r));\n const filtered = filterExpr\n ? enabled.filter((r) => match((r[\"tags\"] as string[]) ?? []))\n : enabled;\n const ordered = sortRecords(filtered, sort);\n const tiles = ordered.map((r): FeedTile => {\n const tile: FeedTile = {};\n const m = r[\"media\"] as { media?: string; type?: string } | undefined;\n if (m?.media) {\n const media = framed(resolvers.mediaFor(m.media, m.type));\n if (media) tile.media = media;\n }\n // Feed record title/body are stored as HTML (entities encoded, `<br>`\n // markup) — keep them VERBATIM, never re-escape.\n if (!titleOff && r[\"title\"]) tile.title = String(r[\"title\"]);\n if (!bodyOff && r[\"body\"]) tile.body = String(r[\"body\"]);\n return tile;\n });\n return tiles.length ? tiles : null;\n}\n\n/** An overlay tile treatment (Blux `layout: \"behind\"`, `overlay: true`): the\n * caption sits OVER the cropped image (a colored panel revealed on hover), so a\n * tile is only as tall as its image — not image + a caption row below. Threaded\n * onto the tile stack as `_overlay` presentation hints the render consumes. */\nexport type TileOverlay = { ratio: string; color?: string; valign?: string };\n\n/** A materialized feed grid: the band's heading (if any) over a row of tile\n * cells. Each tile is a stack of its image + title + body (whichever it has);\n * a lone image/heading stays bare. With `overlay`, the tile becomes an\n * overlay card (caption over the cropped image). `columns` sets each cell's\n * grid token so the render lays them out in a grid (the source `columns`/\n * `data-columns`; default 3). Returns the heading alone when there are no\n * tiles, null when neither. */\nexport function materializeFeedGrid(opts: {\n heading?: { html: string; level: number; role?: string };\n tiles: FeedTile[] | null;\n columns: number;\n spacing?: number;\n overlay?: TileOverlay;\n}): Node | null {\n const { heading, tiles, columns, spacing, overlay } = opts;\n const headingNode: Node | null = heading\n ? {\n kind: \"heading\",\n level: heading.level,\n html: heading.html,\n ...(heading.role ? { role: heading.role } : {}),\n }\n : null;\n if (!tiles || !tiles.length) return headingNode;\n\n const cols = Math.max(1, Math.round(columns));\n const cells: Cell[] = tiles.map((t) => {\n const parts: Node[] = [];\n if (t.media) parts.push({ kind: \"media\", media: t.media });\n // title/body are already render-ready HTML (feed records store HTML;\n // __media plain text was escaped at resolve time) — place them verbatim.\n if (t.title) parts.push({ kind: \"heading\", level: 6, html: t.title, role: \"text6\" });\n if (t.body) parts.push({ kind: \"body\", html: t.body });\n let node: Node;\n if (overlay && t.media) {\n // Overlay card: the stack carries the crop ratio + panel style; the\n // render cover-fills the media and overlays the caption on it.\n node = {\n kind: \"stack\",\n children: parts,\n style: {\n _overlay: overlay.ratio,\n ...(overlay.color ? { _overlayColor: overlay.color } : {}),\n ...(overlay.valign ? { _overlayValign: overlay.valign } : {}),\n },\n };\n } else {\n node = parts.length === 1 ? parts[0]! : { kind: \"stack\", children: parts };\n }\n return {\n token: {\n cols,\n raw: `grid-${cols}${spacing ? `-s${spacing}` : \"\"}`,\n ...(spacing ? { spacing } : {}),\n },\n node,\n };\n });\n const row: Node = { kind: \"row\", cells };\n return headingNode ? { kind: \"stack\", children: [headingNode, row] } : row;\n}\n\n/** HTML-escape a PLAIN-text value (a `__media` name/description) for placement\n * into an html-bearing node. Feed-record title/body are already HTML and skip\n * this. */\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\n/** A plain-text body (`__media` description) as body html: escaped, with the\n * source's hard newlines becoming `<br>` (Blux descriptions are multi-line —\n * \"DESIGN: …\\nPROCUREMENT: …\"), wrapped in a `<p>`. */\nfunction plainToHtml(s: string): string {\n const inner = escapeHtml(s.trim()).replace(/\\r?\\n/g, \"<br>\");\n return `<p>${inner}</p>`;\n}\n\n/** A file extension for a media asset: the mime map first (image/jpeg → jpg),\n * else the extension off the entry's own filename (`name`) — Blux names carry\n * the real extension, so an unmapped/absent mime (image/jpg, avif, heic, a\n * bare `custom`) still resolves instead of silently dropping the tile. null\n * only when neither yields an image extension. */\nexport function extFor(mime: string | undefined, name: string | undefined): string | null {\n const byMime: Record<string, string> = {\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n \"image/avif\": \"avif\",\n \"image/bmp\": \"bmp\",\n \"image/tiff\": \"tiff\",\n };\n if (mime && byMime[mime]) return byMime[mime];\n const m = /\\.([a-z0-9]{2,5})$/i.exec(name ?? \"\");\n const ext = m?.[1]?.toLowerCase();\n const IMG = new Set([\"jpg\", \"jpeg\", \"png\", \"svg\", \"gif\", \"webp\", \"avif\", \"bmp\", \"tif\", \"tiff\"]);\n return ext && IMG.has(ext) ? (ext === \"jpeg\" ? \"jpg\" : ext) : null;\n}\n\n/** Is this a feed-driven band? — its site.json item declares `sources`. */\nexport function isFeedBand(\n item: unknown,\n): item is { sources: unknown[]; sourceConfig?: Record<string, unknown> } {\n return (\n !!item &&\n typeof item === \"object\" &&\n Array.isArray((item as { sources?: unknown }).sources) &&\n (item as { sources: unknown[] }).sources.length > 0\n );\n}\n\n/** The CDN base (`https://<host>/<siteId>/`) an export actually serves assets\n * from — scraped from a rendered `data-base` so the RIGHT host is used (Blux\n * spreads assets across two CDN hosts; a hardcoded host would 404 the other).\n * Falls back to the first known host + siteId when no data-base is present. */\nexport function feedAssetBase(htmls: string[], siteId: string): string {\n for (const html of htmls) {\n const m = /data-base=\"(https?:\\/\\/[^\"]+?\\/)\"/i.exec(html);\n if (m?.[1]) return m[1].replace(/^http:/, \"https:\");\n }\n return `https://${CDN_HOSTS[0]}/${siteId}/`;\n}\n\n/** Build the feed resolvers from a parsed site: feed records (site.json.feeds\n * carries them inline), the media library, and a Media builder that\n * reconstructs the CDN url (`<base><uuid>.<ext>` — the untransformed base the\n * export's own `data-base` uses, which resolves full-res). `base` comes from\n * `feedAssetBase` (the export's real host); `ext` from mime-or-filename. Pure. */\nexport function buildFeedResolvers(\n feeds: Record<string, { items?: FeedRecord[] } | undefined>,\n mediaLibrary: Record<string, FeedRecord>,\n base: string,\n): FeedResolvers {\n const feedMap = new Map<string, FeedRecord[]>();\n for (const [id, f] of Object.entries(feeds)) {\n if (Array.isArray(f?.items)) feedMap.set(id, f.items);\n }\n const mediaMap = new Map<string, FeedRecord>(Object.entries(mediaLibrary));\n return {\n feeds: feedMap,\n media: mediaMap,\n mediaFor: (uuid, type, name) => {\n const ext = extFor(type, name);\n if (!ext) return null;\n return { kind: \"image\", assetId: uuid, base, ext };\n },\n };\n}\n","// Products feed → a clean, materialized product catalog. Blux renders a detail\n// page per feed record at /products/<slug> from a Handlebars template; the\n// static export ships only the template, so this rebuilds the catalog\n// deterministically at convert time: canonical categories (the raw feed data is\n// dirty — whitespace/case variants + typos), the faithful url-or-derive slug,\n// and resolved main + gallery images. Pure; the convert injects `resolveImage`.\n\nexport type ProductImage = { assetId: string; url: string };\n\nexport type Product = {\n /** The detail-page slug: /products/<slug>. */\n slug: string;\n title: string;\n /** Canonical category (drives the back-link + faceting). */\n category: string;\n /** Canonical sub-category, or \"\" when the record has none. */\n subCategory: string;\n /** Rendered verbatim — the export's dimension strings are inconsistent\n * (`94\"W x 38\"D` vs `94\" W x 38\" D`) and that's what the live page shows. */\n dimensions: string;\n tags: string[];\n /** Hidden from listing grids, but Blux still serves its detail page (200), so\n * the page is generated regardless. */\n disabled: boolean;\n /** The main image, when the record has one (39/552 have none). */\n image?: ProductImage;\n /** Additional images shown as detail-page thumbnails (160/552 have ≥1). */\n gallery: ProductImage[];\n};\n\nexport type ProductRecord = {\n title?: unknown;\n category?: unknown;\n sub_category?: unknown;\n dimensions?: unknown;\n tags?: unknown;\n disabled?: unknown;\n /** Present on only a handful of records; when set it is the authoritative\n * slug (and can override what the title would derive). */\n url?: unknown;\n media?: unknown;\n items?: unknown;\n};\n\n// The five product categories the site publishes. Upholstered/Case/Exterior are\n// populated by the feed; Metal/Finishes are listing pages with no feed records.\nconst CANONICAL_CATEGORIES = [\"Upholstered\", \"Case\", \"Exterior\", \"Metal\", \"Finishes\"];\n\n// Misspellings in the raw data that don't fold by case/whitespace alone.\nconst CATEGORY_ALIASES: Record<string, string> = {\n upholstrered: \"Upholstered\",\n upholsered: \"Upholstered\",\n};\n\n// Sub-category variants that need explicit folding (order/wording/typos).\nconst SUBCATEGORY_ALIASES: Record<string, string> = {\n banuette: \"Banquette\",\n \"benches & ottomans\": \"Ottomans & Benches\",\n miscellaneous: \"Misc.\",\n};\n\nfunction cleanWs(s: unknown): string {\n return String(s ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(s: string): string {\n return s.replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Canonicalize a raw product category. Folds whitespace/case variants and\n * known typos onto the canonical set; an unrecognized category is kept\n * (title-cased) rather than dropped, so no product is silently lost. */\nexport function normalizeCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (CATEGORY_ALIASES[lower]) return CATEGORY_ALIASES[lower];\n const canon = CANONICAL_CATEGORIES.find((c) => c.toLowerCase() === lower);\n return canon ?? titleCase(lower);\n}\n\n/** Canonicalize a raw sub-category (own facet; folds typos/word-order). \"\" when\n * the record has none. */\nexport function normalizeSubCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (SUBCATEGORY_ALIASES[lower]) return SUBCATEGORY_ALIASES[lower];\n return titleCase(lower);\n}\n\n/** The detail-page slug for a record: the stored `url` wins (a few records\n * carry an editorial slug the title wouldn't derive — \"Howdy Set\" → howdyset),\n * else derive from the title (lowercase, non-alphanumeric runs → single \"-\",\n * trimmed). */\nexport function productSlug(record: ProductRecord): string {\n const url = typeof record.url === \"string\" ? record.url.trim() : \"\";\n if (url) return url.replace(/^\\/+products\\/+/i, \"\").replace(/^\\/+|\\/+$/g, \"\");\n return String(record.title ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction mediaUuid(media: unknown): string | undefined {\n if (media && typeof media === \"object\") {\n const m = (media as { media?: unknown }).media;\n if (typeof m === \"string\" && m) return m;\n }\n return undefined;\n}\n\nfunction imageFor(\n uuid: string | undefined,\n resolveImage: (uuid: string) => string | null,\n): ProductImage | undefined {\n if (!uuid) return undefined;\n const url = resolveImage(uuid);\n return url ? { assetId: uuid, url } : undefined;\n}\n\n/** Build the product catalog from the feed records. `resolveImage` turns an\n * asset uuid into a CDN url (null when unresolvable). Slug-collision safe: two\n * records that map to the same slug (a handful of duplicate/legacy names) keep\n * ONE page — an enabled record wins over a disabled one, else the first seen.\n * Records with no usable slug are dropped. */\nexport function materializeProducts(\n records: ProductRecord[],\n resolveImage: (uuid: string) => string | null,\n): Product[] {\n const bySlug = new Map<string, Product>();\n for (const r of records) {\n const slug = productSlug(r);\n if (!slug) continue;\n\n const gallery: ProductImage[] = [];\n if (Array.isArray(r.items)) {\n for (const it of r.items as ProductRecord[]) {\n const img = imageFor(mediaUuid(it?.media), resolveImage);\n if (img) gallery.push(img);\n }\n }\n\n const image = imageFor(mediaUuid(r.media), resolveImage);\n const product: Product = {\n slug,\n title: cleanWs(r.title),\n category: normalizeCategory(r.category),\n subCategory: normalizeSubCategory(r.sub_category),\n dimensions: String(r.dimensions ?? \"\"),\n tags: Array.isArray(r.tags) ? r.tags.filter((t): t is string => typeof t === \"string\") : [],\n disabled: r.disabled === true,\n gallery,\n // `exactOptionalPropertyTypes`: omit the key rather than set undefined.\n ...(image ? { image } : {}),\n };\n\n const existing = bySlug.get(slug);\n if (!existing || (existing.disabled && !product.disabled)) bySlug.set(slug, product);\n }\n return [...bySlug.values()];\n}\n","import { blockPlainText, type SliceSpec } from \"../grid/index.js\";\nimport { type PlanSlice, richText } from \"./plan.js\";\n\n/** Strip all tags → the plain text a Prismic \"Text\" (key-text) field holds. */\nfunction stripTags(html: string): string {\n return html\n .replace(/<[^>]*>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Map one classified band to its page-doc slice. Text + band index only —\n * media, layout, style and map all live in the presentation manifest. */\nexport function sliceSpecToPlanSlice(spec: SliceSpec): PlanSlice {\n switch (spec.slice) {\n case \"Hero\":\n return {\n slice_type: \"hero\",\n variation: \"band\",\n items: [],\n primary: {\n band: spec.index,\n ...(spec.heading ? { heading: spec.heading } : {}),\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n ...(spec.body ? { body: stripTags(spec.body) } : {}),\n },\n };\n case \"TitleBand\":\n return {\n slice_type: \"title_band\",\n variation: \"default\",\n items: [],\n primary: {\n band: spec.index,\n heading: spec.heading,\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n },\n };\n case \"RichText\":\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n items: [],\n primary: { content: richText(spec.html), band: spec.index },\n };\n case \"SplitFeature\":\n return {\n slice_type: \"split_feature\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Gallery\":\n return {\n slice_type: \"gallery\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Carousel\":\n // One item per slide, in slide order (the render zips by index); an\n // uncaptioned slide contributes {} so the alignment holds. Captions are\n // heading nodes, so blockPlainText (entities decoded, <br> kept as a\n // newline) — same as the Hero/TitleBand heading path. A hero slide's\n // secondary line (location) rides as `subcaption`.\n return {\n slice_type: \"carousel\",\n variation: \"default\",\n items: spec.slides.map((s) => ({\n ...(s.caption ? { caption: blockPlainText(s.caption.html) } : {}),\n ...(s.subcaption ? { subcaption: blockPlainText(s.subcaption.html) } : {}),\n })),\n primary: { band: spec.index },\n };\n case \"MediaFull\":\n case \"VideoFeature\":\n return {\n slice_type: \"media_full\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"LocationMap\":\n return {\n slice_type: \"location_map\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Grid\":\n return {\n slice_type: \"grid_band\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n }\n}\n","import { type SliceSpec, type Media, collectMedia } from \"../grid/index.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sliceSpecToPlanSlice } from \"./grid-slice.js\";\nimport { type MigrationPlan, type PlanAsset, type PlanDocument, richText } from \"./plan.js\";\n\n/** Build the CDN url for a media from its parser-captured base + uuid + ext.\n * Null when the node carried no `data-base` (the manifest resolver then falls\n * back to the IR asset's sourceUrl — see `buildGridPlan`). Exported so the\n * `blux convert` manifest resolver builds byte-identical urls (a later task\n * rewrites the manifest by matching plan.assets urls). */\nexport function mediaCdnUrl(m: Media): string | null {\n return m.base ? `${m.base}${m.assetId}${m.ext ? `.${m.ext}` : \"\"}` : null;\n}\n\n/** The absolute source url for a media: the parser-captured CDN url\n * (data-base + uuid + ext) if present, else the IR asset's scraped sourceUrl,\n * else null. Shared by the plan's asset list and the manifest resolver so the\n * two url strings are byte-identical (the migrate rewrite keys on that). */\nexport function mediaUrl(\n m: Media,\n sourceUrlById: Map<string, string | null | undefined>,\n): string | null {\n return mediaCdnUrl(m) ?? sourceUrlById.get(m.assetId) ?? null;\n}\n\n/** Every Media referenced across all specs: band backgrounds, direct media\n * fields, and media inside node trees (SplitFeature.text / Grid.root). Deduped\n * by assetId, first occurrence wins, insertion order preserved. `resolve`\n * turns a media into its upload entry (CDN-base url, else IR sourceUrl) or null\n * when neither is available — an unresolvable media is dropped and (if a\n * `diagnostics` sink is passed) recorded once per assetId. */\nexport function collectPlanAssets(\n specs: SliceSpec[],\n resolve: (m: Media) => PlanAsset | null,\n diagnostics?: Diagnostic[],\n): PlanAsset[] {\n const byId = new Map<string, PlanAsset>();\n const seen = new Set<string>();\n const add = (m: Media) => {\n if (seen.has(m.assetId)) return;\n seen.add(m.assetId);\n const asset = resolve(m);\n if (asset) byId.set(m.assetId, asset);\n else\n diagnostics?.push({\n kind: \"unresolved-asset\",\n where: m.assetId,\n message: `media ${m.assetId} has no CDN base nor IR source url — not uploaded`,\n });\n };\n for (const spec of specs) {\n if (spec.background) add(spec.background);\n switch (spec.slice) {\n case \"Gallery\":\n spec.media.forEach(add);\n break;\n case \"Carousel\":\n spec.slides.forEach((s) => add(s.media));\n break;\n case \"MediaFull\":\n case \"VideoFeature\":\n add(spec.media);\n break;\n case \"SplitFeature\":\n add(spec.media);\n collectMedia(spec.text).forEach(add);\n break;\n case \"Grid\":\n collectMedia(spec.root).forEach(add);\n break;\n default:\n break; // Hero/TitleBand/RichText/LocationMap: only background (handled above)\n }\n }\n return [...byId.values()];\n}\n\n/** Build the Prismic migration plan for a grid-converted site: one text-only\n * page document + the assets its manifest references (uploaded so the manifest\n * can be rewritten to Prismic urls at migrate time). Collections flow through\n * `buildCustomType`, unchanged from archetype. */\nexport function buildGridPlan(specs: SliceSpec[], ir: SiteIR): MigrationPlan {\n const page = ir.pages[0];\n return buildGridSitePlan(\n [{ uid: page?.uid ?? \"home\", title: page?.title ?? page?.uid ?? \"home\", specs }],\n ir,\n );\n}\n\n/** The multi-page grid plan: one page document per converted page (uid-keyed,\n * mirroring the archetype plan's per-page loop), the asset union across all\n * pages' specs, and the collections' custom types. */\nexport function buildGridSitePlan(\n pages: { uid: string; title: string; specs: SliceSpec[] }[],\n ir: SiteIR,\n): MigrationPlan {\n const documents: PlanDocument[] = pages.map((p) => ({\n type: \"page\",\n uid: p.uid,\n data: { title: richText(`<h1>${p.title}</h1>`), slices: p.specs.map(sliceSpecToPlanSlice) },\n }));\n // Upload-url resolution MUST stay identical to the later manifest resolver:\n // CDN-base url first, else the IR asset's sourceUrl. `mediaUrl` is the\n // single shared implementation (see its doc comment above).\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const resolve = (m: Media): PlanAsset | null => {\n const asset = assetById.get(m.assetId);\n const url = mediaUrl(m, sourceUrlById);\n return url ? { id: m.assetId, url, alt: asset?.alt ?? \"\" } : null;\n };\n const diagnostics: Diagnostic[] = [...(ir.diagnostics ?? [])];\n const assets = collectPlanAssets(\n pages.flatMap((p) => p.specs),\n resolve,\n diagnostics,\n );\n const customTypes = ir.collections.map(buildCustomType);\n return { customTypes, documents, assets, stylesManifest: [], diagnostics };\n}\n","// Render-faithful presentation manifest (plan 5). Transforms the source\n// node tree (src/blux/grid/types.ts `Node`) and the classified `SliceSpec[]`\n// into `blux-presentation.json`, the render-side contract the-pointe's\n// Svelte layer loads (src/lib/blux/presentation.ts — that file is the fixed\n// target; these mirror types must match it exactly). Media/style/map are\n// resolved via injected `deps` so this builder stays pure and offline.\nimport type {\n Media,\n Node,\n GridToken as SrcToken,\n SliceSpec,\n VideoPlayback,\n} from \"../grid/index.js\";\nimport type { BlockDefaults } from \"./block-styles.js\";\n\n// ---------------------------------------------------------------------------\n// Render-side mirror types (must match the-pointe's src/lib/blux/presentation.ts)\n// ---------------------------------------------------------------------------\n\nexport type RenderMedia = {\n kind: \"image\" | \"video\";\n url: string;\n alt?: string;\n // Intrinsic sizing carried from the source. `width`/`aspect` size a foreground\n // graphic (capped to its cell) so rules/logos keep their true size. `fit` +\n // `position` carry a band background's `background-size`/`-position` so a\n // corner-anchored `auto` accent isn't centered full-bleed. `playback` carries a\n // video's source `<video>` attributes. Mirrors the-pointe's src/lib/blux/presentation.ts.\n width?: number;\n aspect?: number;\n fit?: \"contain\" | \"cover\" | \"auto\";\n position?: string;\n /** The source holder's inline min-height (e.g. \"80vh\" on slider slides), so\n * a cover-frame carousel reserves the original's height. */\n minHeight?: string;\n /** A feed grid's tile crop ratio (\"W:H\", e.g. \"4:3\") — the render frames the\n * image in a fixed-aspect object-cover box (uniform gallery tiles). */\n cropRatio?: string;\n playback?: VideoPlayback;\n};\n\nexport type RenderToken = { cols: number | \"any\"; ratio?: number; spacing?: number };\n\nexport type RenderNode =\n | {\n kind: \"row\";\n cells: RenderCell[];\n style?: Record<string, string>;\n /** This row's cells are the map widget's toggle-switched content panels\n * (the Blux clickMap wiring): cell i shows when toggle i is active, the\n * rest are hidden. Set only on the row that directly follows a widget:map\n * sibling and has exactly one cell per map toggle. */\n panels?: boolean;\n }\n | { kind: \"stack\"; children: RenderNode[]; style?: Record<string, string> }\n | {\n kind: \"heading\";\n level: number;\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"body\";\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"subtitle\";\n text: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | { kind: \"media\"; media: RenderMedia }\n | { kind: \"raw\"; html: string }\n | { kind: \"widget\"; widget: { type: \"map\" } };\n\nexport type RenderCell = { token: RenderToken; node: RenderNode };\n\nexport type MapLayer = {\n name: string;\n lid: string;\n initiallyVisible: boolean;\n preserveViewport: boolean;\n};\nexport type MapToggle = { label: string; layers: string[]; panelIndex: number };\nexport type MapRenderConfig = {\n mid: string;\n layers: MapLayer[];\n toggles: MapToggle[];\n styles: unknown[];\n center?: { lat: number; lng: number };\n zoom?: number;\n height?: string;\n defaultToggle?: number;\n};\n\nexport type BandPresentation = {\n style?: Record<string, string>;\n background?: RenderMedia;\n tree?: RenderNode;\n split?: { mediaSide: \"left\" | \"right\"; ratio: number; media: RenderMedia; text: RenderNode };\n gallery?: RenderMedia[];\n /** Carousel payload: the band is a source slider (.caslider). Caption TEXT\n * lives in the page doc's items (Prismic-editable); the manifest carries the\n * media and the caption's role metadata. `columns` = slides visible at once\n * (source data-columns). */\n carousel?: {\n slides: {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n }[];\n columns?: number;\n };\n media?: RenderMedia;\n map?: MapRenderConfig;\n /** Hero/TitleBand heading textN role + h-level and subtitle role, so the\n * render applies the right display font/tag. The text itself is the Prismic\n * page-doc string; this is presentation metadata only. */\n text?: { headingRole?: string; headingLevel?: number; subtitleRole?: string };\n};\n\nexport type Presentation = { bands: Record<string, BandPresentation> };\n\n/** A whole-site manifest: per-page band manifests keyed by page uid. Band\n * indices are page-local (page-block-N restarts at 0 on every page), so a flat\n * bands map would collide across pages. `blux convert` writes this shape;\n * single-page consumers select a page with `selectPagePresentation`. The\n * render mirror is reddoor-starter's `SitePresentation`. */\nexport type SitePresentation = Presentation | { pages: Record<string, Presentation> };\n\n/** One page's slice of a site manifest (the flat form passes through). The\n * `&& m.pages` guard tolerates a hand-edited `{ pages: undefined }`; the flat\n * branch is a `Presentation` by construction (cast bridges the union gap). */\nexport function selectPagePresentation(m: SitePresentation, uid = \"home\"): Presentation {\n if (\"pages\" in m && m.pages) return m.pages[uid] ?? { bands: {} };\n return m as Presentation;\n}\n\nexport type PresentationDeps = {\n resolveMedia: (media: Media) => RenderMedia | null;\n styleFor: (index: number) => Record<string, string> | undefined;\n /** The `.blocksNcontainer` class defaults for a band's `blockClass`\n * (see `blockClassDefaults`) — filled into `BandPresentation.style` only\n * where the block's own styles omit the key. */\n defaultsFor: (blockClass: string) => BlockDefaults | undefined;\n map?: MapRenderConfig | null;\n};\n\n// ---------------------------------------------------------------------------\n// Node-tree serializer: source Node → RenderNode\n// ---------------------------------------------------------------------------\n\n/** Drop the source-only `raw` field from a grid token; keep only cols/ratio/spacing. */\nfunction renderToken(t: SrcToken): RenderToken {\n return {\n cols: t.cols,\n ...(t.ratio !== undefined ? { ratio: t.ratio } : {}),\n ...(t.spacing !== undefined ? { spacing: t.spacing } : {}),\n };\n}\n\n/** Recursively serialize a source Node → RenderNode: resolve media (dropping\n * unresolved media nodes), strip token.raw. Never mutates the input. */\nfunction renderNode(node: Node, resolve: PresentationDeps[\"resolveMedia\"]): RenderNode | null {\n switch (node.kind) {\n case \"row\": {\n const cells: RenderCell[] = [];\n for (const c of node.cells) {\n const rn = renderNode(c.node, resolve);\n if (rn) cells.push({ token: renderToken(c.token), node: rn });\n }\n return { kind: \"row\", cells, ...(node.style ? { style: node.style } : {}) };\n }\n case \"stack\": {\n const children: RenderNode[] = [];\n for (const c of node.children) {\n const rn = renderNode(c, resolve);\n if (rn) children.push(rn);\n }\n return { kind: \"stack\", children, ...(node.style ? { style: node.style } : {}) };\n }\n case \"heading\":\n return {\n kind: \"heading\",\n level: node.level,\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"body\":\n return {\n kind: \"body\",\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"subtitle\":\n return {\n kind: \"subtitle\",\n text: node.text,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"media\": {\n const m = resolve(node.media);\n return m ? { kind: \"media\", media: m } : null; // drop unresolved media\n }\n case \"raw\":\n return { kind: \"raw\", html: node.html };\n case \"widget\":\n return { kind: \"widget\", widget: node.widget };\n }\n}\n\n/** Does a (source) node tree contain a map widget anywhere? */\nexport function hasMapWidget(node: Node): boolean {\n if (node.kind === \"widget\") return node.widget.type === \"map\";\n if (node.kind === \"row\") return node.cells.some((c) => hasMapWidget(c.node));\n if (node.kind === \"stack\") return node.children.some(hasMapWidget);\n return false;\n}\n\n/** Mark the map widget's content-panel row: in the Blux export the clickMap\n * widget switches the area below the map between N sibling panels (the address\n * grid + lazy logo strips on the-pointe), one per toggle. Structurally that is\n * a row directly following the widget:map inside a stack, with exactly one cell\n * per toggle — the marker lets the render show only the active toggle's panel\n * instead of stacking all of them. Anything else is left untouched. */\nexport function markPanelRows(node: RenderNode, toggleCount: number): RenderNode {\n if (node.kind === \"row\") {\n return {\n ...node,\n cells: node.cells.map((c) => ({ ...c, node: markPanelRows(c.node, toggleCount) })),\n };\n }\n if (node.kind === \"stack\") {\n const children = node.children.map((c) => markPanelRows(c, toggleCount));\n for (let i = 0; i + 1 < children.length; i++) {\n const cur = children[i];\n const next = children[i + 1];\n if (\n cur?.kind === \"widget\" &&\n cur.widget.type === \"map\" &&\n next?.kind === \"row\" &&\n next.cells.length === toggleCount\n ) {\n children[i + 1] = { ...next, panels: true };\n }\n }\n return { ...node, children };\n }\n return node;\n}\n\n// ---------------------------------------------------------------------------\n// Per-variant builder: SliceSpec[] → Presentation\n// ---------------------------------------------------------------------------\n\nexport function buildPresentation(specs: SliceSpec[], deps: PresentationDeps): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const spec of specs) {\n const bp: BandPresentation = {};\n const style = deps.styleFor(spec.index);\n if (style) bp.style = style;\n // Class-default padding/max-width — for EVERY slice type (TitleBand/Hero\n // bands need their band padding too), filling only the keys the block's\n // own styles omit. The trigger is \"no `_contentPadding` in the block's\n // styles\", never \"no style record\" (a block can style other things and\n // still rely on the class padding). `_contentPaddingMobile` only ever\n // pairs with a filled default: a block's own padding has no mobile twin.\n const defaults = spec.blockClass ? deps.defaultsFor(spec.blockClass) : undefined;\n if (defaults) {\n const own = bp.style ?? {};\n const fill: Record<string, string> = {};\n if (own[\"_contentPadding\"] === undefined && defaults.padding) {\n fill[\"_contentPadding\"] = defaults.padding;\n if (defaults.mobilePadding) fill[\"_contentPaddingMobile\"] = defaults.mobilePadding;\n }\n if (own[\"_max-content-width\"] === undefined && defaults.maxWidth) {\n fill[\"_max-content-width\"] = defaults.maxWidth;\n }\n // Copy, never mutate — the styleFor record may be shared/cached.\n if (Object.keys(fill).length) bp.style = { ...own, ...fill };\n }\n if (spec.background) {\n const bg = deps.resolveMedia(spec.background);\n if (bg) bp.background = bg;\n }\n\n switch (spec.slice) {\n case \"Hero\":\n case \"TitleBand\": {\n // Text lives in the page doc; carry only the role/level metadata so the\n // render picks the right display font + heading tag.\n const meta = {\n ...(spec.headingRole ? { headingRole: spec.headingRole } : {}),\n ...(spec.headingLevel !== undefined ? { headingLevel: spec.headingLevel } : {}),\n ...(spec.subtitleRole ? { subtitleRole: spec.subtitleRole } : {}),\n };\n if (Object.keys(meta).length) bp.text = meta;\n break;\n }\n case \"RichText\":\n break; // content is in the page doc\n case \"Gallery\": {\n const g = spec.media.map(deps.resolveMedia).filter((m): m is RenderMedia => m !== null);\n if (g.length) bp.gallery = g;\n break;\n }\n case \"Carousel\": {\n // Caption TEXT lives in the page doc's items; only its role metadata\n // rides the manifest. An unresolved media TRUNCATES the slide list —\n // splicing it out would shift later slides onto the wrong page-doc\n // caption (the render zips items↔slides by index). Either way the\n // count shrink trips validateLayout's media-dropped finding.\n type CarouselManifestSlide = {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n };\n const slides: CarouselManifestSlide[] = [];\n for (const s of spec.slides) {\n const media = deps.resolveMedia(s.media);\n if (!media) break;\n const slide: CarouselManifestSlide = { media };\n if (s.caption) {\n const caption: { level?: number; role?: string } = { level: s.caption.level };\n if (s.caption.role !== undefined) caption.role = s.caption.role;\n slide.caption = caption;\n }\n if (s.subcaption) {\n slide.subcaption = s.subcaption.role !== undefined ? { role: s.subcaption.role } : {};\n }\n slides.push(slide);\n }\n if (slides.length > 0) {\n bp.carousel = spec.columns !== undefined ? { slides, columns: spec.columns } : { slides };\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n const m = deps.resolveMedia(spec.media);\n if (m) bp.media = m;\n break;\n }\n case \"SplitFeature\": {\n const media = deps.resolveMedia(spec.media);\n const text = renderNode(spec.text, deps.resolveMedia);\n if (media && text) bp.split = { mediaSide: spec.mediaSide, ratio: spec.ratio, media, text };\n break;\n }\n case \"LocationMap\":\n if (deps.map) bp.map = deps.map;\n break;\n case \"Grid\": {\n const tree = renderNode(spec.root, deps.resolveMedia);\n // Co-located map (widget:map inside the tree): attach the map config and\n // mark the toggle-switched panel row so the render can wire them up.\n const co = deps.map && hasMapWidget(spec.root) ? deps.map : null;\n if (co) bp.map = co;\n if (tree) bp.tree = co ? markPanelRows(tree, co.toggles.length) : tree;\n break;\n }\n }\n bands[String(spec.index)] = bp;\n }\n return { bands };\n}\n","import {\n parseGridBands,\n extractMapConfig,\n classifyBands,\n makeIsMapMount,\n type Band,\n type MapConfig,\n type SliceSpec,\n} from \"../grid/index.js\";\nimport { assembleIR } from \"../assemble.js\";\nimport { parseBluxSite } from \"../parse.js\";\nimport { normalizePages } from \"../normalize.js\";\nimport {\n buildFeedResolvers,\n cropRatioOf,\n feedAssetBase,\n isFeedBand,\n materializeFeedGrid,\n resolveFeedTiles,\n type FeedResolvers,\n type TileOverlay,\n} from \"../grid/feed-grid.js\";\nimport type { Node } from \"../grid/types.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { blockClassDefaults, blockStylesByIndex } from \"./block-styles.js\";\nimport { buildGridPlan, buildGridSitePlan, mediaUrl } from \"./grid-plan.js\";\nimport type { MigrationPlan } from \"./plan.js\";\nimport {\n buildPresentation,\n type MapRenderConfig,\n type Presentation,\n type PresentationDeps,\n type RenderMedia,\n} from \"./presentation.js\";\n\nexport type ConvertResult = {\n bands: Band[];\n specs: SliceSpec[];\n ir: SiteIR;\n mapConfig: MapConfig | null;\n plan: MigrationPlan;\n presentation: Presentation;\n};\n\n/** Drop the source-only `mountId` from an extracted MapConfig → the render-side\n * MapRenderConfig the presentation manifest carries. */\nexport function mapRenderFromConfig(c: MapConfig): MapRenderConfig {\n return {\n mid: c.mid,\n layers: c.layers,\n toggles: c.toggles,\n styles: c.styles,\n ...(c.center ? { center: c.center } : {}),\n ...(c.zoom !== undefined ? { zoom: c.zoom } : {}),\n ...(c.height ? { height: c.height } : {}),\n ...(c.defaultToggle !== undefined ? { defaultToggle: c.defaultToggle } : {}),\n };\n}\n\n/** The offline convert pipeline shared by `blux convert` (writes files) and\n * `blux validate` (checks fidelity): parse + classify index.html, assemble the\n * IR from site.json, and build both emit artifacts through a single media\n * resolver (CDN base ?? IR sourceUrl) so plan, manifest, and validation all\n * agree on which media resolve. Pure + offline — no writes, no network. */\nexport function convertExport({\n html,\n siteJson,\n}: {\n html: string;\n siteJson: unknown;\n}): ConvertResult {\n const bands = parseGridBands(html);\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const ir = assembleIR({ siteJson, htmls: [html] });\n\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const styles = blockStylesByIndex(siteJson);\n const defaults = blockClassDefaults(siteJson);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n\n const plan = buildGridPlan(specs, ir);\n const presentation = buildPresentation(specs, deps);\n return { bands, specs, ir, mapConfig, plan, presentation };\n}\n\n/** The leading heading/subtitle nodes of a parsed band root — a feed band's\n * real heading survives the parse (only the tile template was dropped), so we\n * keep it and append the materialized tile row below. */\nfunction leadingHeadings(root: Node): Node[] {\n if (root.kind === \"heading\" || root.kind === \"subtitle\") return [root];\n if (root.kind === \"stack\") {\n const out: Node[] = [];\n for (const c of root.children) {\n if (c.kind === \"heading\" || c.kind === \"subtitle\") out.push(c);\n else break; // headings lead; stop at the first non-heading (the dropped grid)\n }\n return out;\n }\n return [];\n}\n\n/** A feed band's grid column count: the source `columns` (site.json), else a\n * sensible default. */\nfunction feedColumns(item: { columns?: unknown }): number {\n const n = Number(item.columns);\n return Number.isFinite(n) && n > 0 ? n : 3;\n}\n\n/** The overlay-tile treatment of a feed band, or null. A grid with\n * `sourceConfig.overlay` + a crop `ratio`/`mediaRatio` (gallery/portfolio)\n * shows its caption OVER the cropped image — the render frames each tile at\n * the ratio and reveals the caption panel on hover, so a tile is only as tall\n * as its image. */\nfunction tileOverlay(item: unknown): TileOverlay | null {\n const cfg = (item as { sourceConfig?: Record<string, unknown> })?.sourceConfig;\n if (!cfg || cfg[\"overlay\"] !== true) return null;\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n if (!ratio) return null;\n const color = typeof cfg[\"overlayColor\"] === \"string\" ? cfg[\"overlayColor\"] : undefined;\n const valign = typeof cfg[\"contentvalign\"] === \"string\" ? cfg[\"contentvalign\"] : undefined;\n return { ratio, ...(color ? { color } : {}), ...(valign ? { valign } : {}) };\n}\n\n/** Did this band lose its content to the feed-template drop? — its parsed root\n * is empty (just heading(s)/subtitle/empty-raw, no media and no populated\n * row). Only such a band is a safe materialization target: a band that already\n * parsed real content is NOT a JS-hydrated feed grid, so the positional\n * site.json join landed on the wrong band and must not clobber it. */\nfunction isEmptyish(root: Node): boolean {\n switch (root.kind) {\n case \"heading\":\n case \"subtitle\":\n return true;\n case \"raw\":\n return root.html.trim() === \"\";\n case \"media\":\n case \"widget\":\n return false;\n case \"row\":\n return root.cells.length === 0;\n case \"stack\":\n return root.children.every(isEmptyish);\n default:\n return false;\n }\n}\n\n/** Replace each feed band's root (parsed to just its heading, the tile\n * template having been dropped) with the heading over a materialized tile row\n * rebuilt from the feed records. Feed bands whose source resolves to no tiles\n * keep their heading and get an `empty-feed-grid` diagnostic. Mutates `bands`.\n * The join is positional: site.json `items[i]` ↔ the band whose index is `i`\n * (the block-styles convention); a page with non-contiguous band ids simply\n * finds no item and is left as parsed. */\nfunction materializeFeedBands(\n bands: Band[],\n pageItems: unknown[] | undefined,\n resolvers: FeedResolvers,\n diagnostics: Diagnostic[],\n): void {\n if (!Array.isArray(pageItems)) return;\n for (const band of bands) {\n const item = pageItems[band.index];\n if (!isFeedBand(item)) continue;\n // Guard the positional join: only a band that lost its content to the\n // template drop is a real feed grid. A band that already parsed real\n // content means items[band.index] misaligned (a non-contiguous page) —\n // materializing would CLOBBER it, so skip with a diagnostic instead.\n if (!isEmptyish(band.root)) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `band ${band.index} has parsed content but site.json item is a feed source — positional join misaligned, left as parsed`,\n });\n continue;\n }\n const tiles = resolveFeedTiles(item, resolvers);\n if (!tiles) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `feed band ${band.index} (source ${String(item.sources[0])}) resolved to no tiles`,\n });\n continue;\n }\n const spacing = parseInt(String((item as { spacing?: unknown }).spacing ?? \"\"), 10);\n const row = materializeFeedGrid({\n tiles,\n columns: feedColumns(item as { columns?: unknown }),\n ...(Number.isFinite(spacing) && spacing > 0 ? { spacing } : {}),\n ...(tileOverlay(item) ? { overlay: tileOverlay(item)! } : {}),\n });\n if (!row) continue;\n const headings = leadingHeadings(band.root);\n band.root = headings.length ? { kind: \"stack\", children: [...headings, row] } : row;\n }\n}\n\n/** The site's page routing table (uid + export path + title), derivable from\n * site.json alone — the CLI uses it to locate each page's rendered html\n * (root index.html for the homepage, `<path>/index.html` for the rest)\n * before running convertSite. */\nexport function sitePages(siteJson: unknown): { uid: string; path: string; title: string }[] {\n const { pages } = normalizePages(parseBluxSite(siteJson));\n return pages.map((p) => ({ uid: p.uid, path: p.path, title: p.title }));\n}\n\n/** One converted page of a multi-page site. */\nexport type ConvertedPage = {\n uid: string;\n title: string;\n path: string;\n bands: Band[];\n specs: SliceSpec[];\n mapConfig: MapConfig | null;\n};\n\n/** A multi-page presentation manifest: per-page band manifests keyed by page\n * uid. Band indices are page-local (`page-block-N` restarts at 0 on every\n * page), so a flat bands map would collide across pages — the render side's\n * `loadPresentation(uid)` selects the page slice. */\nexport type MultiPagePresentation = { pages: Record<string, Presentation> };\n\n/** The whole-site faithful-grid convert: every page of the export (each page\n * dir's rendered index.html) through the same parse → classify → presentation\n * pipeline `convertExport` runs for one page. One IR assembled from ALL page\n * htmls (the asset urlMap then resolves media that only appear on inner\n * pages), one migration plan (a document per page, the asset union), and a\n * per-page presentation manifest. Pages whose html is missing from\n * `htmlByUid` (unexported drafts) are skipped with a diagnostic. */\nexport function convertSite({\n siteJson,\n htmlByUid,\n}: {\n siteJson: unknown;\n htmlByUid: Map<string, string>;\n}): {\n pages: ConvertedPage[];\n ir: SiteIR;\n plan: MigrationPlan;\n presentation: MultiPagePresentation;\n} {\n const ir = assembleIR({ siteJson, htmls: [...htmlByUid.values()] });\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const defaults = blockClassDefaults(siteJson);\n\n // Feed-grid materialization: gallery/portfolio tiles render client-side from\n // feed records (the static export ships only the dropped {{…}} template), so\n // we rebuild them deterministically from the feed data (see feed-grid.ts).\n // The asset base is scraped from the export's own data-base so feed images\n // use the RIGHT CDN host (Blux spreads assets across two).\n const raw = parseBluxSite(siteJson);\n const assetBase = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const feedResolvers = buildFeedResolvers(raw.feeds, raw.media, assetBase);\n const pageItemsByIndex = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content\n ?.pages;\n\n const pages: ConvertedPage[] = [];\n const presentation: MultiPagePresentation = { pages: {} };\n ir.pages.forEach((page, pageIndex) => {\n const html = htmlByUid.get(page.uid);\n if (html === undefined) {\n ir.diagnostics.push({\n kind: \"missing-page-html\",\n where: page.uid,\n message: `no rendered html for page \"${page.uid}\" (${page.path || \"/\"}) — page skipped`,\n });\n return;\n }\n const bands = parseGridBands(html);\n materializeFeedBands(\n bands,\n pageItemsByIndex?.[pageIndex]?.items,\n feedResolvers,\n ir.diagnostics,\n );\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const styles = blockStylesByIndex(siteJson, pageIndex);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n presentation.pages[page.uid] = buildPresentation(specs, deps);\n pages.push({ uid: page.uid, title: page.title, path: page.path, bands, specs, mapConfig });\n });\n\n const plan = buildGridSitePlan(\n pages.map((p) => ({ uid: p.uid, title: p.title, specs: p.specs })),\n ir,\n );\n return { pages, ir, plan, presentation };\n}\n","import type { MapConfig } from \"../grid/extract-map.js\";\nimport type { Media } from \"../grid/types.js\";\n\n/** The Prismic document type the catalog pipeline emits for a migrated page.\n * The whole downstream stack (migrated repos + starter render routing via\n * getPageDoc's [\"page\",\"catalog_page\"]) keys on this; it is the migration-owned\n * page type, kept distinct from the template's own `page` type. */\nexport const CATALOG_PAGE_TYPE = \"catalog_page\";\n\n/** A rich-text run kept as raw HTML — emit turns it into a `{__richtext_html}`\n * marker; the parser already produces HTML for heading/body nodes. */\nexport type CatalogRichText = string;\n\n/** One catalog cell — the structural unit the Plan-2 `BluxCell` renders.\n * `kind` mirrors the Prismic Select (\"text\" | \"media\" | \"embed\" | \"subgrid\"). */\nexport type CatalogCell = {\n kind: \"text\" | \"media\" | \"embed\" | \"subgrid\";\n title?: CatalogRichText;\n bodyHtml?: string;\n media?: Media;\n mediaRatio?: string;\n embedHtml?: string;\n // --- visual-fidelity fields (Blux catalog visual layer) ---\n width?: string;\n spacing?: number;\n cover?: boolean;\n valign?: boolean;\n backgroundColor?: string;\n contentPadding?: string;\n titleRole?: string;\n subgrid?: CatalogCell[];\n};\n\n/** Fields shared by every catalog spec. `index` is the slice-zone position\n * (kept for parity with SliceSpec + future manifest-free ordering).\n * Decision-B widget routing (plan 4b, round 3): ANY spec can carry a custom\n * mount (the map) as a widget — the ORIGINAL mount html rides `widgetHtml`\n * (pristine — sanitize is emit's concern) with `widgetKind` naming it; for\n * maps the extracted `MapConfig` rides along so emit can inline it into the\n * document. The triple lives HERE (round 3) because every real fleet map band\n * holds the mount PLUS panel rows, so it routes Grid/Block — not just the\n * Section/Collection containers. */\nexport type CatalogBase = {\n index: number;\n background?: Media;\n backgroundColor?: string;\n // --- band-level visual-fidelity fields (Blux catalog visual layer) ---\n minHeight?: string;\n contentPadding?: string;\n contentPaddingMobile?: string;\n maxContentWidth?: string;\n verticalAlign?: string;\n textAlign?: string;\n columnWidth?: string;\n columnSide?: string;\n headingRole?: string;\n widgetKind?: string;\n widgetHtml?: string;\n mapConfig?: MapConfig;\n};\n\n/** A container band → the Plan-2 `blux_section` slice. A band whose sole\n * content is a custom mount becomes a section carrying the widget triple\n * (see CatalogBase) with no cells. */\nexport type BluxSectionSpec = CatalogBase & {\n slice: \"BluxSection\";\n heading?: CatalogRichText;\n cells: CatalogCell[];\n};\n\nexport type BluxGridSpec = CatalogBase & {\n slice: \"BluxGrid\";\n heading?: CatalogRichText;\n columns?: number;\n cells: CatalogCell[];\n};\nexport type BluxGallerySpec = CatalogBase & {\n slice: \"BluxGallery\";\n heading?: CatalogRichText;\n cells: CatalogCell[]; // all kind:\"media\"\n};\nexport type BluxCarouselSpec = CatalogBase & {\n slice: \"BluxCarousel\";\n heading?: CatalogRichText;\n columnsVisible?: number;\n cells: CatalogCell[];\n};\nexport type BluxMediaSpec = CatalogBase & {\n slice: \"BluxMedia\";\n media: Media;\n caption?: CatalogRichText;\n};\nexport type BluxMediaTextSpec = CatalogBase & {\n slice: \"BluxMediaText\";\n mediaSide: \"left\" | \"right\";\n layoutRatio?: number;\n media: Media;\n title?: CatalogRichText;\n body?: CatalogRichText;\n};\n/** A feed-backed band → the `blux_collection` query-spec slice (spec §6 row 5,\n * §7 rule 1). The slice carries NO cells — the starter resolves the mapped\n * entity documents at load time and renders them through the tagFilter DSL.\n * Collection is a CONTAINER (decision B): a widget mount can ride it exactly\n * like BluxSection (the CatalogBase widget triple, emitted through the same\n * widgetFields path). */\nexport type BluxCollectionSpec = CatalogBase & {\n slice: \"BluxCollection\";\n heading?: CatalogRichText;\n entityType: string; // mapped Prismic type the renderer queries\n feedIds: string[]; // original Blux feed ids (traceability)\n filterTag?: string; // tagFilter DSL expression\n sort?: string;\n limit?: number;\n mediaRatio?: string;\n layout: \"grid\" | \"carousel\";\n scrollLoadMore?: boolean;\n};\n\n/** Content-preserving fallback: the serialized node tree (Prismic can't nest\n * deeper than cell→subgrid). `payload` is a `{tag,children,html,image,style}`\n * tree the Plan-2 BluxBlock slice renders recursively. */\nexport type BluxBlockSpec = CatalogBase & {\n slice: \"BluxBlock\";\n payload: BlockNode;\n /** Every Media under the source subtree. The payload inlines them as CDN\n * urls, but emit still needs the Media list so the assets upload (payload\n * urls stay CDN until the 4d migrate-time rewrite — runMigration's\n * assetUrlByCdn is the map for it). */\n media: Media[];\n};\n\n/** The serialized-tree shape BluxBlock renders (mirror of starter\n * src/lib/slices/BluxBlock/node.ts BluxNode). */\nexport type BlockNode = {\n tag?: string;\n html?: string;\n image?: { url: string; alt?: string };\n style?: Record<string, string>;\n className?: string;\n children?: BlockNode[];\n};\n\n/** The catalog classify target: every breadth slice + the BluxBlock fallback. */\nexport type CatalogSpec =\n | BluxSectionSpec\n | BluxGridSpec\n | BluxGallerySpec\n | BluxCarouselSpec\n | BluxMediaSpec\n | BluxMediaTextSpec\n | BluxCollectionSpec\n | BluxBlockSpec;\n\nexport type CatalogKind = CatalogSpec[\"slice\"];\n","// The migration-owned `catalog_page` custom type. A migrated Blux site emits\n// `catalog_page` documents (see spec.ts CATALOG_PAGE_TYPE), so the migration\n// tool must also OWN + push the type definition — a cloned starter repo does\n// not ship it (the starter's own `page` type is the /new-site flow's; see the\n// starter's src/lib/blux-catalog/page-doc.ts module note). This is the type the\n// migrate action pushes via pushCustomTypes, so its `json` is the FULL Prismic\n// Custom Types API definition (id/label/format/status + the tabbed model),\n// exactly like the entity types buildEntityEmit emits.\nimport type { PlanCustomType } from \"../emit/plan.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\n\nconst CATALOG_PAGE_LABEL = \"Catalog Page (Blux migration)\";\n\n/** The SharedSlice choices a migrated catalog page can hold — the `blux_*`\n * catalog slices ONLY. The starter's `page` type also lists its native slice\n * choices (lead_text/hero/…), but a migrated page emits nothing but `blux_*`\n * slices (see emit.ts catalogSpecToPlanSlice), so the native choices are\n * dropped: the type models exactly what the migration populates. Mirrors the\n * starter page type's `blux_*` entries (customtypes/page/index.json). */\nconst BLUX_SLICE_CHOICES = [\n \"blux_section\",\n \"blux_text\",\n \"blux_block\",\n \"blux_grid\",\n \"blux_gallery\",\n \"blux_carousel\",\n \"blux_media\",\n \"blux_media_text\",\n \"blux_embed\",\n \"blux_table\",\n \"blux_collection\",\n] as const;\n\nfunction sliceChoices(): Record<string, { type: \"SharedSlice\" }> {\n return Object.fromEntries(BLUX_SLICE_CHOICES.map((t) => [t, { type: \"SharedSlice\" }]));\n}\n\n/** The `catalog_page` custom type: a `format:\"page\"` clone of the starter's\n * `page` type (customtypes/page/index.json) — the Main tab (uid + title + the\n * `blux_*` SharedSlices zone) and the \"SEO & Metadata\" tab\n * (meta_title/meta_description/meta_image). The SEO field types match the\n * starter render's reads (blux-catalog/page-doc.ts pageMeta): meta_title +\n * meta_description are Text (plain string), meta_image is an Image field\n * (`.url`/`.alt`). Shipped on EVERY catalog plan so the migration owns the type\n * it emits documents for. */\nexport function catalogPageCustomType(): PlanCustomType {\n return {\n id: CATALOG_PAGE_TYPE,\n label: CATALOG_PAGE_LABEL,\n repeatable: true,\n json: {\n id: CATALOG_PAGE_TYPE,\n label: CATALOG_PAGE_LABEL,\n format: \"page\",\n repeatable: true,\n status: true,\n json: {\n Main: {\n uid: { type: \"UID\", config: { label: \"UID\", placeholder: \"\" } },\n title: {\n type: \"StructuredText\",\n config: {\n label: \"Title\",\n placeholder: \"\",\n allowTargetBlank: true,\n single: \"heading1\",\n },\n },\n slices: {\n type: \"Slices\",\n fieldset: \"Slice Zone\",\n config: { choices: sliceChoices() },\n },\n },\n \"SEO & Metadata\": {\n meta_title: {\n type: \"Text\",\n config: {\n label: \"Meta Title\",\n placeholder: \"A title of the page used for social media and search engines\",\n },\n },\n meta_description: {\n type: \"Text\",\n config: {\n label: \"Meta Description\",\n placeholder: \"A brief summary of the page\",\n },\n },\n meta_image: {\n type: \"Image\",\n config: {\n label: \"Meta Image\",\n constraint: { width: 2400, height: 1260 },\n thumbnails: [],\n },\n },\n },\n },\n },\n };\n}\n","// Emit-stage HTML sanitizer (slim plan 4b). Raw Blux html reaches three emit\n// boundaries — cell `embed_html`, section `widget_html`, and BluxBlock payload\n// `html` leaves — and the source markup routinely carries behavior <script>\n// blocks (map init, clickMap toggles) that must never ship inside Prismic\n// documents. The CatalogSpec upstream stays pristine (classify is\n// emit-agnostic); sanitizing lives at the emit boundary only.\n\n/** Closed `<script …>…</script>` blocks — case-insensitive, dotall, multiple. */\nconst SCRIPT_BLOCK_RE = /<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi;\n/** A script opened but never closed — swallow to end of input. */\nconst SCRIPT_TAIL_RE = /<script\\b[\\s\\S]*$/i;\n/** Inline `on*=` handler attributes (onclick, onerror, …), any quoting style. */\nconst ON_HANDLER_RE = /\\s+on\\w+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi;\n/** `javascript:` urls in href/src — quoted and bare forms. */\nconst JS_URL_QUOTED_RE = /\\b(href|src)\\s*=\\s*([\"'])\\s*javascript:[\\s\\S]*?\\2/gi;\nconst JS_URL_BARE_RE = /\\b(href|src)\\s*=\\s*javascript:[^\\s>]*/gi;\n\n/** Strip active content from raw html: `<script>` blocks (multiple,\n * case-insensitive, tolerating one left unclosed at EOF), inline `on*=`\n * handler attributes, and `javascript:` urls in href/src (neutralized to\n * `#`). Everything else passes through verbatim — the sanitizer is a\n * scalpel, not a rewriter. */\nexport function sanitizeHtml(html: string): string {\n return html\n .replace(SCRIPT_BLOCK_RE, \"\")\n .replace(SCRIPT_TAIL_RE, \"\")\n .replace(ON_HANDLER_RE, \"\")\n .replace(JS_URL_QUOTED_RE, \"$1=$2#$2\")\n .replace(JS_URL_BARE_RE, '$1=\"#\"');\n}\n\n/** Whether sanitized html still shows anything: non-whitespace text, or a\n * media element (img/iframe/video). A mount whose only payload was a behavior\n * script has no visible content — the emit layer drops it (recording a\n * diagnostic) instead of shipping an empty shell. */\nexport function hasVisibleContent(html: string): boolean {\n const clean = sanitizeHtml(html);\n if (/<(img|iframe|video)\\b/i.test(clean)) return true;\n return clean.replace(/<[^>]*>/g, \"\").trim().length > 0;\n}\n","import type { Media, Node, GridToken } from \"../grid/types.js\";\nimport { collectMedia, blockPlainText } from \"../grid/index.js\";\n// `collectText`/`isEmptyRaw` are not re-exported via the grid barrel\n// (index.ts) — import them from their module directly.\nimport { collectText, isEmptyRaw } from \"../grid/classify-band.js\";\nimport { mediaCdnUrl } from \"../emit/grid-plan.js\";\nimport type { BlockNode, CatalogCell } from \"./spec.js\";\n\n/** Peel one-child style-box stacks (mirror of classify-band unboxed). */\nfunction unbox(n: Node): Node {\n let cur = n;\n while (cur.kind === \"stack\" && cur.children.length === 1) {\n const only = cur.children[0];\n if (!only) break;\n cur = only;\n }\n return cur;\n}\n\n/** Raw nodes under a node (recursive). classify-band has the same helper but\n * does not export it — reimplemented locally rather than widening the grid\n * module's public surface. */\nfunction collectRaws(node: Node): Extract<Node, { kind: \"raw\" }>[] {\n switch (node.kind) {\n case \"raw\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectRaws(c.node));\n case \"stack\":\n return node.children.flatMap(collectRaws);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n return [];\n }\n}\n\n/** The joined non-empty raw html under a node — a cell's `embedHtml`. Keeps\n * the visible runs the text model would otherwise drop (the-pointe's 'Visit\n * Website' link buttons parse to `raw`); empty raws are client-mount shells. */\nfunction rawHtmlOf(node: Node): string | undefined {\n const raws = collectRaws(node).filter((r) => !isEmptyRaw(r));\n return raws.length ? raws.map((r) => r.html).join(\"\\n\") : undefined;\n}\n\n/** Wrap BARE (untagged) html in `<p>` — html whose trimmed text does not start\n * with `<`. Body parts folded next to a wrapped `<hN>` heading MUST carry a\n * block tag of their own: `htmlAsRichText` merges trailing bare text INTO the\n * preceding heading node, concatenating the words and dropping the paragraph.\n * Already-tagged parts (wrapped headings, `<p>`-carrying body html, raw block\n * html) pass through untouched. */\nfunction wrapBare(html: string): string {\n return html.trim().startsWith(\"<\") ? html : `<p>${html.trim()}</p>`;\n}\n\n/** Per-cell visual fields recovered from the grid token + the (unboxed) node's\n * card style. `token.ratio` is the cell's width share; card keys land on\n * `node.style` via parse-grid's withCardStyle. */\nfunction visualFieldsOf(u: Node, token?: GridToken): Partial<CatalogCell> {\n const style = \"style\" in u && u.style ? u.style : {};\n const media = collectMedia(u)[0];\n const out: Partial<CatalogCell> = {};\n if (typeof token?.ratio === \"number\") out.width = `${token.ratio}%`;\n if (typeof token?.spacing === \"number\") out.spacing = token.spacing;\n if (style[\"background-color\"]) out.backgroundColor = style[\"background-color\"];\n if (style[\"padding\"]) out.contentPadding = style[\"padding\"];\n if (style[\"_valign\"] === \"middle\") out.valign = true;\n if (style[\"_fill\"] === \"column\" || media?.fit === \"cover\") out.cover = true;\n return out;\n}\n\n/** Wrap a body-block html string in its Blux type-role div (`txt-role-textN`)\n * so the starter theme's `.txt-role-textN :is(hN,p)` rule sizes it; a roleless\n * block passes through untouched. The string sibling of `roleWrap` (which wraps\n * a BlockNode for the BluxBlock fallback path). */\nfunction roleWrapHtml(role: string | undefined, inner: string): string {\n return role ? `<div class=\"txt-role-${role}\">${inner}</div>` : inner;\n}\n\n/** Title + body html under a node (recursive, document order). The FIRST\n * non-blank heading becomes the title; every LATER heading is folded into the\n * body parts in document order relative to bodies/subtitles, so no heading is\n * dropped. The parser's `heading.html` carries no `<hN>` wrapper, so headings\n * are wrapped here — otherwise `htmlAsRichText` resolves them to `paragraph`\n * nodes, violating heading-only StructuredText fields and losing levels.\n * Folded headings keep their TRUE level (body fields render every block kind;\n * clamping to a field's heading window is emit's concern); bare body html is\n * `<p>`-wrapped so a folded heading never swallows it (see `wrapBare`). Each\n * folded block is baked into its own `txt-role-textN` div (approach B) so a\n * multi-role cell body keeps every block's display size — a RichText field\n * cannot carry the class, so the roled html is emitted as a plain string. */\nfunction textOf(n: Node): {\n title?: string;\n titleRole?: string;\n bodyHtml?: string;\n} {\n let title: string | undefined;\n let titleRole: string | undefined;\n const bodyParts: string[] = [];\n for (const t of collectText(n)) {\n if (t.kind === \"heading\") {\n if (blockPlainText(t.html) === \"\") continue; // whitespace-only heading\n const wrapped = `<h${t.level}>${t.html}</h${t.level}>`;\n if (title === undefined) {\n title = wrapped;\n titleRole = t.role;\n } else bodyParts.push(roleWrapHtml(t.role, wrapped));\n } else if (t.kind === \"body\") {\n if (t.html) bodyParts.push(roleWrapHtml(t.role, wrapBare(t.html)));\n } else if (t.kind === \"subtitle\") {\n bodyParts.push(roleWrapHtml(t.role, `<p>${t.text}</p>`));\n }\n }\n return {\n ...(title ? { title } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...(bodyParts.length ? { bodyHtml: bodyParts.join(\"\\n\") } : {}),\n };\n}\n\n/** Shared build state: `flattened` records that a subgrid item's subtree\n * carried structure the cell model cannot store — a nested row (at any depth\n * under the item) or more than one media (see `cellDepthExceedsTwo`). */\ntype CellBuildState = { flattened: boolean };\n\n/** Whether a subtree contains any row, at any depth. */\nfunction containsRow(node: Node): boolean {\n switch (node.kind) {\n case \"row\":\n return true;\n case \"stack\":\n return node.children.some(containsRow);\n default:\n return false;\n }\n}\n\n/** One node → one catalog cell. `depth` 0 builds a top-level cell: a row\n * becomes a subgrid (the ONE nesting level the Prismic model stores), and any\n * OTHER subtree carrying MORE than one media splits into a subgrid of one\n * text item (the whole subtree's title/body/raw html) plus one media item per\n * media in document order — the band stays editable and no media is dropped.\n * At subgrid-item depth (1) neither trick is available: a subtree with a\n * nested row or a second media cannot be stored, so its content is flattened\n * into the cell (first media + all text + raws) and the loss is recorded on\n * `state` — builder and guard can never disagree on legality. */\nfunction buildCell(\n node: Node,\n depth: number,\n state: CellBuildState,\n token?: GridToken,\n): CatalogCell {\n const u = unbox(node);\n if (u.kind === \"row\" && depth === 0) {\n return {\n kind: \"subgrid\",\n subgrid: u.cells.map((c) => buildCell(c.node, depth + 1, state, c.token)),\n };\n }\n const allMedia = collectMedia(u);\n if (depth === 0 && allMedia.length > 1) {\n // Depth-0 subgrid split: every media survives as its own item; the\n // subtree's whole text/raw content rides one leading text item — carrying\n // its type roles so the split text sizes like any other cell title/body.\n const { title, titleRole, bodyHtml } = textOf(u);\n const embedHtml = rawHtmlOf(u);\n const textItem: CatalogCell[] =\n title || bodyHtml || embedHtml\n ? [\n {\n kind: \"text\",\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n },\n ]\n : [];\n return {\n kind: \"subgrid\",\n subgrid: [...textItem, ...allMedia.map((m): CatalogCell => ({ kind: \"media\", media: m }))],\n };\n }\n if (depth > 0 && (allMedia.length > 1 || containsRow(u))) state.flattened = true;\n const media = allMedia[0];\n const vis = visualFieldsOf(u, token);\n const { title, titleRole, bodyHtml } = textOf(u);\n const embedHtml = rawHtmlOf(u);\n if (u.kind === \"media\" || (media && !title && !bodyHtml)) {\n return {\n kind: \"media\",\n ...(media ? { media } : {}),\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...vis,\n };\n }\n return {\n kind: title || bodyHtml ? \"text\" : embedHtml ? \"embed\" : \"text\",\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(media ? { media } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...vis,\n };\n}\n\n/** One node → one catalog cell (public wrapper; the cell is top-level). */\nexport function cellFromNode(node: Node): CatalogCell {\n return buildCell(node, 0, { flattened: false });\n}\n\nfunction buildCells(node: Node, state: CellBuildState): CatalogCell[] {\n const u = unbox(node);\n if (u.kind === \"row\") return u.cells.map((c) => buildCell(c.node, 0, state, c.token));\n if (u.kind === \"stack\") return u.children.map((c) => buildCell(c, 0, state));\n return [buildCell(u, 0, state)];\n}\n\n/** A row's cells → catalog cells; a bare content node → a single cell. Never\n * emits a subgrid item that itself has a subgrid (rows past that depth are\n * flattened — see `buildCell`). */\nexport function nodeToCells(node: Node): CatalogCell[] {\n return buildCells(node, { flattened: false });\n}\n\n/** True when `node` needs more nesting than the cell model stores — i.e.\n * building its cells would flatten structure at subgrid-item depth (a nested\n * row anywhere under one item, or a second media inside one item). Implemented\n * BY running the builder, so guard and emission share one source of truth:\n * guard false ⟹ every media and text run in the band is present in the\n * emitted cells (structure-complete up to the cell→subgrid model);\n * guard true ⟹ the caller should fall back to BluxBlock (content-preserving). */\nexport function cellDepthExceedsTwo(node: Node): boolean {\n const state: CellBuildState = { flattened: false };\n buildCells(node, state);\n return state.flattened;\n}\n\n/** Wrap a block text leaf in its Blux type-role div (`txt-role-textN`) when it\n * carries a role, so the starter theme's `.txt-role-textN :is(hN,p)` rule sizes\n * it exactly as grid/section text; a roleless leaf passes through untouched. */\nfunction roleWrap(role: string | undefined, node: BlockNode): BlockNode {\n return role ? { tag: \"div\", className: `txt-role-${role}`, children: [node] } : node;\n}\n\n/** Serialize a node tree to a BluxBlock payload — the content-preserving\n * fallback for bands too deep/irregular for the cell model. Never drops media\n * or text (the whole point). */\nexport function blockPayload(node: Node): BlockNode {\n const u = node;\n switch (u.kind) {\n case \"row\":\n return {\n tag: \"div\",\n style: { display: \"grid\" },\n children: u.cells.map((c) => blockPayload(c.node)),\n };\n case \"stack\":\n return { tag: \"div\", children: u.children.map(blockPayload) };\n // Text leaves keep their Blux type role (text1…textN): the starter theme\n // sizes text ONLY through `.txt-role-textN :is(h1..h6,p)`, so a bare heading\n // in a Block-fallback band would render unstyled. Wrap in the role div (the\n // same mechanism BluxCell uses for cell titles) so block-band headings size\n // identically to grid/section headings.\n case \"heading\":\n return roleWrap(u.role, { tag: `h${u.level}`, html: u.html });\n case \"body\":\n return roleWrap(u.role, { tag: \"div\", html: u.html });\n case \"subtitle\":\n return roleWrap(u.role, { tag: \"p\", html: u.text });\n case \"media\":\n return mediaBlock(u.media);\n case \"raw\":\n return { html: u.html };\n case \"widget\":\n return { tag: \"div\", html: \"\" }; // 4b captures widget html\n }\n}\n\nfunction mediaBlock(m: Media): BlockNode {\n // A video renders as an inline <video> — an image url here would make the\n // starter's BluxBlock render a broken <img> pointing at an mp4.\n if (m.kind === \"video\") return { tag: \"figure\", html: videoTag(m) };\n const url = m.base ? `${m.base}${m.assetId}${m.ext ? `.${m.ext}` : \"\"}` : m.assetId;\n // `Media` (grid/types.ts) carries no alt — asset alt lives in the IR asset\n // index and is resolved at emit/migrate time, not here.\n return { tag: \"figure\", image: { url, alt: \"\" } };\n}\n\n/** An inline `<video>` tag for a video Media — shared by the emit layer\n * (`embed_html`/`video_embed` fields) and the BluxBlock payload. Videos cannot\n * ride Prismic Image fields (PrismicImage would render a broken <img>) — they\n * play from the export's CDN url instead (self-hosted upload pends the 4d\n * asset strategy; url logic mirrors `mediaCdnUrl`, bare assetId as last\n * resort). Attributes honor the source `<video>` semantics (`Media.playback`);\n * absent playback falls back to a user-initiated inline video\n * (`controls playsinline`). Autoplay implies `muted` (browsers refuse un-muted\n * autoplay) and `playsinline` (iOS refuses inline autoplay without it). */\nexport function videoTag(m: Media): string {\n const p = m.playback ?? { controls: true, playsinline: true };\n const attrs: string[] = [];\n if (p.autoplay) attrs.push(\"autoplay\");\n if (p.loop) attrs.push(\"loop\");\n if (p.muted || p.autoplay) attrs.push(\"muted\");\n if (p.controls) attrs.push(\"controls\");\n if (p.playsinline || p.autoplay) attrs.push(\"playsinline\");\n const src = `src=\"${mediaCdnUrl(m) ?? m.assetId}\"`;\n return `<video ${[...attrs, src].join(\" \")}></video>`;\n}\n\n/** An inline `<img>` for an image Media — the doubly-nested (subgrid) delivery.\n * The Prismic Migration API can't resolve Image-field asset refs inside a\n * doubly-nested group, so a subgrid image rides a Text field as a url-based\n * `<img>` (exactly as video rides `<video>`). `resolveUrl` returns the\n * plan-asset CDN url (kept RAW so the migrate's `rewriteValueUrls` swaps it to\n * the uploaded Prismic url — escaping a query-string `&` would break that\n * match); `resolveAlt` returns the IR asset alt (escaped for the attribute),\n * else \"\". */\nexport function imgTag(\n m: Media,\n resolveUrl: (m: Media) => string | null,\n resolveAlt: (id: string) => string,\n): string {\n const src = resolveUrl(m) ?? m.assetId;\n const alt = resolveAlt(m.assetId)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n return `<img src=\"${src}\" alt=\"${alt}\">`;\n}\n","/** Frozen feed→entity-type mapping (spec §8). Keyed on the NORMALIZED feed\n * name (lowercase, collapsed whitespace). `collection_item` is the catch-all\n * so nothing is unroutable; \"DO NOT USE…\" feeds are skipped with a report\n * entry instead of migrated. */\nconst NAME_TO_TYPE: Record<string, string> = {\n products: \"product\",\n \"equipment grid\": \"product\",\n \"center features\": \"product\",\n team: \"person\",\n reps: \"person\",\n trainers: \"person\",\n events: \"event\",\n \"donate life observances\": \"event\",\n news: \"news_article\",\n \"outside the lines\": \"news_article\",\n \"all projects list\": \"project\",\n portfolio: \"project\",\n projects: \"project\",\n};\n\nconst normalize = (name: string): string => name.trim().toLowerCase().replace(/\\s+/g, \" \");\n\nexport function feedEntityType(feedName: string): string {\n const name = normalize(feedName);\n const exact = NAME_TO_TYPE[name];\n if (exact) return exact;\n // Real fleet feeds prefix \"Equipment Grid\" with the club name (\"The Pointe\n // Equipment Grid\" ×4 in fitHealthClub) — suffix-match after the exact\n // lookup misses so those still route to product.\n if (/\\bequipment grid$/.test(name)) return \"product\";\n return \"collection_item\";\n}\n\nexport function isSkippedFeed(feedName: string): boolean {\n return normalize(feedName).startsWith(\"do not use\");\n}\n","import type { Band, Media, Node } from \"../grid/types.js\";\nimport type { MapConfig } from \"../grid/extract-map.js\";\nimport type { Diagnostic } from \"../ir.js\";\nimport type { BlockDefaults } from \"../emit/block-styles.js\";\nimport { classifyBand, collectMedia, type CarouselSlide, type SliceSpec } from \"../grid/index.js\";\nimport { cellFromNode, nodeToCells, blockPayload, cellDepthExceedsTwo } from \"./cells.js\";\nimport { cropRatioOf, isFeedBand } from \"../grid/feed-grid.js\";\nimport { feedEntityType, isSkippedFeed } from \"./feeds.js\";\nimport { hasVisibleContent } from \"./sanitize.js\";\nimport type {\n BluxBlockSpec,\n BluxCollectionSpec,\n BluxGridSpec,\n CatalogCell,\n CatalogSpec,\n} from \"./spec.js\";\n\ntype CatalogBaseFields = {\n index: number;\n background?: Media;\n backgroundColor?: string;\n minHeight?: string;\n contentPadding?: string;\n contentPaddingMobile?: string;\n maxContentWidth?: string;\n verticalAlign?: string;\n textAlign?: string;\n columnWidth?: string;\n columnSide?: string;\n headingRole?: string;\n};\n\n/** Options threaded from the caller (the CLI catalog action extracts them per\n * page): `isMapMount` is the classifier's mount predicate (grid\n * `ClassifyOptions`), `mapConfig` the page's extracted map config carried onto\n * the resulting BluxSectionSpec for emit. */\nexport type BandToCatalogOptions = {\n isMapMount?: (node: Node) => boolean;\n mapConfig?: MapConfig;\n /** Classify-time diagnostics sink (positional-join misalignments, unknown/\n * skipped feed sources). Absent → those findings go unrecorded, never throw. */\n diagnostics?: Diagnostic[];\n /** The page being classified — rides diagnostic `where` as `<uid>:<band>`\n * so same-index findings on different pages stay distinguishable (round-2\n * item 10a: fitHealthClub's 4 band-3 diagnostics were identical). */\n pageUid?: string;\n /** Per-band inline block styles keyed by band index (background-color,\n * min-height, text-align, vertical-align, …) — the CLI supplies\n * `blockStylesByIndex(siteJson, pageIndex)`. `baseOf` reads `.get(band.index)`. */\n styles?: Map<number, Record<string, string>>;\n /** Block-class `.blocksNcontainer` defaults (padding/mobilePadding/maxWidth)\n * keyed by the wrapper class — the CLI supplies `blockClassDefaults(siteJson)`.\n * `baseOf` reads `.get(band.blockClass)`, mirroring presentation.ts. */\n defaults?: Map<string, BlockDefaults>;\n};\n\n/** Band-visual fields recovered from the CLI-threaded per-band inline styles\n * (`opts.styles.get(index)`) + the wrapper class defaults\n * (`opts.defaults.get(band.blockClass)`), mirroring presentation.ts's\n * styleFor/defaultsFor resolution. An inline `padding` overrides the class\n * default; every field is omitted when its source is absent (graceful\n * degradation — a site without block styles emits an unstyled band). */\nconst baseOf = (band: Band, opts?: BandToCatalogOptions): CatalogBaseFields => {\n const st = opts?.styles?.get(band.index) ?? {};\n const def = (band.blockClass ? opts?.defaults?.get(band.blockClass) : undefined) ?? {};\n const padding = st[\"padding\"] ?? def.padding;\n return {\n index: band.index,\n ...(band.background ? { background: band.background } : {}),\n ...(st[\"background-color\"] ? { backgroundColor: st[\"background-color\"] } : {}),\n ...(st[\"min-height\"] ? { minHeight: st[\"min-height\"] } : {}),\n ...(st[\"text-align\"] ? { textAlign: st[\"text-align\"] } : {}),\n ...(st[\"vertical-align\"] === \"middle\" ? { verticalAlign: \"middle\" } : {}),\n ...(padding ? { contentPadding: padding } : {}),\n ...(def.mobilePadding ? { contentPaddingMobile: def.mobilePadding } : {}),\n ...(def.maxWidth ? { maxContentWidth: def.maxWidth } : {}),\n ...(st[\"_column-width\"] ? { columnWidth: st[\"_column-width\"] } : {}),\n ...(st[\"_column-side\"] ? { columnSide: st[\"_column-side\"] } : {}),\n };\n};\n\n/** Map a thin routed SliceSpec + its Band to a rich CatalogSpec. Reuses the\n * battle-tested routing; builds full cells by walking the node subtree. */\nexport function sliceSpecToCatalog(\n spec: SliceSpec,\n band: Band,\n opts: BandToCatalogOptions = {},\n): CatalogSpec {\n const base = baseOf(band, opts);\n switch (spec.slice) {\n case \"Hero\":\n case \"TitleBand\":\n case \"RichText\":\n // Overlay/centered/body text → a Section; heading split out of the cells.\n return {\n slice: \"BluxSection\",\n ...base,\n ...splitHeadingAndCells(band.root),\n };\n case \"SplitFeature\": {\n const text = cellFromNode(spec.text);\n // Promote to the thin two-field slice ONLY when the media is an image\n // and the text half really is just title+body. A video cannot ride the\n // slice's Image field (the `{__asset_id}` marker would dangle — videos\n // are excluded from image uploads — silent total loss), and a media/\n // subgrid/raw-bearing text half cannot ride BluxMediaText (band 1's\n // text half holds 2 images) — refuse the lossy promotion, mirroring\n // classifyBand's own philosophy, and enrich the WHOLE band through the\n // grid path (where a video becomes an embed_html <video> cell) so\n // nothing is dropped.\n if (spec.media.kind !== \"video\" && !text.media && !text.subgrid && !text.embedHtml)\n return {\n slice: \"BluxMediaText\",\n ...base,\n mediaSide: spec.mediaSide,\n layoutRatio: spec.ratio,\n media: spec.media,\n ...pick(text),\n };\n return gridOrBlock(band.root, base);\n }\n case \"MediaFull\":\n case \"VideoFeature\":\n return { slice: \"BluxMedia\", ...base, media: spec.media };\n case \"Gallery\":\n return {\n slice: \"BluxGallery\",\n ...base,\n cells: spec.media.map((m) => ({ kind: \"media\", media: m }) as CatalogCell),\n };\n case \"Carousel\":\n return {\n slice: \"BluxCarousel\",\n ...base,\n ...(spec.columns !== undefined ? { columnsVisible: spec.columns } : {}),\n cells: spec.slides.map(slideCell),\n };\n case \"LocationMap\": {\n // Decision B (4b): the map band becomes a BluxSection carrying the\n // ORIGINAL mount raw html as a widget (see liftMapWidget). Emit\n // sanitizes and inlines the MapConfig; classify keeps the html pristine.\n const widget = liftMapWidget(band, opts);\n if (!widget) return blockSpec(band.root, base); // predicate-less caller: 4a fallback\n return { slice: \"BluxSection\", ...base, cells: [], ...widget };\n }\n case \"Grid\":\n default: {\n // Rich grid when it fits the depth-2 model; else the opaque fallback.\n // Round 3: every REAL fleet map band holds the mount PLUS panel rows,\n // so it routes HERE (never LocationMap) — and classifyBand's rewrite\n // left an html-less widget node in spec.root, which the Block path\n // serialized as an empty div: map, legend chips, and MapConfig silently\n // vanished from the plan. Lift the ORIGINAL mount from band.root onto\n // the container spec (the CatalogBase widget triple — both BluxGrid and\n // the BluxBlock fallback carry it); when the band holds a mount the\n // lift cannot recover, say so — never silent.\n const widget = liftMapWidget(band, opts);\n if (!widget) reportUnrecoveredMount(spec.root, band, opts);\n return { ...gridOrBlock(spec.root, base), ...(widget ?? {}) };\n }\n }\n}\n\n/** The band router: reuse classifyBand for routing, enrich to catalog.\n * `opts.isMapMount` flows into the grid classifier (LocationMap promotion)\n * AND into the LocationMap enrichment above (mount html recovery). */\nexport function bandToCatalog(band: Band, opts: BandToCatalogOptions = {}): CatalogSpec {\n return sliceSpecToCatalog(classifyBand(band, opts), band, opts);\n}\n\n/** Feed-band interception (spec §7 rule 1): a band whose site.json item\n * declares `sources[]` becomes a BluxCollection query spec BEFORE the grid\n * router runs — EXCEPT `__media` (media-library galleries already materialize\n * via the grid path) and non-feed items, which fall through to `bandToCatalog`\n * unchanged. `item` is the positional convert-path join:\n * `siteJson.content.pages[p].items[band.index]`. */\nexport function bandOrCollection(\n band: Band,\n item: unknown,\n feeds: Record<string, { name?: unknown } | undefined>,\n opts: BandToCatalogOptions = {},\n): CatalogSpec {\n if (!isFeedBand(item)) return bandToCatalog(band, opts);\n const where = whereOf(band, opts);\n const feedIds = item.sources.map(String);\n if (feedIds[0] === \"__media\") {\n // Media-library galleries materialize via the grid path — but that path\n // renders sources[0] only, so any EXTRA source riding the same item would\n // be silently unconsumed (round-2 item 5): diagnose each.\n for (const id of feedIds.slice(1))\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources \"${id}\" alongside __media — the media-library grid renders sources[0] only, the extra source is not consumed`,\n });\n return bandToCatalog(band, opts);\n }\n // Positional-join guard (mirrors convert.ts materializeFeedBands): only a\n // band that lost its content to the feed-template drop is a real feed grid.\n // A CONTENT-BEARING band whose positional item carries sources means the\n // site.json join landed on the wrong band — routing it to a BluxCollection\n // would clobber its parsed content, so it falls through to bandToCatalog.\n // Emptyishness IGNORES pure feed-template mounts (round-2 item 1: the real\n // fitHealthClub band-3 root is `<div data-exec=\"custom_…\">` — the Blux feed\n // placeholder the collection replaces, not content) and the page's map\n // mount (a widget the collection lifts below, not content either).\n if (!isEmptyish(band.root, (n) => isFeedTemplateMount(n) || (opts.isMapMount?.(n) ?? false))) {\n opts.diagnostics?.push({\n kind: \"feed-band-misalign\",\n where,\n message: `band ${band.index} has parsed content but its site.json item carries feed sources — positional join misaligned, classified as content`,\n });\n return bandToCatalog(band, opts);\n }\n // Validate EVERY source, not just [0] (round-2 item 5). An unknown source\n // (or a DO-NOT-USE feed the entity emit skips) is diagnosed — never silent:\n // the slice would resolve no documents from it at render time — and dropped\n // from the spec's feedIds. When NO source survives, the whole band keeps\n // the existing skip behavior: still a collection (feedIds verbatim), with\n // the diagnostics naming why it will query zero documents.\n const validIds: string[] = [];\n for (const id of feedIds) {\n if (id === \"__media\") {\n // Round-3 5b: the media-library sentinel is only meaningful as\n // sources[0] (where the grid path renders it — see above). In a later\n // position it is a KNOWN sentinel, not an unknown feed — named as such\n // and dropped from the collection's feed ids.\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources the __media media-library sentinel alongside feed sources — it only renders as sources[0]; dropped from the collection`,\n });\n continue;\n }\n const feed = feeds[id];\n const name = String(feed?.name ?? \"\");\n if (!feed) {\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources unknown feed \"${id}\" — the collection will resolve no documents from it`,\n });\n } else if (isSkippedFeed(name)) {\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources feed \"${name}\" (marked DO NOT USE — not migrated) — the collection will resolve no documents from it`,\n });\n } else {\n validIds.push(id);\n }\n }\n // Round-3 5a: duplicate sources ([\"feed-1\",\"feed-1\"]) must not duplicate\n // feed_ids — dedupe, keeping first-seen order.\n const keptIds = [...new Set(validIds.length ? validIds : feedIds)];\n const feedName = String(feeds[keptIds[0]!]?.name ?? \"\");\n // Collection is a container (decision B, round-2 item 2): a MAP mount riding\n // the feed band lifts onto the spec through the same widget triple\n // BluxSection uses — never treated as content, never dropped.\n const widget = liftMapWidget(band, opts);\n const cfg = item.sourceConfig ?? {};\n const filterTag = (cfg[\"filters\"] as { tag?: unknown } | undefined)?.tag;\n const sort = cfg[\"sort\"];\n // Real Blux sourceConfig carries `limit` as a STRING ('9','12','20','0');\n // '0'/0 means unlimited (omitted below, like NaN). `count` never occurs in\n // the fleet.\n const limit = Number(cfg[\"limit\"]);\n const mediaRatio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n const { heading } = splitHeadingAndCells(band.root);\n const spec: BluxCollectionSpec = {\n slice: \"BluxCollection\",\n ...baseOf(band, opts),\n ...(heading ? { heading } : {}),\n entityType: feedEntityType(feedName),\n feedIds: keptIds,\n ...(typeof filterTag === \"string\" && filterTag ? { filterTag } : {}),\n ...(typeof sort === \"string\" && sort ? { sort } : {}),\n ...(Number.isFinite(limit) && limit > 0 ? { limit } : {}),\n ...(mediaRatio ? { mediaRatio } : {}),\n layout: (item as { type?: unknown }).type === \"slides\" ? \"carousel\" : \"grid\",\n ...(cfg[\"scrollLoadMore\"] === true ? { scrollLoadMore: true } : {}),\n ...(widget ?? {}),\n };\n return spec;\n}\n\n// -- helpers --\n/** A raw custom-element feed-template mount: a div whose only substance is a\n * `data-exec` attr — no visible text, no media (fitHealthClub band 3:\n * `<div id=\"custom-element1\" data-exec=\"custom_…\"></div>`). It is the Blux\n * placeholder the collection replaces, so the emptyish guard must not read it\n * as content. A mount whose markup DOES carry visible content is real content. */\nfunction isFeedTemplateMount(node: Node): boolean {\n return (\n node.kind === \"raw\" &&\n /\\bdata-exec=\"custom_[a-f0-9_]+\"/.test(node.html) &&\n !hasVisibleContent(node.html)\n );\n}\n\n/** Did this band lose its content to the feed-template drop? — its parsed\n * root is empty (just heading(s)/subtitle/empty-raw, no media and no populated\n * row). Local reimplementation of convert.ts's isEmptyish (not exported\n * there) — keep the two in sync. `ignore` (feed-band guard only) names nodes\n * that must not count as content: pure feed-template mounts and the page's\n * map mount (see bandOrCollection). */\nfunction isEmptyish(root: Node, ignore?: (n: Node) => boolean): boolean {\n if (ignore?.(root)) return true;\n switch (root.kind) {\n case \"heading\":\n case \"subtitle\":\n return true;\n case \"raw\":\n return root.html.trim() === \"\";\n case \"media\":\n case \"widget\":\n return false;\n case \"row\": {\n // Round-3 5c: `ignore` applies through wrapper stacks too — a mount the\n // parser boxed in a stack inside a row cell is still just the mount.\n const cells = ignore\n ? root.cells.filter((c) => !isIgnoredSubtree(c.node, ignore))\n : root.cells;\n return cells.length === 0;\n }\n case \"stack\":\n return root.children.every((c) => isEmptyish(c, ignore));\n default:\n return false;\n }\n}\n\n/** Depth-first search for the mount node's raw html in the UNREWRITTEN band\n * tree (the predicate only ever matches `raw` nodes — see makeIsMapMount). */\nfunction findMountHtml(node: Node, isMount: (n: Node) => boolean): string | undefined {\n if (node.kind === \"raw\" && isMount(node)) return node.html;\n const children =\n node.kind === \"row\"\n ? node.cells.map((c) => c.node)\n : node.kind === \"stack\"\n ? node.children\n : [];\n for (const c of children) {\n const found = findMountHtml(c, isMount);\n if (found !== undefined) return found;\n }\n return undefined;\n}\n\n/** Diagnostic addressing (round-2 10a): `<pageUid>:<band>` when the caller\n * names the page, else the bare band index (option-less unit callers). */\nfunction whereOf(band: Band, opts: BandToCatalogOptions): string {\n return opts.pageUid !== undefined ? `${opts.pageUid}:${band.index}` : String(band.index);\n}\n\n/** Decision-B mount recovery, shared by every container route (LocationMap →\n * Section, feed band → Collection, and — round 3 — Grid/Block): classifyBand\n * rewrote its COPY of the tree (the mount is an html-less widget node), but\n * `band.root` still holds the ORIGINAL raw mount — re-find it with the same\n * predicate. Returns the CatalogBase widget triple to spread onto the spec,\n * or undefined when no mount html is recoverable. */\nfunction liftMapWidget(\n band: Band,\n opts: BandToCatalogOptions,\n): { widgetKind: \"map\"; widgetHtml: string; mapConfig?: MapConfig } | undefined {\n const mountHtml = opts.isMapMount ? findMountHtml(band.root, opts.isMapMount) : undefined;\n if (mountHtml === undefined) return undefined;\n return {\n widgetKind: \"map\",\n widgetHtml: mountHtml,\n ...(opts.mapConfig ? { mapConfig: opts.mapConfig } : {}),\n };\n}\n\n/** Round-3 never-silent guard: classifyBand marked a mount (an html-less\n * `widget` node in its rewritten tree) but the lift recovered nothing — no\n * predicate, or one that no longer matches band.root. Without this the mount\n * would vanish with ZERO diagnostics (blockPayload serializes the widget node\n * as an empty div). */\nfunction reportUnrecoveredMount(rewrittenRoot: Node, band: Band, opts: BandToCatalogOptions): void {\n if (!containsWidgetNode(rewrittenRoot)) return;\n opts.diagnostics?.push({\n kind: \"dropped-widget\",\n where: whereOf(band, opts),\n message: `band ${band.index} holds a widget mount but no mount html was recovered (no predicate, or one that no longer matches band.root) — the widget is dropped`,\n });\n}\n\nfunction containsWidgetNode(node: Node): boolean {\n if (node.kind === \"widget\") return true;\n const children =\n node.kind === \"row\"\n ? node.cells.map((c) => c.node)\n : node.kind === \"stack\"\n ? node.children\n : [];\n return children.some(containsWidgetNode);\n}\n\n/** Whether `ignore` swallows a whole subtree: the node itself, or a NON-EMPTY\n * stack of nothing but ignored subtrees (round-3 5c: the parser sometimes\n * boxes a mount in a wrapper stack inside a row cell). An empty stack still\n * counts as a cell, exactly as before. */\nfunction isIgnoredSubtree(node: Node, ignore: (n: Node) => boolean): boolean {\n if (ignore(node)) return true;\n return (\n node.kind === \"stack\" &&\n node.children.length > 0 &&\n node.children.every((c) => isIgnoredSubtree(c, ignore))\n );\n}\n/** One carousel slide → a media cell. The caption html is wrapper-less in the\n * parser, so it is wrapped at the slide's own heading level; the subcaption\n * (the hero slide's location line) rides the cell body. */\nfunction slideCell(s: CarouselSlide): CatalogCell {\n return {\n kind: \"media\",\n media: s.media,\n ...(s.caption ? { title: `<h${s.caption.level}>${s.caption.html}</h${s.caption.level}>` } : {}),\n ...(s.subcaption ? { bodyHtml: s.subcaption.html } : {}),\n };\n}\n\n/** Rich grid when the tree fits the cell→subgrid model; else the\n * content-preserving BluxBlock fallback (guard and builder agree by\n * construction — see cellDepthExceedsTwo). */\nfunction gridOrBlock(root: Node, base: CatalogBaseFields): BluxGridSpec | BluxBlockSpec {\n if (cellDepthExceedsTwo(root)) return blockSpec(root, base);\n const columns = gridColumns(root);\n return {\n slice: \"BluxGrid\",\n ...base,\n ...(columns !== undefined ? { columns } : {}),\n ...splitHeadingAndCells(root),\n };\n}\n\nfunction blockSpec(root: Node, base: CatalogBaseFields): BluxBlockSpec {\n return {\n slice: \"BluxBlock\",\n ...base,\n payload: blockPayload(root),\n // Surfaced so emit uploads the payload's assets; the payload's inlined\n // urls remain CDN until the 4d migrate-time rewrite.\n media: collectMedia(root),\n };\n}\n\n/** The top row's column count: the grid token's cols, or the row's cell count\n * when the token doesn't say (\"any\"). */\nfunction gridColumns(root: Node): number | undefined {\n const findRow = (n: Node): Extract<Node, { kind: \"row\" }> | null => {\n if (n.kind === \"row\") return n;\n if (n.kind === \"stack\") {\n for (const c of n.children) {\n const r = findRow(c);\n if (r) return r;\n }\n }\n return null;\n };\n const row = findRow(root);\n if (!row || row.cells.length === 0) return undefined;\n const cols = row.cells[0]?.token.cols;\n return typeof cols === \"number\" ? cols : row.cells.length;\n}\n\n/** Split a root into its section heading (first heading-only cell) + the\n * remaining content cells, so the heading is not duplicated as both the\n * section heading AND a cell title. */\nfunction splitHeadingAndCells(root: Node): {\n heading?: string;\n headingRole?: string;\n cells: CatalogCell[];\n} {\n const cells = nodeToCells(root).filter(\n (c) => c.title || c.bodyHtml || c.media || c.subgrid || c.embedHtml,\n );\n const hIdx = cells.findIndex(\n (c) => c.title && !c.bodyHtml && !c.media && !c.subgrid && !c.embedHtml,\n );\n const h = hIdx >= 0 ? cells[hIdx] : undefined;\n if (h)\n return {\n ...(h.title ? { heading: h.title } : {}),\n ...(h.titleRole ? { headingRole: h.titleRole } : {}),\n cells: cells.filter((_, i) => i !== hIdx),\n };\n return { cells };\n}\n// The two-field BluxMediaText slice keeps a RichText `body` (spec.ts), so its\n// text half maps the cell's baked `bodyHtml` back onto that `body` field. Any\n// txt-role-* wrappers in bodyHtml are intentionally flattened away here — a\n// RichText field cannot carry them, so MediaText body role-fidelity is out of\n// scope (this fix restores per-block roles for CELL bodies, not MediaText).\nfunction pick(c: CatalogCell): { title?: string; body?: string } {\n return {\n ...(c.title ? { title: c.title } : {}),\n ...(c.bodyHtml ? { body: c.bodyHtml } : {}),\n };\n}\n","// Feed records → real Prismic entity documents (spec §8). Each kept feed maps\n// onto its frozen entity type (feeds.ts), its records onto the Plan-2 shared\n// base fields, and any non-base keys ride verbatim as per-site extension\n// fields — with one extension custom type per USED entity type (the shared\n// base Main + the observed extension fields).\nimport type { Media } from \"../grid/types.js\";\nimport type { Diagnostic } from \"../ir.js\";\nimport { assetRef, richText, type PlanCustomType, type PlanDocument } from \"../emit/plan.js\";\nimport { demoteHeadingsHtml } from \"../emit/coerce-html.js\";\nimport { feedEntityType, isSkippedFeed } from \"./feeds.js\";\n\nexport type EntityEmit = {\n documents: PlanDocument[];\n customTypes: PlanCustomType[];\n media: Media[]; // every record media (kind:\"image\"), for plan-asset upload\n diagnostics: Diagnostic[];\n};\n\n/** The Plan-2 shared-base entity Main (frozen — mirrors the starter's\n * customtypes/{product,person,event,news_article,project,collection_item}\n * base JSON exactly). Extension fields are appended per site. */\nconst BASE_MAIN: Record<string, unknown> = {\n uid: { type: \"UID\", config: { label: \"uid\" } },\n title: {\n type: \"StructuredText\",\n config: { label: \"title\", single: \"heading1\" },\n },\n body: {\n type: \"StructuredText\",\n config: {\n label: \"body\",\n multi: \"paragraph,strong,em,hyperlink,list-item\",\n },\n },\n media: {\n type: \"Image\",\n config: { label: \"media\", constraint: {}, thumbnails: [] },\n },\n gallery: {\n type: \"Group\",\n config: {\n label: \"gallery\",\n fields: {\n image: {\n type: \"Image\",\n config: { label: \"image\", constraint: {}, thumbnails: [] },\n },\n caption: { type: \"Text\", config: { label: \"caption\" } },\n },\n },\n },\n tags: { type: \"Text\", config: { label: \"tags (comma-separated)\" } },\n date: { type: \"Date\", config: { label: \"date\" } },\n link: { type: \"Link\", config: { label: \"link\", allowTargetBlank: true } },\n};\n\n/** Record keys the base mapping consumes explicitly, PLUS the base field names\n * a record key must never shadow (`uid`/`gallery`/`link` — an extension\n * assignment would clobber the mapped base field in `data`). `disabled` is NOT\n * here: it is an extension field (it lands verbatim in data and in the\n * extension type) that ALSO drives the enabled-beats-disabled uid dedup.\n * `url` is deliberately absent: the raw url rides as an extension field (the\n * Phase-7 detail-page slug) even though the base mapping also reads it for the\n * uid and the external-only link field. */\nconst BASE_KEYS = new Set([\n \"title\",\n \"body\",\n \"media\",\n \"items\",\n \"tags\",\n \"date\",\n \"link_url\",\n \"uid\",\n \"gallery\",\n \"link\",\n]);\n\n/** Underscore-prefixed keys are per-element style config (collections.ts\n * convention) — never content. */\nconst isStyleKey = (key: string) => key.startsWith(\"_\");\n\ntype ExtKind = \"text\" | \"richtext\" | \"boolean\" | \"number\" | \"group\";\n\nconst EXT_FIELD_CONFIG: Record<ExtKind, (label: string) => Record<string, unknown>> = {\n text: (label) => ({ type: \"Text\", config: { label } }),\n // deriveFields convention: mirrors the base body field's model config.\n richtext: (label) => ({\n type: \"StructuredText\",\n config: { label, multi: \"paragraph,strong,em,hyperlink,list-item\" },\n }),\n boolean: (label) => ({ type: \"Boolean\", config: { label } }),\n number: (label) => ({ type: \"Number\", config: { label } }),\n group: (label) => ({\n type: \"Group\",\n config: {\n label,\n fields: { value: { type: \"Text\", config: { label: \"value\" } } },\n },\n }),\n};\n\nconst kindOf = (key: string, value: unknown): ExtKind => {\n // deriveFields convention: `description` (like `body`) is richtext.\n if (key === \"description\" && typeof value === \"string\") return \"richtext\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n if (Array.isArray(value)) return \"group\";\n return \"text\";\n};\n\n/** \"news_article\" → \"News Article\". */\nconst typeLabel = (type: string): string =>\n type\n .split(\"_\")\n .map((w) => (w ? w[0]!.toUpperCase() + w.slice(1) : w))\n .join(\" \");\n\nconst mediaUuidOf = (v: unknown): string | undefined => {\n if (v && typeof v === \"object\") {\n const m = (v as { media?: unknown }).media;\n if (typeof m === \"string\" && m) return m;\n }\n return undefined;\n};\n\n/** Extension entries of a record: every non-style, non-base key. */\nfunction extEntries(record: Record<string, unknown>): [string, unknown][] {\n return Object.entries(record).filter(\n ([key, value]) => !isStyleKey(key) && !BASE_KEYS.has(key) && value !== undefined,\n );\n}\n\n/** Legacy-parity spelling normalization (round-2 item 4 — grid feed-grid.ts\n * isDisabled honors BOTH `disabled` and `disable`): fold `disable` into the\n * one normalized `disabled` key, so either spelling === true drives the\n * enabled-beats-disabled uid dedup AND emits `data.disabled = true` (the\n * starter filters on `doc.data.disabled !== true`); a separate `disable`\n * extension field never emits. */\nfunction normalizeRecord(record: Record<string, unknown>): Record<string, unknown> {\n if (record.disable === undefined) return record;\n const { disable, ...rest } = record;\n return {\n ...rest,\n disabled: record.disabled === true || disable === true ? true : (record.disabled ?? disable),\n };\n}\n\n/** A valid Prismic UID fragment: alphanumerics/dash/underscore only. */\nconst BARE_SLUG_RE = /^[a-z0-9_-]+$/i;\n\n/** External http(s) url — the ONLY shape the base link field accepts (spec\n * §8). Case-insensitive: real-world data capitalizes schemes (\"HTTPS://…\"). */\nconst EXTERNAL_RE = /^https?:\\/\\//i;\n\n/** The record's uid. Its `url` — but ONLY when it is a bare slug after the\n * productSlug-style `/products/` prefix strip + slash trim: real fleet urls\n * are often absolute (`https://mailchi.mp/...`, strategyAdvantage ×46) or\n * route paths (`/news/<slug>` tosa ×49, `/projects/<slug>` williamsonHomes\n * ×5), which would make INVALID Prismic UIDs. Anything non-bare falls back to\n * the slugified title. */\nfunction recordUid(record: Record<string, unknown>): string {\n const url = typeof record.url === \"string\" ? record.url.trim() : \"\";\n const fromUrl = url.replace(/^\\/+products\\/+/i, \"\").replace(/^\\/+|\\/+$/g, \"\");\n // BARE_SLUG_RE is case-insensitive, so an uppercase slug passes — lowercase\n // it (round-2 item 9): a mixed-case uid is INVALID in Prismic.\n if (fromUrl && BARE_SLUG_RE.test(fromUrl)) return fromUrl.toLowerCase();\n return String(record.title ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\ntype DateOrientation = \"year-month-day\" | \"year-day-month\";\n\nconst DATE_RE = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/;\n\n/** Drop a trailing time suffix (`T10:30:00Z`, ` 10:30`) so a datetime-shaped\n * string keeps its date instead of being dropped as unparseable (round-2\n * item 8 — zero fleet occurrences today, cheap resilience). */\nconst stripTime = (s: string): string => s.replace(/(?:T|\\s+)\\d{1,2}:\\d{2}\\S*$/, \"\").trim();\n\nexport type NormalizedDate = {\n date?: string;\n issue?: \"ambiguous\" | \"unparseable\";\n};\n\n/** Normalize a raw feed date into a valid Prismic `YYYY-MM-DD`. Composition's\n * Products dates are unpadded `YYYY-n-n` with MIXED orientation (207 records\n * year-DAY-month, 191 year-month-day, 135 ambiguous) — verbatim passthrough\n * would land invalid values in a Date field. Already-valid ISO passes; an\n * unambiguous `YYYY-n-n` self-resolves (whichever side > 12 is the day); a\n * both-≤12 value resolves by `feedVote` (the feed's majority orientation), or\n * defaults to year-month-day with an `ambiguous` issue when the feed offers no\n * evidence. Anything else (or an out-of-range resolution) is `unparseable` —\n * the caller omits the date. */\nexport function normalizeDate(raw: unknown, feedVote: DateOrientation | null): NormalizedDate {\n if (typeof raw !== \"string\") return { issue: \"unparseable\" };\n const trimmed = stripTime(raw.trim());\n const m = DATE_RE.exec(trimmed);\n if (!m) return { issue: \"unparseable\" };\n const year = m[1]!;\n const mid = Number(m[2]!);\n const last = Number(m[3]!);\n // Range check PLUS calendar validity (round-2 item 8): \"2017-02-30\" is\n // range-valid but no real date — a UTC round-trip catches month-length and\n // leap-year violations, so impossible dates go unparseable instead of\n // landing verbatim in a Prismic Date field.\n const build = (month: number, day: number): string | undefined => {\n if (month < 1 || month > 12 || day < 1 || day > 31) return undefined;\n const d = new Date(Date.UTC(Number(year), month - 1, day));\n if (\n d.getUTCFullYear() !== Number(year) ||\n d.getUTCMonth() !== month - 1 ||\n d.getUTCDate() !== day\n )\n return undefined;\n return `${year}-${String(month).padStart(2, \"0\")}-${String(day).padStart(2, \"0\")}`;\n };\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed) && build(mid, last) === trimmed) return { date: trimmed }; // already-valid ISO\n if (mid > 12 && last > 12) return { issue: \"unparseable\" };\n if (mid > 12 || last > 12) {\n // Exactly one side can be the day — the orientation is self-evident.\n const date = mid > 12 ? build(last, mid) : build(mid, last);\n return date ? { date } : { issue: \"unparseable\" };\n }\n // Both ≤ 12: orientation-ambiguous — the feed's majority decides;\n // tie/no-evidence reads year-month-day and flags it.\n const date = feedVote === \"year-day-month\" ? build(last, mid) : build(mid, last);\n if (date === undefined) return { issue: \"unparseable\" };\n return feedVote ? { date } : { date, issue: \"ambiguous\" };\n}\n\n/** The feed's majority date orientation, voted by its unambiguous records\n * (exactly one side > 12). Null when tied or the feed offers no evidence. */\nfunction feedDateOrientation(items: unknown[]): DateOrientation | null {\n let ymd = 0;\n let ydm = 0;\n for (const it of items) {\n const raw = it && typeof it === \"object\" ? (it as { date?: unknown }).date : undefined;\n if (typeof raw !== \"string\") continue;\n const m = DATE_RE.exec(stripTime(raw.trim()));\n if (!m) continue;\n const mid = Number(m[2]!);\n const last = Number(m[3]!);\n // The would-be day must be a POSSIBLE day (≤ 31) for the record to cast a\n // vote (round-2 item 8): garbage like \"2017-45-3\" is not ydm evidence and\n // must never silently resolve another record's ambiguous date.\n if (mid > 12 && mid <= 31 && last >= 1 && last <= 12) ydm++;\n else if (last > 12 && last <= 31 && mid >= 1 && mid <= 12) ymd++;\n }\n if (ydm > ymd) return \"year-day-month\";\n if (ymd > ydm) return \"year-month-day\";\n return null;\n}\n\nconst titleOf = (record: Record<string, unknown>): string =>\n typeof record.title === \"string\" && record.title.trim() ? record.title.trim() : \"(untitled)\";\n\ntype RecordCtx = {\n feedId: string;\n dateVote: DateOrientation | null;\n diagnostics: Diagnostic[];\n /** The entity type's extension kinds, RESOLVED across all records of every\n * feed of the type before any document emits (round-2 item 7) — value\n * emission must know the final kind so a scalar under a group-kind key\n * wraps as a row instead of landing raw in a Group-modeled field. */\n extKinds: ReadonlyMap<string, ExtKind>;\n};\n\n/** Base-field mapping for one record → the document data + the media it\n * references. Extension keys ride verbatim (except `description` → richtext\n * and arrays → their Group{value} model shape). */\nfunction recordToDoc(\n record: Record<string, unknown>,\n type: string,\n uid: string,\n ctx: RecordCtx,\n): { doc: PlanDocument; media: Media[] } {\n const data: Record<string, unknown> = {};\n const media: Media[] = [];\n\n if (typeof record.title === \"string\" && record.title.trim())\n data.title = richText(`<h1>${record.title}</h1>`);\n // The base type's body allows no heading blocks — demote, like recordData.\n if (typeof record.body === \"string\" && record.body.trim())\n data.body = richText(demoteHeadingsHtml(record.body));\n\n const mainUuid = mediaUuidOf(record.media);\n if (mainUuid) {\n data.media = assetRef(mainUuid);\n media.push({ kind: \"image\", assetId: mainUuid });\n }\n\n if (Array.isArray(record.items)) {\n const gallery: { image: unknown; caption: string }[] = [];\n for (const it of record.items) {\n const item = it && typeof it === \"object\" ? (it as Record<string, unknown>) : undefined;\n const uuid = item ? mediaUuidOf(item.media) : undefined;\n if (!uuid) continue;\n media.push({ kind: \"image\", assetId: uuid });\n const caption =\n typeof item?.caption === \"string\"\n ? item.caption\n : typeof item?.title === \"string\"\n ? item.title\n : \"\";\n gallery.push({ image: assetRef(uuid), caption });\n }\n if (gallery.length) data.gallery = gallery;\n }\n\n if (Array.isArray(record.tags))\n data.tags = record.tags.filter((t): t is string => typeof t === \"string\").join(\",\");\n else if (typeof record.tags === \"string\" && record.tags) data.tags = record.tags;\n\n if (record.date != null && record.date !== \"\") {\n const norm = normalizeDate(record.date, ctx.dateVote);\n if (norm.date !== undefined) data.date = norm.date;\n if (norm.issue === \"ambiguous\")\n ctx.diagnostics.push({\n kind: \"ambiguous-date\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" date \"${String(record.date)}\" is orientation-ambiguous with no feed evidence — read as year-month-day \"${norm.date}\"`,\n });\n else if (norm.issue === \"unparseable\")\n ctx.diagnostics.push({\n kind: \"malformed-feed-field\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" date \"${String(record.date)}\" does not normalize to YYYY-MM-DD — omitted`,\n });\n }\n\n // Spec §8: the link field is EXTERNAL-only — bare slugs ('steel-chair') and\n // route paths ('/news/x') are detail-page slugs, not links. The raw `url`\n // still rides as an extension field below (Phase 7 detail pages). Round-2\n // item 6: pick the FIRST candidate that IS external (scheme match\n // case-insensitive) — a bare-slug `url` must not eat an external `link_url`.\n const urlVal = typeof record.url === \"string\" ? record.url.trim() : \"\";\n const linkUrlVal = typeof record.link_url === \"string\" ? record.link_url.trim() : \"\";\n const external = [urlVal, linkUrlVal].find((v) => v && EXTERNAL_RE.test(v));\n if (external) data.link = { link_type: \"Web\", url: external };\n // A non-external link_url is dropped either way (link_url is a BASE_KEYS\n // key — it never rides as an extension field) — never silently.\n if (linkUrlVal && !EXTERNAL_RE.test(linkUrlVal))\n ctx.diagnostics.push({\n kind: \"malformed-feed-field\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" link_url \"${linkUrlVal}\" is not an external http(s) url — dropped (the base link field is external-only)`,\n });\n\n for (const [key, value] of extEntries(record)) {\n if (ctx.extKinds.get(key) === \"group\")\n // The key models Group{value:Text} (an array was observed SOMEWHERE in\n // the type's records — round-2 item 7): scalars wrap as a single row so\n // every record's data matches the model, whatever this record holds.\n data[key] = (Array.isArray(value) ? value : [value]).map((x) => ({\n value: String(x),\n }));\n else if (key === \"description\" && typeof value === \"string\")\n // deriveFields convention: description (like body) is richtext; the\n // extension model is StructuredText, so raw HTML must not ride as Text.\n data[key] = richText(demoteHeadingsHtml(value));\n else if (Array.isArray(value))\n // Match the Group{value:Text} extension model the array kind derives.\n data[key] = value.map((x) => ({ value: String(x) }));\n else data[key] = value;\n }\n\n return { doc: { type, uid, data }, media };\n}\n\n/** Feed records → typed entity documents + per-type extension custom types +\n * the media they reference + skip diagnostics. Uid dedup is per entity type:\n * an enabled record beats a disabled one, else first-seen wins (recordUid\n * semantics — the record's bare-slug `url` first, else the title slug); every\n * dropped loser is named by a uid-collision diagnostic. */\nexport function buildEntityEmit(\n feeds: Record<string, { name?: string; items?: unknown[]; fields?: unknown } | undefined>,\n): EntityEmit {\n const diagnostics: Diagnostic[] = [];\n const media: Media[] = [];\n const typeOrder: string[] = [];\n const docsByType = new Map<\n string,\n Map<string, { doc: PlanDocument; disabled: boolean; title: string }>\n >();\n const extByType = new Map<string, Map<string, ExtKind>>();\n\n // Pass 1 — resolve every extension key's kind across ALL records of every\n // feed of the type BEFORE any document emits (round-2 item 7): emission must\n // know the final kind, or a string-then-array key would land a raw string\n // in a Group-modeled field (and boolean/number-then-array the reverse).\n // DO-NOT-USE feeds skip here, once.\n const kept: { feedId: string; type: string; items: unknown[] }[] = [];\n for (const [feedId, feed] of Object.entries(feeds)) {\n if (!feed) continue;\n const name = String(feed.name ?? \"\");\n if (isSkippedFeed(name)) {\n diagnostics.push({\n kind: \"skipped-feed\",\n where: feedId,\n message: `feed \"${name}\" is marked DO NOT USE — not migrated`,\n });\n continue;\n }\n const type = feedEntityType(name);\n if (!docsByType.has(type)) {\n typeOrder.push(type);\n docsByType.set(type, new Map());\n extByType.set(type, new Map());\n }\n const extKinds = extByType.get(type)!;\n const items = Array.isArray(feed.items) ? feed.items : [];\n kept.push({ feedId, type, items });\n for (const item of items) {\n if (!item || typeof item !== \"object\") continue;\n const record = normalizeRecord(item as Record<string, unknown>);\n // Extension fields type from the observed value shapes (deriveFields\n // convention: a later, more specific observation upgrades \"text\");\n // group — an array observed in ANY record — beats every scalar kind.\n for (const [key, value] of extEntries(record)) {\n const next = kindOf(key, value);\n const prev = extKinds.get(key);\n if (!prev || prev === \"text\" || next === \"group\") extKinds.set(key, next);\n }\n }\n }\n\n // Pass 2 — emit documents with the resolved kinds.\n for (const { feedId, type, items } of kept) {\n const byUid = docsByType.get(type)!;\n const extKinds = extByType.get(type)!;\n // The feed's majority date orientation, voted once by its unambiguous\n // records, resolves the both-≤12 dates (see normalizeDate).\n const dateVote = feedDateOrientation(items);\n items.forEach((item, i) => {\n if (!item || typeof item !== \"object\") return;\n const record = normalizeRecord(item as Record<string, unknown>);\n const uid = recordUid(record) || `item-${i}`;\n const { doc, media: recMedia } = recordToDoc(record, type, uid, {\n feedId,\n dateVote,\n diagnostics,\n extKinds,\n });\n media.push(...recMedia);\n const disabled = record.disabled === true;\n const existing = byUid.get(uid);\n const dropped =\n !existing || (existing.disabled && !disabled)\n ? existing // the incoming record wins — the stored one is dropped\n : { title: titleOf(record) }; // the stored record wins — this one is dropped\n if (dropped)\n diagnostics.push({\n kind: \"uid-collision\",\n where: feedId,\n message: `record \"${dropped.title}\" collides on uid \"${uid}\" — dropped (enabled beats disabled, else first-seen wins)`,\n });\n if (!existing || (existing.disabled && !disabled))\n byUid.set(uid, { doc, disabled, title: titleOf(record) });\n });\n }\n\n const documents: PlanDocument[] = [];\n const customTypes: PlanCustomType[] = [];\n for (const type of typeOrder) {\n for (const { doc } of docsByType.get(type)!.values()) documents.push(doc);\n const Main: Record<string, unknown> = { ...BASE_MAIN };\n for (const [key, kind] of extByType.get(type)!) Main[key] = EXT_FIELD_CONFIG[kind](key);\n const label = typeLabel(type);\n customTypes.push({\n id: type,\n label,\n repeatable: true,\n json: {\n id: type,\n label,\n format: \"custom\",\n repeatable: true,\n status: true,\n json: { Main },\n },\n });\n }\n\n return { documents, customTypes, media, diagnostics };\n}\n","import type { PlanDocument } from \"../emit/plan.js\";\n\nexport type RewriteResult = {\n documents: PlanDocument[];\n /** Total url occurrences swapped. */\n rewritten: number;\n /** CDN-looking urls that survived the swap — surface these, never silent. */\n unmatched: string[];\n};\n\n/** Generic rewrite over any JSON value (a deep copy is returned). */\nexport type ValueRewriteResult<T> = {\n value: T;\n /** Total url occurrences swapped. */\n rewritten: number;\n /** CDN-looking urls that survived the swap — surface these, never silent. */\n unmatched: string[];\n};\n\n// The exclusion class ends the match at whitespace, `\"`, `'`, `\\`, or `)`, so\n// urls embedded in serialized JSON (followed by `\"` or, when doubly\n// serialized, `\\\"`) and in `background-image:url(...)` are captured exactly —\n// the backslash boundary is pinned by the doubly-serialized-JSON test.\nconst CDN_URL_RE = /https?:\\/\\/[a-z0-9.-]*cloudfront\\.net\\/[^\\s\"'\\\\)]+/g;\n\n/** Swap every occurrence of a plan-asset CDN url for its uploaded Prismic url\n * inside EVERY string value of an arbitrary JSON value. Pure: returns a deep\n * copy. Shared by the document rewrite (serialized BluxBlock payloads,\n * widget_html, embed_html, background wrappers) and the site-config / chrome\n * rewrite (nav + footer logo urls). Non-string, non-CDN values pass through\n * untouched. */\nexport function rewriteValueUrls<T>(\n value: T,\n urlByCdn: Map<string, string>,\n): ValueRewriteResult<T> {\n let rewritten = 0;\n const unmatched = new Set<string>();\n // Longest key first: a mapped url that is a prefix of another mapped url can\n // then never fire inside the longer url's occurrence.\n const entries = [...urlByCdn].sort(([a], [b]) => b.length - a.length);\n const swapString = (s: string): string => {\n // Fast path — safe for both passes: every map key and CDN_URL_RE alike\n // require the cloudfront.net host, so this string can neither swap nor\n // surface an unmatched url.\n if (!s.includes(\"cloudfront.net\")) return s;\n let out = s;\n for (const [cdn, prismic] of entries) {\n if (!out.includes(cdn)) continue;\n const parts = out.split(cdn);\n rewritten += parts.length - 1;\n out = parts.join(prismic);\n }\n for (const m of out.matchAll(CDN_URL_RE)) unmatched.add(m[0]);\n return out;\n };\n const walk = (v: unknown): unknown => {\n if (typeof v === \"string\") return swapString(v);\n if (Array.isArray(v)) return v.map(walk);\n if (v && typeof v === \"object\")\n return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));\n return v;\n };\n return { value: walk(value) as T, rewritten, unmatched: [...unmatched] };\n}\n\n/** Collect every distinct Blux-CDN url appearing as a raw string in any value\n * of a JSON structure (documents, site-config, …). Uses the SAME host-anchored\n * match as the rewrite, so what this finds is exactly what {@link\n * rewriteValueUrls} must evict — the emit backstop registers each as an asset so\n * a url baked into an embed_html `<a href>` (a PDF button), a payload\n * background, or a widget migrates off the CDN too. First-seen order. */\nexport function collectCdnUrls(value: unknown): string[] {\n const found = new Set<string>();\n const walk = (v: unknown): void => {\n if (typeof v === \"string\") {\n if (!v.includes(\"cloudfront.net\")) return;\n for (const m of v.matchAll(CDN_URL_RE)) found.add(m[0]);\n } else if (Array.isArray(v)) {\n v.forEach(walk);\n } else if (v && typeof v === \"object\") {\n Object.values(v).forEach(walk);\n }\n };\n walk(value);\n return [...found];\n}\n\n/** Rewrite plan documents — the surfaces resolveDocData does not touch. Thin\n * wrapper over {@link rewriteValueUrls}; marker objects pass through untouched\n * because they hold no CDN urls. */\nexport function rewriteDocUrls(\n documents: PlanDocument[],\n urlByCdn: Map<string, string>,\n): RewriteResult {\n const r = rewriteValueUrls(documents, urlByCdn);\n return { documents: r.value, rewritten: r.rewritten, unmatched: r.unmatched };\n}\n","import type { Media } from \"../grid/types.js\";\nimport type { Diagnostic } from \"../ir.js\";\n// Only `mediaUrl`/`mediaCdnUrl` are reused from the grid emit; `collectPlanAssets`/\n// `collectMedia` there only understand SliceSpec shapes, so the skeleton resolves\n// its own CatalogSpec media below (see grid-plan.ts `buildGridSitePlan` for the pattern).\nimport { mediaCdnUrl, mediaUrl } from \"../emit/grid-plan.js\";\nimport {\n type MigrationPlan,\n type PlanAsset,\n type PlanDocument,\n type PlanSlice,\n assetRef,\n richText,\n} from \"../emit/plan.js\";\nimport { catalogPageCustomType } from \"./catalog-page-type.js\";\nimport { videoTag, imgTag } from \"./cells.js\";\nimport { buildEntityEmit } from \"./entities.js\";\nimport { collectCdnUrls } from \"./rewrite-doc-urls.js\";\nimport { hasVisibleContent, sanitizeHtml } from \"./sanitize.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\nimport type { BlockNode, CatalogBase, CatalogCell, CatalogSpec } from \"./spec.js\";\n\n/** Clamp a heading level into the `[min, max]` window a target field's model\n * allows. Classify wraps headings at their TRUE source level (section-heading\n * promotion and BluxBlock fidelity need it) — the emit boundary is where the\n * destination field is known, so clamping lives here. */\nfunction clampHeading(level: number, min: number, max: number): number {\n return Math.min(Math.max(level, min), max);\n}\n\n/** Re-tag a wrapped `<hN>…</hN>` rich-text string into the destination\n * field's heading window: cell titles + carousel captions model heading3/\n * heading4 (h3–h4); section/grid heading fields model heading2/heading3\n * (h2–h3). Body fields are NEVER clamped — folded headings keep their true\n * level (bodies are paragraph-modeled; see cells.ts textOf). Non-heading\n * html (e.g. a `<p>` caption) passes through untouched. */\nfunction clampHeadingHtml(html: string, min: number, max: number): string {\n const m = /^<h([1-6])>([\\s\\S]*)<\\/h\\1>$/.exec(html.trim());\n if (!m) return html;\n const level = clampHeading(Number(m[1]), min, max);\n return `<h${level}>${m[2]}</h${level}>`;\n}\n\n/** Emit-time context: the band index + owning page uid (for diagnostics\n * `where`) and the optional diagnostics sink threaded down from\n * `buildCatalogPlan`. */\ntype EmitCtx = {\n index: number;\n pageUid?: string;\n diagnostics?: Diagnostic[];\n /** True while emitting a subgrid's cells (depth 2). At this depth an image\n * media cannot ride an Image field — the Migration API can't resolve a\n * doubly-nested Image ref — so it emits `image_embed` (a url-based <img>). */\n inSubgrid?: boolean;\n /** Resolve a media's plan-asset CDN url (mediaCdnUrl, else IR sourceUrl) so\n * the emitted `image_embed` src matches what `rewriteValueUrls` swaps. */\n resolveUrl?: (m: Media) => string | null;\n /** Resolve a media's IR alt (else \"\"). */\n resolveAlt?: (id: string) => string;\n};\n\nconst MOUNT_RE = /data-exec=\"(custom_[a-f0-9_]+)\"/;\n\n/** Record a dropped behavior-script widget — never silent. A custom mount\n * names its widget uuid; other script-only embeds report generically. `where`\n * is page-qualified (round-3 4: `<pageUid>:<band>`) whenever the caller knows\n * the page — a bare band index is ambiguous across a multi-page site. */\nfunction reportDrop(ctx: EmitCtx, html: string): void {\n const uuid = MOUNT_RE.exec(html)?.[1];\n ctx.diagnostics?.push({\n kind: \"dropped-widget\",\n where: ctx.pageUid !== undefined ? `${ctx.pageUid}:${ctx.index}` : `band ${ctx.index}`,\n message: uuid\n ? `custom widget ${uuid} is a behavior script (no visible content) — not migrated`\n : \"embed html has no visible content after sanitize — not migrated\",\n });\n}\n\n/** One catalog cell → its nested-group item object. Rich text and media become\n * `{__richtext_html}` / `{__asset_id}` markers (resolveDocData resolves them,\n * including at this depth). Absent fields are omitted so the item stays lean.\n * Video media becomes `embed_html` (joined with any captured raw html), never\n * an Image-field marker. */\nfunction cellToItem(cell: CatalogCell, ctx: EmitCtx): Record<string, unknown> {\n const video = cell.media?.kind === \"video\" ? videoTag(cell.media) : undefined;\n // Sanitize captured raw html at the boundary — the spec keeps it pristine.\n const embedHtml = cell.embedHtml ? sanitizeHtml(cell.embedHtml) : undefined;\n const embeds = [video, embedHtml].filter((s): s is string => Boolean(s && s.trim()));\n return {\n kind: cell.kind,\n ...(cell.title ? { title: richText(clampHeadingHtml(cell.title, 3, 4)) } : {}),\n // body_html is a PLAIN Text string (no richText marker) rendered via {@html}\n // on the starter side, so it IS sanitized here — the same boundary treatment\n // as embed_html and the BluxBlock payload. sanitizeHtml is a scalpel: it\n // strips <script>/on*=/javascript: only and passes ALL other markup through\n // verbatim, including the txt-role-* class the roled render depends on — so\n // sanitizing closes the {@html} XSS surface at zero fidelity cost. Input is\n // first-party parser structured-text html (heading/body/subtitle); raw/embed\n // html still routes through the sanitized embedHtml path.\n ...(cell.bodyHtml ? { body_html: sanitizeHtml(cell.bodyHtml) } : {}),\n // A subgrid (depth-2) image can't ride an Image field — the Migration API\n // can't resolve a doubly-nested Image ref — so it emits `image_embed` (a\n // url-based <img>, src rewritten to Prismic at migrate). Top-level images\n // stay Image fields (they resolve fine + keep PrismicImage's srcset).\n ...(cell.media && cell.media.kind !== \"video\"\n ? ctx.inSubgrid\n ? {\n image_embed: sanitizeHtml(\n imgTag(cell.media, ctx.resolveUrl ?? mediaCdnUrl, ctx.resolveAlt ?? (() => \"\")),\n ),\n }\n : { media: assetRef(cell.media.assetId) }\n : {}),\n ...(cell.mediaRatio ? { media_ratio: cell.mediaRatio } : {}),\n // Per-cell visual fields (Blux catalog visual layer). `cover`/`valign` are\n // Text flags emitting the literal \"on\" (mirrors scroll_load_more).\n ...(cell.width ? { width: cell.width } : {}),\n ...(cell.spacing !== undefined ? { spacing: cell.spacing } : {}),\n ...(cell.cover ? { cover: \"on\" } : {}),\n ...(cell.valign ? { valign: \"on\" } : {}),\n ...(cell.backgroundColor ? { background_color: cell.backgroundColor } : {}),\n ...(cell.contentPadding ? { content_padding: cell.contentPadding } : {}),\n ...(cell.titleRole ? { title_role: cell.titleRole } : {}),\n ...(embeds.length ? { embed_html: embeds.join(\"\\n\") } : {}),\n ...(cell.subgrid ? { subgrid: emitCells(cell.subgrid, { ...ctx, inSubgrid: true }) } : {}),\n };\n}\n\n/** Emit a cell list, dropping EMBED cells whose html sanitizes to nothing\n * visible (a behavior-script mount is not content — an empty shell would\n * migrate as a blank cell). By construction (cells.ts buildCell) an \"embed\"\n * cell carries ONLY embedHtml, so nothing else is lost by the drop; every\n * drop is recorded on the diagnostics sink. */\nfunction emitCells(cells: CatalogCell[], ctx: EmitCtx): Record<string, unknown>[] {\n const kept: Record<string, unknown>[] = [];\n for (const c of cells) {\n if (c.kind === \"embed\" && c.embedHtml && !hasVisibleContent(c.embedHtml)) {\n reportDrop(ctx, c.embedHtml);\n continue;\n }\n kept.push(cellToItem(c, ctx));\n }\n return kept;\n}\n\n/** `{slice_type, variation:\"default\", items:[], primary}` — every Plan-2 catalog\n * slice shares this envelope; only the primary differs. */\nfunction sliceOf(type: string, primary: Record<string, unknown>): PlanSlice {\n return { slice_type: type, variation: \"default\", items: [], primary };\n}\n\n/** The optional section heading as a rich-text marker (container specs only),\n * clamped to the heading field's h2–h3 model window. */\nfunction heading(spec: CatalogSpec): Record<string, unknown> {\n return \"heading\" in spec && spec.heading\n ? { heading: richText(clampHeadingHtml(spec.heading, 2, 3)) }\n : {};\n}\n\n/** Decision-B widget emission (plan 4b). Contract with the starter design\n * layer: Plan-2 BluxSection renders `widget_html` via {@html} inside a\n * `.blux-widget` div; when a MapConfig was extracted the sanitized mount is\n * wrapped in `.blux-map[data-map-config]` — the design layer parses that JSON\n * and hydrates an interactive Google map on the mount, while the sanitized\n * legend markup renders statically regardless. Config JSON escapes `'` as\n * &#39; so the single-quoted attribute survives any styles content. The\n * parameter is structural (the CatalogBase widget triple) so every spec that\n * can carry a widget — Section, Collection, and (round 3) Grid + Block —\n * shares the exact same emission and Section-identical field names. */\nfunction widgetFields(\n spec: Pick<CatalogBase, \"widgetKind\" | \"widgetHtml\" | \"mapConfig\">,\n): Record<string, unknown> {\n if (!spec.widgetHtml) return {};\n const clean = sanitizeHtml(spec.widgetHtml);\n const html = spec.mapConfig\n ? `<div class=\"blux-map\" data-map-config='${JSON.stringify(spec.mapConfig).replace(/'/g, \"&#39;\")}'>${clean}</div>`\n : clean;\n return {\n ...(spec.widgetKind ? { widget_kind: spec.widgetKind } : {}),\n widget_html: html,\n };\n}\n\n/** Band-visual fields EVERY band slice model defines (blux_grid/section AND\n * blux_gallery/carousel): the box geometry a slider/masonry band honors too.\n * Absent fields are omitted so a band with no captured styles stays lean. */\nfunction bandVisualCore(spec: CatalogBase): Record<string, unknown> {\n return {\n ...(spec.minHeight ? { min_height: spec.minHeight } : {}),\n ...(spec.maxContentWidth ? { max_content_width: spec.maxContentWidth } : {}),\n ...(spec.verticalAlign ? { vertical_align: spec.verticalAlign } : {}),\n };\n}\n\n/** The FULL band-visual set → Plan-2 primary field names, for the flow-layout\n * container slices (blux_grid, blux_section). Adds the content-flow fields only\n * those two models define and render: inner padding, text alignment, the\n * single-column split, and the heading role. The gallery/carousel models omit\n * these (a slider/masonry owns its own spacing), so those slices use\n * `bandVisualCore` — emitting a full field into them fails Migration API\n * validation (\"field not defined in the custom type\"). */\nfunction bandVisual(spec: CatalogBase): Record<string, unknown> {\n return {\n ...(spec.minHeight ? { min_height: spec.minHeight } : {}),\n ...(spec.contentPadding ? { content_padding: spec.contentPadding } : {}),\n ...(spec.contentPaddingMobile ? { content_padding_mobile: spec.contentPaddingMobile } : {}),\n ...(spec.maxContentWidth ? { max_content_width: spec.maxContentWidth } : {}),\n ...(spec.verticalAlign ? { vertical_align: spec.verticalAlign } : {}),\n ...(spec.textAlign ? { text_align: spec.textAlign } : {}),\n ...(spec.columnWidth ? { column_width: spec.columnWidth } : {}),\n ...(spec.columnSide ? { column_side: spec.columnSide } : {}),\n ...(spec.headingRole ? { heading_role: spec.headingRole } : {}),\n };\n}\n\n/** Map one catalog spec to its populated page-doc slice (Plan-2 field names).\n * `diagnostics` (optional) records every content drop — dropped behavior-script\n * widgets are reported, never silent. `pageUid` (optional) page-qualifies the\n * drops' `where` — `buildCatalogPlan` always passes it. */\nexport function catalogSpecToPlanSlice(\n spec: CatalogSpec,\n diagnostics?: Diagnostic[],\n pageUid?: string,\n media?: { resolveUrl: (m: Media) => string | null; resolveAlt: (id: string) => string },\n): PlanSlice {\n const ctx: EmitCtx = {\n index: spec.index,\n ...(pageUid !== undefined ? { pageUid } : {}),\n ...(diagnostics ? { diagnostics } : {}),\n ...(media ? { resolveUrl: media.resolveUrl, resolveAlt: media.resolveAlt } : {}),\n };\n const bg = spec.background ? { background_image: assetRef(spec.background.assetId) } : {};\n const bgc = spec.backgroundColor ? { background_color: spec.backgroundColor } : {};\n switch (spec.slice) {\n case \"BluxSection\":\n return sliceOf(\"blux_section\", {\n ...bg,\n ...bgc,\n ...bandVisual(spec),\n ...heading(spec),\n ...widgetFields(spec),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxGrid\":\n return sliceOf(\"blux_grid\", {\n ...bg,\n ...bgc,\n ...bandVisual(spec),\n ...heading(spec),\n ...widgetFields(spec),\n ...(spec.columns ? { columns: spec.columns } : {}),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxGallery\":\n return sliceOf(\"blux_gallery\", {\n ...bg,\n ...bgc,\n ...bandVisualCore(spec),\n ...heading(spec),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxCarousel\":\n return sliceOf(\"blux_carousel\", {\n ...bg,\n ...bgc,\n ...bandVisualCore(spec),\n ...heading(spec),\n ...(spec.columnsVisible ? { columns_visible: spec.columnsVisible } : {}),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxMedia\":\n return sliceOf(\"blux_media\", {\n ...bg,\n ...bgc,\n // Video plays from its CDN url via `video_embed` — an Image-field\n // marker would render a broken <img> in the starter (PrismicImage).\n ...(spec.media.kind === \"video\"\n ? { video_embed: videoTag(spec.media) }\n : { media: assetRef(spec.media.assetId) }),\n ...(spec.caption ? { caption: richText(spec.caption) } : {}),\n });\n case \"BluxMediaText\":\n return sliceOf(\"blux_media_text\", {\n ...bg,\n ...bgc,\n media: assetRef(spec.media.assetId),\n media_side: spec.mediaSide,\n ...(spec.layoutRatio ? { layout_ratio: spec.layoutRatio } : {}),\n ...(spec.title ? { title: richText(clampHeadingHtml(spec.title, 3, 4)) } : {}),\n ...(spec.body ? { body: richText(spec.body) } : {}),\n });\n case \"BluxCollection\":\n // Query-spec slice (spec §6 row 5): no cells — the starter fetches the\n // mapped entity documents at load time and filters via the tagFilter\n // DSL. scroll_load_more is a Select (\"off\"|\"on\") in the model, so a\n // truthy flag emits the literal \"on\"; absent optionals are omitted.\n return sliceOf(\"blux_collection\", {\n ...bg,\n ...bgc,\n ...heading(spec),\n ...widgetFields(spec),\n collection_type: spec.entityType,\n feed_ids: spec.feedIds.join(\",\"),\n ...(spec.filterTag ? { filter_tag: spec.filterTag } : {}),\n ...(spec.sort ? { sort: spec.sort } : {}),\n ...(spec.limit ? { limit: spec.limit } : {}),\n ...(spec.mediaRatio ? { media_ratio: spec.mediaRatio } : {}),\n layout: spec.layout,\n ...(spec.scrollLoadMore ? { scroll_load_more: \"on\" } : {}),\n });\n case \"BluxBlock\": {\n // The starter's blux_block model has NO background fields (the\n // Migration API may reject unknown primary fields), so the band\n // background rides a payload wrapper div instead. Style keys are\n // kebab-case: the starter's styleString emits keys VERBATIM into the\n // style attribute, so camelCase would parse to zero CSS declarations.\n const wrapStyle: Record<string, string> = {\n ...(spec.background\n ? {\n \"background-image\": `url(${mediaCdnUrl(spec.background) ?? spec.background.assetId})`,\n }\n : {}),\n ...(spec.backgroundColor ? { \"background-color\": spec.backgroundColor } : {}),\n };\n // Sanitize at the boundary: the spec payload stays the pristine source\n // tree; only the serialized copy the document ships loses its scripts.\n const clean = sanitizePayload(spec.payload, ctx);\n const payload = Object.keys(wrapStyle).length\n ? { tag: \"div\", style: wrapStyle, children: [clean] }\n : clean;\n // Round 3: a Block-fallback band can carry a lifted map mount too — the\n // widget triple emits with Section-identical field names (the starter\n // render util is shared across the catalog slices).\n return sliceOf(\"blux_block\", {\n ...widgetFields(spec),\n payload: JSON.stringify(payload),\n });\n }\n }\n}\n\n/** Deep-copy a BluxBlock payload tree with every `html` field sanitized\n * (mirrors the embed_html/widget_html boundaries — see sanitize.ts). A leaf\n * that is a custom mount with no visible content is a behavior script — it is\n * kept (sanitized, harmless) but the drop of its behavior is recorded. */\nfunction sanitizePayload(node: BlockNode, ctx: EmitCtx): BlockNode {\n if (node.html !== undefined && MOUNT_RE.test(node.html) && !hasVisibleContent(node.html)) {\n reportDrop(ctx, node.html);\n }\n return {\n ...node,\n ...(node.html !== undefined ? { html: sanitizeHtml(node.html) } : {}),\n ...(node.children ? { children: node.children.map((c) => sanitizePayload(c, ctx)) } : {}),\n };\n}\n\n/** Every Media a catalog spec references (background + leaf media + cell media,\n * recursing through subgrids). */\nfunction specMedia(spec: CatalogSpec): Media[] {\n const out: Media[] = [];\n if (spec.background) out.push(spec.background);\n const walk = (cells: CatalogCell[]) => {\n for (const c of cells) {\n if (c.media) out.push(c.media);\n if (c.subgrid) walk(c.subgrid);\n }\n };\n switch (spec.slice) {\n case \"BluxMedia\":\n case \"BluxMediaText\":\n out.push(spec.media);\n break;\n case \"BluxSection\":\n case \"BluxGrid\":\n case \"BluxGallery\":\n case \"BluxCarousel\":\n walk(spec.cells);\n break;\n case \"BluxBlock\":\n // The payload inlines each media as a CDN url; the same Media surface\n // on `spec.media` so the assets still upload. Payload urls remain CDN\n // until the 4d migrate-time rewrite (runMigration's assetUrlByCdn is\n // the map for it).\n out.push(...spec.media);\n break;\n }\n // Videos migrate too: the Blux CDN is being retired, so every referenced\n // asset must land in Prismic. A video uploads by its CDN url exactly like an\n // image; the emitted <video src> (that same CDN url, in video_embed /\n // embed_html) is then swapped to the Prismic url by the migrate-time\n // rewriteDocUrls, which keys on the identical mediaCdnUrl string.\n return out;\n}\n\nexport type CatalogAssetIndex = {\n assets: { id: string; url: string; alt: string; sourceUrl?: string | null }[];\n diagnostics?: Diagnostic[];\n};\n\n/** Build the migration plan for catalog-converted pages: one text+slices page\n * document each, plus the asset union (uploaded so nested `{__asset_id}` markers\n * resolve at migrate time). When `feeds` is passed, the entity emit rides the\n * same plan: its documents/custom types/diagnostics merge in and its record\n * media join the asset walk (resolved like all other media). */\nexport function buildCatalogPlan(\n pages: {\n uid: string;\n title: string;\n specs: CatalogSpec[];\n /** Per-page SEO from the Blux export (`content.pages[i]`): the meta\n * description and the social/og image asset uuid. Both optional — a page\n * with no description or social image emits neither meta field. */\n description?: string;\n socialImageId?: string;\n }[],\n ir: CatalogAssetIndex,\n feeds?: Record<string, { name?: string; items?: unknown[]; fields?: unknown } | undefined>,\n): MigrationPlan {\n const diagnostics: Diagnostic[] = [...(ir.diagnostics ?? [])];\n const entity = feeds ? buildEntityEmit(feeds) : null;\n // Media resolvers for subgrid `image_embed` emission: the src must equal the\n // plan-asset url so the migrate's `rewriteValueUrls` swaps it to Prismic.\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const mediaResolvers = {\n resolveUrl: (m: Media) => mediaUrl(m, sourceUrlById),\n resolveAlt: (id: string) => assetById.get(id)?.alt ?? \"\",\n };\n const documents: PlanDocument[] = pages.map((p) => ({\n type: CATALOG_PAGE_TYPE,\n uid: p.uid,\n data: {\n title: richText(`<h1>${p.title}</h1>`),\n // SEO & Metadata tab (catalog_page's second tab — see catalog-page-type.ts).\n // Field types match the starter render's reads (blux-catalog/page-doc.ts\n // pageMeta): meta_title/meta_description are Text → PLAIN strings (never a\n // richtext marker); meta_image is an Image field → an `{__asset_id}` marker\n // like cell media. meta_title defaults to the page title (a sensible SEO\n // default, always present); the other two omit when the Blux page has no\n // value (the-pointe's description is \"\" → meta_description omitted).\n meta_title: p.title,\n ...(p.description ? { meta_description: p.description } : {}),\n ...(p.socialImageId ? { meta_image: assetRef(p.socialImageId) } : {}),\n slices: p.specs.map((s) => catalogSpecToPlanSlice(s, diagnostics, p.uid, mediaResolvers)),\n },\n }));\n if (entity) {\n documents.push(...entity.documents);\n diagnostics.push(...entity.diagnostics);\n }\n const resolve = (m: Media): PlanAsset | null => {\n const asset = assetById.get(m.assetId);\n const url = mediaUrl(m, sourceUrlById);\n return url ? { id: m.assetId, url, alt: asset?.alt ?? \"\" } : null;\n };\n // collectPlanAssets only understands SliceSpec shapes, so gather media here and\n // resolve directly (skeleton) — keep insertion order, dedupe by assetId.\n // Entity record media walk the same path so their assets upload too.\n const seen = new Set<string>();\n const assets: PlanAsset[] = [];\n // The per-page social/og image (meta_image marker above) is an Image with no\n // CDN base — it uploads + its marker resolves like any other media: `resolve`\n // falls back to the IR sourceUrl (the og image is scraped into the IR asset\n // index like page media). Deduped by assetId against spec media by `seen`.\n const socialMedia: Media[] = pages\n .filter((p) => p.socialImageId)\n .map((p) => ({ kind: \"image\", assetId: p.socialImageId! }));\n const allMedia = [\n ...pages.flatMap((p) => p.specs).flatMap(specMedia),\n ...socialMedia,\n ...(entity?.media ?? []),\n ];\n for (const m of allMedia) {\n if (seen.has(m.assetId)) continue;\n seen.add(m.assetId);\n const a = resolve(m);\n if (a) assets.push(a);\n else\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: m.assetId,\n message: `media ${m.assetId} has no CDN base nor IR source url — not uploaded`,\n });\n }\n // CDN-sunset backstop: a Blux-CDN url can also ride a document as a RAW STRING\n // that no media marker captured — an embed_html <a href> to a PDF/file, a\n // payload background, a widget. Register each distinct unregistered CDN url as\n // its own asset so migrate-catalog uploads it and the document rewrite swaps it\n // off the retiring CDN. Deduped by url: a doc's image url is byte-identical to\n // its plan.asset url (mediaUrl === mediaCdnUrl when a base is present), so\n // marker-collected images never double-register — only genuine raw-string\n // file assets are added. Id is the Blux filename stem (its uuid).\n const registeredUrls = new Set(assets.map((a) => a.url));\n for (const url of collectCdnUrls(documents)) {\n if (registeredUrls.has(url)) continue;\n const file = (url.split(\"/\").pop() ?? url).split(\"?\")[0]!;\n // Only adopt a url that ends in a clean short file extension — a genuine Blux\n // asset is `.../{uuid}.{ext}`. The CDN regex can over-capture a BARE url\n // adjacent to trailing punctuation or markup (`…u.pdf.`, `…u.pdf</p>`); that\n // string is not fetchable, so registering it would 404 and (frozen\n // runMigration throws) abort the whole migrate. Leave those for the rewrite to\n // report as unmatched (WARNING + non-zero exit) — the graceful path.\n if (!/\\.[a-z0-9]{2,4}$/i.test(file)) continue;\n registeredUrls.add(url);\n assets.push({ id: file.replace(/\\.[^.]+$/, \"\"), url, alt: \"\" });\n }\n return {\n // The catalog_page type ALWAYS ships (the migration owns the type its\n // documents use); entity extension types (product/person/…) append. No\n // id collision — entity types are `format:\"custom\"`, never \"catalog_page\".\n customTypes: [catalogPageCustomType(), ...(entity ? entity.customTypes : [])],\n documents,\n assets,\n stylesManifest: [],\n diagnostics,\n };\n}\n","import { htmlAsRichText } from \"@prismicio/migrate\";\nimport type { MigrationPlan } from \"../emit/plan.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\n\nexport type FixtureDoc = { type: string; uid: string; data: Record<string, unknown> };\nexport type RenderFixture = {\n documents: FixtureDoc[];\n collections: Record<string, FixtureDoc[]>;\n missingAssets: string[];\n};\n\n/** Resolve plan markers OFFLINE for the starter fidelity-gate route (Task 8):\n * `{__richtext_html}` → rich text NODE ARRAYS (via the same `htmlAsRichText`\n * call the frozen migrate path uses in emit/resolve-doc.ts), `{__asset_id}` →\n * a Prismic-HYDRATED image field `{url, alt, dimensions}` pointing at the plan\n * asset's CDN url. Unlike resolve-doc.ts (which emits Migration API shapes —\n * richtext nodes plus `{id}` asset refs for the network), this produces the\n * shapes the production SliceZone consumes when rendering, so the gate can run\n * documents through it with no Prismic round-trip.\n *\n * Dimensions are a fixed placeholder — `PlanAsset` carries no intrinsic size\n * and the offline gate forbids the network fetch that would find it, so layout\n * fidelity for image slices that key off the INTRINSIC ratio (unconstrained\n * `height:auto`, `aspect-ratio` bound to natural dims) is approximate;\n * container-pinned images (`object-fit:cover`, `aspect-[x/y]`) are unaffected.\n * If ratio-sensitive fidelity ever regresses, capture real dims into\n * `PlanAsset` at scrape/IR time. Missing assets resolve to `null`\n * (isFilled-safe) and are reported. Entity (non-`page`) documents are grouped\n * by type for the SliceZone `context.collections`. Keep the marker NAMES\n * (`__richtext_html`, `__asset_id`) in sync with resolve-doc.ts — the `typeof`\n * guards below are intentionally stricter than resolve-doc's `\"key\" in v` (they\n * won't misfire on a non-string field of the same name); keep them on a sync. */\nexport function resolveFixture(plan: MigrationPlan): RenderFixture {\n const assetById = new Map(plan.assets.map((a) => [a.id, a] as const));\n const missing = new Set<string>();\n const walk = (v: unknown): unknown => {\n if (Array.isArray(v)) return v.map(walk);\n if (v && typeof v === \"object\") {\n const o = v as Record<string, unknown>;\n if (typeof o.__richtext_html === \"string\") return htmlAsRichText(o.__richtext_html).result;\n if (typeof o.__asset_id === \"string\") {\n const a = assetById.get(o.__asset_id);\n if (!a) {\n missing.add(o.__asset_id);\n return null;\n }\n return { url: a.url, alt: a.alt, dimensions: { width: 1600, height: 1200 } };\n }\n return Object.fromEntries(Object.entries(o).map(([k, x]) => [k, walk(x)]));\n }\n return v;\n };\n // `data` is invariantly a field map (PlanDocument.data), never a bare marker,\n // so walk() returns an object here — the cast is sound.\n const resolved: FixtureDoc[] = plan.documents.map((d) => ({\n ...d,\n data: walk(d.data) as Record<string, unknown>,\n }));\n const documents = resolved.filter((d) => d.type === CATALOG_PAGE_TYPE);\n const collections: Record<string, FixtureDoc[]> = {};\n for (const d of resolved) {\n if (d.type === CATALOG_PAGE_TYPE) continue;\n (collections[d.type] ??= []).push(d);\n }\n return { documents, collections, missingAssets: [...missing] };\n}\n","// Site chrome (navigation + footer) → a render-side config. The Blux export's\n// `navigation` is a nested tree (top items with optional dropdown children)\n// and `footer` a list of social-link + copyright items; both were dropped by\n// the page-focused convert. This builds a deterministic `site-config.json` the\n// render's Nav/Footer consume (additive: a site with no config renders the\n// starter's logo-only bar and placeholder footer, exactly as before).\n\n/** A navigation entry: a label + href, with optional dropdown children. */\nexport type NavItem = { label: string; href: string; children?: NavItem[] };\n\n/** A footer social link: the network id (facebook, instagram, …) and, when the\n * export carries one, its url. */\nexport type FooterSocial = { network: string; href?: string };\n\nexport type SiteConfig = {\n nav: {\n /** The logo image url (resolved), if the export declares one. */\n logo?: { url: string; maxWidth?: string };\n items: NavItem[];\n };\n footer: {\n socials: FooterSocial[];\n /** The copyright / rights line. */\n text?: string;\n };\n};\n\ntype RawNavItem = { title?: unknown; link?: unknown; items?: unknown };\n\n/** Parse the nested navigation tree from a Blux `navigation` value. Only items\n * with a title survive; an item with children becomes a dropdown (its own link\n * is often empty — a heading), a leaf keeps its href. Recurses one+ levels. */\nfunction parseNavItems(items: unknown): NavItem[] {\n if (!Array.isArray(items)) return [];\n const out: NavItem[] = [];\n for (const raw of items as RawNavItem[]) {\n const label = typeof raw?.title === \"string\" ? raw.title.trim() : \"\";\n if (!label) continue;\n const href = typeof raw?.link === \"string\" ? raw.link : \"\";\n const children = parseNavItems(raw?.items);\n out.push({ label, href, ...(children.length ? { children } : {}) });\n }\n return out;\n}\n\n/** The domain(s) that identify each Blux social network. Blux's social widget\n * stores only the network *flags* (`networks: { facebook: true, … }`) — the\n * profile urls are injected at render time from account config that isn't in\n * the export, so we recover them from the scraped live footer by matching the\n * link's host against these.\n *\n * Keep this in lockstep with the render's Footer `NETWORK` map (reddoor-starter\n * src/lib/components/Footer.svelte): a network we resolve an href for but the\n * Footer can't draw is silently dropped, so only list networks the Footer\n * renders. */\nconst SOCIAL_DOMAINS: Record<string, string[]> = {\n facebook: [\"facebook.com\", \"fb.com\"],\n twitter: [\"twitter.com\", \"x.com\"],\n instagram: [\"instagram.com\"],\n pinterest: [\"pinterest.com\"],\n linkedin: [\"linkedin.com\"],\n \"linkedin-company\": [\"linkedin.com\"],\n youtube: [\"youtube.com\", \"youtu.be\"],\n reddit: [\"reddit.com\"],\n};\n\n/** True when `host` is `domain` or a subdomain of it — not merely a suffix\n * match (so `notfacebook.com` never matches `facebook.com`). */\nfunction hostMatches(host: string, domain: string): boolean {\n return host === domain || host.endsWith(\".\" + domain);\n}\n\nfunction hostOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n/** The `<footer>…</footer>` region of a page (last occurrence), or the whole\n * html when there's no footer element. Social links live in the footer, so we\n * scan there — a body link to, say, a facebook post must not outrank the real\n * footer profile link. */\nfunction footerRegion(html: string): string {\n const lower = html.toLowerCase();\n const start = lower.lastIndexOf(\"<footer\");\n if (start === -1) return html;\n const end = lower.indexOf(\"</footer>\", start);\n return end === -1 ? html.slice(start) : html.slice(start, end + \"</footer>\".length);\n}\n\n/** A network→href resolver built from scraped page HTML. Blux renders the same\n * footer on every page, so any page's html carries the social links; we scan\n * the footer region for absolute http(s) hrefs and match each to a network by\n * host. Returns a function so `buildSiteConfig` stays free of the html-scan\n * mechanics. */\nexport function socialHrefResolverFromHtml(\n htmls: string[],\n): (network: string) => string | undefined {\n const hrefs: string[] = [];\n const re = /href\\s*=\\s*[\"']([^\"']+)[\"']/gi;\n for (const html of htmls) {\n if (typeof html !== \"string\") continue;\n const region = footerRegion(html);\n let m: RegExpExecArray | null;\n while ((m = re.exec(region))) {\n const url = m[1];\n if (url && /^https?:\\/\\//i.test(url)) hrefs.push(url);\n }\n }\n return (network) => {\n const domains = SOCIAL_DOMAINS[network];\n if (!domains) return undefined;\n return hrefs.find((u) => {\n const host = hostOf(u);\n return host && domains.some((d) => hostMatches(host, d));\n });\n };\n}\n\n/** The known social networks and the http(s) url found for one in a raw footer\n * social item (Blux stores `networks: { facebook: true, … }` — the flags — and\n * sometimes per-network urls elsewhere; we carry the network id and any url).\n * When the export omits a url, `resolveSocialHref` recovers it from the scraped\n * live footer (see `socialHrefResolverFromHtml`). */\nfunction parseSocials(\n raw: unknown,\n resolveSocialHref?: (network: string) => string | undefined,\n): FooterSocial[] {\n const out: FooterSocial[] = [];\n const items = Array.isArray(raw) ? raw : [];\n for (const it of items as { media?: { type?: unknown; networks?: unknown; urls?: unknown } }[]) {\n const media = it?.media;\n if (!media || media.type !== \"social\") continue;\n const networks = media.networks;\n if (!networks || typeof networks !== \"object\") continue;\n const urls = (media.urls ?? {}) as Record<string, unknown>;\n for (const [network, on] of Object.entries(networks as Record<string, unknown>)) {\n if (on !== true) continue;\n const fromExport = typeof urls[network] === \"string\" ? (urls[network] as string) : undefined;\n const url = fromExport ?? resolveSocialHref?.(network);\n out.push({ network, ...(url ? { href: url } : {}) });\n }\n }\n return out;\n}\n\n/** Every non-empty `title` under a footer subtree, in document order. The\n * copyright often sits nested inside a column item, not at the top level. */\nfunction collectTitles(node: unknown, acc: string[]): string[] {\n if (Array.isArray(node)) {\n for (const n of node) collectTitles(n, acc);\n return acc;\n }\n if (node && typeof node === \"object\") {\n const rec = node as Record<string, unknown>;\n if (typeof rec.title === \"string\" && rec.title.trim()) acc.push(rec.title.trim());\n for (const k of Object.keys(rec)) if (k !== \"title\") collectTitles(rec[k], acc);\n }\n return acc;\n}\n\n/** The footer's rights/copyright line. Blux footers interleave link-column\n * headings (\"About Us\", \"Quick Links\") with the copyright — and the copyright\n * is often nested inside a column — so \"first titled item\" grabs a heading on\n * many sites. Match the copyright by its shape (©, \"copyright\", or \"rights\n * reserved\") across the whole footer subtree instead. Returns undefined when\n * none matches (the render shows its generic notice, not a wrong heading). */\nconst COPYRIGHT_RE = /©|\\(c\\)|\\bcopyright\\b|rights reserved/i;\nfunction parseFooterText(raw: unknown): string | undefined {\n return collectTitles(raw, []).find((t) => COPYRIGHT_RE.test(t));\n}\n\n/** Build the render-side site config from the export's navigation + footer.\n * `resolveLogo` turns the nav logo's asset uuid into a url (null when\n * unresolved). `resolveSocialHref` recovers a footer social's profile url from\n * the scraped live footer when the export omits it. Pure. */\nexport function buildSiteConfig(\n siteJson: unknown,\n resolveLogo: (uuid: string) => string | null,\n resolveSocialHref?: (network: string) => string | undefined,\n): SiteConfig {\n const j = siteJson as { navigation?: unknown; footer?: unknown };\n const navRoot = (Array.isArray(j.navigation) ? j.navigation[0] : j.navigation) as\n | { items?: unknown; logo?: { media?: unknown } }\n | undefined;\n const footRoot = (Array.isArray(j.footer) ? j.footer[0] : j.footer) as\n | { items?: unknown }\n | undefined;\n\n const items = parseNavItems(navRoot?.items);\n // The logo is `logo: { media: { media: <uuid>, \"max-width\": … } }` — the\n // asset uuid and its render sizing are nested one level inside `logo.media`.\n const logoMedia = navRoot?.logo?.media as { media?: unknown; \"max-width\"?: unknown } | undefined;\n const logoUuid = typeof logoMedia?.media === \"string\" ? logoMedia.media : undefined;\n const logoUrl = logoUuid ? resolveLogo(logoUuid) : null;\n const maxWidth =\n typeof logoMedia?.[\"max-width\"] === \"string\" ? logoMedia[\"max-width\"] : undefined;\n\n const socials = parseSocials(footRoot?.items, resolveSocialHref);\n const text = parseFooterText(footRoot?.items);\n\n return {\n nav: {\n ...(logoUrl ? { logo: { url: logoUrl, ...(maxWidth ? { maxWidth } : {}) } } : {}),\n items,\n },\n footer: {\n socials,\n ...(text ? { text } : {}),\n },\n };\n}\n","// Site chrome for the catalog path (Plan 4d Task 4). Nav rides the frozen\n// buildSiteConfig extraction verbatim (labels from `title`, dropdown\n// recursion, `navigation[0].logo` when a site declares one), then patches the\n// shape Blux sites like the-pointe actually use: the logo is a nav ITEM\n// (`hideTitle: true` + `media`), not `navigation[0].logo` — promote it to\n// nav.logo and keep it out of the text links. The footer keeps FULL columns\n// (the-pointe's leasing-contact columns with tel:/mailto: links, logo rows as\n// images) instead of site-config's socials+text reduction — emit/site-config\n// is frozen, so the richer footer lives here.\n\nimport { buildSiteConfig, type SiteConfig } from \"../emit/site-config.js\";\n\n/** One rendered footer row: a text line (optionally linked — tel:/mailto:/\n * http(s) hrefs survive verbatim) or an image (a footer logo, optionally\n * linked). `image.alt` is the row's accessible name (its human title) when the\n * export carries one — omitted for a nameless image, where the render falls\n * back to alt=\"\". */\nexport type ChromeFooterItem =\n | { text: string; href?: string }\n | { image: { url: string; maxWidth?: string; alt?: string }; href?: string };\n\nexport type ChromeConfig = {\n nav: SiteConfig[\"nav\"];\n footer: { columns: { items: ChromeFooterItem[] }[] };\n};\n\ntype RawItem = {\n title?: unknown;\n text?: unknown;\n link?: unknown;\n hideTitle?: unknown;\n media?: { media?: unknown; \"max-width\"?: unknown };\n items?: unknown;\n};\n\n/** Display text of a raw item's `title`. Blux pads layout spacers with\n * `&nbsp;` entities (`\" &nbsp;\"` rows between contact blocks) and escapes\n * ampersands as `&amp;`, so decode both before trimming — a spacer nets \"\" and\n * is dropped by the caller. */\nfunction displayTitle(title: unknown): string {\n if (typeof title !== \"string\") return \"\";\n return title\n .replace(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .trim();\n}\n\nfunction asHref(link: unknown): string | undefined {\n return typeof link === \"string\" && link.trim() ? link.trim() : undefined;\n}\n\n/** The item's media resolved to a drawable image, or undefined when there is\n * no media uuid or the resolver can't produce a url (nothing to draw — never\n * fall back to the row's hidden title text). */\nfunction asImage(\n media: RawItem[\"media\"],\n resolveLogo: (uuid: string) => string | null,\n): { url: string; maxWidth?: string } | undefined {\n const uuid = typeof media?.media === \"string\" ? media.media : undefined;\n const url = uuid ? resolveLogo(uuid) : null;\n if (!url) return undefined;\n const maxWidth = typeof media?.[\"max-width\"] === \"string\" ? media[\"max-width\"] : undefined;\n return { url, ...(maxWidth ? { maxWidth } : {}) };\n}\n\n/** Site chrome for the catalog path: nav via buildSiteConfig (+ hidden-logo\n * promotion), footer with full columns from `footer[0].items[].items[]`.\n * `resolveLogo` turns a media uuid into a url (null when unresolved). Pure. */\nexport function buildChrome(\n siteJson: unknown,\n resolveLogo: (uuid: string) => string | null,\n): ChromeConfig {\n const sc = buildSiteConfig(siteJson, resolveLogo);\n const j = siteJson as { navigation?: unknown; footer?: unknown };\n const navRoot = (Array.isArray(j.navigation) ? j.navigation[0] : j.navigation) as\n | { items?: unknown }\n | undefined;\n const rawNavItems = Array.isArray(navRoot?.items) ? (navRoot.items as RawItem[]) : [];\n\n // Hidden nav items (`hideTitle: true`) are visual, not links: buildSiteConfig\n // keeps them (it has no hideTitle notion), so drop their labels from the text\n // items and promote the first media-bearing one to the logo slot when the\n // export has no `navigation[0].logo`.\n const hidden = rawNavItems.filter((r) => r?.hideTitle === true);\n const hiddenLabels = new Set(\n hidden.map((r) => (typeof r.title === \"string\" ? r.title.trim() : \"\")).filter(Boolean),\n );\n const items = sc.nav.items.filter((i) => !hiddenLabels.has(i.label));\n // NOTE: nav.logo carries no alt — its type is the frozen SiteConfig[\"nav\"]\n // logo shape ({ url, maxWidth? }), which has no name channel. Giving the nav\n // logo an accessible name would need the frozen buildSiteConfig/site-config\n // path to widen, so it's out of scope here (the footer logos, which this\n // module owns end-to-end, do get alt below).\n let logo = sc.nav.logo;\n if (!logo) {\n for (const r of hidden) {\n const image = asImage(r.media, resolveLogo);\n if (image) {\n logo = image;\n break;\n }\n }\n }\n\n const footRoot = (Array.isArray(j.footer) ? j.footer[0] : j.footer) as\n | { items?: unknown }\n | undefined;\n const rawCols = Array.isArray(footRoot?.items) ? (footRoot.items as RawItem[]) : [];\n const columns = rawCols.flatMap((col) => {\n const subs = Array.isArray(col?.items) ? (col.items as RawItem[]) : [];\n const colItems = subs.flatMap((it): ChromeFooterItem[] => {\n const href = asHref(it?.link);\n if (typeof it?.media?.media === \"string\") {\n const image = asImage(it.media, resolveLogo);\n if (!image) return [];\n // Accessible name for the logo: the row's human title (else its text),\n // decoded — a linked footer logo with no name is an unlabelled link.\n // `??` keeps title authoritative; a \"&nbsp;\" spacer title decodes to \"\"\n // and is dropped, so a spacer image stays alt-less rather than mislabelled.\n const alt = displayTitle(it.title ?? it.text);\n return [{ image: { ...image, ...(alt ? { alt } : {}) }, ...(href ? { href } : {}) }];\n }\n if (it?.hideTitle === true) return [];\n const text = displayTitle(it?.title);\n if (!text) return [];\n return [href ? { text, href } : { text }];\n });\n return colItems.length ? [{ items: colItems }] : [];\n });\n\n return { nav: { ...(logo ? { logo } : {}), items }, footer: { columns } };\n}\n","// Offline layout-fidelity gate (plan 6). Diffs the classified source\n// (`SliceSpec[]`, already gated band→spec by grid-classify-golden) against the\n// emitted presentation manifest (plan 5), naming every band whose layout,\n// media, or map drifted. Pure + offline — the render side (Playwright DOM\n// signature) is plan 7's verify. See the plan's \"The comparison, precisely\".\nimport type { Cell, Node, SliceSpec } from \"../grid/index.js\";\nimport {\n hasMapWidget,\n type Presentation,\n type RenderCell,\n type RenderNode,\n} from \"./presentation.js\";\n\n/** Canonical grid-token key — cols + optional ratio/spacing, WITHOUT the\n * source-only `raw` string, so a source token and its render-side twin (which\n * never carries `raw`) compare equal. */\nfunction tokKey(t: { cols: number | \"any\"; ratio?: number; spacing?: number }): string {\n return `${t.cols}${t.ratio !== undefined ? `r${t.ratio}` : \"\"}${t.spacing !== undefined ? `s${t.spacing}` : \"\"}`;\n}\n\n/** Compact structural signature of a node tree, computed identically for a\n * source `Node` and its serialized `RenderNode` twin (they share `kind`s and\n * aligned fields). Prose is excluded; only kinds + grid tokens + media/widget\n * kinds appear — so it snapshots LAYOUT, not content. A source media node the\n * manifest dropped (unresolved url) makes the two signatures diverge, which is\n * exactly the fidelity signal we want. Mirrors `grid/signature.ts`'s exhaustive\n * switch so a new node kind is a compile error, not a silent drop. */\nexport function sigOf(node: Node | RenderNode): string {\n switch (node.kind) {\n case \"row\":\n return `row[${node.cells\n .map((c: Cell | RenderCell) => `${tokKey(c.token)}:${sigOf(c.node)}`)\n .join(\",\")}]`;\n case \"stack\":\n return `stack[${node.children.map(sigOf).join(\",\")}]`;\n case \"heading\":\n return `h${node.level}`;\n case \"body\":\n return \"body\";\n case \"subtitle\":\n return \"subtitle\";\n case \"media\":\n return `media:${node.media.kind}`;\n case \"widget\":\n return `widget:${node.widget.type}`;\n case \"raw\":\n return \"raw\";\n }\n}\n\nexport type LayoutFinding =\n | { kind: \"band-count\"; specs: number; manifest: number }\n | { kind: \"band-missing\"; band: number }\n | { kind: \"tree-drift\"; band: number; expected: string; actual: string }\n | { kind: \"media-dropped\"; band: number; where: string }\n | { kind: \"map-missing\"; band: number };\n\nexport type LayoutRow = {\n band: number;\n slice: SliceSpec[\"slice\"];\n source: string;\n converted: string;\n ok: boolean;\n};\n\nexport type LayoutReport = {\n /** number of source bands (== specs.length) */\n bands: number;\n /** Grid-fallback bands whose tree fidelity was signature-checked */\n gridBands: number;\n faithful: boolean;\n findings: LayoutFinding[];\n rows: LayoutRow[];\n};\n\n/** A short source-side label per slice, used for the report row's `source`\n * column and the missing-band row. Grid uses the full structural signature. */\nfunction sourceLabel(spec: SliceSpec): string {\n switch (spec.slice) {\n case \"Grid\":\n return sigOf(spec.root);\n case \"Gallery\":\n return `gallery(${spec.media.length})`;\n case \"Carousel\":\n return `carousel(${spec.slides.length})`;\n case \"MediaFull\":\n return \"media_full\";\n case \"VideoFeature\":\n return \"video\";\n case \"SplitFeature\":\n return `split(${spec.mediaSide},${spec.ratio})`;\n case \"LocationMap\":\n return \"location_map\";\n case \"Hero\":\n return \"hero\";\n case \"TitleBand\":\n return \"title_band\";\n case \"RichText\":\n return \"rich_text\";\n }\n}\n\n/** Diff the classified source against the emitted manifest. Grid bands must\n * round-trip their structural signature exactly (spec.root vs the serialized\n * tree); smart slices are checked for payload completeness (gallery count,\n * split/media present, background/map present). Returns a structured report;\n * `faithful` is true iff `findings` is empty. */\nexport function validateLayout(specs: SliceSpec[], presentation: Presentation): LayoutReport {\n const findings: LayoutFinding[] = [];\n const rows: LayoutRow[] = [];\n const manifestKeys = Object.keys(presentation.bands);\n let gridBands = 0;\n\n if (specs.length !== manifestKeys.length) {\n findings.push({ kind: \"band-count\", specs: specs.length, manifest: manifestKeys.length });\n }\n\n // We iterate specs (the source answer key): a stray manifest band with no spec surfaces only via the band-count mismatch above, never named — by design.\n for (const spec of specs) {\n const source = sourceLabel(spec);\n const bp = presentation.bands[String(spec.index)];\n if (!bp) {\n findings.push({ kind: \"band-missing\", band: spec.index });\n rows.push({ band: spec.index, slice: spec.slice, source, converted: \"∅\", ok: false });\n continue;\n }\n const before = findings.length;\n let converted = source;\n\n // Band background (any slice) must survive if the source declared one.\n if (spec.background && !bp.background) {\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"background\" });\n }\n\n switch (spec.slice) {\n case \"Grid\": {\n gridBands++;\n converted = bp.tree ? sigOf(bp.tree) : \"∅\";\n if (source !== converted) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n if (hasMapWidget(spec.root) && !bp.map) {\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Gallery\": {\n const got = bp.gallery?.length ?? 0;\n converted = `gallery(${got})`;\n if (got < spec.media.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `gallery ${got}/${spec.media.length}`,\n });\n }\n break;\n }\n case \"Carousel\": {\n const got = bp.carousel?.slides.length ?? 0;\n converted = `carousel(${got})`;\n if (got < spec.slides.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `carousel ${got}/${spec.slides.length}`,\n });\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n if (!bp.media) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"media\" });\n }\n break;\n }\n case \"SplitFeature\": {\n if (!bp.split) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"split\" });\n } else {\n converted = `split(${bp.split.mediaSide},${bp.split.ratio})`;\n // Defensive: the current builder copies mediaSide/ratio verbatim, so\n // this can't fire against real convertExport output — it guards a\n // future transforming builder that reshapes the split.\n if (converted !== source) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n // The split's text side is a full node subtree (band 1 of the-pointe\n // nests media inside it); buildPresentation serializes it via\n // renderNode, which DROPS unresolved media. Signature-check it like a\n // Grid tree so a dropped nested media isn't silently reported faithful.\n const expectedText = sigOf(spec.text);\n const actualText = sigOf(bp.split.text);\n if (expectedText !== actualText) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: `split.text ${expectedText}`,\n actual: `split.text ${actualText}`,\n });\n }\n }\n break;\n }\n case \"LocationMap\": {\n if (!bp.map) {\n converted = \"∅\";\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Hero\":\n case \"TitleBand\":\n case \"RichText\":\n break; // text lives in the page doc (gated by grid-slice.test.ts); nothing manifest-carried to lose beyond background\n default: {\n // A new slice kind must be handled explicitly, not silently pass as\n // faithful. `sourceLabel` above is also exhaustive, so this is a local\n // belt-and-suspenders guard rather than the sole gate.\n const _exhaustive: never = spec;\n throw new Error(`validateLayout: unhandled slice ${(_exhaustive as SliceSpec).slice}`);\n }\n }\n\n rows.push({\n band: spec.index,\n slice: spec.slice,\n source,\n converted,\n ok: findings.length === before,\n });\n }\n\n return { bands: specs.length, gridBands, faithful: findings.length === 0, findings, rows };\n}\n\nfunction fmtFinding(f: LayoutFinding): string {\n switch (f.kind) {\n case \"band-count\":\n return ` band count mismatch: ${f.specs} source bands vs ${f.manifest} manifest bands`;\n case \"band-missing\":\n return ` band ${f.band}: no manifest entry`;\n case \"tree-drift\":\n return ` band ${f.band}: grid tree drift\\n expected ${f.expected}\\n actual ${f.actual}`;\n case \"media-dropped\":\n return ` band ${f.band}: media dropped (${f.where})`;\n case \"map-missing\":\n return ` band ${f.band}: map config missing`;\n }\n}\n\n/** Render a LayoutReport as a terminal summary: a headline verdict, a per-band\n * signature table (`ok` / `!!` per band), then the findings detail. */\nexport function formatLayoutReport(r: LayoutReport): string {\n const headline = `layout fidelity: ${\n r.faithful ? \"FAITHFUL\" : `${r.findings.length} finding(s)`\n } — ${r.bands} bands (${r.gridBands} grid-tree checked)`;\n const table = r.rows.map((x) => {\n const tag = x.ok ? \"ok\" : \"!!\";\n const sig = x.ok ? x.source : `${x.source} -> ${x.converted}`;\n return ` band ${String(x.band).padStart(2)} ${x.slice.padEnd(13)} ${tag} ${sig}`;\n });\n const detail = r.findings.length ? [\"findings:\", ...r.findings.map(fmtFinding)] : [];\n return [headline, ...table, ...detail].join(\"\\n\");\n}\n","import type {\n BandPresentation,\n Presentation,\n RenderMedia,\n RenderNode,\n SitePresentation,\n} from \"./presentation.js\";\n\nconst swap = (m: RenderMedia, map: Map<string, string>): RenderMedia => {\n const url = map.get(m.url);\n return url ? { ...m, url } : m;\n};\n\nfunction walkNode(node: RenderNode, map: Map<string, string>): RenderNode {\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({ token: c.token, node: walkNode(c.node, map) })),\n };\n case \"stack\":\n return { kind: \"stack\", children: node.children.map((c) => walkNode(c, map)) };\n case \"media\":\n return { kind: \"media\", media: swap(node.media, map) };\n default:\n return node; // heading/body/subtitle/raw/widget carry no media url\n }\n}\n\nfunction walkBand(bp: BandPresentation, map: Map<string, string>): BandPresentation {\n const out: BandPresentation = { ...bp };\n if (bp.background) out.background = swap(bp.background, map);\n if (bp.media) out.media = swap(bp.media, map);\n if (bp.gallery) out.gallery = bp.gallery.map((m) => swap(m, map));\n if (bp.carousel)\n out.carousel = {\n ...bp.carousel,\n slides: bp.carousel.slides.map((s) => ({ ...s, media: swap(s.media, map) })),\n };\n if (bp.tree) out.tree = walkNode(bp.tree, map);\n if (bp.split)\n out.split = {\n ...bp.split,\n media: swap(bp.split.media, map),\n text: walkNode(bp.split.text, map),\n };\n return out; // style / map untouched (no media urls)\n}\n\nfunction rewritePage(p: Presentation, map: Map<string, string>): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const [k, bp] of Object.entries(p.bands)) bands[k] = walkBand(bp, map);\n return { bands };\n}\n\n/** Deep copy of the manifest with every RenderMedia.url present in `urlMap`\n * replaced by its mapped value. Unknown urls left intact. Handles BOTH shapes:\n * the flat single-page `{ bands }` and the multi-page `{ pages: { <uid>:\n * { bands } } }` that `blux convert` now writes — the migrate step must rewrite\n * every site's urls regardless of shape. Pure. */\nexport function rewriteManifestUrls(\n manifest: SitePresentation,\n urlMap: Map<string, string>,\n): SitePresentation {\n if (\"pages\" in manifest && manifest.pages) {\n const pages: Record<string, Presentation> = {};\n for (const [uid, p] of Object.entries(manifest.pages)) pages[uid] = rewritePage(p, urlMap);\n return { pages };\n }\n // The flat branch is a Presentation by construction (cast bridges the union).\n return rewritePage(manifest as Presentation, urlMap);\n}\n"],"mappings":"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","names":["heading","base","heading","parse","base","img","base","style","parse","heading","tiles","heading","base","img","collectRaws","title","titleRole","bodyHtml","embedHtml","base","isEmptyish","heading","isStyleKey","recordUid","date","base"]}
1
+ {"version":3,"sources":["../src/cli/commands/blux.ts","../src/blux/parse.ts","../src/blux/archetype.ts","../src/blux/normalize.ts","../src/blux/collections.ts","../src/blux/assemble.ts","../src/blux/emit/custom-types.ts","../src/blux/emit/coerce-html.ts","../src/blux/emit/slices.ts","../src/blux/emit/flatten.ts","../src/blux/emit/migration-plan.ts","../src/blux/emit/theme.ts","../src/blux/emit/review.ts","../src/blux/emit/block-styles.ts","../src/blux/validate.ts","../src/blux/grid/parse-grid.ts","../src/blux/grid/token.ts","../src/blux/grid/leaf.ts","../src/blux/grid/classify-band.ts","../src/blux/grid/feed-grid.ts","../src/blux/products.ts","../src/blux/emit/grid-slice.ts","../src/blux/emit/grid-plan.ts","../src/blux/emit/presentation.ts","../src/blux/emit/convert.ts","../src/blux/catalog/spec.ts","../src/blux/catalog/catalog-page-type.ts","../src/blux/catalog/sanitize.ts","../src/blux/catalog/cells.ts","../src/blux/catalog/feeds.ts","../src/blux/catalog/classify.ts","../src/blux/catalog/entities.ts","../src/blux/catalog/rewrite-doc-urls.ts","../src/blux/catalog/emit.ts","../src/blux/catalog/resolve-fixture.ts","../src/blux/emit/site-config.ts","../src/blux/catalog/chrome.ts","../src/blux/emit/validate-layout.ts","../src/blux/emit/rewrite-manifest.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname, basename } from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { assembleIR } from \"../../blux/assemble.js\";\nimport { buildMigrationPlan } from \"../../blux/emit/migration-plan.js\";\nimport { emitThemeCssFile } from \"../../blux/emit/theme.js\";\nimport { buildReviewManifest } from \"../../blux/emit/review.js\";\nimport { blockStylesByIndex, blockClassDefaults } from \"../../blux/emit/block-styles.js\";\nimport { validateCoverage } from \"../../blux/validate.js\";\nimport { parseGridBands, extractMapConfig, makeIsMapMount } from \"../../blux/grid/index.js\";\nimport { feedAssetBase, extFor, isFeedBand } from \"../../blux/grid/feed-grid.js\";\nimport { materializeProducts, type ProductRecord } from \"../../blux/products.js\";\nimport { convertExport, convertSite, sitePages } from \"../../blux/emit/convert.js\";\nimport { bandOrCollection, buildCatalogPlan } from \"../../blux/catalog/index.js\";\nimport type { CatalogSpec } from \"../../blux/catalog/index.js\";\nimport { resolveFixture } from \"../../blux/catalog/resolve-fixture.js\";\nimport { rewriteDocUrls, rewriteValueUrls } from \"../../blux/catalog/rewrite-doc-urls.js\";\nimport { buildChrome } from \"../../blux/catalog/chrome.js\";\nimport { buildSiteConfig, socialHrefResolverFromHtml } from \"../../blux/emit/site-config.js\";\nimport { validateLayout, formatLayoutReport } from \"../../blux/emit/validate-layout.js\";\nimport { rewriteManifestUrls } from \"../../blux/emit/rewrite-manifest.js\";\nimport type { SitePresentation } from \"../../blux/emit/presentation.js\";\nimport { richText, assetRef } from \"../../blux/emit/plan.js\";\nimport type { MigrationPlan, PlanAsset, PlanDocument } from \"../../blux/emit/plan.js\";\nimport { frozenPageCustomType, FROZEN_PAGE_TYPE } from \"../../blux/freeze/frozen-page-type.js\";\nimport type { FrozenManifest } from \"../../blux/freeze/types.js\";\nimport type { Diagnostic } from \"../../blux/ir.js\";\n\nexport type BluxCommandOptions = {\n /** Output directory for emit (default: <exportDir>/blux-out). */\n out?: string;\n /** Base URL of the converted site for the review manifest. */\n convertedBase?: string;\n /** Base URL of the original Blux site (default: https://<site.json domain>). */\n bluxBase?: string;\n /** Reconstruct + HEAD-probe CDN URLs for used assets the HTML scrape missed (network). */\n probe?: boolean;\n /** validate: the converted site's rendered HTML — a file path or http(s) URL. */\n against?: string;\n /** freeze: site slug for the emitted template/manifest (default: slugified dir name). */\n site?: string;\n /** freeze: path to the site's extra-slots declaration (see\n * blux/freeze/types.ts `ExtraSlotsFile`) — editable content the byte-faithful\n * template carries no token for, e.g. a video poster or a panel the render\n * rebuilds from a baked image. Optional; omitted means no extra slots. */\n extraSlots?: string;\n /** Test seam for --probe; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n cwd?: string;\n verbose?: boolean;\n};\n\n/** `blux <action> [dir]` — emit: Blux export dir → migration plan + custom-type\n * schemas + theme CSS + review manifest, all deterministic and offline.\n * migrate: a previously emitted plan → live Prismic (creds-gated; the runner\n * is imported lazily so emit runs never touch @prismicio).\n * migrate-catalog: a catalog plan → live Prismic in TWO phases — assets\n * first (returns the cdn→Prismic url map), rewriteDocUrls over the plan\n * documents, then the rewritten documents (assets hit the reuse branch).\n * validate: offline layout-fidelity gate (parse+classify index.html, diff the\n * emitted manifest vs the source answer key) — exits non-zero on drift.\n * --against <file|url> additionally runs content coverage of a rendered page.\n * grid: parse rendered index.html → grid-tree.json (layout tree).\n * convert: parse+classify index.html + assemble IR from site.json → the grid\n * migration-plan.json (page doc) + blux-presentation.json (render manifest) +\n * theme.css (+ map-config.json when a map is present), all offline, no creds. */\nexport async function runBluxCommand(\n action: string,\n dir: string | undefined,\n opts: BluxCommandOptions,\n): Promise<{ output: string; code: number }> {\n if (action === \"emit\") {\n if (!dir) return { output: \"blux emit needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return {\n output: `could not read site.json in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const out = opts.out ?? join(dir, \"blux-out\");\n const htmlPaths = (\n await glob([\"**/*.html\"], { cwd: dir, absolute: true, ignore: [\"blux-out/**\"] })\n ).sort();\n const htmls = await Promise.all(htmlPaths.map((p) => readFile(p, \"utf-8\")));\n\n const ir = assembleIR({ siteJson, htmls });\n\n let probeLine = \"\";\n if (opts.probe) {\n const { probeAssetUrls } = await import(\"../../blux/emit/probe.js\");\n // derive \"used\" from the plan's own __asset_id markers — the single\n // source of truth for which assets documents actually reference\n const used = new Set<string>();\n const collect = (v: unknown): void => {\n if (!v || typeof v !== \"object\") return;\n if (\"__asset_id\" in v) used.add((v as { __asset_id: string }).__asset_id);\n else if (Array.isArray(v)) v.forEach(collect);\n else Object.values(v).forEach(collect);\n };\n buildMigrationPlan(ir).documents.forEach((d) => collect(d.data));\n\n const targets = ir.assets.filter((a) => used.has(a.id) && !a.sourceUrl);\n const probed = await probeAssetUrls(targets, ir.meta.bluxSiteId, opts.fetchImpl ?? fetch);\n let hits = 0;\n for (const a of ir.assets) {\n const url = probed.get(a.id);\n if (url) {\n a.sourceUrl = url;\n hits++;\n }\n }\n ir.diagnostics = ir.diagnostics.filter(\n (d) => !(d.kind === \"unresolved-asset\" && probed.get(d.where)),\n );\n probeLine = `probe resolved ${hits}/${targets.length} used assets`;\n }\n\n const plan = buildMigrationPlan(ir);\n const manifest = buildReviewManifest(ir, {\n convertedBase: opts.convertedBase ?? \"http://localhost:5173\",\n bluxBase: opts.bluxBase ?? `https://${ir.meta.domain}`,\n });\n\n await mkdir(join(out, \"customtypes\"), { recursive: true });\n await writeFile(join(out, \"ir.json\"), JSON.stringify(ir, null, 2));\n await writeFile(join(out, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n await writeFile(join(out, \"theme.css\"), emitThemeCssFile(ir.theme));\n await writeFile(join(out, \"review-manifest.json\"), JSON.stringify(manifest, null, 2));\n await writeFile(\n join(out, \"styles-manifest.json\"),\n JSON.stringify(plan.stylesManifest, null, 2),\n );\n for (const ct of plan.customTypes) {\n await writeFile(join(out, \"customtypes\", `${ct.id}.json`), JSON.stringify(ct.json, null, 2));\n }\n\n const resolved = ir.assets.filter((a) => a.sourceUrl !== null).length;\n const diagnostics = [...ir.diagnostics, ...plan.diagnostics];\n const lines = [\n `site: ${ir.meta.name} (${ir.meta.domain})`,\n ...(probeLine ? [probeLine] : []),\n `pages: ${ir.pages.length} | custom types: ${plan.customTypes.length} | documents: ${plan.documents.length} | assets: ${resolved}/${ir.assets.length} resolved`,\n `diagnostics: ${diagnostics.length}`,\n ...diagnostics.map((d) => ` - [${d.kind}] ${d.where}: ${d.message}`),\n `wrote ${out}`,\n ];\n return { output: lines.join(\"\\n\"), code: 0 };\n }\n\n if (action === \"migrate\") {\n if (!dir) {\n return {\n output: \"blux migrate needs an emitted output directory (or a plan .json path).\",\n code: 1,\n };\n }\n if (!process.env.PRISMIC_REPOSITORY_NAME || !process.env.PRISMIC_WRITE_TOKEN) {\n return {\n output: \"Set PRISMIC_REPOSITORY_NAME and PRISMIC_WRITE_TOKEN to run a live migration.\",\n code: 1,\n };\n }\n const planPath = dir.endsWith(\".json\") ? dir : join(dir, \"migration-plan.json\");\n const plan = JSON.parse(await readFile(planPath, \"utf-8\")) as MigrationPlan;\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n const pushed = await pushCustomTypes(plan.customTypes);\n // stream progress to stderr — a throttled run over many assets/docs takes\n // minutes and silence reads as a hang; stdout stays the result summary\n const r = await runMigration(plan, (line) => process.stderr.write(`${line}\\n`));\n const missing = r.missingAssets.length\n ? `\\nWARNING missing assets: ${r.missingAssets.join(\", \")}`\n : \"\";\n // Rewrite the render manifest's media urls from the CDN url the export\n // carries → the durable Prismic url we just uploaded to. Skipped silently\n // when no manifest sits beside the plan (e.g. an archetype-only emit).\n let manifestNote = \"\";\n const manifestPath = join(dirname(planPath), \"blux-presentation.json\");\n let manifestRaw: string | null = null;\n try {\n manifestRaw = await readFile(manifestPath, \"utf-8\");\n } catch {\n /* no manifest beside the plan (e.g. archetype-only emit) — nothing to\n rewrite; not an error. A rewrite failure on a manifest that DOES\n exist must surface, not be swallowed (it silently strands the render\n on Blux CDN urls). */\n }\n if (manifestRaw !== null) {\n const manifest = JSON.parse(manifestRaw) as SitePresentation;\n const rewritten = rewriteManifestUrls(manifest, r.assetUrlByCdn);\n await writeFile(manifestPath, JSON.stringify(rewritten, null, 2) + \"\\n\");\n manifestNote = \"\\nmanifest media rewritten to Prismic urls\";\n }\n return {\n output:\n `custom types pushed: ${pushed.join(\", \") || \"none\"}\\n` +\n `assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | ` +\n `documents: ${r.docsCreated} created, ${r.docsUpdated} updated → ` +\n `${process.env.PRISMIC_REPOSITORY_NAME} (publish the migration release in the dashboard)` +\n missing +\n manifestNote,\n code: 0,\n };\n }\n\n if (action === \"migrate-catalog\") {\n if (!dir) return { output: \"blux migrate-catalog needs the catalog --out dir.\", code: 1 };\n let plan: MigrationPlan;\n try {\n plan = JSON.parse(await readFile(join(dir, \"migration-plan.json\"), \"utf-8\")) as MigrationPlan;\n } catch (err) {\n return {\n output: `could not read migration-plan.json in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n // Progress streams to stderr as it happens (a throttled two-phase run over\n // many assets/docs takes minutes and silence reads as a hang) and is\n // collected ONLY for the failure path, where it IS the diagnostic — a\n // success over hundreds of docs must not replay the whole log. Warnings\n // ride warn(): into the success output AND (via log) onto stderr the\n // moment they happen.\n const lines: string[] = [];\n const log = (line: string): void => {\n lines.push(line);\n process.stderr.write(`${line}\\n`);\n };\n const warnings: string[] = [];\n const warn = (line: string): void => {\n warnings.push(line);\n log(line);\n };\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n try {\n const pushed = plan.customTypes.length ? await pushCustomTypes(plan.customTypes) : [];\n // Phase 1: assets only — populates the media library and returns the\n // complete cdn→Prismic url map. runMigration uploads assets first but\n // only returns the map at the end, so a single call can never rewrite\n // documents before posting them; hence the two-phase shape.\n const assetsPass = await runMigration({ ...plan, documents: [] }, log);\n // Rewrite the serialized-string surfaces resolveDocData never touches\n // (BluxBlock payloads, widget_html, embed_html, background wrappers).\n const r = rewriteDocUrls(plan.documents, assetsPass.assetUrlByCdn);\n if (r.unmatched.length) {\n warn(\n `WARNING: ${r.unmatched.length} CDN url(s) survived the rewrite: ${r.unmatched\n .slice(0, 5)\n .join(\", \")}`,\n );\n }\n // Chrome (nav + footer logos) rides site-config.json beside the plan, not\n // in plan.documents — rewrite it with the SAME asset map so it too leaves\n // the CDN. Absent on older/non-catalog plans (ENOENT) — then nothing to do.\n let chromeRewritten = 0;\n const chromeUnmatched: string[] = [];\n const siteConfigPath = join(dir, \"site-config.json\");\n try {\n const parsed: unknown = JSON.parse(await readFile(siteConfigPath, \"utf-8\"));\n const sc = rewriteValueUrls(parsed, assetsPass.assetUrlByCdn);\n await writeFile(siteConfigPath, JSON.stringify(sc.value, null, 2) + \"\\n\");\n chromeRewritten = sc.rewritten;\n chromeUnmatched.push(...sc.unmatched);\n if (sc.unmatched.length) {\n warn(\n `WARNING: ${sc.unmatched.length} chrome CDN url(s) survived the rewrite: ${sc.unmatched\n .slice(0, 5)\n .join(\", \")}`,\n );\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n // Phase 2: documents — every asset re-lists into the by-filename reuse\n // branch (uploaded in phase 1), so nothing re-uploads.\n const docsPass = await runMigration({ ...plan, documents: r.documents }, log);\n if (docsPass.missingAssets.length) {\n warn(`WARNING missing assets: ${docsPass.missingAssets.join(\", \")}`);\n }\n return {\n output: [\n `custom types pushed: ${pushed.join(\", \") || \"none\"}`,\n ...warnings,\n `migrate-catalog: assets ${assetsPass.assetsUploaded} uploaded/${assetsPass.assetsReused} reused; ` +\n `urls rewritten ${r.rewritten} (${r.unmatched.length} unmatched); ` +\n `chrome urls rewritten ${chromeRewritten} (${chromeUnmatched.length} unmatched); ` +\n `docs ${docsPass.docsCreated} created/${docsPass.docsUpdated} updated ` +\n `(publish the migration release in the dashboard)`,\n ].join(\"\\n\"),\n // No warning blocks phase 2 — the docs land in an UNPUBLISHED migration\n // release, so a non-zero exit reaches the operator before anything goes\n // live.\n code: r.unmatched.length || chromeUnmatched.length || docsPass.missingAssets.length ? 1 : 0,\n };\n } catch (err) {\n return {\n output: [...lines, `migrate-catalog failed: ${(err as Error).message}`].join(\"\\n\"),\n code: 1,\n };\n }\n }\n\n if (action === \"validate\") {\n if (!dir) return { output: \"blux validate needs a Blux export directory.\", code: 1 };\n let exportHtml: string;\n try {\n exportHtml = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return { output: `could not read index.html in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n // Resolve the optional --against render FIRST so a bad target hard-fails\n // before we spend the convert pipeline (and so its error message wins).\n let rendered: string | null = null;\n if (opts.against) {\n try {\n if (/^https?:\\/\\//.test(opts.against)) {\n const res = await (opts.fetchImpl ?? fetch)(opts.against);\n if (!res.ok) {\n return {\n output: `could not fetch --against ${opts.against}: HTTP ${res.status}`,\n code: 1,\n };\n }\n rendered = await res.text();\n } else {\n rendered = await readFile(opts.against, \"utf-8\");\n }\n } catch (err) {\n return {\n output: `could not read --against ${opts.against}: ${(err as Error).message}`,\n code: 1,\n };\n }\n }\n\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read site.json in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n const { specs, presentation } = convertExport({ html: exportHtml, siteJson });\n const layout = validateLayout(specs, presentation);\n const lines = [formatLayoutReport(layout)];\n\n // Content coverage is informational only — it names export text the render\n // dropped, but layout fidelity alone gates the exit code below. A coverage\n // gap never flips a faithful layout to a non-zero exit.\n if (rendered !== null) {\n const report = validateCoverage(exportHtml, rendered);\n lines.push(\n \"\",\n `content coverage: ${report.covered}/${report.total} runs (${report.coveragePct}%)`,\n ...(report.missing.length\n ? [\n \"missing runs — export text absent from the render:\",\n ...report.missing.map((m) => ` - ${m}`),\n ]\n : [\"all export text runs present in the render\"]),\n );\n }\n\n return { output: lines.join(\"\\n\"), code: layout.faithful ? 0 : 1 };\n }\n\n if (action === \"grid\") {\n if (!dir) return { output: \"blux grid needs a Blux export directory.\", code: 1 };\n let html: string;\n try {\n html = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return {\n output: `could not read index.html in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const bands = parseGridBands(html);\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"grid-tree.json\"), JSON.stringify(bands, null, 2));\n const mapConfig = extractMapConfig(html);\n if (mapConfig) {\n await writeFile(\n join(outDir, \"map-config.json\"),\n JSON.stringify(mapConfig, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n return {\n output:\n `Parsed ${bands.length} bands → ${join(outDir, \"grid-tree.json\")}` +\n (mapConfig ? \", map config extracted\" : \"\"),\n code: 0,\n };\n }\n\n if (action === \"convert\") {\n if (!dir) return { output: \"blux convert needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read export in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n // Every site page renders to its own index.html: the homepage at the\n // export root, the rest at <path>/index.html. A page dir the export\n // doesn't contain (an unexported draft) is skipped — convertSite records\n // the missing-page-html diagnostic.\n const htmlByUid = new Map<string, string>();\n for (const p of sitePages(siteJson)) {\n const file = p.path ? join(dir, p.path, \"index.html\") : join(dir, \"index.html\");\n try {\n htmlByUid.set(p.uid, await readFile(file, \"utf-8\"));\n } catch {\n /* missing page dir — diagnosed by convertSite */\n }\n }\n if (!htmlByUid.size) {\n return { output: `could not read any page html in ${dir}`, code: 1 };\n }\n const { pages, ir, plan, presentation } = convertSite({ siteJson, htmlByUid });\n\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n await writeFile(\n join(outDir, \"blux-presentation.json\"),\n JSON.stringify(presentation, null, 2) + \"\\n\",\n );\n // theme.css via the single-source-of-truth assembler — this convert output\n // now carries the runtime :root mirror too (was missing it before).\n await writeFile(join(outDir, \"theme.css\"), emitThemeCssFile(ir.theme));\n // Map configs are per page now (informational — the presentation manifest\n // co-locates each map on its band).\n const mapConfigs = Object.fromEntries(\n pages.filter((p) => p.mapConfig).map((p) => [p.uid, p.mapConfig]),\n );\n if (Object.keys(mapConfigs).length) {\n await writeFile(join(outDir, \"map-config.json\"), JSON.stringify(mapConfigs, null, 2) + \"\\n\");\n }\n // Site chrome (nav dropdowns + footer socials/copyright) → site-config.json,\n // consumed by the render's Nav/Footer. The nav logo is chrome, not on any\n // page grid, so it isn't in the scraped urlMap — resolve it the scraped url\n // first, else reconstruct the CDN url (base + uuid + ext, like feed media).\n {\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaDict = (siteJson as { media?: Record<string, { type?: string }> }).media ?? {};\n const extByMime: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n };\n const resolveLogo = (uuid: string): string | null => {\n const scraped = sourceUrlById.get(uuid);\n if (scraped) return scraped;\n const ext = extByMime[String(mediaDict[uuid]?.type ?? \"\")];\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n // Footer social profile urls aren't in the export (Blux injects them at\n // render time), but they ride the scraped live footer — recover them by\n // host from the same page html the grid was built from.\n const resolveSocialHref = socialHrefResolverFromHtml([...htmlByUid.values()]);\n const siteConfig = buildSiteConfig(siteJson, resolveLogo, resolveSocialHref);\n await writeFile(join(outDir, \"site-config.json\"), JSON.stringify(siteConfig, null, 2) + \"\\n\");\n }\n // Product catalog → products.json. A Blux \"products\" feed drives a detail\n // page per record (/products/<slug>) from a template the static export\n // drops; rebuild the catalog deterministically with cleaned categories +\n // resolved images. Image urls reconstruct like feed media (base + uuid +\n // ext) — the scraped url map only covers page assets, not feed records.\n {\n const feeds =\n (siteJson as { feeds?: Record<string, { publish?: unknown; items?: unknown }> }).feeds ??\n {};\n const productFeed = Object.values(feeds).find((f) => f?.publish === \"products\");\n if (productFeed && Array.isArray(productFeed.items)) {\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaMeta =\n (siteJson as { media?: Record<string, { type?: string; name?: string }> }).media ?? {};\n const resolveImage = (uuid: string): string | null => {\n const ext = extFor(mediaMeta[uuid]?.type, mediaMeta[uuid]?.name);\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n const products = materializeProducts(productFeed.items as ProductRecord[], resolveImage);\n await writeFile(join(outDir, \"products.json\"), JSON.stringify(products, null, 2) + \"\\n\");\n }\n }\n // The favicon never rides the migration plan (plan assets get uploaded to\n // Prismic media — the wrong destination), so convert downloads it directly\n // beside the other outputs. This is convert's ONLY network touch and it\n // uses the same injectable fetch seam as --probe, so tests stay offline\n // and convertExport itself stays pure. A fetch failure never fails the\n // command — the {assetId, url} pair is preserved as favicon.json so the\n // download can be re-run by hand.\n let faviconLine = \"\";\n if (ir.meta.favicon?.sourceUrl) {\n const { assetId, sourceUrl } = ir.meta.favicon;\n try {\n const res = await (opts.fetchImpl ?? fetch)(sourceUrl);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n await writeFile(join(outDir, \"favicon.png\"), new Uint8Array(await res.arrayBuffer()));\n faviconLine = `\\nfavicon → ${join(outDir, \"favicon.png\")}`;\n } catch (err) {\n await writeFile(\n join(outDir, \"favicon.json\"),\n JSON.stringify({ assetId, url: sourceUrl }, null, 2) + \"\\n\",\n );\n faviconLine =\n `\\nfavicon fetch failed (${(err as Error).message}) — ` +\n `url preserved in ${join(outDir, \"favicon.json\")}`;\n }\n }\n const layoutByUid: Record<string, ReturnType<typeof validateLayout>> = {};\n const reportLines: string[] = [];\n for (const p of pages) {\n const pagePresentation = presentation.pages[p.uid];\n if (!pagePresentation) continue;\n const layout = validateLayout(p.specs, pagePresentation);\n layoutByUid[p.uid] = layout;\n reportLines.push(`[${p.uid}] ${formatLayoutReport(layout)}`);\n }\n await writeFile(\n join(outDir, \"layout-report.json\"),\n JSON.stringify({ pages: layoutByUid }, null, 2) + \"\\n\",\n );\n const totalBands = pages.reduce((n, p) => n + p.bands.length, 0);\n const missing = ir.diagnostics.filter((d) => d.kind === \"missing-page-html\");\n return {\n output:\n `Converted ${pages.length} pages / ${totalBands} bands → ${outDir} ` +\n `(${plan.documents.length} page documents` +\n (Object.keys(mapConfigs).length\n ? `, map config on ${Object.keys(mapConfigs).join(\", \")}`\n : \"\") +\n \")\" +\n (missing.length ? `\\nskipped (no html): ${missing.map((d) => d.where).join(\", \")}` : \"\") +\n faviconLine +\n \"\\n\" +\n reportLines.join(\"\\n\"),\n code: 0,\n };\n }\n\n if (action === \"catalog\") {\n if (!dir) return { output: \"blux catalog needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read export in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n // Read every site page's index.html (homepage at the export root, the rest\n // at <path>/index.html) exactly like convert, then route every band through\n // the breadth classifier (classifyBand → rich CatalogSpec) and emit a\n // plan-only, sidecar-free migration plan (full field data in the page doc).\n // Feed bands intercept FIRST (spec §7 rule 1): the positional convert-path\n // join `content.pages[p].items[band.index]` names the band's site.json\n // item; one with `sources[]` becomes a blux_collection query-spec slice.\n const feeds =\n (\n siteJson as {\n feeds?: Record<\n string,\n { name?: string; items?: unknown[]; fields?: unknown } | undefined\n >;\n }\n ).feeds ?? {};\n // Per-page content also carries the SEO fields (title lives on sitePages;\n // `description` + the social/og image `media` ride here) — same positional\n // `content.pages[pageIndex]` join the items use, so they thread through\n // without touching the parse/normalize layer.\n const pageContentByIndex = (\n siteJson as {\n content?: {\n pages?: { items?: unknown[]; description?: unknown; media?: { media?: unknown } }[];\n };\n }\n )?.content?.pages;\n // Classify-time diagnostics (positional-join misalignments, unknown/\n // skipped feed sources) ride the plan alongside emit-time ones.\n const classifyDiagnostics: Diagnostic[] = [];\n const pages: {\n uid: string;\n title: string;\n specs: CatalogSpec[];\n description?: string;\n socialImageId?: string;\n }[] = [];\n // Every successfully-read page html feeds the IR assembly below: the asset\n // index is scraped from the rendered pages (site.json's media dict has no\n // urls), so a media without a CDN data-base can still resolve.\n const htmls: string[] = [];\n // Band-visual capture (Task 9): the `.blocksNcontainer` class defaults are\n // site-wide (compute once); per-band inline styles are per page.\n const blockDefaults = blockClassDefaults(siteJson);\n for (const [pageIndex, p] of sitePages(siteJson).entries()) {\n const file = p.path ? join(dir, p.path, \"index.html\") : join(dir, \"index.html\");\n let html: string;\n try {\n html = await readFile(file, \"utf-8\");\n } catch {\n continue; // missing page dir (unexported draft) — skip\n }\n htmls.push(html);\n // Decision B (plan 4b): when the page html carries the Blux map script,\n // inject the mount predicate so the map band routes to a BluxSection\n // widget (config inlined at emit); pages without one classify as before.\n const mapConfig = extractMapConfig(html);\n const blockStyles = blockStylesByIndex(siteJson, pageIndex);\n const catalogOpts = mapConfig\n ? {\n isMapMount: makeIsMapMount(mapConfig),\n mapConfig,\n diagnostics: classifyDiagnostics,\n pageUid: p.uid,\n styles: blockStyles,\n defaults: blockDefaults,\n }\n : {\n diagnostics: classifyDiagnostics,\n pageUid: p.uid,\n styles: blockStyles,\n defaults: blockDefaults,\n };\n const pageContent = pageContentByIndex?.[pageIndex];\n const pageItems = pageContent?.items;\n const bands = parseGridBands(html);\n const specs: CatalogSpec[] = bands.map((b) =>\n bandOrCollection(b, pageItems?.[b.index], feeds, catalogOpts),\n );\n // The positional join runs band→item; a feed-bearing item whose index\n // has NO band (williamsonHomes homepage: items[1] sources Projects but\n // parseGridBands yields 0,2-8) is never consumed by the loop above —\n // diagnose it, never silent. Diagnostic-only: with no band there is no\n // legitimate place to emit the collection.\n const bandIdx = new Set(bands.map((b) => b.index));\n for (const [i, it] of (pageItems ?? []).entries()) {\n if (!isFeedBand(it) || bandIdx.has(i)) continue;\n const firstSource = String(it.sources[0]);\n const feedName = String(feeds[firstSource]?.name ?? firstSource);\n classifyDiagnostics.push({\n kind: \"feed-band-misalign\",\n where: `${p.uid}:${i}`,\n message: `feed item at index ${i} (\"${feedName}\") has no matching band — collection not emitted`,\n });\n }\n // Per-page SEO (Blux `content.pages[i]`): meta description + the social/\n // og image asset uuid (a nested `media.media` string, like feed record\n // media). Both optional — buildCatalogPlan omits the meta field when absent\n // (\"\" description → no meta_description).\n const description =\n typeof pageContent?.description === \"string\" ? pageContent.description : undefined;\n const socialMedia = pageContent?.media?.media;\n const socialImageId =\n typeof socialMedia === \"string\" && socialMedia ? socialMedia : undefined;\n pages.push({\n uid: p.uid,\n title: p.title,\n specs,\n ...(description ? { description } : {}),\n ...(socialImageId ? { socialImageId } : {}),\n });\n }\n if (!pages.length) {\n return { output: `could not read any page html in ${dir}`, code: 1 };\n }\n // The real IR asset index (Plan 4d Task 1): assembleIR scrapes the CDN\n // urls out of the page htmls, so a media without a CDN data-base falls\n // back to its AssetRef.sourceUrl instead of an unresolved-asset\n // diagnostic. IR diagnostics (unresolved assets, page normalization)\n // merge with the classify-time set — both ride the written plan. The\n // `url` field is unused by buildCatalogPlan (it derives urls via\n // mediaUrl) but the CatalogAssetIndex type requires it.\n const ir = assembleIR({ siteJson, htmls });\n const plan = buildCatalogPlan(\n pages,\n {\n assets: ir.assets.map((a) => ({\n id: a.id,\n url: a.sourceUrl ?? \"\",\n alt: a.alt,\n sourceUrl: a.sourceUrl,\n })),\n diagnostics: [...classifyDiagnostics, ...ir.diagnostics],\n },\n feeds,\n );\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n // Site chrome (Task 4): nav via the frozen buildSiteConfig extraction,\n // footer with FULL columns (the-pointe's leasing contacts with tel/mailto\n // links — site-config's socials+text reduction is too thin). Chrome media\n // (nav/footer logos) resolve the plan's own asset url first — a media the\n // page grid also uses keeps its parser-captured base (e.g. a shared\n // parent-site siteID), so tier-1-first avoids a wrong-siteID tier-3\n // reconstruction. Then the IR's scraped sourceUrl, else reconstruct the\n // CDN url (base + uuid + ext) — static exports download chrome images\n // locally, so the html scrape routinely misses them (the convert action's\n // resolveLogo fallback).\n //\n // The Blux CDN is being retired, so a chrome-ONLY logo (one the page grid\n // never uses, hence absent from plan.assets) is registered here as its own\n // plan asset: it uploads in migrate-catalog's asset phase, and that action's\n // site-config.json rewrite swaps its CDN url to the Prismic one. A logo the\n // grid also uses is already a plan asset (the tier-1 branch), so we never\n // double-register it. Chrome is built BEFORE the plan is written so these\n // assets ride the emitted migration-plan.json.\n //\n // We register a scraped (tier-2) cloudfront sourceUrl — a real url from the\n // export html — AND a tier-3 RECONSTRUCTION (`${chromeBase}${uuid}.${ext}`)\n // WHEN chromeBase is CORROBORATED by a real IMAGE served from it. the-pointe's\n // own nav/footer logos are exactly this case: referenced by id, unscraped,\n // served from the site's base alongside 45 feed images. Two things make a\n // corroborated reconstruction about as reliable as one of those feed images:\n // (1) the base is proven to resolve (an image already loads from it), and\n // (2) the ext is the logo's LITERAL filename ext — the byte Blux serves, the\n // same way a feed image uses its literal data-ext (NOT a mime-normalized\n // guess, which would turn a `.jpeg` object into a 404-ing `.jpg`). The\n // residual risk is only a referenced-but-deleted own asset — a loud, accepted\n // 404 (runMigration throws), the same failure any feed image would hit if\n // deleted. Corroboration requires an IMAGE (not just any plan.asset): a\n // backstop file asset (a PDF, often on a different host) is not evidence a\n // logo resolves, and `siteID` is provenance not serving base — so a served\n // image is the only sound signal. An UNcorroborated reconstruction is a pure\n // guess: left on the CDN, flagged by migrate-catalog's site-config rewrite as\n // an unmatched url (WARNING + non-zero exit) for manual follow-up — never\n // silent, never a mid-migrate abort.\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const planUrlById = new Map(plan.assets.map((a) => [a.id, a.url] as const));\n const chromeBase = feedAssetBase(htmls, ir.meta.bluxSiteId);\n const isImageUrl = (u: string) => /\\.(png|jpe?g|gif|webp|svg|avif|ico)$/i.test(u);\n const chromeBaseCorroborated = plan.assets.some(\n (a) => a.url.startsWith(chromeBase) && isImageUrl(a.url),\n );\n const chromeOnlyAssets = new Map<string, { id: string; url: string; alt: string }>();\n const chrome = buildChrome(siteJson, (uuid) => {\n const fromPlan = planUrlById.get(uuid);\n if (fromPlan !== undefined) return fromPlan;\n const a = assetById.get(uuid);\n if (!a) return null;\n if (a.sourceUrl) {\n // tier-2: a real scraped url. Evict only a Blux CDN one — an external\n // (non-cloudfront) logo url needs no migration.\n if (a.sourceUrl.includes(\"cloudfront.net\") && !chromeOnlyAssets.has(uuid))\n chromeOnlyAssets.set(uuid, { id: uuid, url: a.sourceUrl, alt: a.alt ?? \"\" });\n return a.sourceUrl;\n }\n // tier-3: reconstruct against the site's own base with the LITERAL filename\n // ext (feed-image-equivalent — see note above), mime-derived only as a\n // fallback. Register it only when that base is corroborated by an image.\n const ext = a.name.match(/\\.([a-z0-9]{2,4})$/i)?.[1]?.toLowerCase() ?? extFor(a.mime, a.name);\n if (!ext) return null;\n const url = `${chromeBase}${uuid}.${ext}`;\n if (chromeBaseCorroborated && !chromeOnlyAssets.has(uuid))\n chromeOnlyAssets.set(uuid, { id: uuid, url, alt: a.alt ?? \"\" });\n return url;\n });\n // These chrome logos join the plan so migrate-catalog uploads them and its\n // site-config.json rewrite can swap their urls off the retiring CDN.\n plan.assets.push(...chromeOnlyAssets.values());\n await writeFile(join(outDir, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n // Task 5: an OFFLINE render fixture for the starter's fidelity-gate route\n // (Task 8). resolveFixture resolves the plan's markers into the Prismic-\n // HYDRATED shapes the production SliceZone consumes (richtext → node arrays,\n // asset → {url,alt,dimensions}) — the live migrate path resolves the same\n // markers into Migration API shapes instead. It runs on the plan BEFORE any\n // migrate url-rewrite, so image urls are still the Blux CDN (correct for the\n // offline gate, which allowlists CDN 404s).\n await writeFile(\n join(outDir, \"render-fixture.json\"),\n JSON.stringify(resolveFixture(plan), null, 2),\n );\n await writeFile(join(outDir, \"site-config.json\"), JSON.stringify(chrome, null, 2) + \"\\n\");\n // theme.css: the single-source-of-truth theme file (build @theme + runtime\n // :root mirror + roles + buttons) — identical across emit/catalog/convert.\n await writeFile(join(outDir, \"theme.css\"), emitThemeCssFile(ir.theme));\n const totalBands = pages.reduce((n, p) => n + p.specs.length, 0);\n return {\n output:\n `Cataloged ${pages.length} pages / ${totalBands} bands → ` +\n `${join(outDir, \"migration-plan.json\")} (${plan.documents.length} page documents)`,\n code: 0,\n };\n }\n\n if (action === \"freeze\") {\n if (!dir) return { output: \"blux freeze needs a Blux export directory.\", code: 1 };\n const { freezeSite, emitFrozen } = await import(\"../../blux/freeze/index.js\");\n const { emitFavicon } = await import(\"../../blux/freeze/favicon.js\");\n const site = (opts.site ?? basename(dir)).replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n const outDir = opts.out ?? join(dir, \"frozen-out\");\n // A malformed declaration must stop the freeze, not ship: these become live\n // CMS fields. Both the read and the parse are deliberately un-caught.\n let extraSlots: unknown;\n if (opts.extraSlots) {\n extraSlots = JSON.parse(await readFile(opts.extraSlots, \"utf-8\"));\n }\n const result = await freezeSite({\n indexHtmlPath: join(dir, \"index.html\"),\n site,\n ...(extraSlots === undefined ? {} : { extraSlots }),\n });\n const paths = await emitFrozen(outDir, result);\n // Fail-soft: a freeze without a favicon is still a good freeze (the icon\n // can be re-fetched while the CDN lives; see favicon.ts).\n let favicon: string;\n try {\n const exportHtml = await readFile(join(dir, \"index.html\"), \"utf-8\");\n const fav = await emitFavicon(exportHtml, dir, outDir);\n favicon = fav ? `, favicon.png` : \"\";\n } catch (err) {\n favicon = ` (favicon FAILED: ${(err as Error).message})`;\n }\n const imgs = result.manifest.slots.filter((s) => s.kind === \"image\").length;\n const txts = result.manifest.slots.filter((s) => s.kind === \"text\").length;\n const map = result.mapConfig ? `, map (${result.mapConfig.layers.length} layers)` : \"\";\n return {\n output:\n `froze ${site}: ${imgs} image slots, ${txts} text slots, ` +\n `${result.manifest.fontLinks.length} font links${map} → ` +\n `${paths.template}, ${paths.manifest}${favicon}`,\n code: 0,\n };\n }\n\n if (action === \"migrate-frozen\") {\n if (!dir) return { output: \"blux migrate-frozen needs the frozen --out dir.\", code: 1 };\n // The freeze artifact `<site>.slots.json` lives in the --out dir (opts.out ??\n // dir; see the `freeze` action). --site pins it; otherwise the sole\n // *.slots.json in the dir (freeze emits exactly one per site).\n const outDir = opts.out ?? dir;\n let manifestPath: string;\n if (opts.site) {\n const site = opts.site.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n manifestPath = join(outDir, `${site}.slots.json`);\n } else {\n const found = (await glob([\"*.slots.json\"], { cwd: outDir, absolute: true })).sort();\n if (!found.length)\n return { output: `no *.slots.json in ${outDir} — run 'blux freeze' first.`, code: 1 };\n manifestPath = found[0]!;\n }\n let manifest: FrozenManifest;\n try {\n manifest = JSON.parse(await readFile(manifestPath, \"utf-8\")) as FrozenManifest;\n } catch (err) {\n return { output: `could not read ${manifestPath}: ${(err as Error).message}`, code: 1 };\n }\n\n // Build the migration plan from the manifest. Image slots (+ the optional\n // meta image) become plan ASSETS keyed by a stable synthetic id; the\n // frozen_page doc references each via an `assetRef` marker — the SAME\n // Image-field representation catalog docs use (resolveDocData turns\n // `{__asset_id}` into `{id}`, the depth-1-safe Migration API shape; the\n // slots group is a top-level repeatable group, so this resolves cleanly).\n // Text slots become single-paragraph Rich Text markers. The freeze stores a\n // text node's RAW source (entities already encoded, e.g. \"&amp;\"/\"&nbsp;\",\n // never a bare \"<\"), so it is a valid HTML fragment — pass it straight into\n // the <p> for htmlAsRichText. Re-escaping would double-encode (\"&amp;\" →\n // \"&amp;amp;\" → literal \"&amp;\" on the page). Assets dedupe by url so an\n // image reused across slots uploads once.\n const assetIdByUrl = new Map<string, string>();\n const assets: PlanAsset[] = [];\n const assetFor = (url: string): string => {\n let id = assetIdByUrl.get(url);\n if (!id) {\n id = `frozen-asset-${assets.length}`;\n assetIdByUrl.set(url, id);\n assets.push({ id, url, alt: \"\" });\n }\n return id;\n };\n // An Image field only accepts image assets; non-image media (video/pdf) must\n // ride a Text `media_url` instead. Register + upload every media asset the\n // same way, but flag the non-image ones to patch after phase 1 (once their\n // Prismic url is known).\n const isImageUrl = (u: string): boolean => /\\.(jpe?g|png|gif|webp|svg|avif)(\\?|$)/i.test(u);\n const mediaSlots = new Map<string, string>(); // non-image slot key -> cdn url\n const slotRows: Record<string, unknown>[] = manifest.slots.map((s) => {\n if (s.kind === \"image\" && s.url) {\n if (!isImageUrl(s.url)) mediaSlots.set(s.key, s.url);\n return { key: s.key, kind: s.kind, image: assetRef(assetFor(s.url)) };\n }\n return { key: s.key, kind: s.kind, text: richText(`<p>${s.text ?? \"\"}</p>`) };\n });\n const data: Record<string, unknown> = {\n title: manifest.title,\n meta_title: manifest.metaTitle ?? \"\",\n meta_description: \"\",\n slots: slotRows,\n };\n if (manifest.metaImageUrl) data.meta_image = assetRef(assetFor(manifest.metaImageUrl));\n const doc: PlanDocument = { type: FROZEN_PAGE_TYPE, uid: manifest.uid, data };\n const plan: MigrationPlan = {\n customTypes: [frozenPageCustomType()],\n documents: [doc],\n assets,\n stylesManifest: [],\n diagnostics: [],\n };\n const imageSlots = manifest.slots.filter((s) => s.kind === \"image\" && s.url).length;\n\n // Progress streams to stderr (a throttled two-phase run reads as a hang\n // otherwise); collected only for the failure path where it IS the diagnostic.\n const lines: string[] = [];\n const log = (line: string): void => {\n lines.push(line);\n process.stderr.write(`${line}\\n`);\n };\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n try {\n const pushed = await pushCustomTypes(plan.customTypes);\n // Phase 1: assets only — populates the media library and returns the\n // cdn→Prismic url map (mirrors migrate-catalog's two-phase shape).\n const assetsPass = await runMigration({ ...plan, documents: [] }, log);\n // Every image slot's CDN url must resolve to a Prismic asset — surface any\n // that didn't rather than silently shipping a doc with a dead reference.\n const unresolved = assets.filter((a) => !assetsPass.assetUrlByCdn.has(a.url));\n if (unresolved.length) {\n return {\n output:\n `frozen migrate aborted: ${unresolved.length} image url(s) did not resolve to a ` +\n `Prismic asset (first: ${unresolved[0]!.url}).`,\n code: 1,\n };\n }\n // Non-image media (video/pdf) can't sit in an Image field — now that\n // phase 1 has resolved their Prismic urls, swap those slots off the image\n // assetRef and onto a `media_url` text field before posting the doc.\n for (const row of slotRows) {\n const cdn = mediaSlots.get(row.key as string);\n if (cdn) {\n delete row.image;\n row.media_url = assetsPass.assetUrlByCdn.get(cdn) ?? \"\";\n }\n }\n // Phase 2: the doc. Assets re-list into the reuse branch (uploaded in phase\n // 1), and resolveDocData rewrites each `assetRef` marker to `{id}` — the\n // uploaded Prismic asset, never the CDN url. Lands in an UNPUBLISHED\n // migration release (the runner never publishes).\n const docsPass = await runMigration(plan, log);\n if (docsPass.missingAssets.length) {\n return {\n output:\n `frozen migrate aborted: ${docsPass.missingAssets.length} asset ref(s) went ` +\n `unresolved in the doc (first: ${docsPass.missingAssets[0]}).`,\n code: 1,\n };\n }\n const docWord = docsPass.docsUpdated ? \"updated\" : \"created\";\n return {\n output:\n `frozen_page pushed: ${pushed.join(\", \") || \"none\"}; ` +\n `assets ${assetsPass.assetsUploaded} uploaded/${assetsPass.assetsReused} reused; ` +\n `${imageSlots} image slot(s) rewritten to Prismic; ` +\n `doc ${manifest.uid} ${docWord} → ${process.env.PRISMIC_REPOSITORY_NAME} ` +\n `(publish the migration release in the dashboard)`,\n code: 0,\n };\n } catch (err) {\n return {\n output: `frozen migrate failed: ${(err as Error).message}\\n${lines.join(\"\\n\")}`,\n code: 1,\n };\n }\n }\n\n return {\n output: `unknown blux action '${action}'. Use: emit, migrate, migrate-catalog, migrate-frozen, validate, grid, convert, catalog, freeze.`,\n code: 1,\n };\n}\n","/** Style config for a block's title/body. `class: \"disable\"` (or the bare\n * string \"disable\") hides that element on the rendered site. */\nexport type BluxTextStyle = string | { class?: string; [key: string]: unknown };\n\nexport type BluxBlock = {\n title?: string;\n _title?: BluxTextStyle;\n body?: string;\n _body?: BluxTextStyle;\n media?: { media?: string };\n backgroundMedia?: { media?: string };\n class?: string;\n ratio?: string;\n loadEffect?: string;\n items?: BluxBlock[];\n styles?: Record<string, unknown>;\n};\n\nconst hasDisable = (cls: unknown) =>\n typeof cls === \"string\" && cls.split(/\\s+/).includes(\"disable\");\n\n/** Display text of a Blux title/body pair, or undefined when the element is\n * hidden. Blux stores the text itself in `title`/`body`; the underscore twin\n * (`_title`/`_body`) is style config whose `class: \"disable\"` hides the\n * element, so its text must not be migrated. */\nexport function visibleText(text: unknown, style: BluxTextStyle | undefined): string | undefined {\n const s = typeof text === \"number\" && Number.isFinite(text) ? String(text) : text;\n if (typeof s !== \"string\" || s.trim() === \"\") return undefined;\n if (hasDisable(style)) return undefined;\n if (typeof style === \"object\" && style !== null && hasDisable(style.class)) return undefined;\n return s.trim();\n}\nexport type BluxPage = {\n title?: string;\n description?: string;\n /** The page's routing slug. Often empty/absent — the slug then derives from\n * the title. The FIRST page is the homepage regardless (Blux is positional). */\n url?: string;\n items?: BluxBlock[];\n};\nexport type BluxFeed = {\n name?: string;\n source?: string;\n publish?: string;\n fields?: { title?: string; field?: string; type?: string }[];\n items?: Record<string, unknown>[];\n};\nexport type BluxMedia = { name?: string; type?: string; size?: unknown; siteID?: string };\nexport type BluxRaw = {\n meta: { name: string; domain: string; bluxSiteId: string };\n pages: BluxPage[];\n feeds: Record<string, BluxFeed>;\n media: Record<string, BluxMedia>;\n styles: {\n colors?: Record<string, string>;\n text?: Record<string, unknown>;\n buttons?: Record<string, unknown>;\n };\n nav: { title?: string; url?: string }[];\n settings: {\n fonts?: { heading?: string; body?: string };\n /** The site favicon, declared as a bare media uuid ({\"media\": \"<uuid>\"}). */\n favicon?: { media?: string };\n };\n};\n\nfunction asObject(v: unknown): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) {\n throw new Error(\"Invalid site.json: expected an object\");\n }\n return v as Record<string, unknown>;\n}\n\nexport function parseBluxSite(input: unknown): BluxRaw {\n const j = asObject(input);\n const content = (j.content ?? {}) as { pages?: BluxPage[] };\n const styles = (j.styles ?? {}) as BluxRaw[\"styles\"];\n const nav = ((j.navigation as { items?: unknown }[] | undefined)?.[0]?.items ?? []) as {\n title?: string;\n url?: string;\n }[];\n return {\n meta: {\n name: String(j.name ?? \"\"),\n domain: String(j.domain ?? \"\"),\n bluxSiteId: String(j.id ?? \"\"),\n },\n pages: Array.isArray(content.pages) ? content.pages : [],\n feeds: (j.feeds ?? {}) as Record<string, BluxFeed>,\n media: (j.media ?? {}) as Record<string, BluxMedia>,\n styles,\n nav,\n settings: (j.settings ?? {}) as BluxRaw[\"settings\"],\n };\n}\n","import { visibleText, type BluxBlock } from \"./parse.js\";\n\nconst nonEmpty = (v: unknown): boolean =>\n v != null &&\n v !== \"\" &&\n !(Array.isArray(v) && v.length === 0) &&\n !(typeof v === \"object\" && !Array.isArray(v) && Object.keys(v as object).length === 0);\n\nexport type ArchetypeResult = {\n sliceType: \"hero\" | \"media_text\" | \"rich_text\" | \"grid\" | \"slider\" | \"collection_list\";\n variation: string;\n confidence: number;\n};\n\nexport function archetype(b: BluxBlock): ArchetypeResult {\n const heading = visibleText(b.title, b._title) !== undefined;\n const text = visibleText(b.body, b._body) !== undefined;\n const media = nonEmpty(b.media?.media);\n const bg = nonEmpty(b.backgroundMedia?.media);\n const kids = Array.isArray(b.items) && b.items.length > 0;\n const cls = nonEmpty(b.class) ? String(b.class) : null;\n\n // Slides keep their grouping even under a background — exploding a\n // carousel into siblings loses more than dropping its backdrop does.\n if (kids && cls === \"slides\")\n return { sliceType: \"slider\", variation: \"default\", confidence: 0.85 };\n // A background image/video makes a hero even with no visible copy — Blux\n // uses text-less full-bleed banners (e.g. a hero video with a disabled label).\n if (bg)\n return { sliceType: \"hero\", variation: \"default\", confidence: heading || text ? 0.9 : 0.7 };\n if (kids)\n return { sliceType: \"grid\", variation: \"default\", confidence: cls === \"grid\" ? 0.9 : 0.7 };\n // Any visible copy next to media is a media_text; media alone still is,\n // just with less certainty.\n if (media && heading && text)\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.9 };\n if (media && (heading || text))\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.75 };\n if (media) return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.6 };\n if (heading && text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.85 };\n if (heading || text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.6 };\n return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.2 };\n}\n","import { visibleText, type BluxBlock, type BluxRaw, type BluxTextStyle } from \"./parse.js\";\nimport { archetype } from \"./archetype.js\";\nimport type {\n ButtonStyleIR,\n FontLoad,\n PageIR,\n SectionIR,\n TextStyleIR,\n ThemeIR,\n Diagnostic,\n} from \"./ir.js\";\n\n/** The styles.text role (\"text5\") a block's _title/_body class points at. */\nfunction textRole(style: BluxTextStyle | undefined): string | undefined {\n const cls = typeof style === \"object\" && style !== null ? style.class : style;\n if (typeof cls !== \"string\") return undefined;\n return cls.split(/\\s+/).find((c) => /^text\\d+$/.test(c));\n}\n\nconst str = (x: unknown): string =>\n typeof x === \"string\" ? x : typeof x === \"number\" ? String(x) : \"\";\n\n/** A cleaned CSS value, or \"\" when the export left a malformed one. Blux emits\n * degenerate placeholders — \"\" and \"px\" for unset lengths, \"0.px\" for a zeroed\n * one — which would poison a Tailwind custom property (an invalid `var()` value\n * collapses the whole declaration). Those are always single tokens, so the\n * numeric-prefix guard runs only on a lone length; multi-value shorthands\n * (\"10px 40px 10px 40px\"), colors, \"0\", and keywords all pass through. */\nexport function cleanCssValue(x: unknown): string {\n const s = str(x).trim();\n if (s === \"\" || s === \"px\") return \"\";\n if (!/\\s/.test(s) && /px$/.test(s) && !/^-?(\\d+(\\.\\d+)?|\\.\\d+)px$/.test(s)) return \"\";\n return s;\n}\n\n/** Per-element inline style overrides on a _title/_body element (color,\n * font-size, margin, …) minus the `class` token, cleaned. undefined when none\n * survive — e.g. a hero title's `{ class: \"text0\", color: \"#fff\" }` white\n * override that a role reference alone would lose. */\nfunction inlineStyle(style: BluxTextStyle | undefined): Record<string, string> | undefined {\n if (typeof style !== \"object\" || style === null) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(style)) {\n if (k === \"class\") continue;\n const cleaned = cleanCssValue(v);\n if (cleaned) out[k] = cleaned;\n }\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Parse `settings.fonts.google` (\"Scope+One:regular|Montserrat:300,500,regular\")\n * into families + numeric weights (\"regular\" → \"400\"). */\nfunction parseGoogleFonts(google: string): FontLoad[] {\n if (!google.trim()) return [];\n return google\n .split(\"|\")\n .map((spec) => {\n const [rawFamily = \"\", rawWeights = \"\"] = spec.split(\":\");\n const family = rawFamily.replace(/\\+/g, \" \").trim();\n const weights = rawWeights\n .split(\",\")\n .map((w) => w.trim())\n .filter(Boolean)\n .map((w) => (w === \"regular\" ? \"400\" : w));\n return { family, weights: weights.length ? weights : [\"400\"] };\n })\n .filter((f) => f.family);\n}\n\n/** The real font-family for a text style. A Typekit `font-ident`\n * (`T:Family:variant:obfuscated`) carries the true family in segment 2 while\n * `font-family` holds the obfuscated id (e.g. `ysxc`); Google idents (`G:…`) and\n * missing idents already have the true family in `font-family`. */\nfunction fontFamilyFromStyle(m: Record<string, unknown>): string {\n const ident = str(m[\"font-ident\"]);\n if (ident.startsWith(\"T:\")) {\n const fam = ident.split(\":\")[1]?.trim();\n if (fam) return fam;\n }\n return str(m[\"font-family\"]).replace(/['\"]/g, \"\");\n}\n\n/** Typekit fonts to preload, parsed from the comma-separated `settings.fonts.string`\n * idents (`T:Montserrat:n6:ysxc`). `fonts.google` omits Typekit faces, so the page's\n * Montserrat 600 would otherwise never be requested. Variant `nN`/`iN` → weight N×100. */\nfunction typekitFontLoads(fontString: string): FontLoad[] {\n const byFamily = new Map<string, string[]>();\n for (const ident of fontString.split(\",\").map((s) => s.trim())) {\n if (!ident.startsWith(\"T:\")) continue;\n const [, family = \"\", variant = \"\"] = ident.split(\":\");\n const fam = family.trim();\n if (!fam) continue;\n const v = variant.trim();\n const digit = /^[ni](\\d)$/.exec(v);\n const weight = digit ? `${Number(digit[1]) * 100}` : v === \"regular\" ? \"400\" : v;\n if (!weight) continue;\n const ws = byFamily.get(fam) ?? [];\n if (!ws.includes(weight)) ws.push(weight);\n byFamily.set(fam, ws);\n }\n return [...byFamily].map(([family, weights]) => ({ family, weights }));\n}\n\n/** Union `extra` font-loads into `base`, preserving `base` order and folding new\n * weights into an existing family (so Montserrat gains 600 instead of duplicating). */\nfunction mergeFontLoads(base: FontLoad[], extra: FontLoad[]): FontLoad[] {\n const out = base.map((f) => ({ family: f.family, weights: [...f.weights] }));\n for (const e of extra) {\n const existing = out.find((f) => f.family === e.family);\n if (existing) {\n for (const w of e.weights) if (!existing.weights.includes(w)) existing.weights.push(w);\n } else {\n out.push({ family: e.family, weights: [...e.weights] });\n }\n }\n return out;\n}\n\nconst CONFIDENCE_MIN = 0.5;\n\nfunction sectionFromBlock(b: BluxBlock, pageUid: string, diagnostics: Diagnostic[]): SectionIR {\n const a = archetype(b);\n if (a.confidence < CONFIDENCE_MIN) {\n diagnostics.push({\n kind: \"low-confidence-block\",\n where: pageUid,\n message: `block mapped to ${a.sliceType} at ${a.confidence}`,\n });\n }\n const heading = visibleText(b.title, b._title);\n const body = visibleText(b.body, b._body);\n const headingRole = heading !== undefined ? textRole(b._title) : undefined;\n const bodyRole = body !== undefined ? textRole(b._body) : undefined;\n const headingStyle = heading !== undefined ? inlineStyle(b._title) : undefined;\n const bodyStyle = body !== undefined ? inlineStyle(b._body) : undefined;\n // Route every value through cleanCssValue (as inlineStyle does) so numeric\n // block styles (a JSON `\"z-index\": 10`) are kept, not silently dropped.\n const block: Record<string, string> = {};\n for (const [k, v] of Object.entries(b.styles ?? {})) {\n const cleaned = cleanCssValue(v);\n if (cleaned) block[k] = cleaned;\n }\n const presentation = {\n ...(headingRole ? { headingRole } : {}),\n ...(bodyRole ? { bodyRole } : {}),\n ...(headingStyle ? { headingStyle } : {}),\n ...(bodyStyle ? { bodyStyle } : {}),\n ...(Object.keys(block).length ? { block } : {}),\n };\n const section: SectionIR = {\n sliceType: a.sliceType,\n variation: a.variation,\n confidence: a.confidence,\n fields: {\n ...(heading !== undefined ? { heading } : {}),\n ...(body !== undefined ? { body } : {}),\n ...(b.media?.media ? { media: b.media.media } : {}),\n ...(b.backgroundMedia?.media ? { backgroundMedia: b.backgroundMedia.media } : {}),\n ...(b.ratio ? { ratio: String(b.ratio) } : {}),\n ...(b.loadEffect ? { anim: String(b.loadEffect) } : {}),\n },\n ...(Object.keys(presentation).length ? { presentation } : {}),\n };\n if (Array.isArray(b.items) && b.items.length > 0) {\n section.children = b.items.map((child) => sectionFromBlock(child, pageUid, diagnostics));\n }\n return section;\n}\n\nfunction slugify(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\") || \"page\"\n );\n}\n\nexport function normalizePages(raw: BluxRaw): { pages: PageIR[]; diagnostics: Diagnostic[] } {\n const diagnostics: Diagnostic[] = [];\n const seen = new Set<string>();\n const pages = raw.pages.map((p, i) => {\n // The routing slug: the source `url` when set, else the title slug. The\n // FIRST page is the homepage (Blux is positional) — its uid is pinned to\n // \"home\", the render's root-route contract (getByUID(\"page\", \"home\")),\n // and its path is \"\" (the export renders it at the root index.html).\n const urlSlug = String(p.url ?? \"\").trim();\n const path = i === 0 ? \"\" : urlSlug ? slugify(urlSlug) : slugify(String(p.title ?? \"\"));\n let uid = i === 0 ? \"home\" : path;\n if (seen.has(uid)) {\n // Two pages slugging identically (e.g. a title-duplicate draft with no\n // url of its own) would silently overwrite each other's documents.\n const base = uid;\n for (let n = 2; seen.has(uid); n++) uid = `${base}-${n}`;\n diagnostics.push({\n kind: \"duplicate-page-uid\",\n where: base,\n message: `page ${i} (\"${String(p.title ?? \"\")}\") slugs to \"${base}\" already used — renamed to \"${uid}\"`,\n });\n }\n seen.add(uid);\n return {\n uid,\n title: String(p.title ?? \"\"),\n description: String(p.description ?? \"\"),\n path,\n sections: (p.items ?? []).map((b) => sectionFromBlock(b, uid, diagnostics)),\n };\n });\n return { pages, diagnostics };\n}\n\nexport function normalizeTheme(raw: BluxRaw): ThemeIR {\n const colors = Object.entries(raw.styles.colors ?? {}).map(([role, value]) => ({\n role,\n value: String(value),\n }));\n // styles.text is a position-stable array whose slots are { _label,\n // \".textN\": { css props incl font-ident } }. A deleted style leaves a\n // { removed: true } tombstone with no \".textN\" key — the role name comes\n // from that inner key (not the array index), so tombstones drop out and\n // roles never renumber if the array is ever compacted.\n const textStyles: TextStyleIR[] = [];\n for (const v of Object.values(raw.styles.text ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.text\\d+$/.test(k));\n if (!innerKey) continue;\n const m = (entry[innerKey] ?? {}) as Record<string, unknown>;\n const transform = cleanCssValue(m[\"text-transform\"]);\n const tracking = cleanCssValue(m[\"letter-spacing\"]);\n // The style's block margin carries Blux's stack rhythm (e.g. \"10px 0\" on\n // Grid Titles / Caption Body). An explicit \"0\" matches the render default,\n // so only real values ride the IR.\n const margin = cleanCssValue(m[\"margin\"]);\n const mobileSize = cleanCssValue(m[\"__media_mobile_font-size\"]);\n const mobileLineHeight = cleanCssValue(m[\"__media_mobile_line-height\"]);\n textStyles.push({\n role: innerKey.slice(1), // \".text11\" -> \"text11\"\n label: str(entry._label),\n fontFamily: fontFamilyFromStyle(m),\n size: cleanCssValue(m[\"font-size\"]) || \"16px\",\n weight:\n typeof m[\"font-weight\"] === \"number\" ? m[\"font-weight\"] : str(m[\"font-weight\"]) || 400,\n lineHeight: cleanCssValue(m[\"line-height\"]) || \"1.5\",\n ...(transform && transform !== \"none\" ? { transform } : {}),\n ...(tracking ? { letterSpacing: tracking } : {}),\n ...(margin && margin !== \"0\" ? { margin } : {}),\n ...(mobileSize ? { mobileSize } : {}),\n ...(mobileLineHeight ? { mobileLineHeight } : {}),\n });\n }\n // Button skins: styles.buttons mirrors the text-styles shape (an entry per\n // role with the values one level down under the \".buttonsN\" key, tombstones\n // as { removed: true }). Values pass through in DECLARATION ORDER — the\n // skins rely on it (a `border` shorthand then `border-top/right/left: 0`\n // overrides nets a bottom-only rule) — dropping empties and the internal\n // `font-ident` marker.\n const cleanCssMap = (v: unknown): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const [k, val] of Object.entries((v ?? {}) as Record<string, unknown>)) {\n if (k === \"font-ident\") continue;\n const s = cleanCssValue(typeof val === \"number\" ? String(val) : val);\n if (s) out[k] = s;\n }\n return out;\n };\n const buttonStyles: ButtonStyleIR[] = [];\n for (const v of Object.values(raw.styles.buttons ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.buttons\\d+$/.test(k));\n if (!innerKey) continue;\n const css = cleanCssMap(entry[innerKey]);\n if (Object.keys(css).length === 0) continue;\n const hover = cleanCssMap(entry[`${innerKey}:hover`]);\n const active = cleanCssMap(entry[`${innerKey}:active`]);\n buttonStyles.push({\n role: innerKey.slice(1), // \".buttons2\" -> \"buttons2\"\n label: str(entry._label),\n css,\n ...(Object.keys(hover).length ? { hover } : {}),\n ...(Object.keys(active).length ? { active } : {}),\n });\n }\n // Fonts: explicit settings win; otherwise Blux's own default roles —\n // text0 \"Title (Default)\" and text1 \"Body (Default)\".\n const fonts = (raw.settings.fonts ?? {}) as Record<string, unknown>;\n const roleFont = (r: string) => textStyles.find((t) => t.role === r)?.fontFamily ?? \"\";\n return {\n colors,\n fonts: {\n heading: str(fonts.heading) || roleFont(\"text0\"),\n body: str(fonts.body) || roleFont(\"text1\"),\n },\n fontLoad: mergeFontLoads(\n parseGoogleFonts(str(fonts.google)),\n typekitFontLoads(str(fonts.string)),\n ),\n textStyles,\n buttonStyles,\n };\n}\n","import type { BluxFeed, BluxRaw } from \"./parse.js\";\nimport type { CollectionIR, FieldDef, RecordIR } from \"./ir.js\";\n\nfunction singularSlug(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n return slug.replace(/s$/, \"\") || \"item\";\n}\n\nconst RICHTEXT_KEYS = new Set([\"body\", \"description\"]);\n\nfunction fieldType(key: string, value: unknown): FieldDef[\"type\"] {\n if (RICHTEXT_KEYS.has(key)) return \"richtext\";\n if (value && typeof value === \"object\" && \"media\" in (value as object)) return \"image\";\n if (Array.isArray(value)) return \"group\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n if (key === \"date\") return \"date\";\n if (/^(url|link)/.test(key)) return \"link\";\n return \"text\";\n}\n\n/** Underscore-prefixed keys are per-element style config (same convention as\n * page blocks' _title/_body), not content — never model or migrate them. */\nconst isStyleKey = (key: string) => key.startsWith(\"_\");\n\nfunction deriveFields(feed: BluxFeed): FieldDef[] {\n const seen = new Map<string, FieldDef[\"type\"]>();\n // Declared custom fields first (Blux feed.fields), then observed item keys.\n for (const d of feed.fields ?? []) {\n if (d.field) seen.set(d.field, \"text\");\n }\n for (const item of feed.items ?? []) {\n for (const [key, value] of Object.entries(item)) {\n if (isStyleKey(key)) continue;\n if (!seen.has(key) || seen.get(key) === \"text\") seen.set(key, fieldType(key, value));\n }\n }\n return [...seen.entries()].map(([key, type]) => ({ key, type }));\n}\n\nfunction recordUid(values: Record<string, unknown>, i: number): string {\n const title = typeof values.title === \"string\" ? values.title : \"\";\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return slug || `item-${i}`;\n}\n\nexport function modelCollections(raw: BluxRaw): CollectionIR[] {\n const out: CollectionIR[] = [];\n for (const feed of Object.values(raw.feeds)) {\n const label = String(feed.name ?? \"\");\n const items = feed.items ?? [];\n const records: RecordIR[] = items.map((item, i) => {\n const mediaRefs: string[] = [];\n for (const value of Object.values(item)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n mediaRefs.push((value as { media: string }).media);\n }\n }\n const values = Object.fromEntries(Object.entries(item).filter(([key]) => !isStyleKey(key)));\n return { uid: recordUid(item, i), values, mediaRefs };\n });\n out.push({\n apiId: singularSlug(label),\n label,\n publishRoute: feed.publish ? String(feed.publish) : null,\n fields: deriveFields(feed),\n records,\n });\n }\n return out;\n}\n","import { parseBluxSite } from \"./parse.js\";\nimport { normalizePages, normalizeTheme } from \"./normalize.js\";\nimport { modelCollections } from \"./collections.js\";\nimport { collectAssetUrls } from \"./assets.js\";\nimport type { AssetRef, Diagnostic, SiteIR } from \"./ir.js\";\n\nexport function assembleIR(input: { siteJson: unknown; htmls: string[] }): SiteIR {\n const raw = parseBluxSite(input.siteJson);\n const { pages, diagnostics: pageDiags } = normalizePages(raw);\n const theme = normalizeTheme(raw);\n const collections = modelCollections(raw);\n const urlMap = collectAssetUrls(input.htmls);\n\n const diagnostics: Diagnostic[] = [...pageDiags];\n const assets: AssetRef[] = Object.entries(raw.media).map(([id, m]) => {\n const sourceUrl = urlMap.get(id) ?? null;\n if (!sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: id,\n message: `no CDN url for ${m.name ?? id}`,\n });\n }\n return {\n id,\n sourceUrl,\n name: String(m.name ?? \"\"),\n mime: String(m.type ?? \"\"),\n alt: String(m.name ?? \"\"),\n };\n });\n\n // The favicon is declared only in settings (settings.favicon.media) and its\n // uuid is routinely ABSENT from the media dict — so resolve it straight from\n // the scraped urlMap (the <link rel=\"icon\"> tags are part of the HTML scrape)\n // rather than through the assets list above. It lives on meta, not in\n // `assets`, because migration-plan assets get uploaded to Prismic media —\n // the wrong destination for a favicon (convert downloads it beside the plan).\n const faviconId = raw.settings.favicon?.media;\n const favicon = faviconId\n ? { assetId: faviconId, sourceUrl: urlMap.get(faviconId) ?? null }\n : undefined;\n if (favicon && !favicon.sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: favicon.assetId,\n message: `no CDN url for favicon ${favicon.assetId}`,\n });\n }\n\n return {\n meta: { ...raw.meta, ...(favicon ? { favicon } : {}) },\n theme,\n pages,\n collections,\n assets,\n diagnostics,\n };\n}\n","import type { CollectionIR, FieldDef } from \"../ir.js\";\nimport type { PlanCustomType } from \"./plan.js\";\n\nconst FIELD_CONFIG: Record<\n FieldDef[\"type\"],\n () => { type: string; config: Record<string, unknown> }\n> = {\n text: () => ({ type: \"Text\", config: {} }),\n richtext: () => ({\n type: \"StructuredText\",\n config: { multi: \"paragraph,strong,em,hyperlink,list-item,o-list-item\" },\n }),\n image: () => ({ type: \"Image\", config: { constraint: {}, thumbnails: [] } }),\n group: () => ({ type: \"Group\", config: { fields: { value: { type: \"Text\", config: {} } } } }),\n date: () => ({ type: \"Date\", config: {} }),\n boolean: () => ({ type: \"Boolean\", config: {} }),\n number: () => ({ type: \"Number\", config: {} }),\n link: () => ({ type: \"Link\", config: { allowTargetBlank: true } }),\n};\n\nexport function buildCustomType(c: CollectionIR): PlanCustomType {\n const Main: Record<string, unknown> = {};\n for (const f of c.fields) {\n const spec = FIELD_CONFIG[f.type]();\n Main[f.key] = { ...spec, config: { ...spec.config, label: f.key } };\n }\n return {\n id: c.apiId,\n label: c.label,\n repeatable: true,\n json: { id: c.apiId, label: c.label, repeatable: true, status: true, json: { Main } },\n };\n}\n","/** HTML-level rich-text coercion so emitted plans validate against the slice\n * models' StructuredText restrictions (heading slots are `single` and\n * heading-restricted; body slots allow no headings — see Plan 4's field-type\n * table). Blux markup is simple generated HTML, so tag rewriting is reliable. */\n\nconst BLOCK_RE = /<(h[1-6]|p|div)(\\s[^>]*)?>[\\s\\S]*?<\\/\\1>/i;\n\n/** Coerce a heading-slot HTML fragment to a single block whose tag is in\n * `allowed` (e.g. [\"h2\",\"h3\"]): keep an allowed tag, clamp other headings to\n * the nearest allowed level, promote paragraphs/bare text to the LOWEST\n * allowed heading. Only the first block survives (the fields are `single`). */\nexport function coerceHeadingHtml(html: string, allowed: string[]): string {\n const m = html.match(BLOCK_RE);\n const block = m ? m[0] : html;\n const tagMatch = block.match(/^<(h[1-6]|p|div)(\\s[^>]*)?>/i);\n const tag = tagMatch?.[1]?.toLowerCase();\n if (tag && allowed.includes(tag)) return block;\n\n const levels = allowed.filter((t) => /^h[1-6]$/.test(t)).map((t) => Number(t[1]));\n const target =\n tag && tag.startsWith(\"h\")\n ? `h${levels.reduce((b, l) => (Math.abs(l - Number(tag[1])) < Math.abs(b - Number(tag[1])) ? l : b))}`\n : `h${Math.max(...levels)}`;\n\n if (!tag) return `<${target}>${block}</${target}>`;\n return block\n .replace(new RegExp(`^<${tag}`, \"i\"), `<${target}`)\n .replace(new RegExp(`</${tag}>$`, \"i\"), `</${target}>`);\n}\n\n/** Demote all headings in a body-slot fragment to paragraphs (body fields\n * allow no heading blocks). Attributes and inline markup pass through. */\nexport function demoteHeadingsHtml(html: string): string {\n return html.replace(/<(\\/?)h[1-6](\\s[^>]*)?>/gi, \"<$1p$2>\");\n}\n","import type { SectionIR } from \"../ir.js\";\nimport { richText, assetRef, type PlanSlice } from \"./plan.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\n\n/** Allowed heading tags per slice heading slot — MUST mirror the\n * StructuredText configs in reddoor-starter/src/lib/slices/<Slice>/model.json. */\nconst HEADING_TAGS = {\n hero: [\"h1\", \"h2\"],\n media_text: [\"h2\", \"h3\"],\n section_grid: [\"h2\", \"h3\"],\n section_grid_item: [\"h3\", \"h4\"],\n} satisfies Record<string, string[]>;\n\nfunction rt(html?: string) {\n return html ? richText(html) : undefined;\n}\nfunction rtHeading(html: string | undefined, slot: keyof typeof HEADING_TAGS) {\n return html ? richText(coerceHeadingHtml(html, HEADING_TAGS[slot])) : undefined;\n}\nfunction rtBody(html?: string) {\n return html ? richText(demoteHeadingsHtml(html)) : undefined;\n}\nfunction img(id?: string) {\n return id ? assetRef(id) : undefined;\n}\nfunction compact(o: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));\n}\n\nexport function sectionToSlice(s: SectionIR): PlanSlice {\n const f = s.fields;\n switch (s.sliceType) {\n case \"hero\":\n return {\n slice_type: \"hero\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"hero\"),\n body: rtBody(f.body),\n background_image: img(f.backgroundMedia ?? f.media),\n }),\n items: [],\n };\n case \"media_text\":\n return {\n slice_type: \"media_text\",\n variation: s.variation === \"imageLeft\" ? \"imageLeft\" : \"imageRight\",\n primary: compact({\n heading: rtHeading(f.heading, \"media_text\"),\n body: rtBody(f.body),\n media: img(f.media),\n }),\n items: [],\n };\n case \"collection_list\":\n return {\n slice_type: \"collection_list\",\n variation: s.variation === \"list\" ? \"list\" : \"grid\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n collection_type: s.collectionRef?.apiId ?? \"\",\n max_items: 24,\n }),\n items: [],\n };\n case \"grid\":\n case \"slider\":\n return {\n slice_type: \"section_grid\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n columns: f.columns ?? 3,\n }),\n items: (s.children ?? [])\n .map((c) =>\n compact({\n item_heading: rtHeading(c.fields.heading, \"section_grid_item\"),\n item_body: rtBody(c.fields.body),\n item_media: img(c.fields.media),\n }),\n )\n // a child whose only content was hidden text has nothing to show\n .filter((item) => Object.keys(item).length > 0),\n };\n case \"rich_text\":\n default:\n // rich_text content allows every block type — no coercion needed\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n primary: compact({ content: rt(f.heading ? `${f.heading}${f.body ?? \"\"}` : f.body) }),\n items: [],\n };\n }\n}\n","import type { SectionIR } from \"../ir.js\";\n\nconst isContainer = (s: SectionIR) => s.sliceType === \"grid\" || s.sliceType === \"slider\";\nconst isFlatLeaf = (s: SectionIR) =>\n (s.sliceType === \"media_text\" || s.sliceType === \"rich_text\") && !(s.children ?? []).length;\n\n/** Depth-first flatten: Prismic slices cannot nest, so a container survives\n * as a section_grid-with-items only when every child is representable as a\n * flat item (childless media_text/rich_text). Anything richer — nested\n * containers, heroes with backgrounds — explodes into sequential sibling\n * sections, and the container's OWN content (heading/body/media) survives\n * as a leading media_text or rich_text section. Proven need: thePointe's\n * depth-4 tree kept only 7/53 images under one-level flattening. */\nexport function flattenSections(sections: SectionIR[]): SectionIR[] {\n const out: SectionIR[] = [];\n for (const s of sections) {\n const children = s.children ?? [];\n\n if (s.sliceType === \"hero\" && s.fields.media && s.fields.backgroundMedia) {\n // the hero slice only models the background image — surface the\n // foreground image (a logo/overlay in Blux) as a sibling media_text\n const { media, ...heroFields } = s.fields;\n const { children: _heroKids, ...heroSelf } = s;\n out.push({ ...heroSelf, fields: heroFields });\n out.push({\n sliceType: \"media_text\",\n variation: \"imageRight\",\n confidence: s.confidence,\n fields: { media },\n });\n out.push(...flattenSections(children));\n continue;\n }\n\n // a container may keep its items form only when it has no content of its\n // own beyond a heading — section_grid's primary cannot carry media/body\n const keepable = isContainer(s) && !s.fields.media && !s.fields.body;\n if (!children.length) {\n if (isContainer(s) && !keepable) {\n out.push({ ...s, sliceType: s.fields.media ? \"media_text\" : \"rich_text\" });\n } else {\n out.push(s);\n }\n } else if (keepable && children.every(isFlatLeaf)) {\n out.push(s);\n } else if (isContainer(s)) {\n // the container's own content leads its exploded children: with media\n // it is a media_text-shaped section that merely carries a subtree,\n // otherwise its heading/body become a rich_text grouping label\n const { children: _kids, ...self } = s;\n if (self.fields.media) {\n out.push({ ...self, sliceType: \"media_text\" });\n } else if (self.fields.heading || self.fields.body) {\n out.push({ ...self, sliceType: \"rich_text\" });\n }\n out.push(...flattenSections(children));\n } else {\n // normalize attaches `children` to ANY block with items — a hero or\n // media_text can carry a subtree. Its own slice mapping ignores\n // children, so hoist them as following siblings instead of losing them.\n const { children: _hoisted, ...self } = s;\n out.push(self);\n out.push(...flattenSections(children));\n }\n }\n return out;\n}\n","import type { SiteIR, RecordIR, PageIR, AssetRef, Diagnostic } from \"../ir.js\";\nimport {\n richText,\n assetRef,\n type MigrationPlan,\n type PlanDocument,\n type SliceStyleEntry,\n} from \"./plan.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sectionToSlice } from \"./slices.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\nimport { flattenSections } from \"./flatten.js\";\n\n/** Slice fields modeled as Prismic Image fields — only image assets may land here. */\nconst IMAGE_FIELDS = new Set([\"background_image\", \"media\", \"item_media\"]);\n\nconst RICHTEXT = new Set([\"body\", \"description\"]);\n\n/** A page with no title and no sections emits no document (and no review pair). */\nexport function isEmptyPage(p: PageIR): boolean {\n return !p.sections.length && !p.title.trim();\n}\n\n/** True when the asset may occupy a Prismic Image field. A known non-image\n * mime is disqualifying; a MISSING mime (exports often omit `type`) falls\n * back to the filename extension and keeps the asset unless it is clearly\n * not an image. */\nconst NON_IMAGE_EXT = /\\.(mp4|mov|webm|avi|mp3|wav|pdf|zip)$/i;\nfunction isImageAsset(a: AssetRef | undefined): boolean {\n if (!a) return true; // unknown asset id: leave it; migrate reports the miss\n if (a.mime) return a.mime.startsWith(\"image/\");\n return !NON_IMAGE_EXT.test(a.name);\n}\n\nfunction recordData(rec: RecordIR): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rec.values)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n data[key] = assetRef((value as { media: string }).media);\n } else if (RICHTEXT.has(key) && typeof value === \"string\") {\n // record rich-text models allow no heading blocks (see buildCustomType)\n data[key] = richText(demoteHeadingsHtml(value));\n } else {\n data[key] = value;\n }\n }\n return data;\n}\n\nexport function buildMigrationPlan(ir: SiteIR): MigrationPlan {\n const diagnostics: Diagnostic[] = [];\n const customTypes = ir.collections.map(buildCustomType);\n const assetById = new Map(ir.assets.map((a) => [a.id, a]));\n\n /** Drop non-image assets referenced by an Image field, with a diagnostic. */\n const dropNonImages = (rec: Record<string, unknown>, keys: Set<string> | null, where: string) => {\n for (const [key, val] of Object.entries(rec)) {\n if ((keys && !keys.has(key)) || !val || typeof val !== \"object\" || !(\"__asset_id\" in val))\n continue;\n const asset = assetById.get((val as { __asset_id: string }).__asset_id);\n if (!isImageAsset(asset)) {\n diagnostics.push({\n kind: \"non-image-in-image-field\",\n where: `${where}.${key}`,\n message: `${asset?.mime || \"unknown mime\"} asset dropped from image field`,\n });\n delete rec[key];\n }\n }\n };\n\n const documents: PlanDocument[] = [];\n const stylesManifest: MigrationPlan[\"stylesManifest\"] = [];\n for (const page of ir.pages) {\n if (isEmptyPage(page)) {\n diagnostics.push({\n kind: \"empty-page\",\n where: page.uid,\n message: \"page has no title and no sections; skipped\",\n });\n continue;\n }\n const slices: ReturnType<typeof sectionToSlice>[] = [];\n const styleEntries: SliceStyleEntry[] = [];\n for (const section of flattenSections(page.sections)) {\n const slice = sectionToSlice(section);\n for (const rec of [slice.primary, ...slice.items]) {\n dropNonImages(rec, IMAGE_FIELDS, `${page.uid}/${slice.slice_type}`);\n }\n // Structural defaults aren't content — a slice with nothing else to\n // show (e.g. a block whose only content was hidden text, or whose\n // sole video was dropped from an image field) is invisible; skip it.\n const STRUCTURAL = new Set([\"columns\", \"collection_type\", \"max_items\"]);\n const hasContent =\n Object.keys(slice.primary).some((k) => !STRUCTURAL.has(k)) || slice.items.length > 0;\n if (!hasContent) {\n diagnostics.push({\n kind: \"empty-slice\",\n where: `${page.uid}/${slice.slice_type}`,\n message: \"slice has no content after filtering; dropped\",\n });\n continue;\n }\n // Presentation hints ride beside the slice, keyed by its final index.\n // Grid items mirror sectionToSlice's emptiness rule so they stay aligned.\n const itemPresentations = (section.children ?? [])\n .filter((c) => c.fields.heading || c.fields.body || c.fields.media)\n .map((c) => c.presentation ?? null);\n const entry: SliceStyleEntry = {\n index: slices.length,\n sliceType: slice.slice_type,\n ...(section.presentation ? { presentation: section.presentation } : {}),\n ...(slice.slice_type === \"section_grid\" && itemPresentations.some(Boolean)\n ? { items: itemPresentations }\n : {}),\n };\n if (entry.presentation || entry.items) styleEntries.push(entry);\n slices.push(slice);\n }\n stylesManifest.push({ pageUid: page.uid, slices: styleEntries });\n documents.push({\n type: \"page\",\n uid: page.uid,\n // the page type's title is StructuredText(single heading1)\n data: {\n title: richText(coerceHeadingHtml(page.title || page.uid, [\"h1\"])),\n slices,\n },\n });\n }\n for (const c of ir.collections) {\n for (const rec of c.records) {\n const data = recordData(rec);\n // record asset markers only ever occupy Image fields — check them all\n dropNonImages(data, null, `${c.apiId}/${rec.uid}`);\n documents.push({ type: c.apiId, uid: rec.uid, data });\n }\n }\n\n const assets = ir.assets\n .filter((a) => a.sourceUrl !== null)\n .map((a) => ({ id: a.id, url: a.sourceUrl as string, alt: a.alt }));\n\n return { customTypes, documents, assets, stylesManifest, diagnostics };\n}\n","import type { ThemeIR } from \"../ir.js\";\n\n/** The shared custom-property lines (colors, fonts, text tokens) that both the\n * build-time `@theme{}` block and the runtime `:root{}` mirror emit. Extracted\n * so the two blocks can never drift. */\nfunction themeVarLines(theme: ThemeIR): string[] {\n const lines: string[] = [];\n for (const c of theme.colors) lines.push(` --color-${c.role}: ${c.value};`);\n lines.push(` --font-heading: ${theme.fonts.heading || \"sans-serif\"};`);\n lines.push(` --font-body: ${theme.fonts.body || \"sans-serif\"};`);\n for (const t of theme.textStyles) {\n if (t.label) lines.push(` /* ${t.role} — ${t.label} */`);\n lines.push(` --text-${t.role}: ${t.size};`);\n lines.push(` --text-${t.role}--line-height: ${t.lineHeight};`);\n lines.push(` --text-${t.role}--font-weight: ${t.weight};`);\n if (t.fontFamily) lines.push(` --text-${t.role}--font-family: ${t.fontFamily};`);\n if (t.transform) lines.push(` --text-${t.role}--text-transform: ${t.transform};`);\n if (t.letterSpacing) lines.push(` --text-${t.role}--letter-spacing: ${t.letterSpacing};`);\n if (t.margin) lines.push(` --text-${t.role}--margin: ${t.margin};`);\n if (t.mobileSize) lines.push(` --text-${t.role}--mobile-font-size: ${t.mobileSize};`);\n if (t.mobileLineHeight)\n lines.push(` --text-${t.role}--mobile-line-height: ${t.mobileLineHeight};`);\n }\n return lines;\n}\n\n/** ThemeIR → a Tailwind v4 `@theme` block of CSS custom properties. Deterministic.\n * A leading comment lists the exact web-font weights the export loads, so the\n * design pass can install them without measuring the rendered site. */\nexport function emitThemeCss(theme: ThemeIR): string {\n const lines: string[] = [];\n if (theme.fontLoad.length) {\n const spec = theme.fontLoad.map((f) => `${f.family} ${f.weights.join(\",\")}`).join(\"; \");\n lines.push(`/* Fonts to load — ${spec} */`);\n }\n lines.push(\"@theme {\");\n lines.push(...themeVarLines(theme));\n lines.push(\"}\");\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** A runtime-resolvable mirror of the @theme token block. Tailwind's `@theme`\n * is BUILD-TIME — injected raw at runtime (the /dev/blux-pointe gate), the\n * browser ignores it and the `.txt-role-*` rules' `var(--text-*)` never\n * resolve. The same tokens inside a real `:root{}` resolve without Tailwind;\n * identical values to what @theme compiles to :root, so it is harmless when the\n * file is also Tailwind-built. */\nexport function emitRootVarsCss(theme: ThemeIR): string {\n return [\":root {\", ...themeVarLines(theme), \"}\"].join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the `.txt-role-textN` utility layer: one rule per text style that\n * maps the role's `@theme` vars (emitted by {@link emitThemeCss}) onto the\n * h1–h6/p it wraps. A slice applies `txt-role-textN` to a block and its\n * headings/paragraphs pick up that role's type — the same mechanism the\n * per-site `gen-blux-theme.mjs` script used to hand-generate, now owned by\n * `blux emit` so a converted site consumes it with zero hand-tuning.\n *\n * letter-spacing and text-transform fall back through `var(…, default)` so a\n * role that omits them is inert for that property. font-family is set only when\n * the role declares one — a family-less role (a body default) keeps the natural\n * cascade (headings stay heading font, paragraphs stay body font) rather than\n * being forced onto either. margin falls back to 0: Blux's vertical rhythm\n * between stacked blocks is the text styles' own margins (e.g. \"10px 0\" on\n * Grid Titles), which collapse in normal flow — a role without one stays\n * flush, exactly like the original. Returns \"\" when the theme has no text\n * styles. */\nexport function emitRolesCss(theme: ThemeIR): string {\n if (!theme.textStyles.length) return \"\";\n const lines: string[] = [\n \"/* Role utilities — one .txt-role-textN per Blux text style, mapping its\",\n \" @theme vars onto the heading/paragraph it wraps. Generated by blux emit. */\",\n ];\n for (const t of theme.textStyles) {\n const r = t.role;\n lines.push(`.txt-role-${r} :is(h1, h2, h3, h4, h5, h6, p) {`);\n if (t.fontFamily) lines.push(` font-family: var(--text-${r}--font-family);`);\n lines.push(\n ` font-size: var(--text-${r});`,\n ` font-weight: var(--text-${r}--font-weight);`,\n ` line-height: var(--text-${r}--line-height);`,\n ` letter-spacing: var(--text-${r}--letter-spacing, normal);`,\n ` text-transform: var(--text-${r}--text-transform, none);`,\n ` margin: var(--text-${r}--margin, 0);`,\n `}`,\n );\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the button-skin layer: one `.buttonsN` rule per declared Blux\n * button style (plus its :hover/:active variants), and the `.ib` inline-block\n * base the raw anchors rely on (`class=\"ib middle buttonsN\"` — an inline `a`\n * would ignore the skin's vertical padding). Properties emit in the export's\n * own declaration order: the skins rely on it (a `border` shorthand followed\n * by `border-top/right/left: 0` overrides nets a bottom-only rule). Returns \"\"\n * when the theme declares no button styles. */\nexport function emitButtonsCss(theme: ThemeIR): string {\n if (!theme.buttonStyles.length) return \"\";\n const lines: string[] = [\n \"/* Button skins — one .buttonsN per Blux button style; raw anchors carry\",\n \" `ib middle buttonsN` verbatim. Generated by blux emit. */\",\n \".ib {\",\n \" display: inline-block;\",\n \"}\",\n \"/* Text links (`ib middle links`) are underlined by the Blux platform CSS;\",\n \" an inline-block box does not inherit an ancestor's text-decoration, so\",\n \" the affordance must be declared on the anchor itself. */\",\n \".links {\",\n \" text-decoration: underline;\",\n \"}\",\n ];\n const rule = (selector: string, css: Record<string, string>) => {\n lines.push(`${selector} {`);\n for (const [k, v] of Object.entries(css)) lines.push(` ${k}: ${v};`);\n lines.push(\"}\");\n };\n for (const b of theme.buttonStyles) {\n if (b.label) lines.push(`/* ${b.role} — ${b.label} */`);\n rule(`.${b.role}`, b.css);\n if (b.hover) rule(`.${b.role}:hover`, b.hover);\n if (b.active) rule(`.${b.role}:active`, b.active);\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** The complete theme.css a CLI action writes: the build-time `@theme` block,\n * its runtime `:root` mirror (so a raw-injected theme.css resolves\n * `var(--text-*)` — the /dev/blux-pointe gate does exactly that), then the role\n * utilities and button skins. The single source of truth for theme.css\n * assembly — every action (emit/catalog/convert) writes exactly this, so no\n * writer can drift or omit the `:root` mirror. */\nexport function emitThemeCssFile(theme: ThemeIR): string {\n const rolesCss = emitRolesCss(theme);\n const buttonsCss = emitButtonsCss(theme);\n return (\n emitThemeCss(theme) +\n \"\\n\" +\n emitRootVarsCss(theme) +\n (rolesCss ? \"\\n\" + rolesCss : \"\") +\n (buttonsCss ? \"\\n\" + buttonsCss : \"\")\n );\n}\n","import type { SiteIR, Diagnostic } from \"../ir.js\";\nimport { isEmptyPage } from \"./migration-plan.js\";\n\nexport type ReviewPair = { uid: string; converted: string; original: string };\nexport type ReviewManifest = { pairs: ReviewPair[]; diagnostics: Diagnostic[] };\n\nexport function buildReviewManifest(\n ir: SiteIR,\n opts: { convertedBase: string; bluxBase: string },\n): ReviewManifest {\n // empty pages emit no document (see buildMigrationPlan) — nothing to review\n const pairs = ir.pages\n .filter((p) => !isEmptyPage(p))\n .map((p) => ({\n uid: p.uid,\n converted: `${opts.convertedBase}/${p.uid}`,\n // The home page maps to the site root; other pages to /uid.\n original: p.uid === \"home\" ? `${opts.bluxBase}/` : `${opts.bluxBase}/${p.uid}`,\n }));\n return { pairs, diagnostics: ir.diagnostics };\n}\n","import { cleanCssValue } from \"../normalize.js\";\n\n/** Per-band block styles (background-color, min-height, text-align, …) sourced\n * from site.json's top-level page blocks, cleaned exactly as normalize.ts does.\n * Keyed by the block's render-order index, which equals the grid `Band.index`\n * (the `page-block-N` number) for a contiguously-rendered page.\n *\n * site.json's page blocks live at `content.pages[0].items` (see\n * `parseBluxSite` in parse.ts, which reads `j.content.pages`, and `BluxPage`,\n * whose blocks field is `items`) — not `pages[0].blocks` as a naive reading\n * of the export's top level might suggest. */\nexport function blockStylesByIndex(\n siteJson: unknown,\n pageIndex = 0,\n): Map<number, Record<string, string>> {\n const out = new Map<number, Record<string, string>>();\n const blocks = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content?.pages?.[\n pageIndex\n ]?.items;\n if (!Array.isArray(blocks)) return out;\n blocks.forEach((b, i) => {\n const styles = (b as { styles?: Record<string, unknown> })?.styles ?? {};\n const cleaned: Record<string, string> = {};\n for (const [k, v] of Object.entries(styles)) {\n const c = cleanCssValue(v);\n if (c) cleaned[k] = c;\n }\n if (Object.keys(cleaned).length) out.set(i, cleaned);\n });\n return out;\n}\n\n/** A block class's `.blocksNcontainer` defaults: the content padding (with its\n * `__media_mobile_padding` responsive override) and max-width that apply when a\n * block's own styles omit them. */\nexport type BlockDefaults = {\n padding?: string;\n mobilePadding?: string;\n maxWidth?: string;\n};\n\nconst CONTAINER_RE = /^\\.(blocks\\d+)container$/;\n\n/** Per-block-class container defaults keyed by the bare class (\"blocks0\",\n * \"blocks2\", …), sourced from site.json's `styles.blocks` — a position-stable\n * array of `{ _label, \".blocksN\": …, \".blocksNcontainer\": … }` slots. Only the\n * `.blocksNcontainer` entries matter here; the head `<style>` is never parsed\n * (styles.blocks is its structured source). Values are cleaned exactly as\n * `blockStylesByIndex` cleans them, so the export's malformed tombstones\n * (`padding: \"px\"`, empty strings) drop instead of leaking. */\nexport function blockClassDefaults(siteJson: unknown): Map<string, BlockDefaults> {\n const out = new Map<string, BlockDefaults>();\n const blocks = (siteJson as { styles?: { blocks?: unknown[] } })?.styles?.blocks;\n if (!Array.isArray(blocks)) return out;\n for (const slot of blocks) {\n if (slot === null || typeof slot !== \"object\") continue;\n for (const [selector, css] of Object.entries(slot as Record<string, unknown>)) {\n const blockClass = CONTAINER_RE.exec(selector)?.[1];\n if (!blockClass || css === null || typeof css !== \"object\") continue;\n const rec = css as Record<string, unknown>;\n const padding = cleanCssValue(rec[\"padding\"]);\n const mobilePadding = cleanCssValue(rec[\"__media_mobile_padding\"]);\n const maxWidth = cleanCssValue(rec[\"max-width\"]);\n const defaults: BlockDefaults = {\n ...(padding ? { padding } : {}),\n ...(mobilePadding ? { mobilePadding } : {}),\n ...(maxWidth ? { maxWidth } : {}),\n };\n if (Object.keys(defaults).length) out.set(blockClass, defaults);\n }\n }\n return out;\n}\n","/** Content-coverage validation: does a converted site actually render every\n * piece of text the Blux export shows? The export's rendered `index.html` is\n * the answer key; we extract its visible text runs and check each appears in\n * the converted site's rendered HTML. A missing run is a content gap the\n * deterministic transform left behind (e.g. a hero title that never mapped\n * onto a slice field) — surfaced without spending a token eyeballing pages. */\n\nconst DROP_ELEMENTS = /<(script|style|head|noscript|svg|template)[\\s\\S]*?<\\/\\1>/gi;\nconst COMMENTS = /<!--[\\s\\S]*?-->/g;\n\n/** Decode the entity forms an export mixes in so they match the raw characters\n * a Prismic render emits. Numeric first, then the common named ones. */\nfunction decodeEntities(s: string): string {\n return (\n s\n .replace(/&#(\\d+);/g, (_, n) => codePoint(Number(n)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => codePoint(parseInt(h, 16)))\n .replace(/&nbsp;/gi, \" \")\n .replace(/&(?:amp);/gi, \"&\")\n .replace(/&(?:lt);/gi, \"<\")\n .replace(/&(?:gt);/gi, \">\")\n .replace(/&(?:quot);/gi, '\"')\n .replace(/&(?:apos|#39);/gi, \"'\")\n // any remaining named entity (&mdash; &rsquo; &copy; …) becomes a space,\n // not the literal token \"mdash\"/\"rsquo\" — a phantom word would break the\n // substring match and report otherwise-present text as a false gap\n .replace(/&[a-z][a-z0-9]*;/gi, \" \")\n );\n}\n\nfunction codePoint(n: number): string {\n try {\n return String.fromCodePoint(n);\n } catch {\n return \" \";\n }\n}\n\n/** Fold text to a matchable form: decode, lowercase, and reduce every run of\n * non-alphanumeric characters (punctuation, smart quotes, em-dashes, symbols)\n * to a single space. Coverage then compares words, not typography — so\n * \"CBRE & Co. — Leasing\" and \"cbre co leasing\" are the same content. */\nexport function normalizeText(s: string): string {\n return decodeEntities(s)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim();\n}\n\n/** A normalized run counts as content only if it carries a real word — at least\n * three alphanumeric characters together. Filters whitespace, lone punctuation,\n * and single/double-letter fragments left by inline tags. */\nfunction hasWord(run: string): boolean {\n return /[a-z0-9]{3,}/.test(run);\n}\n\n/** Strip the non-content elements and all tags from an HTML string, leaving\n * one normalized text blob (used for the render side of a coverage check). */\nexport function flattenText(html: string): string {\n const text = html\n .replace(DROP_ELEMENTS, \" \")\n .replace(COMMENTS, \" \")\n .replace(/<[^>]+>/g, \" \");\n return normalizeText(text);\n}\n\n/** Split an HTML string into de-duplicated, normalized visible text runs — the\n * text between tags, once script/style/head are removed. Inline tags fragment a\n * sentence into several runs; each must still appear in the render to count. */\nexport function extractTextRuns(html: string): string[] {\n const stripped = html.replace(DROP_ELEMENTS, \" \").replace(COMMENTS, \" \");\n const seen = new Set<string>();\n for (const piece of stripped.split(/<[^>]+>/)) {\n const run = normalizeText(piece);\n if (hasWord(run)) seen.add(run);\n }\n return [...seen];\n}\n\nexport type CoverageReport = {\n total: number;\n covered: number;\n missing: string[];\n coveragePct: number;\n};\n\n/** Compare the export's rendered HTML (answer key) against the converted site's\n * rendered HTML. Every text run in the export should appear somewhere in the\n * render; the ones that don't are the content the transform dropped. */\nexport function validateCoverage(exportHtml: string, renderedHtml: string): CoverageReport {\n const runs = extractTextRuns(exportHtml);\n // Pad with spaces and match a space-bounded run so a short label (\"art\")\n // isn't counted as covered because it sits inside a longer word\n // (\"apartments\") — that would silently hide a real content gap.\n const blob = ` ${flattenText(renderedHtml)} `;\n const missing = runs.filter((run) => !blob.includes(` ${run} `));\n const covered = runs.length - missing.length;\n const coveragePct = runs.length === 0 ? 100 : Math.round((covered / runs.length) * 100);\n return { total: runs.length, covered, missing, coveragePct };\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement, Node as HTMLNode } from \"node-html-parser\";\nimport type { Band, Cell, GridToken, Media, Node } from \"./types.js\";\nimport { parseGridToken } from \"./token.js\";\nimport {\n textRoleFromClass,\n headingLevel,\n mediaFromElement,\n stripAssetExt,\n blockPlainText,\n readBgSizing,\n textLeafStyle,\n cssProp,\n} from \"./leaf.js\";\n\nconst DEFAULT_TOKEN: GridToken = { cols: 1, raw: \"grid-1\" };\n\nconst isElement = (n: HTMLNode): n is HTMLElement =>\n (n as HTMLElement).tagName !== undefined && (n as HTMLElement).tagName !== null;\n\nconst hasClass = (el: HTMLElement, c: string) => el.classNames.split(/\\s+/).includes(c);\n\n/** A hidden feed-template prototype: the `display:none` `cagriditem` (id\n * `…-template`) that Blux clones client-side, once per feed record, to render a\n * feed grid at runtime. Its content is Handlebars tokens (`{{media}}`,\n * `{{title}}`) — never real content — so it must be dropped from the static\n * parse; keeping it renders the literal `{{…}}` text and an empty tile. The\n * visible feed tiles are materialized separately (from the feed records), not\n * from this element. */\nconst isFeedTemplate = (el: HTMLElement): boolean => {\n const id = el.getAttribute(\"id\") ?? \"\";\n const style = el.getAttribute(\"style\") ?? \"\";\n const hidden = /display\\s*:\\s*none/i.test(style);\n return hidden && (/-template$/.test(id) || /\\{\\{[a-z_]+\\}\\}/i.test(el.innerHTML));\n};\n\n/** A foreground media holder: a `block-media-holder`, a bare\n * `camediaload[data-media]`, or a `<video>`. */\nconst isMediaHolder = (el: HTMLElement): boolean =>\n hasClass(el, \"block-media-holder\") ||\n (hasClass(el, \"camediaload\") && !!el.getAttribute(\"data-media\")) ||\n el.tagName === \"VIDEO\";\n\nconst isLeafElement = (el: HTMLElement): boolean =>\n hasClass(el, \"block-title\") ||\n hasClass(el, \"block-body\") ||\n hasClass(el, \"block-subtitle\") ||\n isMediaHolder(el);\n\n/** A leaf `<a>`: a CTA button / text link with no structural descendants. Such\n * an anchor is not a wrapper — peeling through it (its inner text/spans are not\n * structural) would drop the link entirely — so it is treated as a structural\n * leaf. A linked media/grid wrapper (`<a>` containing a camediaload/grid) is NOT\n * a leaf anchor: it keeps peeling so the inner media parses normally. */\nconst isLeafAnchor = (el: HTMLElement): boolean =>\n el.tagName === \"A\" && collectStructuralChildren(el).length === 0;\n\n/** Is this element a structural boundary (a leaf, a grid row, or a token-bearing\n * cell/holder), as opposed to a pure wrapper div we should peel through? */\nconst isStructural = (el: HTMLElement): boolean =>\n isLeafElement(el) ||\n el.hasAttribute(\"data-exec\") || // Blux custom-code embed (e.g. map mount)\n hasClass(el, \"cagrid\") ||\n isLeafAnchor(el) ||\n parseGridToken(el.classNames) !== null;\n\n/** The \"card\" styling inherited from the wrapper div(s) peeled to reach a\n * structural child — a Blux card's inline `background-color` (on the `.blocksN`\n * fill) and the `padding` its `.blocksNcontainer` insets the content by, both of\n * which the plain peel drops. The nearest wrapper wins for each. `nested` marks\n * that the walk has passed a grid-cell boundary (block-grid-container /\n * block-subcontent): only cell-level container padding is captured — the\n * BAND-level container's padding is the band's own content padding, already\n * handled via the band style/blockClass defaults, and capturing it here would\n * inset the content twice. */\ntype CardStyle = {\n background?: string;\n padding?: string;\n /** A nested block-in-cell's inline `min-height` (e.g. the-tower band 1's\n * 80vh gradient panel): the cell's block pins its own box height, which the\n * plain peel would drop — the cell then renders at content height. Captured\n * only inside a cell (like padding): a band-level container's min-height is\n * the band's full-height chrome, handled at the band level. */\n minHeight?: string;\n /** A `block-background-layer` sibling's paint — the abs-fill div a nested\n * block uses for gradient (or plain) fills that `inlineBg` never sees\n * (they're not a wrapper `background-color`). Emitted as the `background`\n * shorthand so gradients survive. Image layers (camediaload) stay out of\n * scope — those are band-background territory. */\n layerBackground?: string;\n /** A peeled `valignmiddle` wrapper: the original vertically centers this\n * cell's content against its row siblings (band 6/12's side captions sit\n * centered on their photos; band 3's stats card centers in its column) —\n * and, paired with `minHeight`, centers content within the block's own box.\n * Rides the node style as the `_valign` presentation hint. */\n valign?: boolean;\n /** The cell belongs to a `cagridFlexHeight` grid: the original stretches\n * each cell's direct block to the full row height (`.cagriditem>div\n * {height:100%}`), so a painted block fills its whole column — not just its\n * content box. Rides painted container nodes as the `_fill: column` hint. */\n fill?: boolean;\n nested?: boolean;\n};\n/** A structural child plus any card styling peeled off its wrapper(s). */\ntype StructuralChild = { el: HTMLElement; card?: CardStyle };\n\n/** A grid-cell boundary: once the peel passes one of these, wrappers below are\n * cell-level (their padding is real content inset, not band chrome). */\nconst isCellBoundary = (el: HTMLElement): boolean =>\n hasClass(el, \"block-grid-container\") || hasClass(el, \"block-subcontent\");\n\n/** Inline `background-color` off an element's style attribute, ignoring the\n * transparent default (which is not a deviation worth carrying). */\nfunction inlineBg(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"background-color\")?.trim();\n if (!c) return undefined;\n const low = c.toLowerCase().replace(/\\s+/g, \"\");\n if (low === \"transparent\" || low === \"rgba(0,0,0,0)\") return undefined;\n return c;\n}\n\n/** Inline `padding` shorthand off an element's style attribute, ignoring an\n * all-zero value (no inset worth carrying). */\nfunction inlinePadding(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"padding\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem)?\\s*)+$/i.test(c)) return undefined;\n return c;\n}\n\n/** Inline `min-height` off an element's style attribute, ignoring zero/auto\n * (no sizing worth carrying). Blux repeats the value on the item AND its\n * inner `height: 1px` container — same value, either capture works. */\nfunction inlineMinHeight(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"min-height\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem|vh|vw)?|auto)$/i.test(c)) return undefined;\n return c;\n}\n\n/** The paint of a `block-background-layer` child, if `el` has one: the\n * abs-fill div that fills a nested block-in-cell (gradient or plain color —\n * carried in the `background` shorthand, which `inlineBg`'s background-color\n * read never sees). `none`/`transparent` carry no paint. */\nfunction layerBackground(el: HTMLElement): string | undefined {\n for (const child of el.childNodes) {\n if (!isElement(child) || !hasClass(child, \"block-background-layer\")) continue;\n const s = child.getAttribute(\"style\") ?? \"\";\n const bg = (cssProp(s, \"background\") ?? cssProp(s, \"background-color\"))?.trim();\n if (bg && !/^(none|transparent)$/i.test(bg)) return bg;\n }\n return undefined;\n}\n\n/** The child elements that carry structure, peeling pure wrapper divs. A peeled\n * card wrapper's inline background-color and (cell-level) content padding ride\n * along to the structural node it wraps (the nearest wrapper wins for each) so\n * a card's fill and inset survive the peel.\n *\n * Two wrapper shapes are PROMOTED to structural instead of peeled, so they\n * parse to their own stack and the styling/containment attaches exactly once:\n * - a multi-child `block-subcontent`: a Blux grid CELL groups its blocks —\n * the original contains their margins per cell (a block-content clearfix\n * blocks the collapse), so flattening the boundary away merges rhythm that\n * the original keeps separate;\n * - a padded wrapper around ≥2 structural children: threading the padding\n * onto each child would inset every one of them (duplication) — the group\n * is the thing that's padded. */\nexport function collectStructuralChildren(\n el: HTMLElement,\n inherited: CardStyle = {},\n): StructuralChild[] {\n const out: StructuralChild[] = [];\n // A nested block's background-layer sibling paints the whole block: fold it\n // onto the card its content siblings inherit. Band-level layers stay out —\n // a band's background is SectionBand territory.\n const layerBg = inherited.nested === true ? layerBackground(el) : undefined;\n const base: CardStyle =\n layerBg !== undefined ? { ...inherited, layerBackground: layerBg } : inherited;\n for (const child of el.childNodes) {\n if (!isElement(child)) continue;\n // The layer itself is pure paint (abs-fill, never content) — consumed above.\n if (hasClass(child, \"block-background-layer\")) continue;\n // A hidden feed-template prototype ({{…}} tokens, cloned by JS per record)\n // is not real content — drop it so the band doesn't render literal braces.\n if (isFeedTemplate(child)) continue;\n const nested = base.nested === true || isCellBoundary(child);\n const background = inlineBg(child) ?? base.background;\n // Cell-level wrappers only — a band-level container's padding (and\n // min-height: the full-height band chrome) is the band's own concern\n // (see CardStyle.nested).\n const padding = (nested ? inlinePadding(child) : undefined) ?? base.padding;\n const minHeight = (nested ? inlineMinHeight(child) : undefined) ?? base.minHeight;\n const valign = (nested && hasClass(child, \"valignmiddle\")) || base.valign === true;\n const card: CardStyle = {\n ...(background !== undefined ? { background } : {}),\n ...(padding !== undefined ? { padding } : {}),\n ...(minHeight !== undefined ? { minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(valign ? { valign } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n // A boxed wrapper (padding, min-height, or a paint layer) around ≥2\n // structural children is PROMOTED so the box applies exactly once —\n // threading it onto each child would inset/size/paint every one of them.\n const group =\n !isStructural(child) &&\n ((hasClass(child, \"block-subcontent\") && parseGridToken(child.classNames) === null) ||\n (nested &&\n (inlinePadding(child) !== undefined ||\n inlineMinHeight(child) !== undefined ||\n layerBackground(child) !== undefined))) &&\n collectStructuralChildren(child).length >= 2;\n if (isStructural(child)) {\n // A structural child's card carries ONLY what the peeled wrappers above\n // it accumulated — never the child's own inline style: leaves self-carry\n // it (textLeafStyle, readImgSizing) and boxing it here would apply it\n // twice (band-3 stat labels' 8px inset, once threaded contexts reach\n // inside token-bearing holders).\n const inheritedCard: CardStyle = {\n ...(base.background !== undefined ? { background: base.background } : {}),\n ...(base.padding !== undefined ? { padding: base.padding } : {}),\n ...(base.minHeight !== undefined ? { minHeight: base.minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(base.valign === true ? { valign: true } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n out.push({\n el: child,\n ...(Object.keys(inheritedCard).length > 0 ? { card: inheritedCard } : {}),\n });\n } else if (group) {\n // The promoted wrapper IS the box — its own values ride the group card.\n // Its own paint layer sits one level down where the pre-scan (which only\n // reads el's direct children) won't see it again — fold it on here. A\n // promoted cell boundary keeps its inner walk's capture instead (its\n // own pre-scan runs nested).\n const ownLayer = !isCellBoundary(child) ? layerBackground(child) : undefined;\n const groupCard: CardStyle =\n ownLayer !== undefined ? { ...card, layerBackground: ownLayer } : card;\n out.push({\n el: child,\n ...(Object.keys(groupCard).length > 0 ? { card: groupCard } : {}),\n });\n } else {\n out.push(...collectStructuralChildren(child, card));\n }\n }\n return out;\n}\n\n/** Does a parsed leaf node carry real text (not just empty markup)? Gates caption\n * capture so an empty `.block-*` inside a media holder adds nothing. Routes\n * heading/body through `blockPlainText` (same as the subtitle path) so an\n * entity- or `&nbsp;`-only block reads as empty, not as text. */\nfunction nodeHasText(n: Node): boolean {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) !== \"\";\n if (n.kind === \"subtitle\") return n.text.trim() !== \"\";\n return false;\n}\n\n/** Is a caption element (or an ancestor up to and including the media holder)\n * marked `class:\"disable\"`? Blux omits disabled blocks from the rendered HTML,\n * but guard anyway so hidden copy is never leaked into a caption. */\nfunction isDisabledWithin(caption: HTMLElement, holder: HTMLElement): boolean {\n let a: HTMLElement | null | undefined = caption;\n while (a) {\n if (hasClass(a, \"disable\")) return true;\n if (a === holder) return false;\n a = a.parentNode as HTMLElement | null | undefined;\n }\n return false;\n}\n\n/** Parse one element into a grid Node. Leaves dispatch by role; everything else\n * becomes a row / stack / single / raw via parseContainer. */\nexport function parseNode(el: HTMLElement, nested = false): Node {\n if (hasClass(el, \"block-title\") && /^H[1-6]$/.test(el.tagName ?? \"\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"heading\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n level: headingLevel(el),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-body\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"body\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-subtitle\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n // Route through blockPlainText (not raw `.text`): a `<br>` in a display\n // subtitle survives as a newline while insignificant source whitespace\n // collapses — `.text` alone can't tell a hard break from source formatting.\n return {\n kind: \"subtitle\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n text: blockPlainText(el.innerHTML),\n };\n }\n if (isMediaHolder(el)) {\n const media = mediaFromElement(el);\n if (media) {\n // Blux slider tiles nest the slide's CAPTION inside the media holder (the\n // holder is `data-bgmedia` and the copy overlays it). The holder is an\n // opaque media leaf, so those captions would be dropped. When one carries\n // block-title/body/subtitle text, emit the media PLUS the caption(s) as a\n // stack so the copy survives. Pure-media holders — the vast majority —\n // stay a bare media node, byte-identical (no `.block-*` descendant → no\n // extra work). This does NOT change the peel boundary: the holder is still\n // a structural leaf; only its own internal text is recovered here.\n const captions = el\n .querySelectorAll(\".block-title, .block-body, .block-subtitle\")\n .filter((c) => !isDisabledWithin(c, el))\n .map((c) => parseNode(c))\n .filter(nodeHasText);\n if (captions.length) {\n return { kind: \"stack\", children: [{ kind: \"media\", media }, ...captions] };\n }\n return { kind: \"media\", media };\n }\n }\n if (el.hasAttribute(\"data-exec\")) {\n // Custom-code embed (map, third-party widget). Keep the whole subtree —\n // including id=\"burbank_map\" and any inline initMap/KmlLayer scripts — so\n // extract-map can read it and Grid.svelte can render it verbatim.\n return { kind: \"raw\", html: el.outerHTML };\n }\n if (el.tagName === \"A\") {\n // A leaf CTA button / text link (isLeafAnchor). Preserve the whole anchor —\n // href + label — verbatim so the render layer keeps the clickable link.\n return { kind: \"raw\", html: el.outerHTML };\n }\n return parseContainer(el, nested);\n}\n\n/** Attach a peeled card wrapper's styling to a container node's `style`\n * (row/stack only — a Blux card wraps a grid or a stack of blocks, never a bare\n * leaf). The background is the card's fill; the padding is the content inset its\n * `.blocksNcontainer` applies. Padding rides only when a background marks this a\n * real card, so a plain band container's padding (already handled via the band's\n * blockClass defaults) is not double-captured onto a nested node. */\n/** Does a container node's style carry visible paint or sizing — the cues that\n * make a `cagridFlexHeight` column-fill hint worth emitting? */\nconst nodePainted = (style: Record<string, string>): boolean =>\n \"background\" in style || \"background-color\" in style || \"min-height\" in style;\n\nfunction withCardStyle(node: Node, card?: CardStyle): Node {\n if (\n !card ||\n (card.background === undefined &&\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign &&\n !card.fill)\n )\n return node;\n // background-color first, `background` shorthand second: when a block has\n // both a wrapper fill and a paint layer, the fuller layer paint wins (CSS\n // last-declaration order in the emitted style attribute).\n if (node.kind === \"row\" || node.kind === \"stack\") {\n const style: Record<string, string> = { ...(node.style ?? {}) };\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n // The FlexHeight column fill only matters when there's paint/sizing to\n // extend — an unpainted stack stretching is visually identity.\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n if (Object.keys(style).length === 0) return node;\n return { ...node, style };\n }\n // A MEDIA leaf under a min-height wrapper keeps its leaf shape: the frame\n // height folds into media.minHeight (slider slides repeat the same value on\n // holder and wrapper) instead of boxing the slide in a synthetic stack —\n // which would demote Carousel classification (carouselSlides matches bare\n // media) and turn working sliders into static galleries.\n if (node.kind === \"media\" && card.padding === undefined && !card.valign) {\n if (card.minHeight !== undefined && node.media.minHeight === undefined)\n return { ...node, media: { ...node.media, minHeight: card.minHeight } };\n return node;\n }\n // A boxed wrapper around a bare non-media leaf (padding, min-height, valign,\n // or a paint layer — e.g. band 11's `20px 0 30px` cell container wrapping a\n // single heading, or a flush gradient card holding one): the leaf has no\n // container-style slot, so a synthetic one-child stack carries the box. A\n // wrapper background-color ALONE still drops, as before — those (the\n // carousel captions) are handled by their own render path, and the Grid\n // tree must not invent a box for them.\n if (\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign\n )\n return node;\n const style: Record<string, string> = {};\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n return { kind: \"stack\", children: [node], style };\n}\n\n/** Parse a wrapper/cell/band-body element: a row when it is a grid or holds\n * ≥2 token-bearing children, else a stack / single / raw. */\nexport function parseContainer(el: HTMLElement, nested = false): Node {\n // A container that IS a cell/grid element starts its walk inside the cell\n // context, so its own inner wrappers' padding is captured (CardStyle.nested).\n // A PROMOTED wrapper (a boxed group inside a cell) threads `nested` in from\n // its call site — restarting outside the cell context would silently drop\n // the padding/valign its inner wrappers still carry. A cagridFlexHeight grid\n // additionally stretches each cell's direct block to the full row height —\n // its cells' cards carry `fill` so painted blocks emit the `_fill: column`\n // hint (the fill never threads past the cell: the cell's own parseContainer\n // restarts the walk without it).\n const kids = collectStructuralChildren(\n el,\n isCellBoundary(el) || hasClass(el, \"cagrid\")\n ? { nested: true, ...(hasClass(el, \"cagridFlexHeight\") ? { fill: true } : {}) }\n : nested\n ? { nested: true }\n : {},\n );\n // Parse each structural child up front, then drop any that collapse to an\n // EMPTY raw — an empty `.caslider`/wrapper (no static slides, JS-hydrated)\n // yields `raw:\"\"`, which would otherwise survive as a phantom sibling (e.g.\n // turning a lone poster image into `[media, empty-block]`). A non-empty raw\n // (a `[data-exec]` embed, a leaf `<a>`) always has real html, so it is kept.\n // A structural child may carry `card` styling (background + padding) peeled off\n // a card wrapper — it rides onto that child's container node via withCardStyle.\n const parsed = kids\n .map((k) => ({\n token: parseGridToken(k.el.classNames),\n node: withCardStyle(parseNode(k.el, k.card?.nested === true), k.card),\n }))\n .filter((p) => !(p.node.kind === \"raw\" && p.node.html.trim() === \"\"));\n const isGrid = hasClass(el, \"cagrid\");\n const tokenCount = parsed.filter((p) => p.token).length;\n // A LONE width-constrained cell (grid-2-r60 etc.) still needs its row: the\n // token IS the content column's width (band 9/11's 60% column) — flattening\n // it to a stack silently renders the content full-width. cols 1 / \"any\"\n // carry no width constraint, so a lone one still flattens.\n const hasWidthToken = parsed.some((p) => p.token && p.token.cols !== 1 && p.token.cols !== \"any\");\n\n if ((isGrid || tokenCount >= 2 || hasWidthToken) && parsed.length > 0) {\n const cells: Cell[] = parsed.map((p) => ({ token: p.token ?? DEFAULT_TOKEN, node: p.node }));\n if (hasClass(el, \"caslider\")) {\n // A source slider row. `data-columns` = slides visible at a time; only a\n // positive integer is meaningful (conditional build keeps `columns`\n // absent, not undefined, under exactOptionalPropertyTypes).\n const cols = Number(el.getAttribute(\"data-columns\"));\n const slider = Number.isInteger(cols) && cols > 0 ? { columns: cols } : {};\n return { kind: \"row\", cells, slider };\n }\n return { kind: \"row\", cells };\n }\n const [only] = parsed;\n if (parsed.length === 1 && only) return only.node;\n if (parsed.length === 0) return { kind: \"raw\", html: rawInnerHtml(el) };\n return { kind: \"stack\", children: parsed.map((p) => p.node) };\n}\n\n/** The raw-fallback innerHTML, blanked when it is a JS-hydrated feed grid's\n * leftover `{{…}}` template (a container with no structural content of its\n * own). Real content is already extracted as nodes before this fallback is\n * reached, so Handlebars tokens here are never real content — dumping them\n * would render literal `{{media}}` text. A genuinely-unrecognized container\n * (no tokens) still surfaces verbatim as before. */\nfunction rawInnerHtml(el: HTMLElement): string {\n const html = el.innerHTML;\n return /\\{\\{[a-z_]+\\}\\}/i.test(html) ? \"\" : html;\n}\n\nconst BAND_ID_RE = /^page-block-(\\d+)$/;\n// The band wrapper's blocksN class — kept off the HTML because site.json\n// items[].class is unreliable (null for 7/16 the-pointe blocks).\nconst BLOCK_CLASS_RE = /\\bblocks\\d+\\b/;\n\n/** Read the band-level background media off a `camediaload` band wrapper. */\nfunction bandBackground(el: HTMLElement): Media | undefined {\n if (!hasClass(el, \"camediaload\")) return undefined;\n const rawId = el.getAttribute(\"data-media\");\n if (!rawId) return undefined;\n const ext = el.getAttribute(\"data-ext\") ?? undefined;\n const base = el.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n // A band background carries its own render sizing (background-size/position);\n // a corner-anchored `auto` accent must not be centered + full-bleed.\n ...readBgSizing(el),\n };\n}\n\n/** Parse the rendered Blux index.html into the page's top-level band tree. */\nexport function parseGridBands(html: string): Band[] {\n const root = parse(html);\n const content = root.querySelector(\"#page-content\");\n if (!content) return [];\n const bands: Band[] = [];\n for (const child of content.childNodes) {\n if (!isElement(child)) continue;\n const m = BAND_ID_RE.exec(child.getAttribute(\"id\") ?? \"\");\n if (!m) continue;\n const idStr = m[1];\n if (idStr === undefined) continue;\n const background = bandBackground(child);\n const blockClass = BLOCK_CLASS_RE.exec(child.classNames)?.[0];\n bands.push({\n index: Number(idStr),\n ...(blockClass ? { blockClass } : {}),\n ...(background ? { background } : {}),\n root: parseContainer(child),\n });\n }\n return bands;\n}\n","import type { GridToken } from \"./types.js\";\n\nconst TOKEN_RE = /\\bgrid-(\\d+|any)(?:-(r|s)(\\d+))?\\b/;\n\n/** Parse the `grid-*` layout token out of an element's class string.\n * `grid-2` -> equal 2-col; `grid-2-r60` -> 60% of a 2-col row; `grid-1-s40` ->\n * 1-col with 40px inter-cell spacing (the `s` suffix is the grid's gap, matching\n * `data-spacing`, NOT a width — width comes from the column count). Returns null\n * when the class has no grid token (e.g. `grid-container`, no column count). */\nexport function parseGridToken(className: string): GridToken | null {\n const m = TOKEN_RE.exec(className);\n if (!m) return null;\n const cols = m[1] === \"any\" ? \"any\" : Number(m[1]);\n const token: GridToken = { cols, raw: m[0] };\n if (m[2] === \"r\") token.ratio = Number(m[3]);\n if (m[2] === \"s\") token.spacing = Number(m[3]);\n return token;\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement } from \"node-html-parser\";\nimport type { Media, VideoPlayback } from \"./types.js\";\n\nconst ROLE_RE = /\\btext\\d+\\b/;\n\n/** The Blux text role (`text5`, `text11`, …) carried on a block-title/body/subtitle\n * element's class in the rendered HTML, or undefined when none is present. */\nexport function textRoleFromClass(className: string): string | undefined {\n return ROLE_RE.exec(className)?.[0];\n}\n\n/** The heading level (1..6) for an h1..h6 element. */\nexport function headingLevel(el: HTMLElement): number {\n const m = /^H([1-6])$/.exec(el.tagName ?? \"\");\n return m ? Number(m[1]) : 2;\n}\n\n/** Plain text of a block's inner HTML for a title field: a hard line break\n * (`<br>`, with or without attributes/self-close) becomes a newline; every other\n * tag drops and HTML entities decode (`Bar &amp; Grill` → `Bar & Grill`); all\n * source-formatting whitespace collapses to single spaces. Robust to\n * pretty-printed exports — insignificant newlines in the markup are NOT mistaken\n * for hard breaks — by routing `<br>` through a sentinel that survives the\n * whitespace collapse. */\nexport function blockPlainText(html: string): string {\n const BR = \"\\uE000\";\n // node-html-parser `.text` strips tags AND decodes entities; the <br>→BR swap\n // runs first so hard breaks survive as the sentinel through the collapse below.\n const text = parse(html.replace(/<br\\b[^>]*>/gi, BR)).text;\n return text\n .replace(/\\s+/g, \" \")\n .replace(/ *\\uE000 */g, \"\\n\")\n .trim();\n}\n\n/** The last path segment of a CDN url, sans extension (the Blux asset uuid). */\nfunction uuidFromUrl(url: string): { id: string; ext?: string } {\n const base = url.split(/[?#]/)[0] ?? \"\";\n const file = base.split(\"/\").pop() ?? \"\";\n const dot = file.lastIndexOf(\".\");\n return dot > 0 ? { id: file.slice(0, dot), ext: file.slice(dot + 1) } : { id: file };\n}\n\n/** Blux `data-media` is sometimes `uuid.ext` and sometimes a bare `uuid`; the\n * existing asset pipeline keys on the bare uuid (extension stripped), matching the\n * video path. Strip a trailing `.<ext>` when `data-ext` names it. */\nexport function stripAssetExt(rawId: string, ext?: string): string {\n return ext && rawId.endsWith(`.${ext}`) ? rawId.slice(0, -(ext.length + 1)) : rawId;\n}\n\n/** Read a single CSS declaration's value out of an inline `style` string. */\nexport function cssProp(style: string, prop: string): string | undefined {\n const m = new RegExp(`(?:^|;|\\\\s)${prop}\\\\s*:\\\\s*([^;]+)`, \"i\").exec(style);\n return m?.[1]?.trim();\n}\n\nconst MARGIN_UTIL_RE = /\\bmargin-(\\d+)(r|l|t|b)\\b/g;\nconst MARGIN_SIDES = { r: \"right\", l: \"left\", t: \"top\", b: \"bottom\" } as const;\n\n/** Decode Blux margin utility classes: `margin-20r` → `margin-right: 20%`.\n * Only `margin-N(r|l|t|b)` decodes — the `pd_*` padding utilities are always\n * duplicated inline in the export, so inline capture already covers them. */\nexport function utilityStylesFromClass(className: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const m of className.matchAll(MARGIN_UTIL_RE)) {\n const [, n, side] = m;\n if (n && side) out[`margin-${MARGIN_SIDES[side as keyof typeof MARGIN_SIDES]}`] = `${n}%`;\n }\n return out;\n}\n\n/** A text leaf's style deviations: the allowlisted inline declarations (`color`,\n * `padding`, `margin*` — everything else is theme noise) merged with the margin\n * utilities decoded off its class. An inline declaration wins over a class\n * utility on conflict. Null when the leaf carries neither, so callers can keep\n * the `style` key absent (exactOptionalPropertyTypes). */\nexport function textLeafStyle(el: HTMLElement): Record<string, string> | null {\n const out = utilityStylesFromClass(el.classNames ?? \"\");\n for (const decl of (el.getAttribute(\"style\") ?? \"\").split(\";\")) {\n const colon = decl.indexOf(\":\");\n if (colon < 0) continue;\n const prop = decl.slice(0, colon).trim().toLowerCase();\n const value = decl.slice(colon + 1).trim();\n if (!value) continue;\n if (prop === \"color\" || prop === \"padding\" || prop.startsWith(\"margin\")) out[prop] = value;\n }\n return Object.keys(out).length ? out : null;\n}\n\nconst CENTERED = new Set([\"center\", \"center center\", \"50% 50%\", \"50%\"]);\n\n/** Background sizing off a BAND-background wrapper's inline style: its\n * `background-size` → `fit` (a background's `auto`/`contain` is meaningful — a\n * native-size decorative accent, not a full-bleed `cover`) and its\n * `background-position` → `position` (e.g. \"right bottom\"). Unlike `readImgSizing`\n * (foreground), `auto` is kept; `cover` and a centered position are the render\n * defaults, so they are left absent to keep the manifest to deviations only. */\nexport function readBgSizing(el: HTMLElement): Pick<Media, \"fit\" | \"position\"> {\n const style = el.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"fit\" | \"position\"> = {};\n const size = cssProp(style, \"background-size\")?.toLowerCase();\n if (size === \"auto\" || size === \"contain\") out.fit = size;\n const pos = cssProp(style, \"background-position\");\n if (pos && !CENTERED.has(pos.toLowerCase())) out.position = pos;\n return out;\n}\n\n/** Intrinsic render sizing off a foreground image holder: the inline pixel\n * `width`, the `.mediaRatio` `data-og-ratio` (→ `aspect`), and the\n * `background-size` (→ `fit`, only when contain/cover — a background's `auto`\n * is not foreground sizing). Each field is present only when the source has it,\n * so a plain holder still yields a bare `Media`. */\nfunction readImgSizing(holder: HTMLElement): Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> {\n const style = holder.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> = {};\n // Only a pixel width is a faithful intrinsic size. A `%`/`vw`/`em`/`calc()`\n // width is relative to context and must NOT be mistaken for px (which the\n // render layer would then apply literally) — skip it, leaving `width` absent.\n const w = cssProp(style, \"width\");\n const wpx = w ? /^(\\d+(?:\\.\\d+)?)px$/i.exec(w) : null;\n if (wpx?.[1]) out.width = Math.round(parseFloat(wpx[1]));\n const ogr = holder.querySelector(\".mediaRatio\")?.getAttribute(\"data-og-ratio\");\n if (ogr) {\n const n = Number(ogr);\n if (Number.isFinite(n)) out.aspect = Math.round(n * 1000) / 1000;\n }\n const fit = cssProp(style, \"background-size\")?.toLowerCase();\n if (fit === \"contain\" || fit === \"cover\") out.fit = fit;\n // The holder's inline min-height (e.g. \"80vh\" on a slider slide) is the\n // height the export reserves for a cover-rendered frame — keep it.\n const mh = cssProp(style, \"min-height\");\n if (mh) out.minHeight = mh;\n return out;\n}\n\n/** Playback semantics from a `<video>`'s boolean attributes — only those PRESENT\n * are set (an absent field = attribute absent). Undefined when none is present. */\nfunction readVideoPlayback(video: HTMLElement): VideoPlayback | undefined {\n const flags = [\"controls\", \"playsinline\", \"autoplay\", \"loop\", \"muted\"] as const;\n const pb: VideoPlayback = {};\n for (const f of flags) if (video.hasAttribute(f)) pb[f] = true;\n return Object.keys(pb).length ? pb : undefined;\n}\n\n/** Intrinsic aspect for a foreground `<video>`, reserved on a nearby\n * `.ib[data-og-ratio]` holder OR a `.mediaRatio` (inline `padding-bottom:NN%`).\n * Values are percent-suffixed strings (e.g. \"56.25%\") — strip the `%` (raw\n * `Number()` NaNs), then reuse the `aspect` = height-%-of-width convention.\n * Fail-safe: no parseable ratio → undefined (video keeps its bare shape). */\nfunction readVideoAspect(video: HTMLElement): number | undefined {\n let raw: string | undefined;\n let anc: HTMLElement | null | undefined = video.parentNode;\n for (let i = 0; i < 3 && anc && !raw; i++) {\n raw = anc.getAttribute?.(\"data-og-ratio\") ?? undefined;\n if (!raw) {\n const mr = anc.querySelector?.(\".mediaRatio\");\n raw = mr ? cssProp(mr.getAttribute(\"style\") ?? \"\", \"padding-bottom\") : undefined;\n }\n anc = anc.parentNode as HTMLElement | null | undefined;\n }\n if (!raw) return undefined;\n const n = parseFloat(raw.replace(/%\\s*$/, \"\"));\n return Number.isFinite(n) ? Math.round(n * 1000) / 1000 : undefined;\n}\n\n/** Resolve the media an element carries: a `.camediaload` descendant (image, via\n * `data-media`) or a `<video>` (via its src uuid). Returns null when there is none. */\nexport function mediaFromElement(el: HTMLElement): Media | null {\n if (el.tagName === \"VIDEO\") {\n const src = el.getAttribute(\"src\") ?? \"\";\n const { id, ext } = uuidFromUrl(src);\n if (!id) return null;\n // The full CDN url sits on `<video src>`; capture its prefix as `base` (the\n // same field an image carries from `data-base`) so `mediaCdnUrl` rebuilds it\n // OFFLINE. Without this a video resolves only via the IR sourceUrl, i.e. it\n // depends on site.json listing the asset — breaking convert's offline\n // invariant even though the url is right here in the markup.\n const clean = src.split(/[?#]/)[0] ?? \"\";\n const slash = clean.lastIndexOf(\"/\");\n const base = slash >= 0 ? clean.slice(0, slash + 1) : undefined;\n const aspect = readVideoAspect(el);\n const playback = readVideoPlayback(el);\n return {\n kind: \"video\",\n assetId: id,\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...(aspect !== undefined ? { aspect } : {}),\n ...(playback ? { playback } : {}),\n };\n }\n const img =\n el.classList.contains(\"camediaload\") && el.getAttribute(\"data-media\")\n ? el\n : el.querySelector(\".camediaload[data-media]\");\n if (img) {\n const rawId = img.getAttribute(\"data-media\");\n if (rawId) {\n const ext = img.getAttribute(\"data-ext\") ?? undefined;\n const base = img.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...readImgSizing(img),\n };\n }\n }\n const video = el.querySelector(\"video\");\n if (video) return mediaFromElement(video);\n return null;\n}\n","import type { Band, Cell, Media, Node, Widget } from \"./types.js\";\nimport type { CarouselSlide, CarouselSpec, SliceSpec } from \"./slice-spec.js\";\nimport { blockPlainText } from \"./leaf.js\";\n\n/** Depth-first collect of every `media` node's `Media` in a subtree. */\nexport function collectMedia(node: Node): Media[] {\n switch (node.kind) {\n case \"media\":\n return [node.media];\n case \"row\":\n return node.cells.flatMap((c) => collectMedia(c.node));\n case \"stack\":\n return node.children.flatMap(collectMedia);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of text nodes (heading/body/subtitle). */\nexport function collectText(node: Node): Node[] {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectText(c.node));\n case \"stack\":\n return node.children.flatMap(collectText);\n case \"media\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every widget in a subtree. */\nexport function collectWidgets(node: Node): Widget[] {\n switch (node.kind) {\n case \"widget\":\n return [node.widget];\n case \"row\":\n return node.cells.flatMap((c) => collectWidgets(c.node));\n case \"stack\":\n return node.children.flatMap(collectWidgets);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every `raw` node in a subtree. */\nfunction collectRaws(node: Node): Node[] {\n switch (node.kind) {\n case \"raw\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectRaws(c.node));\n case \"stack\":\n return node.children.flatMap(collectRaws);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n return [];\n }\n}\n\n/** The root row NODE, or null when the root is not a single row. A `stack`\n * whose only child is a row also counts (Blux wraps rows in holders). */\nexport function topRowNode(node: Node): Extract<Node, { kind: \"row\" }> | null {\n if (node.kind === \"row\") return node;\n if (node.kind === \"stack\" && node.children.length === 1) {\n const [only] = node.children;\n if (only && only.kind === \"row\") return only;\n }\n return null;\n}\n\n/** The cells of the root row, or null when the root is not a single row. */\nexport function topRow(node: Node): Cell[] | null {\n return topRowNode(node)?.cells ?? null;\n}\n\n/** A `raw` node carrying no rendered text or nested block — the shape a\n * client-injected mount (e.g. the map container) parses to. */\nexport function isEmptyRaw(node: Node): boolean {\n if (node.kind !== \"raw\") return false;\n const text = node.html.replace(/<[^>]*>/g, \"\").trim();\n return text.length === 0;\n}\n\n/** Options for the classifier. `isMapMount` is injected by plan 4\n * (`extract-map.ts`); by default nothing is recognized as a map. */\nexport type ClassifyOptions = {\n isMapMount?: (node: Node) => boolean;\n};\n\n/** The band's slice-zone base carried onto every spec (conditional spread keeps\n * `blockClass`/`background` absent, not `undefined`, under\n * exactOptionalPropertyTypes). */\nfunction base(band: Band): { index: number; blockClass?: string; background?: Media } {\n return {\n index: band.index,\n ...(band.blockClass ? { blockClass: band.blockClass } : {}),\n ...(band.background ? { background: band.background } : {}),\n };\n}\n\n/** Plain text of a heading/subtitle/body node. Hard line breaks (Blux `<br>`)\n * survive as newlines so the render layer can split a display title back into\n * lines; all other tags and source whitespace collapse to single spaces. */\nfunction nodeText(node: Node): string {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n // Raw markup — `<br>` → newline, other tags + source formatting → spaces.\n return blockPlainText(node.html);\n case \"subtitle\":\n // Already normalized at parse via blockPlainText (entities decoded, hard\n // breaks as newlines, source whitespace collapsed) — pass it through.\n return node.text.trim();\n case \"row\":\n case \"stack\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return \"\";\n }\n}\n\n/** The single media of a pure-media cell, or null if the cell isn't pure media. */\n/** See through synthetic style boxes — a one-child stack carrying a peeled\n * wrapper's padding/fill — when pattern-matching. The box is presentation, not\n * structure: a cell must not flip from SplitFeature/Gallery to Grid just\n * because its container gained a captured inset. */\nfunction unboxed(node: Node): Node {\n let n = node;\n while (n.kind === \"stack\" && n.children.length === 1) {\n const only = n.children[0];\n if (!only) break;\n n = only;\n }\n return n;\n}\n\nfunction pureCellMedia(cell: Cell): Media | null {\n const n = unboxed(cell.node);\n return n.kind === \"media\" ? n.media : null;\n}\n\n/** TitleBand/Hero presentation metadata: the heading's textN role + h-level and\n * the subtitle's role. Carried alongside the plain-string text (page-doc) so the\n * render applies the right display font/tag — band 15's script accent heading\n * (`h2.text11`) must not render like a plain `text5` title. */\nfunction textRoleMeta(\n heading: Node | undefined,\n subtitle: Node | undefined,\n): { headingRole?: string; headingLevel?: number; subtitleRole?: string } {\n return {\n ...(heading?.kind === \"heading\" && heading.role ? { headingRole: heading.role } : {}),\n ...(heading?.kind === \"heading\" ? { headingLevel: heading.level } : {}),\n ...(subtitle?.kind === \"subtitle\" && subtitle.role ? { subtitleRole: subtitle.role } : {}),\n };\n}\n\n/** A cell's effective column share as a percentage, from its grid token.\n *\n * Width comes from the explicit `ratio` (`grid-2-r60` → 60) or, absent that,\n * an equal split of the column count. `spacing` (the `s` suffix) is a gap, not\n * a width, so it never factors in here. The `\"any\"` → 50 fallback assumes a\n * 2-cell row — the only shape that reaches here today (SplitFeature requires\n * exactly two cells); a bare-`any` cell in a wider row would need real division. */\nfunction cellRatio(cell: Cell): number {\n const t = cell.token;\n if (typeof t.ratio === \"number\") return t.ratio;\n if (t.cols === \"any\") return 50;\n return Math.round(100 / t.cols);\n}\n\nconst isTextNode = (n: Node): boolean =>\n n.kind === \"heading\" || n.kind === \"body\" || n.kind === \"subtitle\";\n\n/** A text node with no rendered text — an empty hero body block (`<p></p>`) or\n * a whitespace-only heading/subtitle. Not a real caption line. */\nconst isBlankText = (n: Node): boolean => {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) === \"\";\n if (n.kind === \"subtitle\") return n.text.trim() === \"\";\n return false;\n};\n\n/** The carousel slides of a slider row, or null when any cell isn't a media\n * slide. A slide is a bare `media` cell, or a stack whose FIRST child is media\n * followed only by text nodes — the band-8 gallery slide `stack[media,\n * heading]` and the full-page hero slide `stack[media, heading, body]` (title +\n * location). The heading is the caption; the first non-blank body/subtitle\n * after it is the `subcaption` (the hero's location line). A slide with a\n * non-text tail (a nested row/media) is richer than a captioned slide, so the\n * band stays a faithful Grid. ≥2 qualifying slides required. */\nfunction carouselSlides(cells: Cell[]): CarouselSlide[] | null {\n const out: CarouselSlide[] = [];\n for (const c of cells) {\n const n = c.node;\n if (n.kind === \"media\") {\n out.push({ media: n.media });\n continue;\n }\n if (n.kind === \"stack\" && n.children.length >= 2) {\n const [m, ...rest] = n.children;\n const h = rest.find((r) => r.kind === \"heading\");\n if (m?.kind === \"media\" && h?.kind === \"heading\" && rest.every(isTextNode)) {\n // The secondary line: the first body/subtitle after the title that\n // carries real text (the hero's location; skip empty body blocks).\n const sub = rest.find(\n (r) => r !== h && (r.kind === \"body\" || r.kind === \"subtitle\") && !isBlankText(r),\n );\n const subcaption =\n sub?.kind === \"body\"\n ? { html: sub.html, ...(sub.role ? { role: sub.role } : {}) }\n : sub?.kind === \"subtitle\"\n ? { html: sub.text, ...(sub.role ? { role: sub.role } : {}) }\n : undefined;\n out.push({\n media: m.media,\n caption: { html: h.html, level: h.level, ...(h.role ? { role: h.role } : {}) },\n ...(subcaption ? { subcaption } : {}),\n });\n continue;\n }\n }\n return null;\n }\n return out.length >= 2 ? out : null;\n}\n\n/** If every cell of a row is exactly one media node, return them in order. */\nfunction galleryMedia(cells: Cell[]): Media[] | null {\n const out: Media[] = [];\n for (const c of cells) {\n const m = pureCellMedia(c);\n if (!m) return null;\n out.push(m);\n }\n return out.length >= 2 ? out : null;\n}\n\n/** Return a copy of the tree with every node matching `isMapMount` replaced by a\n * `widget:map` node. Pure — does not mutate the input. Top-down: a matched\n * container is replaced whole, so its children are never visited. */\nfunction rewriteMapMounts(node: Node, isMapMount: (n: Node) => boolean): Node {\n if (isMapMount(node)) return { kind: \"widget\", widget: { type: \"map\" } };\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({\n token: c.token,\n node: rewriteMapMounts(c.node, isMapMount),\n })),\n // Preserve the row's own markers when rebuilding: dropping the slider\n // would silently demote a Carousel to Grid, and dropping the style would\n // lose a card background — for every band whenever a map config exists.\n ...(node.slider ? { slider: node.slider } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"stack\":\n return {\n kind: \"stack\",\n children: node.children.map((n) => rewriteMapMounts(n, isMapMount)),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return node;\n }\n}\n\n/** The single significant child of a container (ignoring empty raw), or the node\n * itself. Used to detect a band whose dominant content is one widget. */\nfunction soleSignificant(node: Node): Node {\n let kids: Node[];\n switch (node.kind) {\n case \"row\":\n kids = node.cells.map((c) => c.node);\n break;\n case \"stack\":\n kids = node.children;\n break;\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n kids = [node];\n break;\n }\n const significant = kids.filter((n) => !isEmptyRaw(n));\n return significant.length === 1 && significant[0] ? significant[0] : node;\n}\n\n/** Classify one band into a SliceSpec. Conservative: only unambiguous shapes\n * become pattern slices; everything else is a render-faithful Grid fallback. */\nexport function classifyBand(band: Band, opts: ClassifyOptions = {}): SliceSpec {\n // Widget rewrite runs FIRST, so the pattern branches and the Grid fallback\n // all see `widget` nodes in place of injected mounts.\n const root = opts.isMapMount ? rewriteMapMounts(band.root, opts.isMapMount) : band.root;\n const widgets = collectWidgets(root);\n const media = collectMedia(root);\n const text = collectText(root);\n const rowNode = topRowNode(root);\n const row = rowNode ? rowNode.cells : null;\n // A raw node with real content is text we cannot account for — every\n // promotion must refuse to fire over it (only the Grid fallback keeps it).\n const hasSignificantRaw = collectRaws(root).some((n) => !isEmptyRaw(n));\n\n // Top-level widget promotion (before the structural patterns).\n const sole = soleSignificant(root);\n if (sole.kind === \"widget\" && sole.widget.type === \"map\") {\n return { slice: \"LocationMap\", ...base(band) };\n }\n if (\n media.length === 1 &&\n media[0]?.kind === \"video\" &&\n text.length === 0 &&\n widgets.length === 0 &&\n row === null &&\n !hasSignificantRaw\n ) {\n const v = media[0];\n return { slice: \"VideoFeature\", ...base(band), media: v };\n }\n\n const headings = text.filter((n) => n.kind === \"heading\");\n const subtitles = text.filter((n) => n.kind === \"subtitle\");\n const bodies = text.filter((n) => n.kind === \"body\");\n\n // Text-only bands (no media, no row, no widgets, no significant raw — any\n // co-located content must survive via the Grid fallback, not be swallowed).\n if (media.length === 0 && row === null && widgets.length === 0 && !hasSignificantRaw) {\n // TitleBand: exactly one heading + at most one subtitle, nothing else —\n // TitleBandSpec has nowhere to carry surplus text.\n if (headings.length === 1 && subtitles.length <= 1 && bodies.length === 0 && !band.background) {\n const first = headings[0];\n const sub = subtitles[0];\n return {\n slice: \"TitleBand\",\n ...base(band),\n heading: first ? nodeText(first) : \"\",\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...textRoleMeta(first, sub),\n };\n }\n // RichText: body node(s) only — a subtitle would be dropped from the html.\n if (headings.length === 0 && subtitles.length === 0 && bodies.length > 0 && !band.background) {\n return {\n slice: \"RichText\",\n ...base(band),\n html: bodies.map((b) => (b.kind === \"body\" ? b.html : \"\")).join(\"\\n\"),\n };\n }\n }\n\n // Full-bleed hero: a background image with overlay text and no grid row. At\n // most one of each overlay text kind — HeroSpec keeps one heading/subtitle/\n // body, so surplus would be silently dropped.\n if (\n band.background &&\n headings.length === 1 &&\n subtitles.length <= 1 &&\n bodies.length <= 1 &&\n row === null &&\n media.length === 0 &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const h = headings[0];\n const sub = subtitles[0];\n const bod = bodies[0];\n return {\n slice: \"Hero\",\n ...base(band),\n ...(h ? { heading: nodeText(h) } : {}),\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...(bod && bod.kind === \"body\" ? { body: bod.html } : {}),\n ...textRoleMeta(h, sub),\n };\n }\n\n // Carousel: a source slider row (.caslider) whose every cell is a media\n // slide, optionally captioned (stack[media, heading]). Anything richer\n // falls through to the faithful Grid fallback.\n if (rowNode?.slider) {\n const slides = carouselSlides(rowNode.cells);\n if (slides) {\n const spec: CarouselSpec = { slice: \"Carousel\", ...base(band), slides };\n if (rowNode.slider.columns !== undefined) spec.columns = rowNode.slider.columns;\n return spec;\n }\n }\n\n // Gallery: a row whose cells are all single media.\n if (row) {\n const gm = galleryMedia(row);\n if (gm) return { slice: \"Gallery\", ...base(band), media: gm };\n }\n\n // MediaFull: one media, no text, no top row, no widgets, no significant raw —\n // a row sibling, a co-located widget (e.g. a map mount), or raw prose would\n // be silently dropped, so those stay Grid.\n if (\n media.length === 1 &&\n text.length === 0 &&\n row === null &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const m = media[0];\n if (m) return { slice: \"MediaFull\", ...base(band), media: m };\n }\n\n // SplitFeature: exactly two cells, one pure media, one text-bearing.\n if (row && row.length === 2) {\n const [c0, c1] = row;\n if (c0 && c1) {\n const m0 = pureCellMedia(c0);\n const m1 = pureCellMedia(c1);\n const t0 = collectText(c0.node).length > 0;\n const t1 = collectText(c1.node).length > 0;\n if (m0 && !m1 && t1) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m0,\n mediaSide: \"left\",\n ratio: cellRatio(c0),\n text: c1.node,\n };\n }\n if (m1 && !m0 && t0) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m1,\n mediaSide: \"right\",\n ratio: cellRatio(c1),\n text: c0.node,\n };\n }\n }\n }\n\n return { slice: \"Grid\", ...base(band), root };\n}\n\nexport function classifyBands(bands: Band[], opts: ClassifyOptions = {}): SliceSpec[] {\n return bands.map((b) => classifyBand(b, opts));\n}\n","// Feed-grid materialization. Blux feed grids (gallery/portfolio) render their\n// tiles CLIENT-SIDE from feed records — the static export ships only a\n// display:none {{…}} template (dropped by the parser). This module rebuilds\n// the visible tiles DETERMINISTICALLY from the feed data at convert time, so\n// the faithful render shows the real content instead of an empty band.\n//\n// A feed band's site.json item carries `sources` (a feed id, or `__media` for\n// the media library) and `sourceConfig` (a tag filter, a sort, per-tile style\n// config). We resolve the matching records, expand each into a tile\n// (image + optional title/body), and return a Grid node tree — so the band\n// classifies and renders as any other Grid, with no new render surface.\nimport type { Cell, Media, Node } from \"./types.js\";\nimport { CDN_HOSTS } from \"../assets.js\";\n\n/** A media-library entry (`site.json.media[uuid]`) or a feed record. Both are\n * loose bags; we read only the fields the tile needs. */\nexport type FeedRecord = Record<string, unknown>;\n\n/** A resolved tile: an image (already a Media) and/or display text. `title`\n * and `body` are RENDER-READY HTML — feed records store them as HTML (entities\n * pre-encoded, `<br>` markup), and `__media` plain text (name/description) is\n * escaped at resolve time — so the render places them verbatim, never\n * re-escaping (which would double-encode `&amp;` or show a literal `<br>`). */\nexport type FeedTile = { media?: Media; title?: string; body?: string };\n\n/** Everything the materializer needs from the export, injected so the module\n * stays pure/offline and testable. */\nexport type FeedResolvers = {\n /** Feed records by feed id (site.json feeds → their JSON arrays). */\n feeds: Map<string, FeedRecord[]>;\n /** The media library: uuid → its `{ name, type, tags }` entry. */\n media: Map<string, FeedRecord>;\n /** Build a Media (with a resolved url base) for an asset uuid, given its mime\n * type and/or filename (either can supply the extension) — the render\n * resolver turns Media→url the same way it does for parsed media, so feed\n * images flow through one url path. */\n mediaFor: (uuid: string, type: string | undefined, name?: string) => Media | null;\n};\n\n/** A term matches a tag when they're equal OR differ only by a trailing `s`\n * (singular/plural) — Blux's server-side feed resolver stems this way, so a\n * `projects` filter also selects `project`-tagged media (7 real gallery tiles\n * that an exact match drops). Conservative: only a single trailing `s`, so it\n * never over-selects unrelated tags. */\nconst termMatchesTag = (term: string, tag: string): boolean =>\n term === tag ||\n (term.endsWith(\"s\") && term.slice(0, -1) === tag) ||\n (tag.endsWith(\"s\") && tag.slice(0, -1) === term);\n\n/** Parse a Blux tag filter expression into a predicate over a tag set. The\n * DSL: `&&` joins AND terms, `||` joins OR groups; a record matches when ANY\n * OR group has ALL its terms present (singular/plural-insensitive, see\n * `termMatchesTag`). Leading/empty terms (`&&metal&&sofa`) are ignored.\n * Case-insensitive. An empty/absent expression matches all. */\nexport function tagFilter(expr: string | undefined): (tags: string[]) => boolean {\n const groups = (expr ?? \"\")\n .split(\"||\")\n .map((g) =>\n g\n .split(\"&&\")\n .map((t) => t.trim().toLowerCase())\n .filter(Boolean),\n )\n .filter((g) => g.length > 0);\n if (!groups.length) return () => true;\n return (tags) => {\n const set = tags.map((t) => t.toLowerCase());\n return groups.some((g) => g.every((term) => set.some((tag) => termMatchesTag(term, tag))));\n };\n}\n\n/** Records sorted by a Blux `sort` key. `title` → by title with\n * `localeCompare` (matching Blux's own client sort, which uses\n * `((a.sort||\"\")+\"\").localeCompare(b.sort)` for non-numeric sort values);\n * `fdate`/`date` → by the record's date descending (newest first, Blux's\n * default), the undated last; anything else → source order preserved.\n * Stable. */\nfunction sortRecords(records: FeedRecord[], sort: string | undefined): FeedRecord[] {\n if (sort === \"title\") {\n return [...records].sort((a, b) =>\n String(a[\"title\"] ?? \"\").localeCompare(String(b[\"title\"] ?? \"\")),\n );\n }\n if (sort === \"fdate\" || sort === \"date\") {\n return [...records].sort((a, b) =>\n String(b[\"date\"] ?? \"\").localeCompare(String(a[\"date\"] ?? \"\")),\n );\n }\n return records;\n}\n\n/** A validated \"W:H\" crop ratio (e.g. \"4:3\") from a sourceConfig value, or\n * undefined when absent/malformed. Both terms must be positive numbers. */\nexport function cropRatioOf(v: unknown): string | undefined {\n if (typeof v !== \"string\") return undefined;\n const m = /^\\s*(\\d+(?:\\.\\d+)?)\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(v);\n if (!m) return undefined;\n const w = Number(m[1]);\n const h = Number(m[2]);\n return w > 0 && h > 0 ? `${w}:${h}` : undefined;\n}\n\nconst isDisabled = (r: FeedRecord): boolean => r[\"disabled\"] === true || r[\"disable\"] === true;\nconst styleDisabled = (cfg: unknown): boolean =>\n !!cfg && typeof cfg === \"object\" && (cfg as { class?: string })[\"class\"] === \"disable\";\n\n/** Resolve a feed band's source into ordered tiles. `sources[0]` is either\n * `__media` (the media library, tiles = tag-matched images) or a feed id\n * (tiles = its records, filtered + sorted, expanded via the template config).\n * Returns null when the source is unknown or yields nothing. */\nexport function resolveFeedTiles(\n bandDef: {\n sources?: unknown;\n sourceConfig?: Record<string, unknown>;\n },\n resolvers: FeedResolvers,\n): FeedTile[] | null {\n const sources = Array.isArray(bandDef.sources) ? bandDef.sources.map(String) : [];\n const source = sources[0];\n if (!source) return null;\n const cfg = bandDef.sourceConfig ?? {};\n const filterExpr = (cfg[\"filters\"] as { tag?: string } | undefined)?.tag;\n const match = tagFilter(filterExpr);\n const sort = cfg[\"sort\"] as string | undefined;\n const bodyOff = styleDisabled(cfg[\"_body\"]);\n const titleOff = styleDisabled(cfg[\"_title\"]);\n // The grid's tile crop ratio (\"4:3\"): the render frames each tile image in a\n // fixed-aspect cover box (uniform gallery tiles, like the original).\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n const framed = (m: Media | null): Media | null =>\n m && ratio ? { ...m, cropRatio: ratio, fit: \"cover\" } : m;\n\n if (source === \"__media\") {\n // Media-library grid: every tag-matched image, sorted by the config (the\n // gallery/portfolio grids are `fdate` — newest first). Library entries DO\n // carry display text: `name` is a caption (a real title, not a filename)\n // and `description` the body — Blux binds both into the tile overlay\n // (unless _title/_body is disabled). These are PLAIN text, so escape them.\n const matched: FeedRecord[] = [];\n for (const [uuid, entry] of resolvers.media) {\n const type = String(entry[\"type\"] ?? \"\");\n if (!type.startsWith(\"image/\")) continue;\n if (!match((entry[\"tags\"] as string[] | undefined) ?? [])) continue;\n matched.push({ ...entry, __uuid: uuid });\n }\n const tiles = sortRecords(matched, sort)\n .map((entry): FeedTile => {\n const media = framed(\n resolvers.mediaFor(\n String(entry[\"__uuid\"]),\n entry[\"type\"] as string | undefined,\n entry[\"name\"] as string | undefined,\n ),\n );\n const tile: FeedTile = {};\n if (media) tile.media = media;\n if (!titleOff && entry[\"name\"]) tile.title = escapeHtml(String(entry[\"name\"]));\n if (!bodyOff && entry[\"description\"]) tile.body = plainToHtml(String(entry[\"description\"]));\n return tile;\n })\n .filter((t) => t.media || t.title || t.body);\n return tiles.length ? tiles : null;\n }\n\n const records = resolvers.feeds.get(source);\n if (!records) return null;\n const enabled = records.filter((r) => !isDisabled(r));\n const filtered = filterExpr\n ? enabled.filter((r) => match((r[\"tags\"] as string[]) ?? []))\n : enabled;\n const ordered = sortRecords(filtered, sort);\n const tiles = ordered.map((r): FeedTile => {\n const tile: FeedTile = {};\n const m = r[\"media\"] as { media?: string; type?: string } | undefined;\n if (m?.media) {\n const media = framed(resolvers.mediaFor(m.media, m.type));\n if (media) tile.media = media;\n }\n // Feed record title/body are stored as HTML (entities encoded, `<br>`\n // markup) — keep them VERBATIM, never re-escape.\n if (!titleOff && r[\"title\"]) tile.title = String(r[\"title\"]);\n if (!bodyOff && r[\"body\"]) tile.body = String(r[\"body\"]);\n return tile;\n });\n return tiles.length ? tiles : null;\n}\n\n/** An overlay tile treatment (Blux `layout: \"behind\"`, `overlay: true`): the\n * caption sits OVER the cropped image (a colored panel revealed on hover), so a\n * tile is only as tall as its image — not image + a caption row below. Threaded\n * onto the tile stack as `_overlay` presentation hints the render consumes. */\nexport type TileOverlay = { ratio: string; color?: string; valign?: string };\n\n/** A materialized feed grid: the band's heading (if any) over a row of tile\n * cells. Each tile is a stack of its image + title + body (whichever it has);\n * a lone image/heading stays bare. With `overlay`, the tile becomes an\n * overlay card (caption over the cropped image). `columns` sets each cell's\n * grid token so the render lays them out in a grid (the source `columns`/\n * `data-columns`; default 3). Returns the heading alone when there are no\n * tiles, null when neither. */\nexport function materializeFeedGrid(opts: {\n heading?: { html: string; level: number; role?: string };\n tiles: FeedTile[] | null;\n columns: number;\n spacing?: number;\n overlay?: TileOverlay;\n}): Node | null {\n const { heading, tiles, columns, spacing, overlay } = opts;\n const headingNode: Node | null = heading\n ? {\n kind: \"heading\",\n level: heading.level,\n html: heading.html,\n ...(heading.role ? { role: heading.role } : {}),\n }\n : null;\n if (!tiles || !tiles.length) return headingNode;\n\n const cols = Math.max(1, Math.round(columns));\n const cells: Cell[] = tiles.map((t) => {\n const parts: Node[] = [];\n if (t.media) parts.push({ kind: \"media\", media: t.media });\n // title/body are already render-ready HTML (feed records store HTML;\n // __media plain text was escaped at resolve time) — place them verbatim.\n if (t.title) parts.push({ kind: \"heading\", level: 6, html: t.title, role: \"text6\" });\n if (t.body) parts.push({ kind: \"body\", html: t.body });\n let node: Node;\n if (overlay && t.media) {\n // Overlay card: the stack carries the crop ratio + panel style; the\n // render cover-fills the media and overlays the caption on it.\n node = {\n kind: \"stack\",\n children: parts,\n style: {\n _overlay: overlay.ratio,\n ...(overlay.color ? { _overlayColor: overlay.color } : {}),\n ...(overlay.valign ? { _overlayValign: overlay.valign } : {}),\n },\n };\n } else {\n node = parts.length === 1 ? parts[0]! : { kind: \"stack\", children: parts };\n }\n return {\n token: {\n cols,\n raw: `grid-${cols}${spacing ? `-s${spacing}` : \"\"}`,\n ...(spacing ? { spacing } : {}),\n },\n node,\n };\n });\n const row: Node = { kind: \"row\", cells };\n return headingNode ? { kind: \"stack\", children: [headingNode, row] } : row;\n}\n\n/** HTML-escape a PLAIN-text value (a `__media` name/description) for placement\n * into an html-bearing node. Feed-record title/body are already HTML and skip\n * this. */\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\n/** A plain-text body (`__media` description) as body html: escaped, with the\n * source's hard newlines becoming `<br>` (Blux descriptions are multi-line —\n * \"DESIGN: …\\nPROCUREMENT: …\"), wrapped in a `<p>`. */\nfunction plainToHtml(s: string): string {\n const inner = escapeHtml(s.trim()).replace(/\\r?\\n/g, \"<br>\");\n return `<p>${inner}</p>`;\n}\n\n/** A file extension for a media asset: the mime map first (image/jpeg → jpg),\n * else the extension off the entry's own filename (`name`) — Blux names carry\n * the real extension, so an unmapped/absent mime (image/jpg, avif, heic, a\n * bare `custom`) still resolves instead of silently dropping the tile. null\n * only when neither yields an image extension. */\nexport function extFor(mime: string | undefined, name: string | undefined): string | null {\n const byMime: Record<string, string> = {\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n \"image/avif\": \"avif\",\n \"image/bmp\": \"bmp\",\n \"image/tiff\": \"tiff\",\n };\n if (mime && byMime[mime]) return byMime[mime];\n const m = /\\.([a-z0-9]{2,5})$/i.exec(name ?? \"\");\n const ext = m?.[1]?.toLowerCase();\n const IMG = new Set([\"jpg\", \"jpeg\", \"png\", \"svg\", \"gif\", \"webp\", \"avif\", \"bmp\", \"tif\", \"tiff\"]);\n return ext && IMG.has(ext) ? (ext === \"jpeg\" ? \"jpg\" : ext) : null;\n}\n\n/** Is this a feed-driven band? — its site.json item declares `sources`. */\nexport function isFeedBand(\n item: unknown,\n): item is { sources: unknown[]; sourceConfig?: Record<string, unknown> } {\n return (\n !!item &&\n typeof item === \"object\" &&\n Array.isArray((item as { sources?: unknown }).sources) &&\n (item as { sources: unknown[] }).sources.length > 0\n );\n}\n\n/** The CDN base (`https://<host>/<siteId>/`) an export actually serves assets\n * from — scraped from a rendered `data-base` so the RIGHT host is used (Blux\n * spreads assets across two CDN hosts; a hardcoded host would 404 the other).\n * Falls back to the first known host + siteId when no data-base is present. */\nexport function feedAssetBase(htmls: string[], siteId: string): string {\n for (const html of htmls) {\n const m = /data-base=\"(https?:\\/\\/[^\"]+?\\/)\"/i.exec(html);\n if (m?.[1]) return m[1].replace(/^http:/, \"https:\");\n }\n return `https://${CDN_HOSTS[0]}/${siteId}/`;\n}\n\n/** Build the feed resolvers from a parsed site: feed records (site.json.feeds\n * carries them inline), the media library, and a Media builder that\n * reconstructs the CDN url (`<base><uuid>.<ext>` — the untransformed base the\n * export's own `data-base` uses, which resolves full-res). `base` comes from\n * `feedAssetBase` (the export's real host); `ext` from mime-or-filename. Pure. */\nexport function buildFeedResolvers(\n feeds: Record<string, { items?: FeedRecord[] } | undefined>,\n mediaLibrary: Record<string, FeedRecord>,\n base: string,\n): FeedResolvers {\n const feedMap = new Map<string, FeedRecord[]>();\n for (const [id, f] of Object.entries(feeds)) {\n if (Array.isArray(f?.items)) feedMap.set(id, f.items);\n }\n const mediaMap = new Map<string, FeedRecord>(Object.entries(mediaLibrary));\n return {\n feeds: feedMap,\n media: mediaMap,\n mediaFor: (uuid, type, name) => {\n const ext = extFor(type, name);\n if (!ext) return null;\n return { kind: \"image\", assetId: uuid, base, ext };\n },\n };\n}\n","// Products feed → a clean, materialized product catalog. Blux renders a detail\n// page per feed record at /products/<slug> from a Handlebars template; the\n// static export ships only the template, so this rebuilds the catalog\n// deterministically at convert time: canonical categories (the raw feed data is\n// dirty — whitespace/case variants + typos), the faithful url-or-derive slug,\n// and resolved main + gallery images. Pure; the convert injects `resolveImage`.\n\nexport type ProductImage = { assetId: string; url: string };\n\nexport type Product = {\n /** The detail-page slug: /products/<slug>. */\n slug: string;\n title: string;\n /** Canonical category (drives the back-link + faceting). */\n category: string;\n /** Canonical sub-category, or \"\" when the record has none. */\n subCategory: string;\n /** Rendered verbatim — the export's dimension strings are inconsistent\n * (`94\"W x 38\"D` vs `94\" W x 38\" D`) and that's what the live page shows. */\n dimensions: string;\n tags: string[];\n /** Hidden from listing grids, but Blux still serves its detail page (200), so\n * the page is generated regardless. */\n disabled: boolean;\n /** The main image, when the record has one (39/552 have none). */\n image?: ProductImage;\n /** Additional images shown as detail-page thumbnails (160/552 have ≥1). */\n gallery: ProductImage[];\n};\n\nexport type ProductRecord = {\n title?: unknown;\n category?: unknown;\n sub_category?: unknown;\n dimensions?: unknown;\n tags?: unknown;\n disabled?: unknown;\n /** Present on only a handful of records; when set it is the authoritative\n * slug (and can override what the title would derive). */\n url?: unknown;\n media?: unknown;\n items?: unknown;\n};\n\n// The five product categories the site publishes. Upholstered/Case/Exterior are\n// populated by the feed; Metal/Finishes are listing pages with no feed records.\nconst CANONICAL_CATEGORIES = [\"Upholstered\", \"Case\", \"Exterior\", \"Metal\", \"Finishes\"];\n\n// Misspellings in the raw data that don't fold by case/whitespace alone.\nconst CATEGORY_ALIASES: Record<string, string> = {\n upholstrered: \"Upholstered\",\n upholsered: \"Upholstered\",\n};\n\n// Sub-category variants that need explicit folding (order/wording/typos).\nconst SUBCATEGORY_ALIASES: Record<string, string> = {\n banuette: \"Banquette\",\n \"benches & ottomans\": \"Ottomans & Benches\",\n miscellaneous: \"Misc.\",\n};\n\nfunction cleanWs(s: unknown): string {\n return String(s ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(s: string): string {\n return s.replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Canonicalize a raw product category. Folds whitespace/case variants and\n * known typos onto the canonical set; an unrecognized category is kept\n * (title-cased) rather than dropped, so no product is silently lost. */\nexport function normalizeCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (CATEGORY_ALIASES[lower]) return CATEGORY_ALIASES[lower];\n const canon = CANONICAL_CATEGORIES.find((c) => c.toLowerCase() === lower);\n return canon ?? titleCase(lower);\n}\n\n/** Canonicalize a raw sub-category (own facet; folds typos/word-order). \"\" when\n * the record has none. */\nexport function normalizeSubCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (SUBCATEGORY_ALIASES[lower]) return SUBCATEGORY_ALIASES[lower];\n return titleCase(lower);\n}\n\n/** The detail-page slug for a record: the stored `url` wins (a few records\n * carry an editorial slug the title wouldn't derive — \"Howdy Set\" → howdyset),\n * else derive from the title (lowercase, non-alphanumeric runs → single \"-\",\n * trimmed). */\nexport function productSlug(record: ProductRecord): string {\n const url = typeof record.url === \"string\" ? record.url.trim() : \"\";\n if (url) return url.replace(/^\\/+products\\/+/i, \"\").replace(/^\\/+|\\/+$/g, \"\");\n return String(record.title ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction mediaUuid(media: unknown): string | undefined {\n if (media && typeof media === \"object\") {\n const m = (media as { media?: unknown }).media;\n if (typeof m === \"string\" && m) return m;\n }\n return undefined;\n}\n\nfunction imageFor(\n uuid: string | undefined,\n resolveImage: (uuid: string) => string | null,\n): ProductImage | undefined {\n if (!uuid) return undefined;\n const url = resolveImage(uuid);\n return url ? { assetId: uuid, url } : undefined;\n}\n\n/** Build the product catalog from the feed records. `resolveImage` turns an\n * asset uuid into a CDN url (null when unresolvable). Slug-collision safe: two\n * records that map to the same slug (a handful of duplicate/legacy names) keep\n * ONE page — an enabled record wins over a disabled one, else the first seen.\n * Records with no usable slug are dropped. */\nexport function materializeProducts(\n records: ProductRecord[],\n resolveImage: (uuid: string) => string | null,\n): Product[] {\n const bySlug = new Map<string, Product>();\n for (const r of records) {\n const slug = productSlug(r);\n if (!slug) continue;\n\n const gallery: ProductImage[] = [];\n if (Array.isArray(r.items)) {\n for (const it of r.items as ProductRecord[]) {\n const img = imageFor(mediaUuid(it?.media), resolveImage);\n if (img) gallery.push(img);\n }\n }\n\n const image = imageFor(mediaUuid(r.media), resolveImage);\n const product: Product = {\n slug,\n title: cleanWs(r.title),\n category: normalizeCategory(r.category),\n subCategory: normalizeSubCategory(r.sub_category),\n dimensions: String(r.dimensions ?? \"\"),\n tags: Array.isArray(r.tags) ? r.tags.filter((t): t is string => typeof t === \"string\") : [],\n disabled: r.disabled === true,\n gallery,\n // `exactOptionalPropertyTypes`: omit the key rather than set undefined.\n ...(image ? { image } : {}),\n };\n\n const existing = bySlug.get(slug);\n if (!existing || (existing.disabled && !product.disabled)) bySlug.set(slug, product);\n }\n return [...bySlug.values()];\n}\n","import { blockPlainText, type SliceSpec } from \"../grid/index.js\";\nimport { type PlanSlice, richText } from \"./plan.js\";\n\n/** Strip all tags → the plain text a Prismic \"Text\" (key-text) field holds. */\nfunction stripTags(html: string): string {\n return html\n .replace(/<[^>]*>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Map one classified band to its page-doc slice. Text + band index only —\n * media, layout, style and map all live in the presentation manifest. */\nexport function sliceSpecToPlanSlice(spec: SliceSpec): PlanSlice {\n switch (spec.slice) {\n case \"Hero\":\n return {\n slice_type: \"hero\",\n variation: \"band\",\n items: [],\n primary: {\n band: spec.index,\n ...(spec.heading ? { heading: spec.heading } : {}),\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n ...(spec.body ? { body: stripTags(spec.body) } : {}),\n },\n };\n case \"TitleBand\":\n return {\n slice_type: \"title_band\",\n variation: \"default\",\n items: [],\n primary: {\n band: spec.index,\n heading: spec.heading,\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n },\n };\n case \"RichText\":\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n items: [],\n primary: { content: richText(spec.html), band: spec.index },\n };\n case \"SplitFeature\":\n return {\n slice_type: \"split_feature\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Gallery\":\n return {\n slice_type: \"gallery\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Carousel\":\n // One item per slide, in slide order (the render zips by index); an\n // uncaptioned slide contributes {} so the alignment holds. Captions are\n // heading nodes, so blockPlainText (entities decoded, <br> kept as a\n // newline) — same as the Hero/TitleBand heading path. A hero slide's\n // secondary line (location) rides as `subcaption`.\n return {\n slice_type: \"carousel\",\n variation: \"default\",\n items: spec.slides.map((s) => ({\n ...(s.caption ? { caption: blockPlainText(s.caption.html) } : {}),\n ...(s.subcaption ? { subcaption: blockPlainText(s.subcaption.html) } : {}),\n })),\n primary: { band: spec.index },\n };\n case \"MediaFull\":\n case \"VideoFeature\":\n return {\n slice_type: \"media_full\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"LocationMap\":\n return {\n slice_type: \"location_map\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Grid\":\n return {\n slice_type: \"grid_band\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n }\n}\n","import { type SliceSpec, type Media, collectMedia } from \"../grid/index.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sliceSpecToPlanSlice } from \"./grid-slice.js\";\nimport { type MigrationPlan, type PlanAsset, type PlanDocument, richText } from \"./plan.js\";\n\n/** Build the CDN url for a media from its parser-captured base + uuid + ext.\n * Null when the node carried no `data-base` (the manifest resolver then falls\n * back to the IR asset's sourceUrl — see `buildGridPlan`). Exported so the\n * `blux convert` manifest resolver builds byte-identical urls (a later task\n * rewrites the manifest by matching plan.assets urls). */\nexport function mediaCdnUrl(m: Media): string | null {\n return m.base ? `${m.base}${m.assetId}${m.ext ? `.${m.ext}` : \"\"}` : null;\n}\n\n/** The absolute source url for a media: the parser-captured CDN url\n * (data-base + uuid + ext) if present, else the IR asset's scraped sourceUrl,\n * else null. Shared by the plan's asset list and the manifest resolver so the\n * two url strings are byte-identical (the migrate rewrite keys on that). */\nexport function mediaUrl(\n m: Media,\n sourceUrlById: Map<string, string | null | undefined>,\n): string | null {\n return mediaCdnUrl(m) ?? sourceUrlById.get(m.assetId) ?? null;\n}\n\n/** Every Media referenced across all specs: band backgrounds, direct media\n * fields, and media inside node trees (SplitFeature.text / Grid.root). Deduped\n * by assetId, first occurrence wins, insertion order preserved. `resolve`\n * turns a media into its upload entry (CDN-base url, else IR sourceUrl) or null\n * when neither is available — an unresolvable media is dropped and (if a\n * `diagnostics` sink is passed) recorded once per assetId. */\nexport function collectPlanAssets(\n specs: SliceSpec[],\n resolve: (m: Media) => PlanAsset | null,\n diagnostics?: Diagnostic[],\n): PlanAsset[] {\n const byId = new Map<string, PlanAsset>();\n const seen = new Set<string>();\n const add = (m: Media) => {\n if (seen.has(m.assetId)) return;\n seen.add(m.assetId);\n const asset = resolve(m);\n if (asset) byId.set(m.assetId, asset);\n else\n diagnostics?.push({\n kind: \"unresolved-asset\",\n where: m.assetId,\n message: `media ${m.assetId} has no CDN base nor IR source url — not uploaded`,\n });\n };\n for (const spec of specs) {\n if (spec.background) add(spec.background);\n switch (spec.slice) {\n case \"Gallery\":\n spec.media.forEach(add);\n break;\n case \"Carousel\":\n spec.slides.forEach((s) => add(s.media));\n break;\n case \"MediaFull\":\n case \"VideoFeature\":\n add(spec.media);\n break;\n case \"SplitFeature\":\n add(spec.media);\n collectMedia(spec.text).forEach(add);\n break;\n case \"Grid\":\n collectMedia(spec.root).forEach(add);\n break;\n default:\n break; // Hero/TitleBand/RichText/LocationMap: only background (handled above)\n }\n }\n return [...byId.values()];\n}\n\n/** Build the Prismic migration plan for a grid-converted site: one text-only\n * page document + the assets its manifest references (uploaded so the manifest\n * can be rewritten to Prismic urls at migrate time). Collections flow through\n * `buildCustomType`, unchanged from archetype. */\nexport function buildGridPlan(specs: SliceSpec[], ir: SiteIR): MigrationPlan {\n const page = ir.pages[0];\n return buildGridSitePlan(\n [{ uid: page?.uid ?? \"home\", title: page?.title ?? page?.uid ?? \"home\", specs }],\n ir,\n );\n}\n\n/** The multi-page grid plan: one page document per converted page (uid-keyed,\n * mirroring the archetype plan's per-page loop), the asset union across all\n * pages' specs, and the collections' custom types. */\nexport function buildGridSitePlan(\n pages: { uid: string; title: string; specs: SliceSpec[] }[],\n ir: SiteIR,\n): MigrationPlan {\n const documents: PlanDocument[] = pages.map((p) => ({\n type: \"page\",\n uid: p.uid,\n data: { title: richText(`<h1>${p.title}</h1>`), slices: p.specs.map(sliceSpecToPlanSlice) },\n }));\n // Upload-url resolution MUST stay identical to the later manifest resolver:\n // CDN-base url first, else the IR asset's sourceUrl. `mediaUrl` is the\n // single shared implementation (see its doc comment above).\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const resolve = (m: Media): PlanAsset | null => {\n const asset = assetById.get(m.assetId);\n const url = mediaUrl(m, sourceUrlById);\n return url ? { id: m.assetId, url, alt: asset?.alt ?? \"\" } : null;\n };\n const diagnostics: Diagnostic[] = [...(ir.diagnostics ?? [])];\n const assets = collectPlanAssets(\n pages.flatMap((p) => p.specs),\n resolve,\n diagnostics,\n );\n const customTypes = ir.collections.map(buildCustomType);\n return { customTypes, documents, assets, stylesManifest: [], diagnostics };\n}\n","// Render-faithful presentation manifest (plan 5). Transforms the source\n// node tree (src/blux/grid/types.ts `Node`) and the classified `SliceSpec[]`\n// into `blux-presentation.json`, the render-side contract the-pointe's\n// Svelte layer loads (src/lib/blux/presentation.ts — that file is the fixed\n// target; these mirror types must match it exactly). Media/style/map are\n// resolved via injected `deps` so this builder stays pure and offline.\nimport type {\n Media,\n Node,\n GridToken as SrcToken,\n SliceSpec,\n VideoPlayback,\n} from \"../grid/index.js\";\nimport type { BlockDefaults } from \"./block-styles.js\";\n\n// ---------------------------------------------------------------------------\n// Render-side mirror types (must match the-pointe's src/lib/blux/presentation.ts)\n// ---------------------------------------------------------------------------\n\nexport type RenderMedia = {\n kind: \"image\" | \"video\";\n url: string;\n alt?: string;\n // Intrinsic sizing carried from the source. `width`/`aspect` size a foreground\n // graphic (capped to its cell) so rules/logos keep their true size. `fit` +\n // `position` carry a band background's `background-size`/`-position` so a\n // corner-anchored `auto` accent isn't centered full-bleed. `playback` carries a\n // video's source `<video>` attributes. Mirrors the-pointe's src/lib/blux/presentation.ts.\n width?: number;\n aspect?: number;\n fit?: \"contain\" | \"cover\" | \"auto\";\n position?: string;\n /** The source holder's inline min-height (e.g. \"80vh\" on slider slides), so\n * a cover-frame carousel reserves the original's height. */\n minHeight?: string;\n /** A feed grid's tile crop ratio (\"W:H\", e.g. \"4:3\") — the render frames the\n * image in a fixed-aspect object-cover box (uniform gallery tiles). */\n cropRatio?: string;\n playback?: VideoPlayback;\n};\n\nexport type RenderToken = { cols: number | \"any\"; ratio?: number; spacing?: number };\n\nexport type RenderNode =\n | {\n kind: \"row\";\n cells: RenderCell[];\n style?: Record<string, string>;\n /** This row's cells are the map widget's toggle-switched content panels\n * (the Blux clickMap wiring): cell i shows when toggle i is active, the\n * rest are hidden. Set only on the row that directly follows a widget:map\n * sibling and has exactly one cell per map toggle. */\n panels?: boolean;\n }\n | { kind: \"stack\"; children: RenderNode[]; style?: Record<string, string> }\n | {\n kind: \"heading\";\n level: number;\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"body\";\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"subtitle\";\n text: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | { kind: \"media\"; media: RenderMedia }\n | { kind: \"raw\"; html: string }\n | { kind: \"widget\"; widget: { type: \"map\" } };\n\nexport type RenderCell = { token: RenderToken; node: RenderNode };\n\nexport type MapLayer = {\n name: string;\n lid: string;\n initiallyVisible: boolean;\n preserveViewport: boolean;\n};\nexport type MapToggle = { label: string; layers: string[]; panelIndex: number };\nexport type MapRenderConfig = {\n mid: string;\n layers: MapLayer[];\n toggles: MapToggle[];\n styles: unknown[];\n center?: { lat: number; lng: number };\n zoom?: number;\n height?: string;\n defaultToggle?: number;\n};\n\nexport type BandPresentation = {\n style?: Record<string, string>;\n background?: RenderMedia;\n tree?: RenderNode;\n split?: { mediaSide: \"left\" | \"right\"; ratio: number; media: RenderMedia; text: RenderNode };\n gallery?: RenderMedia[];\n /** Carousel payload: the band is a source slider (.caslider). Caption TEXT\n * lives in the page doc's items (Prismic-editable); the manifest carries the\n * media and the caption's role metadata. `columns` = slides visible at once\n * (source data-columns). */\n carousel?: {\n slides: {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n }[];\n columns?: number;\n };\n media?: RenderMedia;\n map?: MapRenderConfig;\n /** Hero/TitleBand heading textN role + h-level and subtitle role, so the\n * render applies the right display font/tag. The text itself is the Prismic\n * page-doc string; this is presentation metadata only. */\n text?: { headingRole?: string; headingLevel?: number; subtitleRole?: string };\n};\n\nexport type Presentation = { bands: Record<string, BandPresentation> };\n\n/** A whole-site manifest: per-page band manifests keyed by page uid. Band\n * indices are page-local (page-block-N restarts at 0 on every page), so a flat\n * bands map would collide across pages. `blux convert` writes this shape;\n * single-page consumers select a page with `selectPagePresentation`. The\n * render mirror is reddoor-starter's `SitePresentation`. */\nexport type SitePresentation = Presentation | { pages: Record<string, Presentation> };\n\n/** One page's slice of a site manifest (the flat form passes through). The\n * `&& m.pages` guard tolerates a hand-edited `{ pages: undefined }`; the flat\n * branch is a `Presentation` by construction (cast bridges the union gap). */\nexport function selectPagePresentation(m: SitePresentation, uid = \"home\"): Presentation {\n if (\"pages\" in m && m.pages) return m.pages[uid] ?? { bands: {} };\n return m as Presentation;\n}\n\nexport type PresentationDeps = {\n resolveMedia: (media: Media) => RenderMedia | null;\n styleFor: (index: number) => Record<string, string> | undefined;\n /** The `.blocksNcontainer` class defaults for a band's `blockClass`\n * (see `blockClassDefaults`) — filled into `BandPresentation.style` only\n * where the block's own styles omit the key. */\n defaultsFor: (blockClass: string) => BlockDefaults | undefined;\n map?: MapRenderConfig | null;\n};\n\n// ---------------------------------------------------------------------------\n// Node-tree serializer: source Node → RenderNode\n// ---------------------------------------------------------------------------\n\n/** Drop the source-only `raw` field from a grid token; keep only cols/ratio/spacing. */\nfunction renderToken(t: SrcToken): RenderToken {\n return {\n cols: t.cols,\n ...(t.ratio !== undefined ? { ratio: t.ratio } : {}),\n ...(t.spacing !== undefined ? { spacing: t.spacing } : {}),\n };\n}\n\n/** Recursively serialize a source Node → RenderNode: resolve media (dropping\n * unresolved media nodes), strip token.raw. Never mutates the input. */\nfunction renderNode(node: Node, resolve: PresentationDeps[\"resolveMedia\"]): RenderNode | null {\n switch (node.kind) {\n case \"row\": {\n const cells: RenderCell[] = [];\n for (const c of node.cells) {\n const rn = renderNode(c.node, resolve);\n if (rn) cells.push({ token: renderToken(c.token), node: rn });\n }\n return { kind: \"row\", cells, ...(node.style ? { style: node.style } : {}) };\n }\n case \"stack\": {\n const children: RenderNode[] = [];\n for (const c of node.children) {\n const rn = renderNode(c, resolve);\n if (rn) children.push(rn);\n }\n return { kind: \"stack\", children, ...(node.style ? { style: node.style } : {}) };\n }\n case \"heading\":\n return {\n kind: \"heading\",\n level: node.level,\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"body\":\n return {\n kind: \"body\",\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"subtitle\":\n return {\n kind: \"subtitle\",\n text: node.text,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"media\": {\n const m = resolve(node.media);\n return m ? { kind: \"media\", media: m } : null; // drop unresolved media\n }\n case \"raw\":\n return { kind: \"raw\", html: node.html };\n case \"widget\":\n return { kind: \"widget\", widget: node.widget };\n }\n}\n\n/** Does a (source) node tree contain a map widget anywhere? */\nexport function hasMapWidget(node: Node): boolean {\n if (node.kind === \"widget\") return node.widget.type === \"map\";\n if (node.kind === \"row\") return node.cells.some((c) => hasMapWidget(c.node));\n if (node.kind === \"stack\") return node.children.some(hasMapWidget);\n return false;\n}\n\n/** Mark the map widget's content-panel row: in the Blux export the clickMap\n * widget switches the area below the map between N sibling panels (the address\n * grid + lazy logo strips on the-pointe), one per toggle. Structurally that is\n * a row directly following the widget:map inside a stack, with exactly one cell\n * per toggle — the marker lets the render show only the active toggle's panel\n * instead of stacking all of them. Anything else is left untouched. */\nexport function markPanelRows(node: RenderNode, toggleCount: number): RenderNode {\n if (node.kind === \"row\") {\n return {\n ...node,\n cells: node.cells.map((c) => ({ ...c, node: markPanelRows(c.node, toggleCount) })),\n };\n }\n if (node.kind === \"stack\") {\n const children = node.children.map((c) => markPanelRows(c, toggleCount));\n for (let i = 0; i + 1 < children.length; i++) {\n const cur = children[i];\n const next = children[i + 1];\n if (\n cur?.kind === \"widget\" &&\n cur.widget.type === \"map\" &&\n next?.kind === \"row\" &&\n next.cells.length === toggleCount\n ) {\n children[i + 1] = { ...next, panels: true };\n }\n }\n return { ...node, children };\n }\n return node;\n}\n\n// ---------------------------------------------------------------------------\n// Per-variant builder: SliceSpec[] → Presentation\n// ---------------------------------------------------------------------------\n\nexport function buildPresentation(specs: SliceSpec[], deps: PresentationDeps): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const spec of specs) {\n const bp: BandPresentation = {};\n const style = deps.styleFor(spec.index);\n if (style) bp.style = style;\n // Class-default padding/max-width — for EVERY slice type (TitleBand/Hero\n // bands need their band padding too), filling only the keys the block's\n // own styles omit. The trigger is \"no `_contentPadding` in the block's\n // styles\", never \"no style record\" (a block can style other things and\n // still rely on the class padding). `_contentPaddingMobile` only ever\n // pairs with a filled default: a block's own padding has no mobile twin.\n const defaults = spec.blockClass ? deps.defaultsFor(spec.blockClass) : undefined;\n if (defaults) {\n const own = bp.style ?? {};\n const fill: Record<string, string> = {};\n if (own[\"_contentPadding\"] === undefined && defaults.padding) {\n fill[\"_contentPadding\"] = defaults.padding;\n if (defaults.mobilePadding) fill[\"_contentPaddingMobile\"] = defaults.mobilePadding;\n }\n if (own[\"_max-content-width\"] === undefined && defaults.maxWidth) {\n fill[\"_max-content-width\"] = defaults.maxWidth;\n }\n // Copy, never mutate — the styleFor record may be shared/cached.\n if (Object.keys(fill).length) bp.style = { ...own, ...fill };\n }\n if (spec.background) {\n const bg = deps.resolveMedia(spec.background);\n if (bg) bp.background = bg;\n }\n\n switch (spec.slice) {\n case \"Hero\":\n case \"TitleBand\": {\n // Text lives in the page doc; carry only the role/level metadata so the\n // render picks the right display font + heading tag.\n const meta = {\n ...(spec.headingRole ? { headingRole: spec.headingRole } : {}),\n ...(spec.headingLevel !== undefined ? { headingLevel: spec.headingLevel } : {}),\n ...(spec.subtitleRole ? { subtitleRole: spec.subtitleRole } : {}),\n };\n if (Object.keys(meta).length) bp.text = meta;\n break;\n }\n case \"RichText\":\n break; // content is in the page doc\n case \"Gallery\": {\n const g = spec.media.map(deps.resolveMedia).filter((m): m is RenderMedia => m !== null);\n if (g.length) bp.gallery = g;\n break;\n }\n case \"Carousel\": {\n // Caption TEXT lives in the page doc's items; only its role metadata\n // rides the manifest. An unresolved media TRUNCATES the slide list —\n // splicing it out would shift later slides onto the wrong page-doc\n // caption (the render zips items↔slides by index). Either way the\n // count shrink trips validateLayout's media-dropped finding.\n type CarouselManifestSlide = {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n };\n const slides: CarouselManifestSlide[] = [];\n for (const s of spec.slides) {\n const media = deps.resolveMedia(s.media);\n if (!media) break;\n const slide: CarouselManifestSlide = { media };\n if (s.caption) {\n const caption: { level?: number; role?: string } = { level: s.caption.level };\n if (s.caption.role !== undefined) caption.role = s.caption.role;\n slide.caption = caption;\n }\n if (s.subcaption) {\n slide.subcaption = s.subcaption.role !== undefined ? { role: s.subcaption.role } : {};\n }\n slides.push(slide);\n }\n if (slides.length > 0) {\n bp.carousel = spec.columns !== undefined ? { slides, columns: spec.columns } : { slides };\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n const m = deps.resolveMedia(spec.media);\n if (m) bp.media = m;\n break;\n }\n case \"SplitFeature\": {\n const media = deps.resolveMedia(spec.media);\n const text = renderNode(spec.text, deps.resolveMedia);\n if (media && text) bp.split = { mediaSide: spec.mediaSide, ratio: spec.ratio, media, text };\n break;\n }\n case \"LocationMap\":\n if (deps.map) bp.map = deps.map;\n break;\n case \"Grid\": {\n const tree = renderNode(spec.root, deps.resolveMedia);\n // Co-located map (widget:map inside the tree): attach the map config and\n // mark the toggle-switched panel row so the render can wire them up.\n const co = deps.map && hasMapWidget(spec.root) ? deps.map : null;\n if (co) bp.map = co;\n if (tree) bp.tree = co ? markPanelRows(tree, co.toggles.length) : tree;\n break;\n }\n }\n bands[String(spec.index)] = bp;\n }\n return { bands };\n}\n","import {\n parseGridBands,\n extractMapConfig,\n classifyBands,\n makeIsMapMount,\n type Band,\n type MapConfig,\n type SliceSpec,\n} from \"../grid/index.js\";\nimport { assembleIR } from \"../assemble.js\";\nimport { parseBluxSite } from \"../parse.js\";\nimport { normalizePages } from \"../normalize.js\";\nimport {\n buildFeedResolvers,\n cropRatioOf,\n feedAssetBase,\n isFeedBand,\n materializeFeedGrid,\n resolveFeedTiles,\n type FeedResolvers,\n type TileOverlay,\n} from \"../grid/feed-grid.js\";\nimport type { Node } from \"../grid/types.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { blockClassDefaults, blockStylesByIndex } from \"./block-styles.js\";\nimport { buildGridPlan, buildGridSitePlan, mediaUrl } from \"./grid-plan.js\";\nimport type { MigrationPlan } from \"./plan.js\";\nimport {\n buildPresentation,\n type MapRenderConfig,\n type Presentation,\n type PresentationDeps,\n type RenderMedia,\n} from \"./presentation.js\";\n\nexport type ConvertResult = {\n bands: Band[];\n specs: SliceSpec[];\n ir: SiteIR;\n mapConfig: MapConfig | null;\n plan: MigrationPlan;\n presentation: Presentation;\n};\n\n/** Drop the source-only `mountId` from an extracted MapConfig → the render-side\n * MapRenderConfig the presentation manifest carries. */\nexport function mapRenderFromConfig(c: MapConfig): MapRenderConfig {\n return {\n mid: c.mid,\n layers: c.layers,\n toggles: c.toggles,\n styles: c.styles,\n ...(c.center ? { center: c.center } : {}),\n ...(c.zoom !== undefined ? { zoom: c.zoom } : {}),\n ...(c.height ? { height: c.height } : {}),\n ...(c.defaultToggle !== undefined ? { defaultToggle: c.defaultToggle } : {}),\n };\n}\n\n/** The offline convert pipeline shared by `blux convert` (writes files) and\n * `blux validate` (checks fidelity): parse + classify index.html, assemble the\n * IR from site.json, and build both emit artifacts through a single media\n * resolver (CDN base ?? IR sourceUrl) so plan, manifest, and validation all\n * agree on which media resolve. Pure + offline — no writes, no network. */\nexport function convertExport({\n html,\n siteJson,\n}: {\n html: string;\n siteJson: unknown;\n}): ConvertResult {\n const bands = parseGridBands(html);\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const ir = assembleIR({ siteJson, htmls: [html] });\n\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const styles = blockStylesByIndex(siteJson);\n const defaults = blockClassDefaults(siteJson);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n\n const plan = buildGridPlan(specs, ir);\n const presentation = buildPresentation(specs, deps);\n return { bands, specs, ir, mapConfig, plan, presentation };\n}\n\n/** The leading heading/subtitle nodes of a parsed band root — a feed band's\n * real heading survives the parse (only the tile template was dropped), so we\n * keep it and append the materialized tile row below. */\nfunction leadingHeadings(root: Node): Node[] {\n if (root.kind === \"heading\" || root.kind === \"subtitle\") return [root];\n if (root.kind === \"stack\") {\n const out: Node[] = [];\n for (const c of root.children) {\n if (c.kind === \"heading\" || c.kind === \"subtitle\") out.push(c);\n else break; // headings lead; stop at the first non-heading (the dropped grid)\n }\n return out;\n }\n return [];\n}\n\n/** A feed band's grid column count: the source `columns` (site.json), else a\n * sensible default. */\nfunction feedColumns(item: { columns?: unknown }): number {\n const n = Number(item.columns);\n return Number.isFinite(n) && n > 0 ? n : 3;\n}\n\n/** The overlay-tile treatment of a feed band, or null. A grid with\n * `sourceConfig.overlay` + a crop `ratio`/`mediaRatio` (gallery/portfolio)\n * shows its caption OVER the cropped image — the render frames each tile at\n * the ratio and reveals the caption panel on hover, so a tile is only as tall\n * as its image. */\nfunction tileOverlay(item: unknown): TileOverlay | null {\n const cfg = (item as { sourceConfig?: Record<string, unknown> })?.sourceConfig;\n if (!cfg || cfg[\"overlay\"] !== true) return null;\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n if (!ratio) return null;\n const color = typeof cfg[\"overlayColor\"] === \"string\" ? cfg[\"overlayColor\"] : undefined;\n const valign = typeof cfg[\"contentvalign\"] === \"string\" ? cfg[\"contentvalign\"] : undefined;\n return { ratio, ...(color ? { color } : {}), ...(valign ? { valign } : {}) };\n}\n\n/** Did this band lose its content to the feed-template drop? — its parsed root\n * is empty (just heading(s)/subtitle/empty-raw, no media and no populated\n * row). Only such a band is a safe materialization target: a band that already\n * parsed real content is NOT a JS-hydrated feed grid, so the positional\n * site.json join landed on the wrong band and must not clobber it. */\nfunction isEmptyish(root: Node): boolean {\n switch (root.kind) {\n case \"heading\":\n case \"subtitle\":\n return true;\n case \"raw\":\n return root.html.trim() === \"\";\n case \"media\":\n case \"widget\":\n return false;\n case \"row\":\n return root.cells.length === 0;\n case \"stack\":\n return root.children.every(isEmptyish);\n default:\n return false;\n }\n}\n\n/** Replace each feed band's root (parsed to just its heading, the tile\n * template having been dropped) with the heading over a materialized tile row\n * rebuilt from the feed records. Feed bands whose source resolves to no tiles\n * keep their heading and get an `empty-feed-grid` diagnostic. Mutates `bands`.\n * The join is positional: site.json `items[i]` ↔ the band whose index is `i`\n * (the block-styles convention); a page with non-contiguous band ids simply\n * finds no item and is left as parsed. */\nfunction materializeFeedBands(\n bands: Band[],\n pageItems: unknown[] | undefined,\n resolvers: FeedResolvers,\n diagnostics: Diagnostic[],\n): void {\n if (!Array.isArray(pageItems)) return;\n for (const band of bands) {\n const item = pageItems[band.index];\n if (!isFeedBand(item)) continue;\n // Guard the positional join: only a band that lost its content to the\n // template drop is a real feed grid. A band that already parsed real\n // content means items[band.index] misaligned (a non-contiguous page) —\n // materializing would CLOBBER it, so skip with a diagnostic instead.\n if (!isEmptyish(band.root)) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `band ${band.index} has parsed content but site.json item is a feed source — positional join misaligned, left as parsed`,\n });\n continue;\n }\n const tiles = resolveFeedTiles(item, resolvers);\n if (!tiles) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `feed band ${band.index} (source ${String(item.sources[0])}) resolved to no tiles`,\n });\n continue;\n }\n const spacing = parseInt(String((item as { spacing?: unknown }).spacing ?? \"\"), 10);\n const row = materializeFeedGrid({\n tiles,\n columns: feedColumns(item as { columns?: unknown }),\n ...(Number.isFinite(spacing) && spacing > 0 ? { spacing } : {}),\n ...(tileOverlay(item) ? { overlay: tileOverlay(item)! } : {}),\n });\n if (!row) continue;\n const headings = leadingHeadings(band.root);\n band.root = headings.length ? { kind: \"stack\", children: [...headings, row] } : row;\n }\n}\n\n/** The site's page routing table (uid + export path + title), derivable from\n * site.json alone — the CLI uses it to locate each page's rendered html\n * (root index.html for the homepage, `<path>/index.html` for the rest)\n * before running convertSite. */\nexport function sitePages(siteJson: unknown): { uid: string; path: string; title: string }[] {\n const { pages } = normalizePages(parseBluxSite(siteJson));\n return pages.map((p) => ({ uid: p.uid, path: p.path, title: p.title }));\n}\n\n/** One converted page of a multi-page site. */\nexport type ConvertedPage = {\n uid: string;\n title: string;\n path: string;\n bands: Band[];\n specs: SliceSpec[];\n mapConfig: MapConfig | null;\n};\n\n/** A multi-page presentation manifest: per-page band manifests keyed by page\n * uid. Band indices are page-local (`page-block-N` restarts at 0 on every\n * page), so a flat bands map would collide across pages — the render side's\n * `loadPresentation(uid)` selects the page slice. */\nexport type MultiPagePresentation = { pages: Record<string, Presentation> };\n\n/** The whole-site faithful-grid convert: every page of the export (each page\n * dir's rendered index.html) through the same parse → classify → presentation\n * pipeline `convertExport` runs for one page. One IR assembled from ALL page\n * htmls (the asset urlMap then resolves media that only appear on inner\n * pages), one migration plan (a document per page, the asset union), and a\n * per-page presentation manifest. Pages whose html is missing from\n * `htmlByUid` (unexported drafts) are skipped with a diagnostic. */\nexport function convertSite({\n siteJson,\n htmlByUid,\n}: {\n siteJson: unknown;\n htmlByUid: Map<string, string>;\n}): {\n pages: ConvertedPage[];\n ir: SiteIR;\n plan: MigrationPlan;\n presentation: MultiPagePresentation;\n} {\n const ir = assembleIR({ siteJson, htmls: [...htmlByUid.values()] });\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const defaults = blockClassDefaults(siteJson);\n\n // Feed-grid materialization: gallery/portfolio tiles render client-side from\n // feed records (the static export ships only the dropped {{…}} template), so\n // we rebuild them deterministically from the feed data (see feed-grid.ts).\n // The asset base is scraped from the export's own data-base so feed images\n // use the RIGHT CDN host (Blux spreads assets across two).\n const raw = parseBluxSite(siteJson);\n const assetBase = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const feedResolvers = buildFeedResolvers(raw.feeds, raw.media, assetBase);\n const pageItemsByIndex = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content\n ?.pages;\n\n const pages: ConvertedPage[] = [];\n const presentation: MultiPagePresentation = { pages: {} };\n ir.pages.forEach((page, pageIndex) => {\n const html = htmlByUid.get(page.uid);\n if (html === undefined) {\n ir.diagnostics.push({\n kind: \"missing-page-html\",\n where: page.uid,\n message: `no rendered html for page \"${page.uid}\" (${page.path || \"/\"}) — page skipped`,\n });\n return;\n }\n const bands = parseGridBands(html);\n materializeFeedBands(\n bands,\n pageItemsByIndex?.[pageIndex]?.items,\n feedResolvers,\n ir.diagnostics,\n );\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const styles = blockStylesByIndex(siteJson, pageIndex);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n presentation.pages[page.uid] = buildPresentation(specs, deps);\n pages.push({ uid: page.uid, title: page.title, path: page.path, bands, specs, mapConfig });\n });\n\n const plan = buildGridSitePlan(\n pages.map((p) => ({ uid: p.uid, title: p.title, specs: p.specs })),\n ir,\n );\n return { pages, ir, plan, presentation };\n}\n","import type { MapConfig } from \"../grid/extract-map.js\";\nimport type { Media } from \"../grid/types.js\";\n\n/** The Prismic document type the catalog pipeline emits for a migrated page.\n * The whole downstream stack (migrated repos + starter render routing via\n * getPageDoc's [\"page\",\"catalog_page\"]) keys on this; it is the migration-owned\n * page type, kept distinct from the template's own `page` type. */\nexport const CATALOG_PAGE_TYPE = \"catalog_page\";\n\n/** A rich-text run kept as raw HTML — emit turns it into a `{__richtext_html}`\n * marker; the parser already produces HTML for heading/body nodes. */\nexport type CatalogRichText = string;\n\n/** One catalog cell — the structural unit the Plan-2 `BluxCell` renders.\n * `kind` mirrors the Prismic Select (\"text\" | \"media\" | \"embed\" | \"subgrid\"). */\nexport type CatalogCell = {\n kind: \"text\" | \"media\" | \"embed\" | \"subgrid\";\n title?: CatalogRichText;\n bodyHtml?: string;\n media?: Media;\n mediaRatio?: string;\n embedHtml?: string;\n // --- visual-fidelity fields (Blux catalog visual layer) ---\n width?: string;\n spacing?: number;\n cover?: boolean;\n valign?: boolean;\n backgroundColor?: string;\n contentPadding?: string;\n titleRole?: string;\n subgrid?: CatalogCell[];\n};\n\n/** Fields shared by every catalog spec. `index` is the slice-zone position\n * (kept for parity with SliceSpec + future manifest-free ordering).\n * Decision-B widget routing (plan 4b, round 3): ANY spec can carry a custom\n * mount (the map) as a widget — the ORIGINAL mount html rides `widgetHtml`\n * (pristine — sanitize is emit's concern) with `widgetKind` naming it; for\n * maps the extracted `MapConfig` rides along so emit can inline it into the\n * document. The triple lives HERE (round 3) because every real fleet map band\n * holds the mount PLUS panel rows, so it routes Grid/Block — not just the\n * Section/Collection containers. */\nexport type CatalogBase = {\n index: number;\n background?: Media;\n backgroundColor?: string;\n // --- band-level visual-fidelity fields (Blux catalog visual layer) ---\n minHeight?: string;\n contentPadding?: string;\n contentPaddingMobile?: string;\n maxContentWidth?: string;\n verticalAlign?: string;\n textAlign?: string;\n columnWidth?: string;\n columnSide?: string;\n headingRole?: string;\n widgetKind?: string;\n widgetHtml?: string;\n mapConfig?: MapConfig;\n};\n\n/** A container band → the Plan-2 `blux_section` slice. A band whose sole\n * content is a custom mount becomes a section carrying the widget triple\n * (see CatalogBase) with no cells. */\nexport type BluxSectionSpec = CatalogBase & {\n slice: \"BluxSection\";\n heading?: CatalogRichText;\n cells: CatalogCell[];\n};\n\nexport type BluxGridSpec = CatalogBase & {\n slice: \"BluxGrid\";\n heading?: CatalogRichText;\n columns?: number;\n cells: CatalogCell[];\n};\nexport type BluxGallerySpec = CatalogBase & {\n slice: \"BluxGallery\";\n heading?: CatalogRichText;\n cells: CatalogCell[]; // all kind:\"media\"\n};\nexport type BluxCarouselSpec = CatalogBase & {\n slice: \"BluxCarousel\";\n heading?: CatalogRichText;\n columnsVisible?: number;\n cells: CatalogCell[];\n};\nexport type BluxMediaSpec = CatalogBase & {\n slice: \"BluxMedia\";\n media: Media;\n caption?: CatalogRichText;\n};\nexport type BluxMediaTextSpec = CatalogBase & {\n slice: \"BluxMediaText\";\n mediaSide: \"left\" | \"right\";\n layoutRatio?: number;\n media: Media;\n title?: CatalogRichText;\n body?: CatalogRichText;\n};\n/** A feed-backed band → the `blux_collection` query-spec slice (spec §6 row 5,\n * §7 rule 1). The slice carries NO cells — the starter resolves the mapped\n * entity documents at load time and renders them through the tagFilter DSL.\n * Collection is a CONTAINER (decision B): a widget mount can ride it exactly\n * like BluxSection (the CatalogBase widget triple, emitted through the same\n * widgetFields path). */\nexport type BluxCollectionSpec = CatalogBase & {\n slice: \"BluxCollection\";\n heading?: CatalogRichText;\n entityType: string; // mapped Prismic type the renderer queries\n feedIds: string[]; // original Blux feed ids (traceability)\n filterTag?: string; // tagFilter DSL expression\n sort?: string;\n limit?: number;\n mediaRatio?: string;\n layout: \"grid\" | \"carousel\";\n scrollLoadMore?: boolean;\n};\n\n/** Content-preserving fallback: the serialized node tree (Prismic can't nest\n * deeper than cell→subgrid). `payload` is a `{tag,children,html,image,style}`\n * tree the Plan-2 BluxBlock slice renders recursively. */\nexport type BluxBlockSpec = CatalogBase & {\n slice: \"BluxBlock\";\n payload: BlockNode;\n /** Every Media under the source subtree. The payload inlines them as CDN\n * urls, but emit still needs the Media list so the assets upload (payload\n * urls stay CDN until the 4d migrate-time rewrite — runMigration's\n * assetUrlByCdn is the map for it). */\n media: Media[];\n};\n\n/** The serialized-tree shape BluxBlock renders (mirror of starter\n * src/lib/slices/BluxBlock/node.ts BluxNode). */\nexport type BlockNode = {\n tag?: string;\n html?: string;\n image?: { url: string; alt?: string };\n style?: Record<string, string>;\n className?: string;\n children?: BlockNode[];\n};\n\n/** The catalog classify target: every breadth slice + the BluxBlock fallback. */\nexport type CatalogSpec =\n | BluxSectionSpec\n | BluxGridSpec\n | BluxGallerySpec\n | BluxCarouselSpec\n | BluxMediaSpec\n | BluxMediaTextSpec\n | BluxCollectionSpec\n | BluxBlockSpec;\n\nexport type CatalogKind = CatalogSpec[\"slice\"];\n","// The migration-owned `catalog_page` custom type. A migrated Blux site emits\n// `catalog_page` documents (see spec.ts CATALOG_PAGE_TYPE), so the migration\n// tool must also OWN + push the type definition — a cloned starter repo does\n// not ship it (the starter's own `page` type is the /new-site flow's; see the\n// starter's src/lib/blux-catalog/page-doc.ts module note). This is the type the\n// migrate action pushes via pushCustomTypes, so its `json` is the FULL Prismic\n// Custom Types API definition (id/label/format/status + the tabbed model),\n// exactly like the entity types buildEntityEmit emits.\nimport type { PlanCustomType } from \"../emit/plan.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\n\nconst CATALOG_PAGE_LABEL = \"Catalog Page (Blux migration)\";\n\n/** The SharedSlice choices a migrated catalog page can hold — the `blux_*`\n * catalog slices ONLY. The starter's `page` type also lists its native slice\n * choices (lead_text/hero/…), but a migrated page emits nothing but `blux_*`\n * slices (see emit.ts catalogSpecToPlanSlice), so the native choices are\n * dropped: the type models exactly what the migration populates. Mirrors the\n * starter page type's `blux_*` entries (customtypes/page/index.json). */\nconst BLUX_SLICE_CHOICES = [\n \"blux_section\",\n \"blux_text\",\n \"blux_block\",\n \"blux_grid\",\n \"blux_gallery\",\n \"blux_carousel\",\n \"blux_media\",\n \"blux_media_text\",\n \"blux_embed\",\n \"blux_table\",\n \"blux_collection\",\n] as const;\n\nfunction sliceChoices(): Record<string, { type: \"SharedSlice\" }> {\n return Object.fromEntries(BLUX_SLICE_CHOICES.map((t) => [t, { type: \"SharedSlice\" }]));\n}\n\n/** The `catalog_page` custom type: a `format:\"page\"` clone of the starter's\n * `page` type (customtypes/page/index.json) — the Main tab (uid + title + the\n * `blux_*` SharedSlices zone) and the \"SEO & Metadata\" tab\n * (meta_title/meta_description/meta_image). The SEO field types match the\n * starter render's reads (blux-catalog/page-doc.ts pageMeta): meta_title +\n * meta_description are Text (plain string), meta_image is an Image field\n * (`.url`/`.alt`). Shipped on EVERY catalog plan so the migration owns the type\n * it emits documents for. */\nexport function catalogPageCustomType(): PlanCustomType {\n return {\n id: CATALOG_PAGE_TYPE,\n label: CATALOG_PAGE_LABEL,\n repeatable: true,\n json: {\n id: CATALOG_PAGE_TYPE,\n label: CATALOG_PAGE_LABEL,\n format: \"page\",\n repeatable: true,\n status: true,\n json: {\n Main: {\n uid: { type: \"UID\", config: { label: \"UID\", placeholder: \"\" } },\n title: {\n type: \"StructuredText\",\n config: {\n label: \"Title\",\n placeholder: \"\",\n allowTargetBlank: true,\n single: \"heading1\",\n },\n },\n slices: {\n type: \"Slices\",\n fieldset: \"Slice Zone\",\n config: { choices: sliceChoices() },\n },\n },\n \"SEO & Metadata\": {\n meta_title: {\n type: \"Text\",\n config: {\n label: \"Meta Title\",\n placeholder: \"A title of the page used for social media and search engines\",\n },\n },\n meta_description: {\n type: \"Text\",\n config: {\n label: \"Meta Description\",\n placeholder: \"A brief summary of the page\",\n },\n },\n meta_image: {\n type: \"Image\",\n config: {\n label: \"Meta Image\",\n constraint: { width: 2400, height: 1260 },\n thumbnails: [],\n },\n },\n },\n },\n },\n };\n}\n","// Emit-stage HTML sanitizer (slim plan 4b). Raw Blux html reaches three emit\n// boundaries — cell `embed_html`, section `widget_html`, and BluxBlock payload\n// `html` leaves — and the source markup routinely carries behavior <script>\n// blocks (map init, clickMap toggles) that must never ship inside Prismic\n// documents. The CatalogSpec upstream stays pristine (classify is\n// emit-agnostic); sanitizing lives at the emit boundary only.\n\n/** Closed `<script …>…</script>` blocks — case-insensitive, dotall, multiple. */\nconst SCRIPT_BLOCK_RE = /<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi;\n/** A script opened but never closed — swallow to end of input. */\nconst SCRIPT_TAIL_RE = /<script\\b[\\s\\S]*$/i;\n/** Inline `on*=` handler attributes (onclick, onerror, …), any quoting style. */\nconst ON_HANDLER_RE = /\\s+on\\w+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi;\n/** `javascript:` urls in href/src — quoted and bare forms. */\nconst JS_URL_QUOTED_RE = /\\b(href|src)\\s*=\\s*([\"'])\\s*javascript:[\\s\\S]*?\\2/gi;\nconst JS_URL_BARE_RE = /\\b(href|src)\\s*=\\s*javascript:[^\\s>]*/gi;\n\n/** Strip active content from raw html: `<script>` blocks (multiple,\n * case-insensitive, tolerating one left unclosed at EOF), inline `on*=`\n * handler attributes, and `javascript:` urls in href/src (neutralized to\n * `#`). Everything else passes through verbatim — the sanitizer is a\n * scalpel, not a rewriter. */\nexport function sanitizeHtml(html: string): string {\n return html\n .replace(SCRIPT_BLOCK_RE, \"\")\n .replace(SCRIPT_TAIL_RE, \"\")\n .replace(ON_HANDLER_RE, \"\")\n .replace(JS_URL_QUOTED_RE, \"$1=$2#$2\")\n .replace(JS_URL_BARE_RE, '$1=\"#\"');\n}\n\n/** Whether sanitized html still shows anything: non-whitespace text, or a\n * media element (img/iframe/video). A mount whose only payload was a behavior\n * script has no visible content — the emit layer drops it (recording a\n * diagnostic) instead of shipping an empty shell. */\nexport function hasVisibleContent(html: string): boolean {\n const clean = sanitizeHtml(html);\n if (/<(img|iframe|video)\\b/i.test(clean)) return true;\n return clean.replace(/<[^>]*>/g, \"\").trim().length > 0;\n}\n","import type { Media, Node, GridToken } from \"../grid/types.js\";\nimport { collectMedia, blockPlainText } from \"../grid/index.js\";\n// `collectText`/`isEmptyRaw` are not re-exported via the grid barrel\n// (index.ts) — import them from their module directly.\nimport { collectText, isEmptyRaw } from \"../grid/classify-band.js\";\nimport { mediaCdnUrl } from \"../emit/grid-plan.js\";\nimport type { BlockNode, CatalogCell } from \"./spec.js\";\n\n/** Peel one-child style-box stacks (mirror of classify-band unboxed). */\nfunction unbox(n: Node): Node {\n let cur = n;\n while (cur.kind === \"stack\" && cur.children.length === 1) {\n const only = cur.children[0];\n if (!only) break;\n cur = only;\n }\n return cur;\n}\n\n/** Raw nodes under a node (recursive). classify-band has the same helper but\n * does not export it — reimplemented locally rather than widening the grid\n * module's public surface. */\nfunction collectRaws(node: Node): Extract<Node, { kind: \"raw\" }>[] {\n switch (node.kind) {\n case \"raw\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectRaws(c.node));\n case \"stack\":\n return node.children.flatMap(collectRaws);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n return [];\n }\n}\n\n/** The joined non-empty raw html under a node — a cell's `embedHtml`. Keeps\n * the visible runs the text model would otherwise drop (the-pointe's 'Visit\n * Website' link buttons parse to `raw`); empty raws are client-mount shells. */\nfunction rawHtmlOf(node: Node): string | undefined {\n const raws = collectRaws(node).filter((r) => !isEmptyRaw(r));\n return raws.length ? raws.map((r) => r.html).join(\"\\n\") : undefined;\n}\n\n/** Wrap BARE (untagged) html in `<p>` — html whose trimmed text does not start\n * with `<`. Body parts folded next to a wrapped `<hN>` heading MUST carry a\n * block tag of their own: `htmlAsRichText` merges trailing bare text INTO the\n * preceding heading node, concatenating the words and dropping the paragraph.\n * Already-tagged parts (wrapped headings, `<p>`-carrying body html, raw block\n * html) pass through untouched. */\nfunction wrapBare(html: string): string {\n return html.trim().startsWith(\"<\") ? html : `<p>${html.trim()}</p>`;\n}\n\n/** Per-cell visual fields recovered from the grid token + the (unboxed) node's\n * card style. `token.ratio` is the cell's width share; card keys land on\n * `node.style` via parse-grid's withCardStyle. */\nfunction visualFieldsOf(u: Node, token?: GridToken): Partial<CatalogCell> {\n const style = \"style\" in u && u.style ? u.style : {};\n const media = collectMedia(u)[0];\n const out: Partial<CatalogCell> = {};\n if (typeof token?.ratio === \"number\") out.width = `${token.ratio}%`;\n if (typeof token?.spacing === \"number\") out.spacing = token.spacing;\n if (style[\"background-color\"]) out.backgroundColor = style[\"background-color\"];\n if (style[\"padding\"]) out.contentPadding = style[\"padding\"];\n if (style[\"_valign\"] === \"middle\") out.valign = true;\n if (style[\"_fill\"] === \"column\" || media?.fit === \"cover\") out.cover = true;\n return out;\n}\n\n/** Wrap a body-block html string in its Blux type-role div (`txt-role-textN`)\n * so the starter theme's `.txt-role-textN :is(hN,p)` rule sizes it; a roleless\n * block passes through untouched. The string sibling of `roleWrap` (which wraps\n * a BlockNode for the BluxBlock fallback path). */\nfunction roleWrapHtml(role: string | undefined, inner: string): string {\n return role ? `<div class=\"txt-role-${role}\">${inner}</div>` : inner;\n}\n\n/** Title + body html under a node (recursive, document order). The FIRST\n * non-blank heading becomes the title; every LATER heading is folded into the\n * body parts in document order relative to bodies/subtitles, so no heading is\n * dropped. The parser's `heading.html` carries no `<hN>` wrapper, so headings\n * are wrapped here — otherwise `htmlAsRichText` resolves them to `paragraph`\n * nodes, violating heading-only StructuredText fields and losing levels.\n * Folded headings keep their TRUE level (body fields render every block kind;\n * clamping to a field's heading window is emit's concern); bare body html is\n * `<p>`-wrapped so a folded heading never swallows it (see `wrapBare`). Each\n * folded block is baked into its own `txt-role-textN` div (approach B) so a\n * multi-role cell body keeps every block's display size — a RichText field\n * cannot carry the class, so the roled html is emitted as a plain string. */\nfunction textOf(n: Node): {\n title?: string;\n titleRole?: string;\n bodyHtml?: string;\n} {\n let title: string | undefined;\n let titleRole: string | undefined;\n const bodyParts: string[] = [];\n for (const t of collectText(n)) {\n if (t.kind === \"heading\") {\n if (blockPlainText(t.html) === \"\") continue; // whitespace-only heading\n const wrapped = `<h${t.level}>${t.html}</h${t.level}>`;\n if (title === undefined) {\n title = wrapped;\n titleRole = t.role;\n } else bodyParts.push(roleWrapHtml(t.role, wrapped));\n } else if (t.kind === \"body\") {\n if (t.html) bodyParts.push(roleWrapHtml(t.role, wrapBare(t.html)));\n } else if (t.kind === \"subtitle\") {\n bodyParts.push(roleWrapHtml(t.role, `<p>${t.text}</p>`));\n }\n }\n return {\n ...(title ? { title } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...(bodyParts.length ? { bodyHtml: bodyParts.join(\"\\n\") } : {}),\n };\n}\n\n/** Shared build state: `flattened` records that a subgrid item's subtree\n * carried structure the cell model cannot store — a nested row (at any depth\n * under the item) or more than one media (see `cellDepthExceedsTwo`). */\ntype CellBuildState = { flattened: boolean };\n\n/** Whether a subtree contains any row, at any depth. */\nfunction containsRow(node: Node): boolean {\n switch (node.kind) {\n case \"row\":\n return true;\n case \"stack\":\n return node.children.some(containsRow);\n default:\n return false;\n }\n}\n\n/** One node → one catalog cell. `depth` 0 builds a top-level cell: a row\n * becomes a subgrid (the ONE nesting level the Prismic model stores), and any\n * OTHER subtree carrying MORE than one media splits into a subgrid of one\n * text item (the whole subtree's title/body/raw html) plus one media item per\n * media in document order — the band stays editable and no media is dropped.\n * At subgrid-item depth (1) neither trick is available: a subtree with a\n * nested row or a second media cannot be stored, so its content is flattened\n * into the cell (first media + all text + raws) and the loss is recorded on\n * `state` — builder and guard can never disagree on legality. */\nfunction buildCell(\n node: Node,\n depth: number,\n state: CellBuildState,\n token?: GridToken,\n): CatalogCell {\n const u = unbox(node);\n if (u.kind === \"row\" && depth === 0) {\n return {\n kind: \"subgrid\",\n subgrid: u.cells.map((c) => buildCell(c.node, depth + 1, state, c.token)),\n };\n }\n const allMedia = collectMedia(u);\n if (depth === 0 && allMedia.length > 1) {\n // Depth-0 subgrid split: every media survives as its own item; the\n // subtree's whole text/raw content rides one leading text item — carrying\n // its type roles so the split text sizes like any other cell title/body.\n const { title, titleRole, bodyHtml } = textOf(u);\n const embedHtml = rawHtmlOf(u);\n const textItem: CatalogCell[] =\n title || bodyHtml || embedHtml\n ? [\n {\n kind: \"text\",\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n },\n ]\n : [];\n return {\n kind: \"subgrid\",\n subgrid: [...textItem, ...allMedia.map((m): CatalogCell => ({ kind: \"media\", media: m }))],\n };\n }\n if (depth > 0 && (allMedia.length > 1 || containsRow(u))) state.flattened = true;\n const media = allMedia[0];\n const vis = visualFieldsOf(u, token);\n const { title, titleRole, bodyHtml } = textOf(u);\n const embedHtml = rawHtmlOf(u);\n if (u.kind === \"media\" || (media && !title && !bodyHtml)) {\n return {\n kind: \"media\",\n ...(media ? { media } : {}),\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...vis,\n };\n }\n return {\n kind: title || bodyHtml ? \"text\" : embedHtml ? \"embed\" : \"text\",\n ...(title ? { title } : {}),\n ...(bodyHtml ? { bodyHtml } : {}),\n ...(media ? { media } : {}),\n ...(embedHtml ? { embedHtml } : {}),\n ...(titleRole ? { titleRole } : {}),\n ...vis,\n };\n}\n\n/** One node → one catalog cell (public wrapper; the cell is top-level). */\nexport function cellFromNode(node: Node): CatalogCell {\n return buildCell(node, 0, { flattened: false });\n}\n\nfunction buildCells(node: Node, state: CellBuildState): CatalogCell[] {\n const u = unbox(node);\n if (u.kind === \"row\") return u.cells.map((c) => buildCell(c.node, 0, state, c.token));\n if (u.kind === \"stack\") return u.children.map((c) => buildCell(c, 0, state));\n return [buildCell(u, 0, state)];\n}\n\n/** A row's cells → catalog cells; a bare content node → a single cell. Never\n * emits a subgrid item that itself has a subgrid (rows past that depth are\n * flattened — see `buildCell`). */\nexport function nodeToCells(node: Node): CatalogCell[] {\n return buildCells(node, { flattened: false });\n}\n\n/** True when `node` needs more nesting than the cell model stores — i.e.\n * building its cells would flatten structure at subgrid-item depth (a nested\n * row anywhere under one item, or a second media inside one item). Implemented\n * BY running the builder, so guard and emission share one source of truth:\n * guard false ⟹ every media and text run in the band is present in the\n * emitted cells (structure-complete up to the cell→subgrid model);\n * guard true ⟹ the caller should fall back to BluxBlock (content-preserving). */\nexport function cellDepthExceedsTwo(node: Node): boolean {\n const state: CellBuildState = { flattened: false };\n buildCells(node, state);\n return state.flattened;\n}\n\n/** Wrap a block text leaf in its Blux type-role div (`txt-role-textN`) when it\n * carries a role, so the starter theme's `.txt-role-textN :is(hN,p)` rule sizes\n * it exactly as grid/section text; a roleless leaf passes through untouched. */\nfunction roleWrap(role: string | undefined, node: BlockNode): BlockNode {\n return role ? { tag: \"div\", className: `txt-role-${role}`, children: [node] } : node;\n}\n\n/** Serialize a node tree to a BluxBlock payload — the content-preserving\n * fallback for bands too deep/irregular for the cell model. Never drops media\n * or text (the whole point). */\nexport function blockPayload(node: Node): BlockNode {\n const u = node;\n switch (u.kind) {\n case \"row\":\n return {\n tag: \"div\",\n style: { display: \"grid\" },\n children: u.cells.map((c) => blockPayload(c.node)),\n };\n case \"stack\":\n return { tag: \"div\", children: u.children.map(blockPayload) };\n // Text leaves keep their Blux type role (text1…textN): the starter theme\n // sizes text ONLY through `.txt-role-textN :is(h1..h6,p)`, so a bare heading\n // in a Block-fallback band would render unstyled. Wrap in the role div (the\n // same mechanism BluxCell uses for cell titles) so block-band headings size\n // identically to grid/section headings.\n case \"heading\":\n return roleWrap(u.role, { tag: `h${u.level}`, html: u.html });\n case \"body\":\n return roleWrap(u.role, { tag: \"div\", html: u.html });\n case \"subtitle\":\n return roleWrap(u.role, { tag: \"p\", html: u.text });\n case \"media\":\n return mediaBlock(u.media);\n case \"raw\":\n return { html: u.html };\n case \"widget\":\n return { tag: \"div\", html: \"\" }; // 4b captures widget html\n }\n}\n\nfunction mediaBlock(m: Media): BlockNode {\n // A video renders as an inline <video> — an image url here would make the\n // starter's BluxBlock render a broken <img> pointing at an mp4.\n if (m.kind === \"video\") return { tag: \"figure\", html: videoTag(m) };\n const url = m.base ? `${m.base}${m.assetId}${m.ext ? `.${m.ext}` : \"\"}` : m.assetId;\n // `Media` (grid/types.ts) carries no alt — asset alt lives in the IR asset\n // index and is resolved at emit/migrate time, not here.\n return { tag: \"figure\", image: { url, alt: \"\" } };\n}\n\n/** An inline `<video>` tag for a video Media — shared by the emit layer\n * (`embed_html`/`video_embed` fields) and the BluxBlock payload. Videos cannot\n * ride Prismic Image fields (PrismicImage would render a broken <img>) — they\n * play from the export's CDN url instead (self-hosted upload pends the 4d\n * asset strategy; url logic mirrors `mediaCdnUrl`, bare assetId as last\n * resort). Attributes honor the source `<video>` semantics (`Media.playback`);\n * absent playback falls back to a user-initiated inline video\n * (`controls playsinline`). Autoplay implies `muted` (browsers refuse un-muted\n * autoplay) and `playsinline` (iOS refuses inline autoplay without it). */\nexport function videoTag(m: Media): string {\n const p = m.playback ?? { controls: true, playsinline: true };\n const attrs: string[] = [];\n if (p.autoplay) attrs.push(\"autoplay\");\n if (p.loop) attrs.push(\"loop\");\n if (p.muted || p.autoplay) attrs.push(\"muted\");\n if (p.controls) attrs.push(\"controls\");\n if (p.playsinline || p.autoplay) attrs.push(\"playsinline\");\n const src = `src=\"${mediaCdnUrl(m) ?? m.assetId}\"`;\n return `<video ${[...attrs, src].join(\" \")}></video>`;\n}\n\n/** An inline `<img>` for an image Media — the doubly-nested (subgrid) delivery.\n * The Prismic Migration API can't resolve Image-field asset refs inside a\n * doubly-nested group, so a subgrid image rides a Text field as a url-based\n * `<img>` (exactly as video rides `<video>`). `resolveUrl` returns the\n * plan-asset CDN url (kept RAW so the migrate's `rewriteValueUrls` swaps it to\n * the uploaded Prismic url — escaping a query-string `&` would break that\n * match); `resolveAlt` returns the IR asset alt (escaped for the attribute),\n * else \"\". */\nexport function imgTag(\n m: Media,\n resolveUrl: (m: Media) => string | null,\n resolveAlt: (id: string) => string,\n): string {\n const src = resolveUrl(m) ?? m.assetId;\n const alt = resolveAlt(m.assetId)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n return `<img src=\"${src}\" alt=\"${alt}\">`;\n}\n","/** Frozen feed→entity-type mapping (spec §8). Keyed on the NORMALIZED feed\n * name (lowercase, collapsed whitespace). `collection_item` is the catch-all\n * so nothing is unroutable; \"DO NOT USE…\" feeds are skipped with a report\n * entry instead of migrated. */\nconst NAME_TO_TYPE: Record<string, string> = {\n products: \"product\",\n \"equipment grid\": \"product\",\n \"center features\": \"product\",\n team: \"person\",\n reps: \"person\",\n trainers: \"person\",\n events: \"event\",\n \"donate life observances\": \"event\",\n news: \"news_article\",\n \"outside the lines\": \"news_article\",\n \"all projects list\": \"project\",\n portfolio: \"project\",\n projects: \"project\",\n};\n\nconst normalize = (name: string): string => name.trim().toLowerCase().replace(/\\s+/g, \" \");\n\nexport function feedEntityType(feedName: string): string {\n const name = normalize(feedName);\n const exact = NAME_TO_TYPE[name];\n if (exact) return exact;\n // Real fleet feeds prefix \"Equipment Grid\" with the club name (\"The Pointe\n // Equipment Grid\" ×4 in fitHealthClub) — suffix-match after the exact\n // lookup misses so those still route to product.\n if (/\\bequipment grid$/.test(name)) return \"product\";\n return \"collection_item\";\n}\n\nexport function isSkippedFeed(feedName: string): boolean {\n return normalize(feedName).startsWith(\"do not use\");\n}\n","import type { Band, Media, Node } from \"../grid/types.js\";\nimport type { MapConfig } from \"../grid/extract-map.js\";\nimport type { Diagnostic } from \"../ir.js\";\nimport type { BlockDefaults } from \"../emit/block-styles.js\";\nimport { classifyBand, collectMedia, type CarouselSlide, type SliceSpec } from \"../grid/index.js\";\nimport { cellFromNode, nodeToCells, blockPayload, cellDepthExceedsTwo } from \"./cells.js\";\nimport { cropRatioOf, isFeedBand } from \"../grid/feed-grid.js\";\nimport { feedEntityType, isSkippedFeed } from \"./feeds.js\";\nimport { hasVisibleContent } from \"./sanitize.js\";\nimport type {\n BluxBlockSpec,\n BluxCollectionSpec,\n BluxGridSpec,\n CatalogCell,\n CatalogSpec,\n} from \"./spec.js\";\n\ntype CatalogBaseFields = {\n index: number;\n background?: Media;\n backgroundColor?: string;\n minHeight?: string;\n contentPadding?: string;\n contentPaddingMobile?: string;\n maxContentWidth?: string;\n verticalAlign?: string;\n textAlign?: string;\n columnWidth?: string;\n columnSide?: string;\n headingRole?: string;\n};\n\n/** Options threaded from the caller (the CLI catalog action extracts them per\n * page): `isMapMount` is the classifier's mount predicate (grid\n * `ClassifyOptions`), `mapConfig` the page's extracted map config carried onto\n * the resulting BluxSectionSpec for emit. */\nexport type BandToCatalogOptions = {\n isMapMount?: (node: Node) => boolean;\n mapConfig?: MapConfig;\n /** Classify-time diagnostics sink (positional-join misalignments, unknown/\n * skipped feed sources). Absent → those findings go unrecorded, never throw. */\n diagnostics?: Diagnostic[];\n /** The page being classified — rides diagnostic `where` as `<uid>:<band>`\n * so same-index findings on different pages stay distinguishable (round-2\n * item 10a: fitHealthClub's 4 band-3 diagnostics were identical). */\n pageUid?: string;\n /** Per-band inline block styles keyed by band index (background-color,\n * min-height, text-align, vertical-align, …) — the CLI supplies\n * `blockStylesByIndex(siteJson, pageIndex)`. `baseOf` reads `.get(band.index)`. */\n styles?: Map<number, Record<string, string>>;\n /** Block-class `.blocksNcontainer` defaults (padding/mobilePadding/maxWidth)\n * keyed by the wrapper class — the CLI supplies `blockClassDefaults(siteJson)`.\n * `baseOf` reads `.get(band.blockClass)`, mirroring presentation.ts. */\n defaults?: Map<string, BlockDefaults>;\n};\n\n/** Band-visual fields recovered from the CLI-threaded per-band inline styles\n * (`opts.styles.get(index)`) + the wrapper class defaults\n * (`opts.defaults.get(band.blockClass)`), mirroring presentation.ts's\n * styleFor/defaultsFor resolution. An inline `padding` overrides the class\n * default; every field is omitted when its source is absent (graceful\n * degradation — a site without block styles emits an unstyled band). */\nconst baseOf = (band: Band, opts?: BandToCatalogOptions): CatalogBaseFields => {\n const st = opts?.styles?.get(band.index) ?? {};\n const def = (band.blockClass ? opts?.defaults?.get(band.blockClass) : undefined) ?? {};\n const padding = st[\"padding\"] ?? def.padding;\n return {\n index: band.index,\n ...(band.background ? { background: band.background } : {}),\n ...(st[\"background-color\"] ? { backgroundColor: st[\"background-color\"] } : {}),\n ...(st[\"min-height\"] ? { minHeight: st[\"min-height\"] } : {}),\n ...(st[\"text-align\"] ? { textAlign: st[\"text-align\"] } : {}),\n ...(st[\"vertical-align\"] === \"middle\" ? { verticalAlign: \"middle\" } : {}),\n ...(padding ? { contentPadding: padding } : {}),\n ...(def.mobilePadding ? { contentPaddingMobile: def.mobilePadding } : {}),\n ...(def.maxWidth ? { maxContentWidth: def.maxWidth } : {}),\n ...(st[\"_column-width\"] ? { columnWidth: st[\"_column-width\"] } : {}),\n ...(st[\"_column-side\"] ? { columnSide: st[\"_column-side\"] } : {}),\n };\n};\n\n/** Map a thin routed SliceSpec + its Band to a rich CatalogSpec. Reuses the\n * battle-tested routing; builds full cells by walking the node subtree. */\nexport function sliceSpecToCatalog(\n spec: SliceSpec,\n band: Band,\n opts: BandToCatalogOptions = {},\n): CatalogSpec {\n const base = baseOf(band, opts);\n switch (spec.slice) {\n case \"Hero\":\n case \"TitleBand\":\n case \"RichText\":\n // Overlay/centered/body text → a Section; heading split out of the cells.\n return {\n slice: \"BluxSection\",\n ...base,\n ...splitHeadingAndCells(band.root),\n };\n case \"SplitFeature\": {\n const text = cellFromNode(spec.text);\n // Promote to the thin two-field slice ONLY when the media is an image\n // and the text half really is just title+body. A video cannot ride the\n // slice's Image field (the `{__asset_id}` marker would dangle — videos\n // are excluded from image uploads — silent total loss), and a media/\n // subgrid/raw-bearing text half cannot ride BluxMediaText (band 1's\n // text half holds 2 images) — refuse the lossy promotion, mirroring\n // classifyBand's own philosophy, and enrich the WHOLE band through the\n // grid path (where a video becomes an embed_html <video> cell) so\n // nothing is dropped.\n if (spec.media.kind !== \"video\" && !text.media && !text.subgrid && !text.embedHtml)\n return {\n slice: \"BluxMediaText\",\n ...base,\n mediaSide: spec.mediaSide,\n layoutRatio: spec.ratio,\n media: spec.media,\n ...pick(text),\n };\n return gridOrBlock(band.root, base);\n }\n case \"MediaFull\":\n case \"VideoFeature\":\n return { slice: \"BluxMedia\", ...base, media: spec.media };\n case \"Gallery\":\n return {\n slice: \"BluxGallery\",\n ...base,\n cells: spec.media.map((m) => ({ kind: \"media\", media: m }) as CatalogCell),\n };\n case \"Carousel\":\n return {\n slice: \"BluxCarousel\",\n ...base,\n ...(spec.columns !== undefined ? { columnsVisible: spec.columns } : {}),\n cells: spec.slides.map(slideCell),\n };\n case \"LocationMap\": {\n // Decision B (4b): the map band becomes a BluxSection carrying the\n // ORIGINAL mount raw html as a widget (see liftMapWidget). Emit\n // sanitizes and inlines the MapConfig; classify keeps the html pristine.\n const widget = liftMapWidget(band, opts);\n if (!widget) return blockSpec(band.root, base); // predicate-less caller: 4a fallback\n return { slice: \"BluxSection\", ...base, cells: [], ...widget };\n }\n case \"Grid\":\n default: {\n // Rich grid when it fits the depth-2 model; else the opaque fallback.\n // Round 3: every REAL fleet map band holds the mount PLUS panel rows,\n // so it routes HERE (never LocationMap) — and classifyBand's rewrite\n // left an html-less widget node in spec.root, which the Block path\n // serialized as an empty div: map, legend chips, and MapConfig silently\n // vanished from the plan. Lift the ORIGINAL mount from band.root onto\n // the container spec (the CatalogBase widget triple — both BluxGrid and\n // the BluxBlock fallback carry it); when the band holds a mount the\n // lift cannot recover, say so — never silent.\n const widget = liftMapWidget(band, opts);\n if (!widget) reportUnrecoveredMount(spec.root, band, opts);\n return { ...gridOrBlock(spec.root, base), ...(widget ?? {}) };\n }\n }\n}\n\n/** The band router: reuse classifyBand for routing, enrich to catalog.\n * `opts.isMapMount` flows into the grid classifier (LocationMap promotion)\n * AND into the LocationMap enrichment above (mount html recovery). */\nexport function bandToCatalog(band: Band, opts: BandToCatalogOptions = {}): CatalogSpec {\n return sliceSpecToCatalog(classifyBand(band, opts), band, opts);\n}\n\n/** Feed-band interception (spec §7 rule 1): a band whose site.json item\n * declares `sources[]` becomes a BluxCollection query spec BEFORE the grid\n * router runs — EXCEPT `__media` (media-library galleries already materialize\n * via the grid path) and non-feed items, which fall through to `bandToCatalog`\n * unchanged. `item` is the positional convert-path join:\n * `siteJson.content.pages[p].items[band.index]`. */\nexport function bandOrCollection(\n band: Band,\n item: unknown,\n feeds: Record<string, { name?: unknown } | undefined>,\n opts: BandToCatalogOptions = {},\n): CatalogSpec {\n if (!isFeedBand(item)) return bandToCatalog(band, opts);\n const where = whereOf(band, opts);\n const feedIds = item.sources.map(String);\n if (feedIds[0] === \"__media\") {\n // Media-library galleries materialize via the grid path — but that path\n // renders sources[0] only, so any EXTRA source riding the same item would\n // be silently unconsumed (round-2 item 5): diagnose each.\n for (const id of feedIds.slice(1))\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources \"${id}\" alongside __media — the media-library grid renders sources[0] only, the extra source is not consumed`,\n });\n return bandToCatalog(band, opts);\n }\n // Positional-join guard (mirrors convert.ts materializeFeedBands): only a\n // band that lost its content to the feed-template drop is a real feed grid.\n // A CONTENT-BEARING band whose positional item carries sources means the\n // site.json join landed on the wrong band — routing it to a BluxCollection\n // would clobber its parsed content, so it falls through to bandToCatalog.\n // Emptyishness IGNORES pure feed-template mounts (round-2 item 1: the real\n // fitHealthClub band-3 root is `<div data-exec=\"custom_…\">` — the Blux feed\n // placeholder the collection replaces, not content) and the page's map\n // mount (a widget the collection lifts below, not content either).\n if (!isEmptyish(band.root, (n) => isFeedTemplateMount(n) || (opts.isMapMount?.(n) ?? false))) {\n opts.diagnostics?.push({\n kind: \"feed-band-misalign\",\n where,\n message: `band ${band.index} has parsed content but its site.json item carries feed sources — positional join misaligned, classified as content`,\n });\n return bandToCatalog(band, opts);\n }\n // Validate EVERY source, not just [0] (round-2 item 5). An unknown source\n // (or a DO-NOT-USE feed the entity emit skips) is diagnosed — never silent:\n // the slice would resolve no documents from it at render time — and dropped\n // from the spec's feedIds. When NO source survives, the whole band keeps\n // the existing skip behavior: still a collection (feedIds verbatim), with\n // the diagnostics naming why it will query zero documents.\n const validIds: string[] = [];\n for (const id of feedIds) {\n if (id === \"__media\") {\n // Round-3 5b: the media-library sentinel is only meaningful as\n // sources[0] (where the grid path renders it — see above). In a later\n // position it is a KNOWN sentinel, not an unknown feed — named as such\n // and dropped from the collection's feed ids.\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources the __media media-library sentinel alongside feed sources — it only renders as sources[0]; dropped from the collection`,\n });\n continue;\n }\n const feed = feeds[id];\n const name = String(feed?.name ?? \"\");\n if (!feed) {\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources unknown feed \"${id}\" — the collection will resolve no documents from it`,\n });\n } else if (isSkippedFeed(name)) {\n opts.diagnostics?.push({\n kind: \"skipped-feed\",\n where,\n message: `band ${band.index} sources feed \"${name}\" (marked DO NOT USE — not migrated) — the collection will resolve no documents from it`,\n });\n } else {\n validIds.push(id);\n }\n }\n // Round-3 5a: duplicate sources ([\"feed-1\",\"feed-1\"]) must not duplicate\n // feed_ids — dedupe, keeping first-seen order.\n const keptIds = [...new Set(validIds.length ? validIds : feedIds)];\n const feedName = String(feeds[keptIds[0]!]?.name ?? \"\");\n // Collection is a container (decision B, round-2 item 2): a MAP mount riding\n // the feed band lifts onto the spec through the same widget triple\n // BluxSection uses — never treated as content, never dropped.\n const widget = liftMapWidget(band, opts);\n const cfg = item.sourceConfig ?? {};\n const filterTag = (cfg[\"filters\"] as { tag?: unknown } | undefined)?.tag;\n const sort = cfg[\"sort\"];\n // Real Blux sourceConfig carries `limit` as a STRING ('9','12','20','0');\n // '0'/0 means unlimited (omitted below, like NaN). `count` never occurs in\n // the fleet.\n const limit = Number(cfg[\"limit\"]);\n const mediaRatio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n const { heading } = splitHeadingAndCells(band.root);\n const spec: BluxCollectionSpec = {\n slice: \"BluxCollection\",\n ...baseOf(band, opts),\n ...(heading ? { heading } : {}),\n entityType: feedEntityType(feedName),\n feedIds: keptIds,\n ...(typeof filterTag === \"string\" && filterTag ? { filterTag } : {}),\n ...(typeof sort === \"string\" && sort ? { sort } : {}),\n ...(Number.isFinite(limit) && limit > 0 ? { limit } : {}),\n ...(mediaRatio ? { mediaRatio } : {}),\n layout: (item as { type?: unknown }).type === \"slides\" ? \"carousel\" : \"grid\",\n ...(cfg[\"scrollLoadMore\"] === true ? { scrollLoadMore: true } : {}),\n ...(widget ?? {}),\n };\n return spec;\n}\n\n// -- helpers --\n/** A raw custom-element feed-template mount: a div whose only substance is a\n * `data-exec` attr — no visible text, no media (fitHealthClub band 3:\n * `<div id=\"custom-element1\" data-exec=\"custom_…\"></div>`). It is the Blux\n * placeholder the collection replaces, so the emptyish guard must not read it\n * as content. A mount whose markup DOES carry visible content is real content. */\nfunction isFeedTemplateMount(node: Node): boolean {\n return (\n node.kind === \"raw\" &&\n /\\bdata-exec=\"custom_[a-f0-9_]+\"/.test(node.html) &&\n !hasVisibleContent(node.html)\n );\n}\n\n/** Did this band lose its content to the feed-template drop? — its parsed\n * root is empty (just heading(s)/subtitle/empty-raw, no media and no populated\n * row). Local reimplementation of convert.ts's isEmptyish (not exported\n * there) — keep the two in sync. `ignore` (feed-band guard only) names nodes\n * that must not count as content: pure feed-template mounts and the page's\n * map mount (see bandOrCollection). */\nfunction isEmptyish(root: Node, ignore?: (n: Node) => boolean): boolean {\n if (ignore?.(root)) return true;\n switch (root.kind) {\n case \"heading\":\n case \"subtitle\":\n return true;\n case \"raw\":\n return root.html.trim() === \"\";\n case \"media\":\n case \"widget\":\n return false;\n case \"row\": {\n // Round-3 5c: `ignore` applies through wrapper stacks too — a mount the\n // parser boxed in a stack inside a row cell is still just the mount.\n const cells = ignore\n ? root.cells.filter((c) => !isIgnoredSubtree(c.node, ignore))\n : root.cells;\n return cells.length === 0;\n }\n case \"stack\":\n return root.children.every((c) => isEmptyish(c, ignore));\n default:\n return false;\n }\n}\n\n/** Depth-first search for the mount node's raw html in the UNREWRITTEN band\n * tree (the predicate only ever matches `raw` nodes — see makeIsMapMount). */\nfunction findMountHtml(node: Node, isMount: (n: Node) => boolean): string | undefined {\n if (node.kind === \"raw\" && isMount(node)) return node.html;\n const children =\n node.kind === \"row\"\n ? node.cells.map((c) => c.node)\n : node.kind === \"stack\"\n ? node.children\n : [];\n for (const c of children) {\n const found = findMountHtml(c, isMount);\n if (found !== undefined) return found;\n }\n return undefined;\n}\n\n/** Diagnostic addressing (round-2 10a): `<pageUid>:<band>` when the caller\n * names the page, else the bare band index (option-less unit callers). */\nfunction whereOf(band: Band, opts: BandToCatalogOptions): string {\n return opts.pageUid !== undefined ? `${opts.pageUid}:${band.index}` : String(band.index);\n}\n\n/** Decision-B mount recovery, shared by every container route (LocationMap →\n * Section, feed band → Collection, and — round 3 — Grid/Block): classifyBand\n * rewrote its COPY of the tree (the mount is an html-less widget node), but\n * `band.root` still holds the ORIGINAL raw mount — re-find it with the same\n * predicate. Returns the CatalogBase widget triple to spread onto the spec,\n * or undefined when no mount html is recoverable. */\nfunction liftMapWidget(\n band: Band,\n opts: BandToCatalogOptions,\n): { widgetKind: \"map\"; widgetHtml: string; mapConfig?: MapConfig } | undefined {\n const mountHtml = opts.isMapMount ? findMountHtml(band.root, opts.isMapMount) : undefined;\n if (mountHtml === undefined) return undefined;\n return {\n widgetKind: \"map\",\n widgetHtml: mountHtml,\n ...(opts.mapConfig ? { mapConfig: opts.mapConfig } : {}),\n };\n}\n\n/** Round-3 never-silent guard: classifyBand marked a mount (an html-less\n * `widget` node in its rewritten tree) but the lift recovered nothing — no\n * predicate, or one that no longer matches band.root. Without this the mount\n * would vanish with ZERO diagnostics (blockPayload serializes the widget node\n * as an empty div). */\nfunction reportUnrecoveredMount(rewrittenRoot: Node, band: Band, opts: BandToCatalogOptions): void {\n if (!containsWidgetNode(rewrittenRoot)) return;\n opts.diagnostics?.push({\n kind: \"dropped-widget\",\n where: whereOf(band, opts),\n message: `band ${band.index} holds a widget mount but no mount html was recovered (no predicate, or one that no longer matches band.root) — the widget is dropped`,\n });\n}\n\nfunction containsWidgetNode(node: Node): boolean {\n if (node.kind === \"widget\") return true;\n const children =\n node.kind === \"row\"\n ? node.cells.map((c) => c.node)\n : node.kind === \"stack\"\n ? node.children\n : [];\n return children.some(containsWidgetNode);\n}\n\n/** Whether `ignore` swallows a whole subtree: the node itself, or a NON-EMPTY\n * stack of nothing but ignored subtrees (round-3 5c: the parser sometimes\n * boxes a mount in a wrapper stack inside a row cell). An empty stack still\n * counts as a cell, exactly as before. */\nfunction isIgnoredSubtree(node: Node, ignore: (n: Node) => boolean): boolean {\n if (ignore(node)) return true;\n return (\n node.kind === \"stack\" &&\n node.children.length > 0 &&\n node.children.every((c) => isIgnoredSubtree(c, ignore))\n );\n}\n/** One carousel slide → a media cell. The caption html is wrapper-less in the\n * parser, so it is wrapped at the slide's own heading level; the subcaption\n * (the hero slide's location line) rides the cell body. */\nfunction slideCell(s: CarouselSlide): CatalogCell {\n return {\n kind: \"media\",\n media: s.media,\n ...(s.caption ? { title: `<h${s.caption.level}>${s.caption.html}</h${s.caption.level}>` } : {}),\n ...(s.subcaption ? { bodyHtml: s.subcaption.html } : {}),\n };\n}\n\n/** Rich grid when the tree fits the cell→subgrid model; else the\n * content-preserving BluxBlock fallback (guard and builder agree by\n * construction — see cellDepthExceedsTwo). */\nfunction gridOrBlock(root: Node, base: CatalogBaseFields): BluxGridSpec | BluxBlockSpec {\n if (cellDepthExceedsTwo(root)) return blockSpec(root, base);\n const columns = gridColumns(root);\n return {\n slice: \"BluxGrid\",\n ...base,\n ...(columns !== undefined ? { columns } : {}),\n ...splitHeadingAndCells(root),\n };\n}\n\nfunction blockSpec(root: Node, base: CatalogBaseFields): BluxBlockSpec {\n return {\n slice: \"BluxBlock\",\n ...base,\n payload: blockPayload(root),\n // Surfaced so emit uploads the payload's assets; the payload's inlined\n // urls remain CDN until the 4d migrate-time rewrite.\n media: collectMedia(root),\n };\n}\n\n/** The top row's column count: the grid token's cols, or the row's cell count\n * when the token doesn't say (\"any\"). */\nfunction gridColumns(root: Node): number | undefined {\n const findRow = (n: Node): Extract<Node, { kind: \"row\" }> | null => {\n if (n.kind === \"row\") return n;\n if (n.kind === \"stack\") {\n for (const c of n.children) {\n const r = findRow(c);\n if (r) return r;\n }\n }\n return null;\n };\n const row = findRow(root);\n if (!row || row.cells.length === 0) return undefined;\n const cols = row.cells[0]?.token.cols;\n return typeof cols === \"number\" ? cols : row.cells.length;\n}\n\n/** Split a root into its section heading (first heading-only cell) + the\n * remaining content cells, so the heading is not duplicated as both the\n * section heading AND a cell title. */\nfunction splitHeadingAndCells(root: Node): {\n heading?: string;\n headingRole?: string;\n cells: CatalogCell[];\n} {\n const cells = nodeToCells(root).filter(\n (c) => c.title || c.bodyHtml || c.media || c.subgrid || c.embedHtml,\n );\n const hIdx = cells.findIndex(\n (c) => c.title && !c.bodyHtml && !c.media && !c.subgrid && !c.embedHtml,\n );\n const h = hIdx >= 0 ? cells[hIdx] : undefined;\n if (h)\n return {\n ...(h.title ? { heading: h.title } : {}),\n ...(h.titleRole ? { headingRole: h.titleRole } : {}),\n cells: cells.filter((_, i) => i !== hIdx),\n };\n return { cells };\n}\n// The two-field BluxMediaText slice keeps a RichText `body` (spec.ts), so its\n// text half maps the cell's baked `bodyHtml` back onto that `body` field. Any\n// txt-role-* wrappers in bodyHtml are intentionally flattened away here — a\n// RichText field cannot carry them, so MediaText body role-fidelity is out of\n// scope (this fix restores per-block roles for CELL bodies, not MediaText).\nfunction pick(c: CatalogCell): { title?: string; body?: string } {\n return {\n ...(c.title ? { title: c.title } : {}),\n ...(c.bodyHtml ? { body: c.bodyHtml } : {}),\n };\n}\n","// Feed records → real Prismic entity documents (spec §8). Each kept feed maps\n// onto its frozen entity type (feeds.ts), its records onto the Plan-2 shared\n// base fields, and any non-base keys ride verbatim as per-site extension\n// fields — with one extension custom type per USED entity type (the shared\n// base Main + the observed extension fields).\nimport type { Media } from \"../grid/types.js\";\nimport type { Diagnostic } from \"../ir.js\";\nimport { assetRef, richText, type PlanCustomType, type PlanDocument } from \"../emit/plan.js\";\nimport { demoteHeadingsHtml } from \"../emit/coerce-html.js\";\nimport { feedEntityType, isSkippedFeed } from \"./feeds.js\";\n\nexport type EntityEmit = {\n documents: PlanDocument[];\n customTypes: PlanCustomType[];\n media: Media[]; // every record media (kind:\"image\"), for plan-asset upload\n diagnostics: Diagnostic[];\n};\n\n/** The Plan-2 shared-base entity Main (frozen — mirrors the starter's\n * customtypes/{product,person,event,news_article,project,collection_item}\n * base JSON exactly). Extension fields are appended per site. */\nconst BASE_MAIN: Record<string, unknown> = {\n uid: { type: \"UID\", config: { label: \"uid\" } },\n title: {\n type: \"StructuredText\",\n config: { label: \"title\", single: \"heading1\" },\n },\n body: {\n type: \"StructuredText\",\n config: {\n label: \"body\",\n multi: \"paragraph,strong,em,hyperlink,list-item\",\n },\n },\n media: {\n type: \"Image\",\n config: { label: \"media\", constraint: {}, thumbnails: [] },\n },\n gallery: {\n type: \"Group\",\n config: {\n label: \"gallery\",\n fields: {\n image: {\n type: \"Image\",\n config: { label: \"image\", constraint: {}, thumbnails: [] },\n },\n caption: { type: \"Text\", config: { label: \"caption\" } },\n },\n },\n },\n tags: { type: \"Text\", config: { label: \"tags (comma-separated)\" } },\n date: { type: \"Date\", config: { label: \"date\" } },\n link: { type: \"Link\", config: { label: \"link\", allowTargetBlank: true } },\n};\n\n/** Record keys the base mapping consumes explicitly, PLUS the base field names\n * a record key must never shadow (`uid`/`gallery`/`link` — an extension\n * assignment would clobber the mapped base field in `data`). `disabled` is NOT\n * here: it is an extension field (it lands verbatim in data and in the\n * extension type) that ALSO drives the enabled-beats-disabled uid dedup.\n * `url` is deliberately absent: the raw url rides as an extension field (the\n * Phase-7 detail-page slug) even though the base mapping also reads it for the\n * uid and the external-only link field. */\nconst BASE_KEYS = new Set([\n \"title\",\n \"body\",\n \"media\",\n \"items\",\n \"tags\",\n \"date\",\n \"link_url\",\n \"uid\",\n \"gallery\",\n \"link\",\n]);\n\n/** Underscore-prefixed keys are per-element style config (collections.ts\n * convention) — never content. */\nconst isStyleKey = (key: string) => key.startsWith(\"_\");\n\ntype ExtKind = \"text\" | \"richtext\" | \"boolean\" | \"number\" | \"group\";\n\nconst EXT_FIELD_CONFIG: Record<ExtKind, (label: string) => Record<string, unknown>> = {\n text: (label) => ({ type: \"Text\", config: { label } }),\n // deriveFields convention: mirrors the base body field's model config.\n richtext: (label) => ({\n type: \"StructuredText\",\n config: { label, multi: \"paragraph,strong,em,hyperlink,list-item\" },\n }),\n boolean: (label) => ({ type: \"Boolean\", config: { label } }),\n number: (label) => ({ type: \"Number\", config: { label } }),\n group: (label) => ({\n type: \"Group\",\n config: {\n label,\n fields: { value: { type: \"Text\", config: { label: \"value\" } } },\n },\n }),\n};\n\nconst kindOf = (key: string, value: unknown): ExtKind => {\n // deriveFields convention: `description` (like `body`) is richtext.\n if (key === \"description\" && typeof value === \"string\") return \"richtext\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n if (Array.isArray(value)) return \"group\";\n return \"text\";\n};\n\n/** \"news_article\" → \"News Article\". */\nconst typeLabel = (type: string): string =>\n type\n .split(\"_\")\n .map((w) => (w ? w[0]!.toUpperCase() + w.slice(1) : w))\n .join(\" \");\n\nconst mediaUuidOf = (v: unknown): string | undefined => {\n if (v && typeof v === \"object\") {\n const m = (v as { media?: unknown }).media;\n if (typeof m === \"string\" && m) return m;\n }\n return undefined;\n};\n\n/** Extension entries of a record: every non-style, non-base key. */\nfunction extEntries(record: Record<string, unknown>): [string, unknown][] {\n return Object.entries(record).filter(\n ([key, value]) => !isStyleKey(key) && !BASE_KEYS.has(key) && value !== undefined,\n );\n}\n\n/** Legacy-parity spelling normalization (round-2 item 4 — grid feed-grid.ts\n * isDisabled honors BOTH `disabled` and `disable`): fold `disable` into the\n * one normalized `disabled` key, so either spelling === true drives the\n * enabled-beats-disabled uid dedup AND emits `data.disabled = true` (the\n * starter filters on `doc.data.disabled !== true`); a separate `disable`\n * extension field never emits. */\nfunction normalizeRecord(record: Record<string, unknown>): Record<string, unknown> {\n if (record.disable === undefined) return record;\n const { disable, ...rest } = record;\n return {\n ...rest,\n disabled: record.disabled === true || disable === true ? true : (record.disabled ?? disable),\n };\n}\n\n/** A valid Prismic UID fragment: alphanumerics/dash/underscore only. */\nconst BARE_SLUG_RE = /^[a-z0-9_-]+$/i;\n\n/** External http(s) url — the ONLY shape the base link field accepts (spec\n * §8). Case-insensitive: real-world data capitalizes schemes (\"HTTPS://…\"). */\nconst EXTERNAL_RE = /^https?:\\/\\//i;\n\n/** The record's uid. Its `url` — but ONLY when it is a bare slug after the\n * productSlug-style `/products/` prefix strip + slash trim: real fleet urls\n * are often absolute (`https://mailchi.mp/...`, strategyAdvantage ×46) or\n * route paths (`/news/<slug>` tosa ×49, `/projects/<slug>` williamsonHomes\n * ×5), which would make INVALID Prismic UIDs. Anything non-bare falls back to\n * the slugified title. */\nfunction recordUid(record: Record<string, unknown>): string {\n const url = typeof record.url === \"string\" ? record.url.trim() : \"\";\n const fromUrl = url.replace(/^\\/+products\\/+/i, \"\").replace(/^\\/+|\\/+$/g, \"\");\n // BARE_SLUG_RE is case-insensitive, so an uppercase slug passes — lowercase\n // it (round-2 item 9): a mixed-case uid is INVALID in Prismic.\n if (fromUrl && BARE_SLUG_RE.test(fromUrl)) return fromUrl.toLowerCase();\n return String(record.title ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\ntype DateOrientation = \"year-month-day\" | \"year-day-month\";\n\nconst DATE_RE = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/;\n\n/** Drop a trailing time suffix (`T10:30:00Z`, ` 10:30`) so a datetime-shaped\n * string keeps its date instead of being dropped as unparseable (round-2\n * item 8 — zero fleet occurrences today, cheap resilience). */\nconst stripTime = (s: string): string => s.replace(/(?:T|\\s+)\\d{1,2}:\\d{2}\\S*$/, \"\").trim();\n\nexport type NormalizedDate = {\n date?: string;\n issue?: \"ambiguous\" | \"unparseable\";\n};\n\n/** Normalize a raw feed date into a valid Prismic `YYYY-MM-DD`. Composition's\n * Products dates are unpadded `YYYY-n-n` with MIXED orientation (207 records\n * year-DAY-month, 191 year-month-day, 135 ambiguous) — verbatim passthrough\n * would land invalid values in a Date field. Already-valid ISO passes; an\n * unambiguous `YYYY-n-n` self-resolves (whichever side > 12 is the day); a\n * both-≤12 value resolves by `feedVote` (the feed's majority orientation), or\n * defaults to year-month-day with an `ambiguous` issue when the feed offers no\n * evidence. Anything else (or an out-of-range resolution) is `unparseable` —\n * the caller omits the date. */\nexport function normalizeDate(raw: unknown, feedVote: DateOrientation | null): NormalizedDate {\n if (typeof raw !== \"string\") return { issue: \"unparseable\" };\n const trimmed = stripTime(raw.trim());\n const m = DATE_RE.exec(trimmed);\n if (!m) return { issue: \"unparseable\" };\n const year = m[1]!;\n const mid = Number(m[2]!);\n const last = Number(m[3]!);\n // Range check PLUS calendar validity (round-2 item 8): \"2017-02-30\" is\n // range-valid but no real date — a UTC round-trip catches month-length and\n // leap-year violations, so impossible dates go unparseable instead of\n // landing verbatim in a Prismic Date field.\n const build = (month: number, day: number): string | undefined => {\n if (month < 1 || month > 12 || day < 1 || day > 31) return undefined;\n const d = new Date(Date.UTC(Number(year), month - 1, day));\n if (\n d.getUTCFullYear() !== Number(year) ||\n d.getUTCMonth() !== month - 1 ||\n d.getUTCDate() !== day\n )\n return undefined;\n return `${year}-${String(month).padStart(2, \"0\")}-${String(day).padStart(2, \"0\")}`;\n };\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed) && build(mid, last) === trimmed) return { date: trimmed }; // already-valid ISO\n if (mid > 12 && last > 12) return { issue: \"unparseable\" };\n if (mid > 12 || last > 12) {\n // Exactly one side can be the day — the orientation is self-evident.\n const date = mid > 12 ? build(last, mid) : build(mid, last);\n return date ? { date } : { issue: \"unparseable\" };\n }\n // Both ≤ 12: orientation-ambiguous — the feed's majority decides;\n // tie/no-evidence reads year-month-day and flags it.\n const date = feedVote === \"year-day-month\" ? build(last, mid) : build(mid, last);\n if (date === undefined) return { issue: \"unparseable\" };\n return feedVote ? { date } : { date, issue: \"ambiguous\" };\n}\n\n/** The feed's majority date orientation, voted by its unambiguous records\n * (exactly one side > 12). Null when tied or the feed offers no evidence. */\nfunction feedDateOrientation(items: unknown[]): DateOrientation | null {\n let ymd = 0;\n let ydm = 0;\n for (const it of items) {\n const raw = it && typeof it === \"object\" ? (it as { date?: unknown }).date : undefined;\n if (typeof raw !== \"string\") continue;\n const m = DATE_RE.exec(stripTime(raw.trim()));\n if (!m) continue;\n const mid = Number(m[2]!);\n const last = Number(m[3]!);\n // The would-be day must be a POSSIBLE day (≤ 31) for the record to cast a\n // vote (round-2 item 8): garbage like \"2017-45-3\" is not ydm evidence and\n // must never silently resolve another record's ambiguous date.\n if (mid > 12 && mid <= 31 && last >= 1 && last <= 12) ydm++;\n else if (last > 12 && last <= 31 && mid >= 1 && mid <= 12) ymd++;\n }\n if (ydm > ymd) return \"year-day-month\";\n if (ymd > ydm) return \"year-month-day\";\n return null;\n}\n\nconst titleOf = (record: Record<string, unknown>): string =>\n typeof record.title === \"string\" && record.title.trim() ? record.title.trim() : \"(untitled)\";\n\ntype RecordCtx = {\n feedId: string;\n dateVote: DateOrientation | null;\n diagnostics: Diagnostic[];\n /** The entity type's extension kinds, RESOLVED across all records of every\n * feed of the type before any document emits (round-2 item 7) — value\n * emission must know the final kind so a scalar under a group-kind key\n * wraps as a row instead of landing raw in a Group-modeled field. */\n extKinds: ReadonlyMap<string, ExtKind>;\n};\n\n/** Base-field mapping for one record → the document data + the media it\n * references. Extension keys ride verbatim (except `description` → richtext\n * and arrays → their Group{value} model shape). */\nfunction recordToDoc(\n record: Record<string, unknown>,\n type: string,\n uid: string,\n ctx: RecordCtx,\n): { doc: PlanDocument; media: Media[] } {\n const data: Record<string, unknown> = {};\n const media: Media[] = [];\n\n if (typeof record.title === \"string\" && record.title.trim())\n data.title = richText(`<h1>${record.title}</h1>`);\n // The base type's body allows no heading blocks — demote, like recordData.\n if (typeof record.body === \"string\" && record.body.trim())\n data.body = richText(demoteHeadingsHtml(record.body));\n\n const mainUuid = mediaUuidOf(record.media);\n if (mainUuid) {\n data.media = assetRef(mainUuid);\n media.push({ kind: \"image\", assetId: mainUuid });\n }\n\n if (Array.isArray(record.items)) {\n const gallery: { image: unknown; caption: string }[] = [];\n for (const it of record.items) {\n const item = it && typeof it === \"object\" ? (it as Record<string, unknown>) : undefined;\n const uuid = item ? mediaUuidOf(item.media) : undefined;\n if (!uuid) continue;\n media.push({ kind: \"image\", assetId: uuid });\n const caption =\n typeof item?.caption === \"string\"\n ? item.caption\n : typeof item?.title === \"string\"\n ? item.title\n : \"\";\n gallery.push({ image: assetRef(uuid), caption });\n }\n if (gallery.length) data.gallery = gallery;\n }\n\n if (Array.isArray(record.tags))\n data.tags = record.tags.filter((t): t is string => typeof t === \"string\").join(\",\");\n else if (typeof record.tags === \"string\" && record.tags) data.tags = record.tags;\n\n if (record.date != null && record.date !== \"\") {\n const norm = normalizeDate(record.date, ctx.dateVote);\n if (norm.date !== undefined) data.date = norm.date;\n if (norm.issue === \"ambiguous\")\n ctx.diagnostics.push({\n kind: \"ambiguous-date\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" date \"${String(record.date)}\" is orientation-ambiguous with no feed evidence — read as year-month-day \"${norm.date}\"`,\n });\n else if (norm.issue === \"unparseable\")\n ctx.diagnostics.push({\n kind: \"malformed-feed-field\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" date \"${String(record.date)}\" does not normalize to YYYY-MM-DD — omitted`,\n });\n }\n\n // Spec §8: the link field is EXTERNAL-only — bare slugs ('steel-chair') and\n // route paths ('/news/x') are detail-page slugs, not links. The raw `url`\n // still rides as an extension field below (Phase 7 detail pages). Round-2\n // item 6: pick the FIRST candidate that IS external (scheme match\n // case-insensitive) — a bare-slug `url` must not eat an external `link_url`.\n const urlVal = typeof record.url === \"string\" ? record.url.trim() : \"\";\n const linkUrlVal = typeof record.link_url === \"string\" ? record.link_url.trim() : \"\";\n const external = [urlVal, linkUrlVal].find((v) => v && EXTERNAL_RE.test(v));\n if (external) data.link = { link_type: \"Web\", url: external };\n // A non-external link_url is dropped either way (link_url is a BASE_KEYS\n // key — it never rides as an extension field) — never silently.\n if (linkUrlVal && !EXTERNAL_RE.test(linkUrlVal))\n ctx.diagnostics.push({\n kind: \"malformed-feed-field\",\n where: ctx.feedId,\n message: `record \"${titleOf(record)}\" link_url \"${linkUrlVal}\" is not an external http(s) url — dropped (the base link field is external-only)`,\n });\n\n for (const [key, value] of extEntries(record)) {\n if (ctx.extKinds.get(key) === \"group\")\n // The key models Group{value:Text} (an array was observed SOMEWHERE in\n // the type's records — round-2 item 7): scalars wrap as a single row so\n // every record's data matches the model, whatever this record holds.\n data[key] = (Array.isArray(value) ? value : [value]).map((x) => ({\n value: String(x),\n }));\n else if (key === \"description\" && typeof value === \"string\")\n // deriveFields convention: description (like body) is richtext; the\n // extension model is StructuredText, so raw HTML must not ride as Text.\n data[key] = richText(demoteHeadingsHtml(value));\n else if (Array.isArray(value))\n // Match the Group{value:Text} extension model the array kind derives.\n data[key] = value.map((x) => ({ value: String(x) }));\n else data[key] = value;\n }\n\n return { doc: { type, uid, data }, media };\n}\n\n/** Feed records → typed entity documents + per-type extension custom types +\n * the media they reference + skip diagnostics. Uid dedup is per entity type:\n * an enabled record beats a disabled one, else first-seen wins (recordUid\n * semantics — the record's bare-slug `url` first, else the title slug); every\n * dropped loser is named by a uid-collision diagnostic. */\nexport function buildEntityEmit(\n feeds: Record<string, { name?: string; items?: unknown[]; fields?: unknown } | undefined>,\n): EntityEmit {\n const diagnostics: Diagnostic[] = [];\n const media: Media[] = [];\n const typeOrder: string[] = [];\n const docsByType = new Map<\n string,\n Map<string, { doc: PlanDocument; disabled: boolean; title: string }>\n >();\n const extByType = new Map<string, Map<string, ExtKind>>();\n\n // Pass 1 — resolve every extension key's kind across ALL records of every\n // feed of the type BEFORE any document emits (round-2 item 7): emission must\n // know the final kind, or a string-then-array key would land a raw string\n // in a Group-modeled field (and boolean/number-then-array the reverse).\n // DO-NOT-USE feeds skip here, once.\n const kept: { feedId: string; type: string; items: unknown[] }[] = [];\n for (const [feedId, feed] of Object.entries(feeds)) {\n if (!feed) continue;\n const name = String(feed.name ?? \"\");\n if (isSkippedFeed(name)) {\n diagnostics.push({\n kind: \"skipped-feed\",\n where: feedId,\n message: `feed \"${name}\" is marked DO NOT USE — not migrated`,\n });\n continue;\n }\n const type = feedEntityType(name);\n if (!docsByType.has(type)) {\n typeOrder.push(type);\n docsByType.set(type, new Map());\n extByType.set(type, new Map());\n }\n const extKinds = extByType.get(type)!;\n const items = Array.isArray(feed.items) ? feed.items : [];\n kept.push({ feedId, type, items });\n for (const item of items) {\n if (!item || typeof item !== \"object\") continue;\n const record = normalizeRecord(item as Record<string, unknown>);\n // Extension fields type from the observed value shapes (deriveFields\n // convention: a later, more specific observation upgrades \"text\");\n // group — an array observed in ANY record — beats every scalar kind.\n for (const [key, value] of extEntries(record)) {\n const next = kindOf(key, value);\n const prev = extKinds.get(key);\n if (!prev || prev === \"text\" || next === \"group\") extKinds.set(key, next);\n }\n }\n }\n\n // Pass 2 — emit documents with the resolved kinds.\n for (const { feedId, type, items } of kept) {\n const byUid = docsByType.get(type)!;\n const extKinds = extByType.get(type)!;\n // The feed's majority date orientation, voted once by its unambiguous\n // records, resolves the both-≤12 dates (see normalizeDate).\n const dateVote = feedDateOrientation(items);\n items.forEach((item, i) => {\n if (!item || typeof item !== \"object\") return;\n const record = normalizeRecord(item as Record<string, unknown>);\n const uid = recordUid(record) || `item-${i}`;\n const { doc, media: recMedia } = recordToDoc(record, type, uid, {\n feedId,\n dateVote,\n diagnostics,\n extKinds,\n });\n media.push(...recMedia);\n const disabled = record.disabled === true;\n const existing = byUid.get(uid);\n const dropped =\n !existing || (existing.disabled && !disabled)\n ? existing // the incoming record wins — the stored one is dropped\n : { title: titleOf(record) }; // the stored record wins — this one is dropped\n if (dropped)\n diagnostics.push({\n kind: \"uid-collision\",\n where: feedId,\n message: `record \"${dropped.title}\" collides on uid \"${uid}\" — dropped (enabled beats disabled, else first-seen wins)`,\n });\n if (!existing || (existing.disabled && !disabled))\n byUid.set(uid, { doc, disabled, title: titleOf(record) });\n });\n }\n\n const documents: PlanDocument[] = [];\n const customTypes: PlanCustomType[] = [];\n for (const type of typeOrder) {\n for (const { doc } of docsByType.get(type)!.values()) documents.push(doc);\n const Main: Record<string, unknown> = { ...BASE_MAIN };\n for (const [key, kind] of extByType.get(type)!) Main[key] = EXT_FIELD_CONFIG[kind](key);\n const label = typeLabel(type);\n customTypes.push({\n id: type,\n label,\n repeatable: true,\n json: {\n id: type,\n label,\n format: \"custom\",\n repeatable: true,\n status: true,\n json: { Main },\n },\n });\n }\n\n return { documents, customTypes, media, diagnostics };\n}\n","import type { PlanDocument } from \"../emit/plan.js\";\n\nexport type RewriteResult = {\n documents: PlanDocument[];\n /** Total url occurrences swapped. */\n rewritten: number;\n /** CDN-looking urls that survived the swap — surface these, never silent. */\n unmatched: string[];\n};\n\n/** Generic rewrite over any JSON value (a deep copy is returned). */\nexport type ValueRewriteResult<T> = {\n value: T;\n /** Total url occurrences swapped. */\n rewritten: number;\n /** CDN-looking urls that survived the swap — surface these, never silent. */\n unmatched: string[];\n};\n\n// The exclusion class ends the match at whitespace, `\"`, `'`, `\\`, or `)`, so\n// urls embedded in serialized JSON (followed by `\"` or, when doubly\n// serialized, `\\\"`) and in `background-image:url(...)` are captured exactly —\n// the backslash boundary is pinned by the doubly-serialized-JSON test.\nconst CDN_URL_RE = /https?:\\/\\/[a-z0-9.-]*cloudfront\\.net\\/[^\\s\"'\\\\)]+/g;\n\n/** Swap every occurrence of a plan-asset CDN url for its uploaded Prismic url\n * inside EVERY string value of an arbitrary JSON value. Pure: returns a deep\n * copy. Shared by the document rewrite (serialized BluxBlock payloads,\n * widget_html, embed_html, background wrappers) and the site-config / chrome\n * rewrite (nav + footer logo urls). Non-string, non-CDN values pass through\n * untouched. */\nexport function rewriteValueUrls<T>(\n value: T,\n urlByCdn: Map<string, string>,\n): ValueRewriteResult<T> {\n let rewritten = 0;\n const unmatched = new Set<string>();\n // Longest key first: a mapped url that is a prefix of another mapped url can\n // then never fire inside the longer url's occurrence.\n const entries = [...urlByCdn].sort(([a], [b]) => b.length - a.length);\n const swapString = (s: string): string => {\n // Fast path — safe for both passes: every map key and CDN_URL_RE alike\n // require the cloudfront.net host, so this string can neither swap nor\n // surface an unmatched url.\n if (!s.includes(\"cloudfront.net\")) return s;\n let out = s;\n for (const [cdn, prismic] of entries) {\n if (!out.includes(cdn)) continue;\n const parts = out.split(cdn);\n rewritten += parts.length - 1;\n out = parts.join(prismic);\n }\n for (const m of out.matchAll(CDN_URL_RE)) unmatched.add(m[0]);\n return out;\n };\n const walk = (v: unknown): unknown => {\n if (typeof v === \"string\") return swapString(v);\n if (Array.isArray(v)) return v.map(walk);\n if (v && typeof v === \"object\")\n return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));\n return v;\n };\n return { value: walk(value) as T, rewritten, unmatched: [...unmatched] };\n}\n\n/** Collect every distinct Blux-CDN url appearing as a raw string in any value\n * of a JSON structure (documents, site-config, …). Uses the SAME host-anchored\n * match as the rewrite, so what this finds is exactly what {@link\n * rewriteValueUrls} must evict — the emit backstop registers each as an asset so\n * a url baked into an embed_html `<a href>` (a PDF button), a payload\n * background, or a widget migrates off the CDN too. First-seen order. */\nexport function collectCdnUrls(value: unknown): string[] {\n const found = new Set<string>();\n const walk = (v: unknown): void => {\n if (typeof v === \"string\") {\n if (!v.includes(\"cloudfront.net\")) return;\n for (const m of v.matchAll(CDN_URL_RE)) found.add(m[0]);\n } else if (Array.isArray(v)) {\n v.forEach(walk);\n } else if (v && typeof v === \"object\") {\n Object.values(v).forEach(walk);\n }\n };\n walk(value);\n return [...found];\n}\n\n/** Rewrite plan documents — the surfaces resolveDocData does not touch. Thin\n * wrapper over {@link rewriteValueUrls}; marker objects pass through untouched\n * because they hold no CDN urls. */\nexport function rewriteDocUrls(\n documents: PlanDocument[],\n urlByCdn: Map<string, string>,\n): RewriteResult {\n const r = rewriteValueUrls(documents, urlByCdn);\n return { documents: r.value, rewritten: r.rewritten, unmatched: r.unmatched };\n}\n","import type { Media } from \"../grid/types.js\";\nimport type { Diagnostic } from \"../ir.js\";\n// Only `mediaUrl`/`mediaCdnUrl` are reused from the grid emit; `collectPlanAssets`/\n// `collectMedia` there only understand SliceSpec shapes, so the skeleton resolves\n// its own CatalogSpec media below (see grid-plan.ts `buildGridSitePlan` for the pattern).\nimport { mediaCdnUrl, mediaUrl } from \"../emit/grid-plan.js\";\nimport {\n type MigrationPlan,\n type PlanAsset,\n type PlanDocument,\n type PlanSlice,\n assetRef,\n richText,\n} from \"../emit/plan.js\";\nimport { catalogPageCustomType } from \"./catalog-page-type.js\";\nimport { videoTag, imgTag } from \"./cells.js\";\nimport { buildEntityEmit } from \"./entities.js\";\nimport { collectCdnUrls } from \"./rewrite-doc-urls.js\";\nimport { hasVisibleContent, sanitizeHtml } from \"./sanitize.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\nimport type { BlockNode, CatalogBase, CatalogCell, CatalogSpec } from \"./spec.js\";\n\n/** Clamp a heading level into the `[min, max]` window a target field's model\n * allows. Classify wraps headings at their TRUE source level (section-heading\n * promotion and BluxBlock fidelity need it) — the emit boundary is where the\n * destination field is known, so clamping lives here. */\nfunction clampHeading(level: number, min: number, max: number): number {\n return Math.min(Math.max(level, min), max);\n}\n\n/** Re-tag a wrapped `<hN>…</hN>` rich-text string into the destination\n * field's heading window: cell titles + carousel captions model heading3/\n * heading4 (h3–h4); section/grid heading fields model heading2/heading3\n * (h2–h3). Body fields are NEVER clamped — folded headings keep their true\n * level (bodies are paragraph-modeled; see cells.ts textOf). Non-heading\n * html (e.g. a `<p>` caption) passes through untouched. */\nfunction clampHeadingHtml(html: string, min: number, max: number): string {\n const m = /^<h([1-6])>([\\s\\S]*)<\\/h\\1>$/.exec(html.trim());\n if (!m) return html;\n const level = clampHeading(Number(m[1]), min, max);\n return `<h${level}>${m[2]}</h${level}>`;\n}\n\n/** Emit-time context: the band index + owning page uid (for diagnostics\n * `where`) and the optional diagnostics sink threaded down from\n * `buildCatalogPlan`. */\ntype EmitCtx = {\n index: number;\n pageUid?: string;\n diagnostics?: Diagnostic[];\n /** True while emitting a subgrid's cells (depth 2). At this depth an image\n * media cannot ride an Image field — the Migration API can't resolve a\n * doubly-nested Image ref — so it emits `image_embed` (a url-based <img>). */\n inSubgrid?: boolean;\n /** Resolve a media's plan-asset CDN url (mediaCdnUrl, else IR sourceUrl) so\n * the emitted `image_embed` src matches what `rewriteValueUrls` swaps. */\n resolveUrl?: (m: Media) => string | null;\n /** Resolve a media's IR alt (else \"\"). */\n resolveAlt?: (id: string) => string;\n};\n\nconst MOUNT_RE = /data-exec=\"(custom_[a-f0-9_]+)\"/;\n\n/** Record a dropped behavior-script widget — never silent. A custom mount\n * names its widget uuid; other script-only embeds report generically. `where`\n * is page-qualified (round-3 4: `<pageUid>:<band>`) whenever the caller knows\n * the page — a bare band index is ambiguous across a multi-page site. */\nfunction reportDrop(ctx: EmitCtx, html: string): void {\n const uuid = MOUNT_RE.exec(html)?.[1];\n ctx.diagnostics?.push({\n kind: \"dropped-widget\",\n where: ctx.pageUid !== undefined ? `${ctx.pageUid}:${ctx.index}` : `band ${ctx.index}`,\n message: uuid\n ? `custom widget ${uuid} is a behavior script (no visible content) — not migrated`\n : \"embed html has no visible content after sanitize — not migrated\",\n });\n}\n\n/** One catalog cell → its nested-group item object. Rich text and media become\n * `{__richtext_html}` / `{__asset_id}` markers (resolveDocData resolves them,\n * including at this depth). Absent fields are omitted so the item stays lean.\n * Video media becomes `embed_html` (joined with any captured raw html), never\n * an Image-field marker. */\nfunction cellToItem(cell: CatalogCell, ctx: EmitCtx): Record<string, unknown> {\n const video = cell.media?.kind === \"video\" ? videoTag(cell.media) : undefined;\n // Sanitize captured raw html at the boundary — the spec keeps it pristine.\n const embedHtml = cell.embedHtml ? sanitizeHtml(cell.embedHtml) : undefined;\n const embeds = [video, embedHtml].filter((s): s is string => Boolean(s && s.trim()));\n return {\n kind: cell.kind,\n ...(cell.title ? { title: richText(clampHeadingHtml(cell.title, 3, 4)) } : {}),\n // body_html is a PLAIN Text string (no richText marker) rendered via {@html}\n // on the starter side, so it IS sanitized here — the same boundary treatment\n // as embed_html and the BluxBlock payload. sanitizeHtml is a scalpel: it\n // strips <script>/on*=/javascript: only and passes ALL other markup through\n // verbatim, including the txt-role-* class the roled render depends on — so\n // sanitizing closes the {@html} XSS surface at zero fidelity cost. Input is\n // first-party parser structured-text html (heading/body/subtitle); raw/embed\n // html still routes through the sanitized embedHtml path.\n ...(cell.bodyHtml ? { body_html: sanitizeHtml(cell.bodyHtml) } : {}),\n // A subgrid (depth-2) image can't ride an Image field — the Migration API\n // can't resolve a doubly-nested Image ref — so it emits `image_embed` (a\n // url-based <img>, src rewritten to Prismic at migrate). Top-level images\n // stay Image fields (they resolve fine + keep PrismicImage's srcset).\n ...(cell.media && cell.media.kind !== \"video\"\n ? ctx.inSubgrid\n ? {\n image_embed: sanitizeHtml(\n imgTag(cell.media, ctx.resolveUrl ?? mediaCdnUrl, ctx.resolveAlt ?? (() => \"\")),\n ),\n }\n : { media: assetRef(cell.media.assetId) }\n : {}),\n ...(cell.mediaRatio ? { media_ratio: cell.mediaRatio } : {}),\n // Per-cell visual fields (Blux catalog visual layer). `cover`/`valign` are\n // Text flags emitting the literal \"on\" (mirrors scroll_load_more).\n ...(cell.width ? { width: cell.width } : {}),\n ...(cell.spacing !== undefined ? { spacing: cell.spacing } : {}),\n ...(cell.cover ? { cover: \"on\" } : {}),\n ...(cell.valign ? { valign: \"on\" } : {}),\n ...(cell.backgroundColor ? { background_color: cell.backgroundColor } : {}),\n ...(cell.contentPadding ? { content_padding: cell.contentPadding } : {}),\n ...(cell.titleRole ? { title_role: cell.titleRole } : {}),\n ...(embeds.length ? { embed_html: embeds.join(\"\\n\") } : {}),\n ...(cell.subgrid ? { subgrid: emitCells(cell.subgrid, { ...ctx, inSubgrid: true }) } : {}),\n };\n}\n\n/** Emit a cell list, dropping EMBED cells whose html sanitizes to nothing\n * visible (a behavior-script mount is not content — an empty shell would\n * migrate as a blank cell). By construction (cells.ts buildCell) an \"embed\"\n * cell carries ONLY embedHtml, so nothing else is lost by the drop; every\n * drop is recorded on the diagnostics sink. */\nfunction emitCells(cells: CatalogCell[], ctx: EmitCtx): Record<string, unknown>[] {\n const kept: Record<string, unknown>[] = [];\n for (const c of cells) {\n if (c.kind === \"embed\" && c.embedHtml && !hasVisibleContent(c.embedHtml)) {\n reportDrop(ctx, c.embedHtml);\n continue;\n }\n kept.push(cellToItem(c, ctx));\n }\n return kept;\n}\n\n/** `{slice_type, variation:\"default\", items:[], primary}` — every Plan-2 catalog\n * slice shares this envelope; only the primary differs. */\nfunction sliceOf(type: string, primary: Record<string, unknown>): PlanSlice {\n return { slice_type: type, variation: \"default\", items: [], primary };\n}\n\n/** The optional section heading as a rich-text marker (container specs only),\n * clamped to the heading field's h2–h3 model window. */\nfunction heading(spec: CatalogSpec): Record<string, unknown> {\n return \"heading\" in spec && spec.heading\n ? { heading: richText(clampHeadingHtml(spec.heading, 2, 3)) }\n : {};\n}\n\n/** Decision-B widget emission (plan 4b). Contract with the starter design\n * layer: Plan-2 BluxSection renders `widget_html` via {@html} inside a\n * `.blux-widget` div; when a MapConfig was extracted the sanitized mount is\n * wrapped in `.blux-map[data-map-config]` — the design layer parses that JSON\n * and hydrates an interactive Google map on the mount, while the sanitized\n * legend markup renders statically regardless. Config JSON escapes `'` as\n * &#39; so the single-quoted attribute survives any styles content. The\n * parameter is structural (the CatalogBase widget triple) so every spec that\n * can carry a widget — Section, Collection, and (round 3) Grid + Block —\n * shares the exact same emission and Section-identical field names. */\nfunction widgetFields(\n spec: Pick<CatalogBase, \"widgetKind\" | \"widgetHtml\" | \"mapConfig\">,\n): Record<string, unknown> {\n if (!spec.widgetHtml) return {};\n const clean = sanitizeHtml(spec.widgetHtml);\n const html = spec.mapConfig\n ? `<div class=\"blux-map\" data-map-config='${JSON.stringify(spec.mapConfig).replace(/'/g, \"&#39;\")}'>${clean}</div>`\n : clean;\n return {\n ...(spec.widgetKind ? { widget_kind: spec.widgetKind } : {}),\n widget_html: html,\n };\n}\n\n/** Band-visual fields EVERY band slice model defines (blux_grid/section AND\n * blux_gallery/carousel): the box geometry a slider/masonry band honors too.\n * Absent fields are omitted so a band with no captured styles stays lean. */\nfunction bandVisualCore(spec: CatalogBase): Record<string, unknown> {\n return {\n ...(spec.minHeight ? { min_height: spec.minHeight } : {}),\n ...(spec.maxContentWidth ? { max_content_width: spec.maxContentWidth } : {}),\n ...(spec.verticalAlign ? { vertical_align: spec.verticalAlign } : {}),\n };\n}\n\n/** The FULL band-visual set → Plan-2 primary field names, for the flow-layout\n * container slices (blux_grid, blux_section). Adds the content-flow fields only\n * those two models define and render: inner padding, text alignment, the\n * single-column split, and the heading role. The gallery/carousel models omit\n * these (a slider/masonry owns its own spacing), so those slices use\n * `bandVisualCore` — emitting a full field into them fails Migration API\n * validation (\"field not defined in the custom type\"). */\nfunction bandVisual(spec: CatalogBase): Record<string, unknown> {\n return {\n ...(spec.minHeight ? { min_height: spec.minHeight } : {}),\n ...(spec.contentPadding ? { content_padding: spec.contentPadding } : {}),\n ...(spec.contentPaddingMobile ? { content_padding_mobile: spec.contentPaddingMobile } : {}),\n ...(spec.maxContentWidth ? { max_content_width: spec.maxContentWidth } : {}),\n ...(spec.verticalAlign ? { vertical_align: spec.verticalAlign } : {}),\n ...(spec.textAlign ? { text_align: spec.textAlign } : {}),\n ...(spec.columnWidth ? { column_width: spec.columnWidth } : {}),\n ...(spec.columnSide ? { column_side: spec.columnSide } : {}),\n ...(spec.headingRole ? { heading_role: spec.headingRole } : {}),\n };\n}\n\n/** Map one catalog spec to its populated page-doc slice (Plan-2 field names).\n * `diagnostics` (optional) records every content drop — dropped behavior-script\n * widgets are reported, never silent. `pageUid` (optional) page-qualifies the\n * drops' `where` — `buildCatalogPlan` always passes it. */\nexport function catalogSpecToPlanSlice(\n spec: CatalogSpec,\n diagnostics?: Diagnostic[],\n pageUid?: string,\n media?: { resolveUrl: (m: Media) => string | null; resolveAlt: (id: string) => string },\n): PlanSlice {\n const ctx: EmitCtx = {\n index: spec.index,\n ...(pageUid !== undefined ? { pageUid } : {}),\n ...(diagnostics ? { diagnostics } : {}),\n ...(media ? { resolveUrl: media.resolveUrl, resolveAlt: media.resolveAlt } : {}),\n };\n const bg = spec.background ? { background_image: assetRef(spec.background.assetId) } : {};\n const bgc = spec.backgroundColor ? { background_color: spec.backgroundColor } : {};\n switch (spec.slice) {\n case \"BluxSection\":\n return sliceOf(\"blux_section\", {\n ...bg,\n ...bgc,\n ...bandVisual(spec),\n ...heading(spec),\n ...widgetFields(spec),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxGrid\":\n return sliceOf(\"blux_grid\", {\n ...bg,\n ...bgc,\n ...bandVisual(spec),\n ...heading(spec),\n ...widgetFields(spec),\n ...(spec.columns ? { columns: spec.columns } : {}),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxGallery\":\n return sliceOf(\"blux_gallery\", {\n ...bg,\n ...bgc,\n ...bandVisualCore(spec),\n ...heading(spec),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxCarousel\":\n return sliceOf(\"blux_carousel\", {\n ...bg,\n ...bgc,\n ...bandVisualCore(spec),\n ...heading(spec),\n ...(spec.columnsVisible ? { columns_visible: spec.columnsVisible } : {}),\n cells: emitCells(spec.cells, ctx),\n });\n case \"BluxMedia\":\n return sliceOf(\"blux_media\", {\n ...bg,\n ...bgc,\n // Video plays from its CDN url via `video_embed` — an Image-field\n // marker would render a broken <img> in the starter (PrismicImage).\n ...(spec.media.kind === \"video\"\n ? { video_embed: videoTag(spec.media) }\n : { media: assetRef(spec.media.assetId) }),\n ...(spec.caption ? { caption: richText(spec.caption) } : {}),\n });\n case \"BluxMediaText\":\n return sliceOf(\"blux_media_text\", {\n ...bg,\n ...bgc,\n media: assetRef(spec.media.assetId),\n media_side: spec.mediaSide,\n ...(spec.layoutRatio ? { layout_ratio: spec.layoutRatio } : {}),\n ...(spec.title ? { title: richText(clampHeadingHtml(spec.title, 3, 4)) } : {}),\n ...(spec.body ? { body: richText(spec.body) } : {}),\n });\n case \"BluxCollection\":\n // Query-spec slice (spec §6 row 5): no cells — the starter fetches the\n // mapped entity documents at load time and filters via the tagFilter\n // DSL. scroll_load_more is a Select (\"off\"|\"on\") in the model, so a\n // truthy flag emits the literal \"on\"; absent optionals are omitted.\n return sliceOf(\"blux_collection\", {\n ...bg,\n ...bgc,\n ...heading(spec),\n ...widgetFields(spec),\n collection_type: spec.entityType,\n feed_ids: spec.feedIds.join(\",\"),\n ...(spec.filterTag ? { filter_tag: spec.filterTag } : {}),\n ...(spec.sort ? { sort: spec.sort } : {}),\n ...(spec.limit ? { limit: spec.limit } : {}),\n ...(spec.mediaRatio ? { media_ratio: spec.mediaRatio } : {}),\n layout: spec.layout,\n ...(spec.scrollLoadMore ? { scroll_load_more: \"on\" } : {}),\n });\n case \"BluxBlock\": {\n // The starter's blux_block model has NO background fields (the\n // Migration API may reject unknown primary fields), so the band\n // background rides a payload wrapper div instead. Style keys are\n // kebab-case: the starter's styleString emits keys VERBATIM into the\n // style attribute, so camelCase would parse to zero CSS declarations.\n const wrapStyle: Record<string, string> = {\n ...(spec.background\n ? {\n \"background-image\": `url(${mediaCdnUrl(spec.background) ?? spec.background.assetId})`,\n }\n : {}),\n ...(spec.backgroundColor ? { \"background-color\": spec.backgroundColor } : {}),\n };\n // Sanitize at the boundary: the spec payload stays the pristine source\n // tree; only the serialized copy the document ships loses its scripts.\n const clean = sanitizePayload(spec.payload, ctx);\n const payload = Object.keys(wrapStyle).length\n ? { tag: \"div\", style: wrapStyle, children: [clean] }\n : clean;\n // Round 3: a Block-fallback band can carry a lifted map mount too — the\n // widget triple emits with Section-identical field names (the starter\n // render util is shared across the catalog slices).\n return sliceOf(\"blux_block\", {\n ...widgetFields(spec),\n payload: JSON.stringify(payload),\n });\n }\n }\n}\n\n/** Deep-copy a BluxBlock payload tree with every `html` field sanitized\n * (mirrors the embed_html/widget_html boundaries — see sanitize.ts). A leaf\n * that is a custom mount with no visible content is a behavior script — it is\n * kept (sanitized, harmless) but the drop of its behavior is recorded. */\nfunction sanitizePayload(node: BlockNode, ctx: EmitCtx): BlockNode {\n if (node.html !== undefined && MOUNT_RE.test(node.html) && !hasVisibleContent(node.html)) {\n reportDrop(ctx, node.html);\n }\n return {\n ...node,\n ...(node.html !== undefined ? { html: sanitizeHtml(node.html) } : {}),\n ...(node.children ? { children: node.children.map((c) => sanitizePayload(c, ctx)) } : {}),\n };\n}\n\n/** Every Media a catalog spec references (background + leaf media + cell media,\n * recursing through subgrids). */\nfunction specMedia(spec: CatalogSpec): Media[] {\n const out: Media[] = [];\n if (spec.background) out.push(spec.background);\n const walk = (cells: CatalogCell[]) => {\n for (const c of cells) {\n if (c.media) out.push(c.media);\n if (c.subgrid) walk(c.subgrid);\n }\n };\n switch (spec.slice) {\n case \"BluxMedia\":\n case \"BluxMediaText\":\n out.push(spec.media);\n break;\n case \"BluxSection\":\n case \"BluxGrid\":\n case \"BluxGallery\":\n case \"BluxCarousel\":\n walk(spec.cells);\n break;\n case \"BluxBlock\":\n // The payload inlines each media as a CDN url; the same Media surface\n // on `spec.media` so the assets still upload. Payload urls remain CDN\n // until the 4d migrate-time rewrite (runMigration's assetUrlByCdn is\n // the map for it).\n out.push(...spec.media);\n break;\n }\n // Videos migrate too: the Blux CDN is being retired, so every referenced\n // asset must land in Prismic. A video uploads by its CDN url exactly like an\n // image; the emitted <video src> (that same CDN url, in video_embed /\n // embed_html) is then swapped to the Prismic url by the migrate-time\n // rewriteDocUrls, which keys on the identical mediaCdnUrl string.\n return out;\n}\n\nexport type CatalogAssetIndex = {\n assets: { id: string; url: string; alt: string; sourceUrl?: string | null }[];\n diagnostics?: Diagnostic[];\n};\n\n/** Build the migration plan for catalog-converted pages: one text+slices page\n * document each, plus the asset union (uploaded so nested `{__asset_id}` markers\n * resolve at migrate time). When `feeds` is passed, the entity emit rides the\n * same plan: its documents/custom types/diagnostics merge in and its record\n * media join the asset walk (resolved like all other media). */\nexport function buildCatalogPlan(\n pages: {\n uid: string;\n title: string;\n specs: CatalogSpec[];\n /** Per-page SEO from the Blux export (`content.pages[i]`): the meta\n * description and the social/og image asset uuid. Both optional — a page\n * with no description or social image emits neither meta field. */\n description?: string;\n socialImageId?: string;\n }[],\n ir: CatalogAssetIndex,\n feeds?: Record<string, { name?: string; items?: unknown[]; fields?: unknown } | undefined>,\n): MigrationPlan {\n const diagnostics: Diagnostic[] = [...(ir.diagnostics ?? [])];\n const entity = feeds ? buildEntityEmit(feeds) : null;\n // Media resolvers for subgrid `image_embed` emission: the src must equal the\n // plan-asset url so the migrate's `rewriteValueUrls` swaps it to Prismic.\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const mediaResolvers = {\n resolveUrl: (m: Media) => mediaUrl(m, sourceUrlById),\n resolveAlt: (id: string) => assetById.get(id)?.alt ?? \"\",\n };\n const documents: PlanDocument[] = pages.map((p) => ({\n type: CATALOG_PAGE_TYPE,\n uid: p.uid,\n data: {\n title: richText(`<h1>${p.title}</h1>`),\n // SEO & Metadata tab (catalog_page's second tab — see catalog-page-type.ts).\n // Field types match the starter render's reads (blux-catalog/page-doc.ts\n // pageMeta): meta_title/meta_description are Text → PLAIN strings (never a\n // richtext marker); meta_image is an Image field → an `{__asset_id}` marker\n // like cell media. meta_title defaults to the page title (a sensible SEO\n // default, always present); the other two omit when the Blux page has no\n // value (the-pointe's description is \"\" → meta_description omitted).\n meta_title: p.title,\n ...(p.description ? { meta_description: p.description } : {}),\n ...(p.socialImageId ? { meta_image: assetRef(p.socialImageId) } : {}),\n slices: p.specs.map((s) => catalogSpecToPlanSlice(s, diagnostics, p.uid, mediaResolvers)),\n },\n }));\n if (entity) {\n documents.push(...entity.documents);\n diagnostics.push(...entity.diagnostics);\n }\n const resolve = (m: Media): PlanAsset | null => {\n const asset = assetById.get(m.assetId);\n const url = mediaUrl(m, sourceUrlById);\n return url ? { id: m.assetId, url, alt: asset?.alt ?? \"\" } : null;\n };\n // collectPlanAssets only understands SliceSpec shapes, so gather media here and\n // resolve directly (skeleton) — keep insertion order, dedupe by assetId.\n // Entity record media walk the same path so their assets upload too.\n const seen = new Set<string>();\n const assets: PlanAsset[] = [];\n // The per-page social/og image (meta_image marker above) is an Image with no\n // CDN base — it uploads + its marker resolves like any other media: `resolve`\n // falls back to the IR sourceUrl (the og image is scraped into the IR asset\n // index like page media). Deduped by assetId against spec media by `seen`.\n const socialMedia: Media[] = pages\n .filter((p) => p.socialImageId)\n .map((p) => ({ kind: \"image\", assetId: p.socialImageId! }));\n const allMedia = [\n ...pages.flatMap((p) => p.specs).flatMap(specMedia),\n ...socialMedia,\n ...(entity?.media ?? []),\n ];\n for (const m of allMedia) {\n if (seen.has(m.assetId)) continue;\n seen.add(m.assetId);\n const a = resolve(m);\n if (a) assets.push(a);\n else\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: m.assetId,\n message: `media ${m.assetId} has no CDN base nor IR source url — not uploaded`,\n });\n }\n // CDN-sunset backstop: a Blux-CDN url can also ride a document as a RAW STRING\n // that no media marker captured — an embed_html <a href> to a PDF/file, a\n // payload background, a widget. Register each distinct unregistered CDN url as\n // its own asset so migrate-catalog uploads it and the document rewrite swaps it\n // off the retiring CDN. Deduped by url: a doc's image url is byte-identical to\n // its plan.asset url (mediaUrl === mediaCdnUrl when a base is present), so\n // marker-collected images never double-register — only genuine raw-string\n // file assets are added. Id is the Blux filename stem (its uuid).\n const registeredUrls = new Set(assets.map((a) => a.url));\n for (const url of collectCdnUrls(documents)) {\n if (registeredUrls.has(url)) continue;\n const file = (url.split(\"/\").pop() ?? url).split(\"?\")[0]!;\n // Only adopt a url that ends in a clean short file extension — a genuine Blux\n // asset is `.../{uuid}.{ext}`. The CDN regex can over-capture a BARE url\n // adjacent to trailing punctuation or markup (`…u.pdf.`, `…u.pdf</p>`); that\n // string is not fetchable, so registering it would 404 and (frozen\n // runMigration throws) abort the whole migrate. Leave those for the rewrite to\n // report as unmatched (WARNING + non-zero exit) — the graceful path.\n if (!/\\.[a-z0-9]{2,4}$/i.test(file)) continue;\n registeredUrls.add(url);\n assets.push({ id: file.replace(/\\.[^.]+$/, \"\"), url, alt: \"\" });\n }\n return {\n // The catalog_page type ALWAYS ships (the migration owns the type its\n // documents use); entity extension types (product/person/…) append. No\n // id collision — entity types are `format:\"custom\"`, never \"catalog_page\".\n customTypes: [catalogPageCustomType(), ...(entity ? entity.customTypes : [])],\n documents,\n assets,\n stylesManifest: [],\n diagnostics,\n };\n}\n","import { htmlAsRichText } from \"@prismicio/migrate\";\nimport type { MigrationPlan } from \"../emit/plan.js\";\nimport { CATALOG_PAGE_TYPE } from \"./spec.js\";\n\nexport type FixtureDoc = { type: string; uid: string; data: Record<string, unknown> };\nexport type RenderFixture = {\n documents: FixtureDoc[];\n collections: Record<string, FixtureDoc[]>;\n missingAssets: string[];\n};\n\n/** Resolve plan markers OFFLINE for the starter fidelity-gate route (Task 8):\n * `{__richtext_html}` → rich text NODE ARRAYS (via the same `htmlAsRichText`\n * call the frozen migrate path uses in emit/resolve-doc.ts), `{__asset_id}` →\n * a Prismic-HYDRATED image field `{url, alt, dimensions}` pointing at the plan\n * asset's CDN url. Unlike resolve-doc.ts (which emits Migration API shapes —\n * richtext nodes plus `{id}` asset refs for the network), this produces the\n * shapes the production SliceZone consumes when rendering, so the gate can run\n * documents through it with no Prismic round-trip.\n *\n * Dimensions are a fixed placeholder — `PlanAsset` carries no intrinsic size\n * and the offline gate forbids the network fetch that would find it, so layout\n * fidelity for image slices that key off the INTRINSIC ratio (unconstrained\n * `height:auto`, `aspect-ratio` bound to natural dims) is approximate;\n * container-pinned images (`object-fit:cover`, `aspect-[x/y]`) are unaffected.\n * If ratio-sensitive fidelity ever regresses, capture real dims into\n * `PlanAsset` at scrape/IR time. Missing assets resolve to `null`\n * (isFilled-safe) and are reported. Entity (non-`page`) documents are grouped\n * by type for the SliceZone `context.collections`. Keep the marker NAMES\n * (`__richtext_html`, `__asset_id`) in sync with resolve-doc.ts — the `typeof`\n * guards below are intentionally stricter than resolve-doc's `\"key\" in v` (they\n * won't misfire on a non-string field of the same name); keep them on a sync. */\nexport function resolveFixture(plan: MigrationPlan): RenderFixture {\n const assetById = new Map(plan.assets.map((a) => [a.id, a] as const));\n const missing = new Set<string>();\n const walk = (v: unknown): unknown => {\n if (Array.isArray(v)) return v.map(walk);\n if (v && typeof v === \"object\") {\n const o = v as Record<string, unknown>;\n if (typeof o.__richtext_html === \"string\") return htmlAsRichText(o.__richtext_html).result;\n if (typeof o.__asset_id === \"string\") {\n const a = assetById.get(o.__asset_id);\n if (!a) {\n missing.add(o.__asset_id);\n return null;\n }\n return { url: a.url, alt: a.alt, dimensions: { width: 1600, height: 1200 } };\n }\n return Object.fromEntries(Object.entries(o).map(([k, x]) => [k, walk(x)]));\n }\n return v;\n };\n // `data` is invariantly a field map (PlanDocument.data), never a bare marker,\n // so walk() returns an object here — the cast is sound.\n const resolved: FixtureDoc[] = plan.documents.map((d) => ({\n ...d,\n data: walk(d.data) as Record<string, unknown>,\n }));\n const documents = resolved.filter((d) => d.type === CATALOG_PAGE_TYPE);\n const collections: Record<string, FixtureDoc[]> = {};\n for (const d of resolved) {\n if (d.type === CATALOG_PAGE_TYPE) continue;\n (collections[d.type] ??= []).push(d);\n }\n return { documents, collections, missingAssets: [...missing] };\n}\n","// Site chrome (navigation + footer) → a render-side config. The Blux export's\n// `navigation` is a nested tree (top items with optional dropdown children)\n// and `footer` a list of social-link + copyright items; both were dropped by\n// the page-focused convert. This builds a deterministic `site-config.json` the\n// render's Nav/Footer consume (additive: a site with no config renders the\n// starter's logo-only bar and placeholder footer, exactly as before).\n\n/** A navigation entry: a label + href, with optional dropdown children. */\nexport type NavItem = { label: string; href: string; children?: NavItem[] };\n\n/** A footer social link: the network id (facebook, instagram, …) and, when the\n * export carries one, its url. */\nexport type FooterSocial = { network: string; href?: string };\n\nexport type SiteConfig = {\n nav: {\n /** The logo image url (resolved), if the export declares one. */\n logo?: { url: string; maxWidth?: string };\n items: NavItem[];\n };\n footer: {\n socials: FooterSocial[];\n /** The copyright / rights line. */\n text?: string;\n };\n};\n\ntype RawNavItem = { title?: unknown; link?: unknown; items?: unknown };\n\n/** Parse the nested navigation tree from a Blux `navigation` value. Only items\n * with a title survive; an item with children becomes a dropdown (its own link\n * is often empty — a heading), a leaf keeps its href. Recurses one+ levels. */\nfunction parseNavItems(items: unknown): NavItem[] {\n if (!Array.isArray(items)) return [];\n const out: NavItem[] = [];\n for (const raw of items as RawNavItem[]) {\n const label = typeof raw?.title === \"string\" ? raw.title.trim() : \"\";\n if (!label) continue;\n const href = typeof raw?.link === \"string\" ? raw.link : \"\";\n const children = parseNavItems(raw?.items);\n out.push({ label, href, ...(children.length ? { children } : {}) });\n }\n return out;\n}\n\n/** The domain(s) that identify each Blux social network. Blux's social widget\n * stores only the network *flags* (`networks: { facebook: true, … }`) — the\n * profile urls are injected at render time from account config that isn't in\n * the export, so we recover them from the scraped live footer by matching the\n * link's host against these.\n *\n * Keep this in lockstep with the render's Footer `NETWORK` map (reddoor-starter\n * src/lib/components/Footer.svelte): a network we resolve an href for but the\n * Footer can't draw is silently dropped, so only list networks the Footer\n * renders. */\nconst SOCIAL_DOMAINS: Record<string, string[]> = {\n facebook: [\"facebook.com\", \"fb.com\"],\n twitter: [\"twitter.com\", \"x.com\"],\n instagram: [\"instagram.com\"],\n pinterest: [\"pinterest.com\"],\n linkedin: [\"linkedin.com\"],\n \"linkedin-company\": [\"linkedin.com\"],\n youtube: [\"youtube.com\", \"youtu.be\"],\n reddit: [\"reddit.com\"],\n};\n\n/** True when `host` is `domain` or a subdomain of it — not merely a suffix\n * match (so `notfacebook.com` never matches `facebook.com`). */\nfunction hostMatches(host: string, domain: string): boolean {\n return host === domain || host.endsWith(\".\" + domain);\n}\n\nfunction hostOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n/** The `<footer>…</footer>` region of a page (last occurrence), or the whole\n * html when there's no footer element. Social links live in the footer, so we\n * scan there — a body link to, say, a facebook post must not outrank the real\n * footer profile link. */\nfunction footerRegion(html: string): string {\n const lower = html.toLowerCase();\n const start = lower.lastIndexOf(\"<footer\");\n if (start === -1) return html;\n const end = lower.indexOf(\"</footer>\", start);\n return end === -1 ? html.slice(start) : html.slice(start, end + \"</footer>\".length);\n}\n\n/** A network→href resolver built from scraped page HTML. Blux renders the same\n * footer on every page, so any page's html carries the social links; we scan\n * the footer region for absolute http(s) hrefs and match each to a network by\n * host. Returns a function so `buildSiteConfig` stays free of the html-scan\n * mechanics. */\nexport function socialHrefResolverFromHtml(\n htmls: string[],\n): (network: string) => string | undefined {\n const hrefs: string[] = [];\n const re = /href\\s*=\\s*[\"']([^\"']+)[\"']/gi;\n for (const html of htmls) {\n if (typeof html !== \"string\") continue;\n const region = footerRegion(html);\n let m: RegExpExecArray | null;\n while ((m = re.exec(region))) {\n const url = m[1];\n if (url && /^https?:\\/\\//i.test(url)) hrefs.push(url);\n }\n }\n return (network) => {\n const domains = SOCIAL_DOMAINS[network];\n if (!domains) return undefined;\n return hrefs.find((u) => {\n const host = hostOf(u);\n return host && domains.some((d) => hostMatches(host, d));\n });\n };\n}\n\n/** The known social networks and the http(s) url found for one in a raw footer\n * social item (Blux stores `networks: { facebook: true, … }` — the flags — and\n * sometimes per-network urls elsewhere; we carry the network id and any url).\n * When the export omits a url, `resolveSocialHref` recovers it from the scraped\n * live footer (see `socialHrefResolverFromHtml`). */\nfunction parseSocials(\n raw: unknown,\n resolveSocialHref?: (network: string) => string | undefined,\n): FooterSocial[] {\n const out: FooterSocial[] = [];\n const items = Array.isArray(raw) ? raw : [];\n for (const it of items as { media?: { type?: unknown; networks?: unknown; urls?: unknown } }[]) {\n const media = it?.media;\n if (!media || media.type !== \"social\") continue;\n const networks = media.networks;\n if (!networks || typeof networks !== \"object\") continue;\n const urls = (media.urls ?? {}) as Record<string, unknown>;\n for (const [network, on] of Object.entries(networks as Record<string, unknown>)) {\n if (on !== true) continue;\n const fromExport = typeof urls[network] === \"string\" ? (urls[network] as string) : undefined;\n const url = fromExport ?? resolveSocialHref?.(network);\n out.push({ network, ...(url ? { href: url } : {}) });\n }\n }\n return out;\n}\n\n/** Every non-empty `title` under a footer subtree, in document order. The\n * copyright often sits nested inside a column item, not at the top level. */\nfunction collectTitles(node: unknown, acc: string[]): string[] {\n if (Array.isArray(node)) {\n for (const n of node) collectTitles(n, acc);\n return acc;\n }\n if (node && typeof node === \"object\") {\n const rec = node as Record<string, unknown>;\n if (typeof rec.title === \"string\" && rec.title.trim()) acc.push(rec.title.trim());\n for (const k of Object.keys(rec)) if (k !== \"title\") collectTitles(rec[k], acc);\n }\n return acc;\n}\n\n/** The footer's rights/copyright line. Blux footers interleave link-column\n * headings (\"About Us\", \"Quick Links\") with the copyright — and the copyright\n * is often nested inside a column — so \"first titled item\" grabs a heading on\n * many sites. Match the copyright by its shape (©, \"copyright\", or \"rights\n * reserved\") across the whole footer subtree instead. Returns undefined when\n * none matches (the render shows its generic notice, not a wrong heading). */\nconst COPYRIGHT_RE = /©|\\(c\\)|\\bcopyright\\b|rights reserved/i;\nfunction parseFooterText(raw: unknown): string | undefined {\n return collectTitles(raw, []).find((t) => COPYRIGHT_RE.test(t));\n}\n\n/** Build the render-side site config from the export's navigation + footer.\n * `resolveLogo` turns the nav logo's asset uuid into a url (null when\n * unresolved). `resolveSocialHref` recovers a footer social's profile url from\n * the scraped live footer when the export omits it. Pure. */\nexport function buildSiteConfig(\n siteJson: unknown,\n resolveLogo: (uuid: string) => string | null,\n resolveSocialHref?: (network: string) => string | undefined,\n): SiteConfig {\n const j = siteJson as { navigation?: unknown; footer?: unknown };\n const navRoot = (Array.isArray(j.navigation) ? j.navigation[0] : j.navigation) as\n { items?: unknown; logo?: { media?: unknown } } | undefined;\n const footRoot = (Array.isArray(j.footer) ? j.footer[0] : j.footer) as\n { items?: unknown } | undefined;\n\n const items = parseNavItems(navRoot?.items);\n // The logo is `logo: { media: { media: <uuid>, \"max-width\": … } }` — the\n // asset uuid and its render sizing are nested one level inside `logo.media`.\n const logoMedia = navRoot?.logo?.media as { media?: unknown; \"max-width\"?: unknown } | undefined;\n const logoUuid = typeof logoMedia?.media === \"string\" ? logoMedia.media : undefined;\n const logoUrl = logoUuid ? resolveLogo(logoUuid) : null;\n const maxWidth =\n typeof logoMedia?.[\"max-width\"] === \"string\" ? logoMedia[\"max-width\"] : undefined;\n\n const socials = parseSocials(footRoot?.items, resolveSocialHref);\n const text = parseFooterText(footRoot?.items);\n\n return {\n nav: {\n ...(logoUrl ? { logo: { url: logoUrl, ...(maxWidth ? { maxWidth } : {}) } } : {}),\n items,\n },\n footer: {\n socials,\n ...(text ? { text } : {}),\n },\n };\n}\n","// Site chrome for the catalog path (Plan 4d Task 4). Nav rides the frozen\n// buildSiteConfig extraction verbatim (labels from `title`, dropdown\n// recursion, `navigation[0].logo` when a site declares one), then patches the\n// shape Blux sites like the-pointe actually use: the logo is a nav ITEM\n// (`hideTitle: true` + `media`), not `navigation[0].logo` — promote it to\n// nav.logo and keep it out of the text links. The footer keeps FULL columns\n// (the-pointe's leasing-contact columns with tel:/mailto: links, logo rows as\n// images) instead of site-config's socials+text reduction — emit/site-config\n// is frozen, so the richer footer lives here.\n\nimport { buildSiteConfig, type SiteConfig } from \"../emit/site-config.js\";\n\n/** One rendered footer row: a text line (optionally linked — tel:/mailto:/\n * http(s) hrefs survive verbatim) or an image (a footer logo, optionally\n * linked). `image.alt` is the row's accessible name (its human title) when the\n * export carries one — omitted for a nameless image, where the render falls\n * back to alt=\"\". */\nexport type ChromeFooterItem =\n | { text: string; href?: string }\n | { image: { url: string; maxWidth?: string; alt?: string }; href?: string };\n\nexport type ChromeConfig = {\n nav: SiteConfig[\"nav\"];\n footer: { columns: { items: ChromeFooterItem[] }[] };\n};\n\ntype RawItem = {\n title?: unknown;\n text?: unknown;\n link?: unknown;\n hideTitle?: unknown;\n media?: { media?: unknown; \"max-width\"?: unknown };\n items?: unknown;\n};\n\n/** Display text of a raw item's `title`. Blux pads layout spacers with\n * `&nbsp;` entities (`\" &nbsp;\"` rows between contact blocks) and escapes\n * ampersands as `&amp;`, so decode both before trimming — a spacer nets \"\" and\n * is dropped by the caller. */\nfunction displayTitle(title: unknown): string {\n if (typeof title !== \"string\") return \"\";\n return title\n .replace(/&nbsp;/gi, \" \")\n .replace(/&amp;/gi, \"&\")\n .trim();\n}\n\nfunction asHref(link: unknown): string | undefined {\n return typeof link === \"string\" && link.trim() ? link.trim() : undefined;\n}\n\n/** The item's media resolved to a drawable image, or undefined when there is\n * no media uuid or the resolver can't produce a url (nothing to draw — never\n * fall back to the row's hidden title text). */\nfunction asImage(\n media: RawItem[\"media\"],\n resolveLogo: (uuid: string) => string | null,\n): { url: string; maxWidth?: string } | undefined {\n const uuid = typeof media?.media === \"string\" ? media.media : undefined;\n const url = uuid ? resolveLogo(uuid) : null;\n if (!url) return undefined;\n const maxWidth = typeof media?.[\"max-width\"] === \"string\" ? media[\"max-width\"] : undefined;\n return { url, ...(maxWidth ? { maxWidth } : {}) };\n}\n\n/** Site chrome for the catalog path: nav via buildSiteConfig (+ hidden-logo\n * promotion), footer with full columns from `footer[0].items[].items[]`.\n * `resolveLogo` turns a media uuid into a url (null when unresolved). Pure. */\nexport function buildChrome(\n siteJson: unknown,\n resolveLogo: (uuid: string) => string | null,\n): ChromeConfig {\n const sc = buildSiteConfig(siteJson, resolveLogo);\n const j = siteJson as { navigation?: unknown; footer?: unknown };\n const navRoot = (Array.isArray(j.navigation) ? j.navigation[0] : j.navigation) as\n { items?: unknown } | undefined;\n const rawNavItems = Array.isArray(navRoot?.items) ? (navRoot.items as RawItem[]) : [];\n\n // Hidden nav items (`hideTitle: true`) are visual, not links: buildSiteConfig\n // keeps them (it has no hideTitle notion), so drop their labels from the text\n // items and promote the first media-bearing one to the logo slot when the\n // export has no `navigation[0].logo`.\n const hidden = rawNavItems.filter((r) => r?.hideTitle === true);\n const hiddenLabels = new Set(\n hidden.map((r) => (typeof r.title === \"string\" ? r.title.trim() : \"\")).filter(Boolean),\n );\n const items = sc.nav.items.filter((i) => !hiddenLabels.has(i.label));\n // NOTE: nav.logo carries no alt — its type is the frozen SiteConfig[\"nav\"]\n // logo shape ({ url, maxWidth? }), which has no name channel. Giving the nav\n // logo an accessible name would need the frozen buildSiteConfig/site-config\n // path to widen, so it's out of scope here (the footer logos, which this\n // module owns end-to-end, do get alt below).\n let logo = sc.nav.logo;\n if (!logo) {\n for (const r of hidden) {\n const image = asImage(r.media, resolveLogo);\n if (image) {\n logo = image;\n break;\n }\n }\n }\n\n const footRoot = (Array.isArray(j.footer) ? j.footer[0] : j.footer) as\n { items?: unknown } | undefined;\n const rawCols = Array.isArray(footRoot?.items) ? (footRoot.items as RawItem[]) : [];\n const columns = rawCols.flatMap((col) => {\n const subs = Array.isArray(col?.items) ? (col.items as RawItem[]) : [];\n const colItems = subs.flatMap((it): ChromeFooterItem[] => {\n const href = asHref(it?.link);\n if (typeof it?.media?.media === \"string\") {\n const image = asImage(it.media, resolveLogo);\n if (!image) return [];\n // Accessible name for the logo: the row's human title (else its text),\n // decoded — a linked footer logo with no name is an unlabelled link.\n // `??` keeps title authoritative; a \"&nbsp;\" spacer title decodes to \"\"\n // and is dropped, so a spacer image stays alt-less rather than mislabelled.\n const alt = displayTitle(it.title ?? it.text);\n return [{ image: { ...image, ...(alt ? { alt } : {}) }, ...(href ? { href } : {}) }];\n }\n if (it?.hideTitle === true) return [];\n const text = displayTitle(it?.title);\n if (!text) return [];\n return [href ? { text, href } : { text }];\n });\n return colItems.length ? [{ items: colItems }] : [];\n });\n\n return { nav: { ...(logo ? { logo } : {}), items }, footer: { columns } };\n}\n","// Offline layout-fidelity gate (plan 6). Diffs the classified source\n// (`SliceSpec[]`, already gated band→spec by grid-classify-golden) against the\n// emitted presentation manifest (plan 5), naming every band whose layout,\n// media, or map drifted. Pure + offline — the render side (Playwright DOM\n// signature) is plan 7's verify. See the plan's \"The comparison, precisely\".\nimport type { Cell, Node, SliceSpec } from \"../grid/index.js\";\nimport {\n hasMapWidget,\n type Presentation,\n type RenderCell,\n type RenderNode,\n} from \"./presentation.js\";\n\n/** Canonical grid-token key — cols + optional ratio/spacing, WITHOUT the\n * source-only `raw` string, so a source token and its render-side twin (which\n * never carries `raw`) compare equal. */\nfunction tokKey(t: { cols: number | \"any\"; ratio?: number; spacing?: number }): string {\n return `${t.cols}${t.ratio !== undefined ? `r${t.ratio}` : \"\"}${t.spacing !== undefined ? `s${t.spacing}` : \"\"}`;\n}\n\n/** Compact structural signature of a node tree, computed identically for a\n * source `Node` and its serialized `RenderNode` twin (they share `kind`s and\n * aligned fields). Prose is excluded; only kinds + grid tokens + media/widget\n * kinds appear — so it snapshots LAYOUT, not content. A source media node the\n * manifest dropped (unresolved url) makes the two signatures diverge, which is\n * exactly the fidelity signal we want. Mirrors `grid/signature.ts`'s exhaustive\n * switch so a new node kind is a compile error, not a silent drop. */\nexport function sigOf(node: Node | RenderNode): string {\n switch (node.kind) {\n case \"row\":\n return `row[${node.cells\n .map((c: Cell | RenderCell) => `${tokKey(c.token)}:${sigOf(c.node)}`)\n .join(\",\")}]`;\n case \"stack\":\n return `stack[${node.children.map(sigOf).join(\",\")}]`;\n case \"heading\":\n return `h${node.level}`;\n case \"body\":\n return \"body\";\n case \"subtitle\":\n return \"subtitle\";\n case \"media\":\n return `media:${node.media.kind}`;\n case \"widget\":\n return `widget:${node.widget.type}`;\n case \"raw\":\n return \"raw\";\n }\n}\n\nexport type LayoutFinding =\n | { kind: \"band-count\"; specs: number; manifest: number }\n | { kind: \"band-missing\"; band: number }\n | { kind: \"tree-drift\"; band: number; expected: string; actual: string }\n | { kind: \"media-dropped\"; band: number; where: string }\n | { kind: \"map-missing\"; band: number };\n\nexport type LayoutRow = {\n band: number;\n slice: SliceSpec[\"slice\"];\n source: string;\n converted: string;\n ok: boolean;\n};\n\nexport type LayoutReport = {\n /** number of source bands (== specs.length) */\n bands: number;\n /** Grid-fallback bands whose tree fidelity was signature-checked */\n gridBands: number;\n faithful: boolean;\n findings: LayoutFinding[];\n rows: LayoutRow[];\n};\n\n/** A short source-side label per slice, used for the report row's `source`\n * column and the missing-band row. Grid uses the full structural signature. */\nfunction sourceLabel(spec: SliceSpec): string {\n switch (spec.slice) {\n case \"Grid\":\n return sigOf(spec.root);\n case \"Gallery\":\n return `gallery(${spec.media.length})`;\n case \"Carousel\":\n return `carousel(${spec.slides.length})`;\n case \"MediaFull\":\n return \"media_full\";\n case \"VideoFeature\":\n return \"video\";\n case \"SplitFeature\":\n return `split(${spec.mediaSide},${spec.ratio})`;\n case \"LocationMap\":\n return \"location_map\";\n case \"Hero\":\n return \"hero\";\n case \"TitleBand\":\n return \"title_band\";\n case \"RichText\":\n return \"rich_text\";\n }\n}\n\n/** Diff the classified source against the emitted manifest. Grid bands must\n * round-trip their structural signature exactly (spec.root vs the serialized\n * tree); smart slices are checked for payload completeness (gallery count,\n * split/media present, background/map present). Returns a structured report;\n * `faithful` is true iff `findings` is empty. */\nexport function validateLayout(specs: SliceSpec[], presentation: Presentation): LayoutReport {\n const findings: LayoutFinding[] = [];\n const rows: LayoutRow[] = [];\n const manifestKeys = Object.keys(presentation.bands);\n let gridBands = 0;\n\n if (specs.length !== manifestKeys.length) {\n findings.push({ kind: \"band-count\", specs: specs.length, manifest: manifestKeys.length });\n }\n\n // We iterate specs (the source answer key): a stray manifest band with no spec surfaces only via the band-count mismatch above, never named — by design.\n for (const spec of specs) {\n const source = sourceLabel(spec);\n const bp = presentation.bands[String(spec.index)];\n if (!bp) {\n findings.push({ kind: \"band-missing\", band: spec.index });\n rows.push({ band: spec.index, slice: spec.slice, source, converted: \"∅\", ok: false });\n continue;\n }\n const before = findings.length;\n let converted = source;\n\n // Band background (any slice) must survive if the source declared one.\n if (spec.background && !bp.background) {\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"background\" });\n }\n\n switch (spec.slice) {\n case \"Grid\": {\n gridBands++;\n converted = bp.tree ? sigOf(bp.tree) : \"∅\";\n if (source !== converted) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n if (hasMapWidget(spec.root) && !bp.map) {\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Gallery\": {\n const got = bp.gallery?.length ?? 0;\n converted = `gallery(${got})`;\n if (got < spec.media.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `gallery ${got}/${spec.media.length}`,\n });\n }\n break;\n }\n case \"Carousel\": {\n const got = bp.carousel?.slides.length ?? 0;\n converted = `carousel(${got})`;\n if (got < spec.slides.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `carousel ${got}/${spec.slides.length}`,\n });\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n if (!bp.media) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"media\" });\n }\n break;\n }\n case \"SplitFeature\": {\n if (!bp.split) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"split\" });\n } else {\n converted = `split(${bp.split.mediaSide},${bp.split.ratio})`;\n // Defensive: the current builder copies mediaSide/ratio verbatim, so\n // this can't fire against real convertExport output — it guards a\n // future transforming builder that reshapes the split.\n if (converted !== source) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n // The split's text side is a full node subtree (band 1 of the-pointe\n // nests media inside it); buildPresentation serializes it via\n // renderNode, which DROPS unresolved media. Signature-check it like a\n // Grid tree so a dropped nested media isn't silently reported faithful.\n const expectedText = sigOf(spec.text);\n const actualText = sigOf(bp.split.text);\n if (expectedText !== actualText) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: `split.text ${expectedText}`,\n actual: `split.text ${actualText}`,\n });\n }\n }\n break;\n }\n case \"LocationMap\": {\n if (!bp.map) {\n converted = \"∅\";\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Hero\":\n case \"TitleBand\":\n case \"RichText\":\n break; // text lives in the page doc (gated by grid-slice.test.ts); nothing manifest-carried to lose beyond background\n default: {\n // A new slice kind must be handled explicitly, not silently pass as\n // faithful. `sourceLabel` above is also exhaustive, so this is a local\n // belt-and-suspenders guard rather than the sole gate.\n const _exhaustive: never = spec;\n throw new Error(`validateLayout: unhandled slice ${(_exhaustive as SliceSpec).slice}`);\n }\n }\n\n rows.push({\n band: spec.index,\n slice: spec.slice,\n source,\n converted,\n ok: findings.length === before,\n });\n }\n\n return { bands: specs.length, gridBands, faithful: findings.length === 0, findings, rows };\n}\n\nfunction fmtFinding(f: LayoutFinding): string {\n switch (f.kind) {\n case \"band-count\":\n return ` band count mismatch: ${f.specs} source bands vs ${f.manifest} manifest bands`;\n case \"band-missing\":\n return ` band ${f.band}: no manifest entry`;\n case \"tree-drift\":\n return ` band ${f.band}: grid tree drift\\n expected ${f.expected}\\n actual ${f.actual}`;\n case \"media-dropped\":\n return ` band ${f.band}: media dropped (${f.where})`;\n case \"map-missing\":\n return ` band ${f.band}: map config missing`;\n }\n}\n\n/** Render a LayoutReport as a terminal summary: a headline verdict, a per-band\n * signature table (`ok` / `!!` per band), then the findings detail. */\nexport function formatLayoutReport(r: LayoutReport): string {\n const headline = `layout fidelity: ${\n r.faithful ? \"FAITHFUL\" : `${r.findings.length} finding(s)`\n } — ${r.bands} bands (${r.gridBands} grid-tree checked)`;\n const table = r.rows.map((x) => {\n const tag = x.ok ? \"ok\" : \"!!\";\n const sig = x.ok ? x.source : `${x.source} -> ${x.converted}`;\n return ` band ${String(x.band).padStart(2)} ${x.slice.padEnd(13)} ${tag} ${sig}`;\n });\n const detail = r.findings.length ? [\"findings:\", ...r.findings.map(fmtFinding)] : [];\n return [headline, ...table, ...detail].join(\"\\n\");\n}\n","import type {\n BandPresentation,\n Presentation,\n RenderMedia,\n RenderNode,\n SitePresentation,\n} from \"./presentation.js\";\n\nconst swap = (m: RenderMedia, map: Map<string, string>): RenderMedia => {\n const url = map.get(m.url);\n return url ? { ...m, url } : m;\n};\n\nfunction walkNode(node: RenderNode, map: Map<string, string>): RenderNode {\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({ token: c.token, node: walkNode(c.node, map) })),\n };\n case \"stack\":\n return { kind: \"stack\", children: node.children.map((c) => walkNode(c, map)) };\n case \"media\":\n return { kind: \"media\", media: swap(node.media, map) };\n default:\n return node; // heading/body/subtitle/raw/widget carry no media url\n }\n}\n\nfunction walkBand(bp: BandPresentation, map: Map<string, string>): BandPresentation {\n const out: BandPresentation = { ...bp };\n if (bp.background) out.background = swap(bp.background, map);\n if (bp.media) out.media = swap(bp.media, map);\n if (bp.gallery) out.gallery = bp.gallery.map((m) => swap(m, map));\n if (bp.carousel)\n out.carousel = {\n ...bp.carousel,\n slides: bp.carousel.slides.map((s) => ({ ...s, media: swap(s.media, map) })),\n };\n if (bp.tree) out.tree = walkNode(bp.tree, map);\n if (bp.split)\n out.split = {\n ...bp.split,\n media: swap(bp.split.media, map),\n text: walkNode(bp.split.text, map),\n };\n return out; // style / map untouched (no media urls)\n}\n\nfunction rewritePage(p: Presentation, map: Map<string, string>): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const [k, bp] of Object.entries(p.bands)) bands[k] = walkBand(bp, map);\n return { bands };\n}\n\n/** Deep copy of the manifest with every RenderMedia.url present in `urlMap`\n * replaced by its mapped value. Unknown urls left intact. Handles BOTH shapes:\n * the flat single-page `{ bands }` and the multi-page `{ pages: { <uid>:\n * { bands } } }` that `blux convert` now writes — the migrate step must rewrite\n * every site's urls regardless of shape. Pure. */\nexport function rewriteManifestUrls(\n manifest: SitePresentation,\n urlMap: Map<string, string>,\n): SitePresentation {\n if (\"pages\" in manifest && manifest.pages) {\n const pages: Record<string, Presentation> = {};\n for (const [uid, p] of Object.entries(manifest.pages)) pages[uid] = rewritePage(p, urlMap);\n return { pages };\n }\n // The flat branch is a Presentation by construction (cast bridges the union).\n return rewritePage(manifest as Presentation, urlMap);\n}\n"],"mappings":"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","names":["heading","base","heading","parse","base","img","base","style","parse","heading","tiles","heading","base","img","collectRaws","title","titleRole","bodyHtml","embedHtml","base","isEmptyish","heading","isStyleKey","recordUid","date","base"]}