# @actuate-media/sections-react

## 0.18.14

### Patch Changes

- 9944f83: Security fixes from the October 2026 audit.

  **@prenta/core**
  - Authors can no longer edit, delete, restore or purge documents someone else created. The ownership check ran before the document was loaded, so it never had an owner to compare.
  - Scheduling or cancelling a publish or unpublish now needs a publishing role. Before, any signed-in user could do it on collections without review.
  - Owners can publish. The publish allow-list left out the OWNER role.
  - New `isSafeRedirectUrl` in `@prenta/core/forms/client`. A form's redirect URL must be `https://`, `http://` or a same-site path. A `javascript:` URL was stored and followed after submit. Forms saved earlier are checked again on submit.
  - Only owners and admins can change form webhooks and notification recipients, or create a form with recipients. Forms can no longer be written through the generic `/collections/forms` routes.
  - `PATCH /forms/entries/:id` applies the PHI gate and refuses an empty patch. Star and archive webhooks leave out entry data when PHI safeguards are on. Bulk entry updates are capped at 500.
  - Public form submissions are capped at 1 MB (50 MB for uploads) before the body is parsed.
  - `PRENTA_DISABLE_RATE_LIMIT` is ignored on Vercel deployments and on production servers outside CI.
  - Dashboard stats routes need a session even when `CMS_SECRET` is missing (they answer 503).

  **@prenta/sections-react**
  - `PrentaForm` only follows an `http(s)` or same-site redirect after submit.

- Updated dependencies [9944f83]
  - @prenta/core@8.14.0

## 0.18.13

### Patch Changes

- Updated dependencies [1bc3e04]
  - @prenta/core@8.13.5

## 0.18.12

### Patch Changes

- Updated dependencies [acb59e6]
  - @prenta/core@8.13.4

## 0.18.11

### Patch Changes

- Updated dependencies [239452f]
  - @prenta/core@8.13.3

## 0.18.10

### Patch Changes

- Updated dependencies [d225ddf]
- Updated dependencies [5bc2cd6]
- Updated dependencies [8688e6d]
- Updated dependencies [1f4cd7d]
- Updated dependencies [2a8aead]
  - @prenta/core@8.13.2

## 0.18.9

### Patch Changes

- Updated dependencies [7ad26ef]
  - @prenta/core@8.13.1

## 0.18.8

### Patch Changes

- Updated dependencies [97470b0]
  - @prenta/core@8.13.0

## 0.18.7

### Patch Changes

- Updated dependencies [1a86054]
  - @prenta/core@8.12.0

## 0.18.6

### Patch Changes

- Updated dependencies [a54fa87]
- Updated dependencies [0a0e61b]
  - @prenta/core@8.11.0

## 0.18.5

### Patch Changes

- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [394da5c]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
- Updated dependencies [69b9667]
  - @prenta/core@8.10.0

## 0.18.4

### Patch Changes

- Updated dependencies [8e8b4c5]
  - @prenta/core@8.9.2

## 0.18.3

### Patch Changes

- Updated dependencies [68dee33]
  - @prenta/core@8.9.1

## 0.18.2

### Patch Changes

- Updated dependencies [7f317c4]
  - @prenta/core@8.9.0

## 0.18.1

### Patch Changes

- Updated dependencies [626b0fb]
  - @prenta/core@8.8.1

## 0.18.0

### Minor Changes

- c74a0d6: Public pages and the editor canvas follow a site theme stored in Appearance. Agents can read it from prenta://design-system along with the public brand voice, without provider credentials.

### Patch Changes

- Updated dependencies [c74a0d6]
  - @prenta/core@8.8.0

## 0.17.77

### Patch Changes

- Updated dependencies [f707c23]
- Updated dependencies [73398de]
- Updated dependencies [5c668a0]
  - @prenta/core@8.7.0

## 0.17.76

### Patch Changes

- Updated dependencies [cf10ad1]
  - @prenta/core@8.6.1

## 0.17.75

### Patch Changes

- Updated dependencies [b9033ec]
  - @prenta/core@8.6.0

## 0.17.74

### Patch Changes

- Updated dependencies [fa1d880]
  - @prenta/core@8.5.1

## 0.17.73

### Patch Changes

- Updated dependencies [5aec1a6]
  - @prenta/core@8.5.0

## 0.17.72

### Patch Changes

- Updated dependencies [d51bb58]
  - @prenta/core@8.4.0

## 0.17.71

### Patch Changes

- Updated dependencies [8cf5922]
  - @prenta/core@8.3.0

## 0.17.70

### Patch Changes

- Updated dependencies [81f6ad7]
  - @prenta/core@8.2.0

## 0.17.69

### Patch Changes

- Updated dependencies [674bf6c]
  - @prenta/core@8.1.0

## 0.17.68

### Patch Changes

- Updated dependencies [7164a44]
  - @prenta/core@8.0.0

## 0.17.67

### Patch Changes

- Updated dependencies [59c9b17]
  - @prenta/core@7.8.0

## 0.17.66

### Patch Changes

- Updated dependencies [23fbe86]
  - @prenta/core@7.7.2

## 0.17.65

### Patch Changes

- Updated dependencies [3343dac]
  - @prenta/core@7.7.1

## 0.17.64

### Patch Changes

- Updated dependencies [1758225]
  - @prenta/core@7.7.0

## 0.17.63

### Patch Changes

- Updated dependencies [52f8117]
  - @prenta/core@7.6.6

## 0.17.62

### Patch Changes

- Updated dependencies [fb832d5]
  - @prenta/core@7.6.5

## 0.17.61

### Patch Changes

- Updated dependencies [8d0330d]
- Updated dependencies [5224542]
  - @prenta/core@7.6.4

## 0.17.60

### Patch Changes

- Updated dependencies [f6e2be3]
- Updated dependencies [bf76838]
  - @prenta/core@7.6.3

## 0.17.59

### Patch Changes

- Updated dependencies [14b40d3]
  - @prenta/core@7.6.2

## 0.17.58

### Patch Changes

- Updated dependencies [abbcebd]
  - @prenta/core@7.6.1

## 0.17.57

### Patch Changes

- Updated dependencies [67286c0]
  - @prenta/core@7.6.0

## 0.17.56

### Patch Changes

- 0eea737: A post type can hide the built-in header (`layout: 'none'`) and seed its own default section stack. The post canvas uses the document's author and related posts instead of the logged-in editor and placeholder cards.
- Updated dependencies [0eea737]
  - @prenta/core@7.5.4

## 0.17.55

### Patch Changes

- Updated dependencies [6b2e26e]
  - @prenta/core@7.5.3

## 0.17.54

### Patch Changes

- a91ceda: CI fails when coverage on these packages drops below the current floor. The Stripe webhook signature check stays gated on its own file, so removing those tests fails the build even if the package average holds.
- Updated dependencies [a91ceda]
  - @prenta/core@7.5.2

## 0.17.53

### Patch Changes

- Updated dependencies [e73692c]
- Updated dependencies [afc1dc7]
  - @prenta/core@7.5.1

## 0.17.52

### Patch Changes

- Updated dependencies [eadf991]
  - @prenta/core@7.5.0

## 0.17.51

### Patch Changes

- Updated dependencies [f873399]
  - @prenta/core@7.4.2

## 0.17.50

### Patch Changes

- Updated dependencies [b154b3f]
  - @prenta/core@7.4.1

## 0.17.49

### Patch Changes

- Updated dependencies [7bf6853]
  - @prenta/core@7.4.0

## 0.17.48

### Patch Changes

- Updated dependencies [24683d9]
  - @prenta/core@7.3.1

## 0.17.47

### Patch Changes

- Updated dependencies [bb48375]
  - @prenta/core@7.3.0

## 0.17.46

### Patch Changes

- Updated dependencies [485371a]
  - @prenta/core@7.2.1

## 0.17.45

### Patch Changes

- Updated dependencies [30d88ab]
  - @prenta/core@7.2.0

## 0.17.44

### Patch Changes

- Updated dependencies [8ceb0d8]
  - @prenta/core@7.1.1

## 0.17.43

### Patch Changes

- Updated dependencies [2a46f45]
- Updated dependencies [2a46f45]
  - @prenta/core@7.1.0

## 0.17.42

### Patch Changes

- Updated dependencies [075cce2]
  - @prenta/core@7.0.0

## 0.17.41

### Patch Changes

- Updated dependencies [a094fcc]
  - @prenta/core@6.0.0

## 0.17.40

### Patch Changes

- Updated dependencies [01b22db]
  - @prenta/core@5.10.0

## 0.17.39

### Patch Changes

- Updated dependencies [9084656]
  - @prenta/core@5.9.0

## 0.17.38

### Patch Changes

- Updated dependencies [ce5ab9e]
  - @prenta/core@5.8.1

## 0.17.37

### Patch Changes

- Updated dependencies [52cb662]
  - @prenta/core@5.8.0

## 0.17.36

### Patch Changes

- Updated dependencies [2f0c3b3]
  - @prenta/core@5.7.0

## 0.17.35

### Patch Changes

- Updated dependencies [8b6b25c]
  - @prenta/core@5.6.0

## 0.17.34

### Patch Changes

- Updated dependencies [9b3a11c]
  - @prenta/core@5.5.0

## 0.17.33

### Patch Changes

- Updated dependencies [1dee132]
  - @prenta/core@5.4.1

## 0.17.32

### Patch Changes

- Updated dependencies [fda3a91]
- Updated dependencies [a112369]
  - @prenta/core@5.4.0

## 0.17.31

### Patch Changes

- Updated dependencies [6db4f47]
  - @prenta/core@5.3.1

## 0.17.30

### Patch Changes

- 4a744e3: Let a section type declare `hideable: false` so fat whole-page types cannot be hidden. The editor omits Hide/Show, hide writes return 400, and CMS read paths treat leftover `visible: false` as still on the page without rewriting storage.
- Updated dependencies [4a744e3]
  - @prenta/core@5.3.0

## 0.17.29

### Patch Changes

- Updated dependencies [d57e7d6]
  - @prenta/core@5.2.0

## 0.17.28

### Patch Changes

- Updated dependencies [de9086f]
  - @prenta/core@5.1.0

## 0.17.27

### Patch Changes

- Updated dependencies [42fc82c]
  - @prenta/core@5.0.3

## 0.17.26

### Patch Changes

- Updated dependencies [75ba990]
  - @prenta/core@5.0.2

## 0.17.25

### Patch Changes

- Updated dependencies [a3ac18b]
  - @prenta/core@5.0.1

## 0.17.24

### Patch Changes

- Updated dependencies [63fb576]
- Updated dependencies [63fb576]
- Updated dependencies [3a9718d]
  - @prenta/core@5.0.0

## 0.17.23

### Patch Changes

- Updated dependencies [f05c662]
  - @prenta/core@4.3.2

## 0.17.22

### Patch Changes

- Updated dependencies [af9fc45]
  - @prenta/core@4.3.1

## 0.17.21

### Patch Changes

- Updated dependencies [4af732b]
  - @prenta/core@4.3.0

## 0.17.20

### Patch Changes

- Updated dependencies [2ff1c74]
  - @prenta/core@4.2.0

## 0.17.19

### Patch Changes

- Updated dependencies [b86fa37]
  - @prenta/core@4.1.1

## 0.17.18

### Patch Changes

- Updated dependencies [afb8757]
  - @prenta/core@4.1.0

## 0.17.17

### Patch Changes

- Updated dependencies [172b6f4]
  - @prenta/core@4.0.0

## 0.17.16

### Patch Changes

- Updated dependencies [2082a3a]
  - @prenta/core@3.0.0

## 0.17.15

### Patch Changes

- Updated dependencies [0e73d4f]
  - @prenta/core@2.5.0

## 0.17.14

### Patch Changes

- Updated dependencies [eff3b76]
  - @prenta/core@2.4.1

## 0.17.13

### Patch Changes

- Updated dependencies [c13e9b3]
  - @prenta/core@2.4.0

## 0.17.12

### Patch Changes

- Updated dependencies [ead6241]
  - @prenta/core@2.3.2

## 0.17.11

### Patch Changes

- Updated dependencies [48777ad]
  - @prenta/core@2.3.1

## 0.17.10

### Patch Changes

- Updated dependencies [e2c239a]
  - @prenta/core@2.3.0

## 0.17.9

### Patch Changes

- Updated dependencies [9aa753a]
  - @prenta/core@2.2.0

## 0.17.8

### Patch Changes

- Updated dependencies [21acc95]
  - @prenta/core@2.1.0

## 0.17.7

### Patch Changes

- Updated dependencies [a0de0bf]
  - @prenta/core@2.0.0

## 0.17.6

### Patch Changes

- Updated dependencies [ceec262]
  - @prenta/core@1.1.1

## 0.17.5

### Patch Changes

- Updated dependencies [71cdabd]
  - @prenta/core@1.1.0

## 0.17.4

### Patch Changes

- Updated dependencies [8905eb2]
  - @prenta/core@1.0.0

## 0.17.3

### Patch Changes

- Updated dependencies [b28599c]
- Updated dependencies [b28599c]
- Updated dependencies [b28599c]
- Updated dependencies [b28599c]
- Updated dependencies [b28599c]
  - @prenta/core@0.124.0

## 0.17.2

### Patch Changes

- Updated dependencies [538720e]
  - @prenta/core@0.123.0

## 0.17.1

### Patch Changes

- Updated dependencies [8614518]
  - @prenta/core@0.122.0

## 0.17.0

### Minor Changes

- c5a8d16: Close the section-type gaps: pasted widget code, a real video source switch, and a divider

  The three outstanding gaps against the page-builder handoff's nine section types. The shape of each was decided by the product constraint rather than the drawing: the CMS is for words and images, not design.

  **`widget-embed` now takes the vendor's snippet.** It used to ask for a provider dropdown, a "Widget ID", and a "Secondary ID / URL" whose meaning changed per provider — so an editor holding an Elfsight snippet had to know its app id lives inside a class name. Now you paste the snippet.

  The paste is **parsed, never injected**. The provider is identified from the container element, the ids are token-validated, and the renderer builds the mount point itself and loads the vendor script from a URL hardcoded in the renderer — so nothing pasted reaches the page as markup or script. Any `<script>` in a paste is ignored, and because an editor who pasted one reasonably expects it to run, validation now names it and says it will not.

  Content saved under the old shape keeps rendering, and `migrateContent` regenerates the equivalent snippet so the editor shows the real embed code rather than an empty box. The old `provider`/`widgetId`/`embedUrl` keys are left in place, so a rollback does not lose the widget.

  **`embed` gets a real source switch** — a YouTube/Vimeo/Loom link, an uploaded MP4/WebM, or a pasted snippet. Hosted video was already supported end to end by the media pipeline; nothing had ever exposed it. Uploaded files render as a plain `<video controls preload="metadata">` with no third party involved. A pasted snippet has only its frame `src` read out of it, and that still has to pass the same provider allowlist.

  The chosen source is honoured exactly: switching to Uploaded and leaving the file blank renders nothing rather than falling back to a stale provider URL left in `url`. Playing a different video than the one chosen is worse than playing none.

  **`divider` is added, deliberately bare.** One field: rule, or space. Thickness, colour, width and inset are design decisions and design lives in code — what is left is whether the break reads as a stated separation or simply as room. It carries no background of its own either; a divider that could paint its own band would be a design control wearing a structural label.

  **Two general mechanisms came with this**, both because the alternative was a lie in the UI:
  - `migrateContent` on a section type definition, applied by `coerceSections` on every read. Must be pure and idempotent.
  - `showWhen` on a field, read through the exported `visibleFields`. The editor uses it to decide what to draw and the validator to decide what to require — if they diverged, a hidden `required` field could block publish with an error pointing at a box that is not on screen.

  New exports from `@prenta/core/page-sections`: `parseEmbedCode`, `foreignScriptHosts`, `widgetEmbedCodeFrom`, `migrateWidgetEmbedContent`, `parseEmbedIframe`, `resolveVideo`, `isFieldVisible`, `visibleFields`, and the `ResolvedVideo` / `HostedVideo` / `ProviderVideo` types.

### Patch Changes

- Updated dependencies [02a638d]
- Updated dependencies [c5a8d16]
  - @prenta/core@0.121.0

## 0.16.3

### Patch Changes

- Updated dependencies [7f60722]
  - @prenta/core@0.120.2

## 0.16.2

### Patch Changes

- Updated dependencies [931e466]
  - @prenta/core@0.120.1

## 0.16.1

### Patch Changes

- Updated dependencies [385dd10]
  - @prenta/core@0.120.0

## 0.16.0

### Minor Changes

- 6f2eb6d: Two new section types: Awards & Partners, and Common Questions

  The design prototype gained these after the handoff this work was built from —
  `badges` and `faq` appear zero times in that handoff and are now in the seeded
  page, so the editor was missing two of the seven sections the design shows.
  - **`badges`** — a strip of award or partner logos. Each row is an image, its
    alt text, and an optional link. The template declares bounds (3–8 rows),
    matching the prototype's `TEMPLATE_STYLE`: the design owns how many the layout
    holds, and the CMS works inside them. A badge with a link renders as an
    anchor; one without stays a plain figure, so an unlinked logo does not put a
    dead stop in the tab order.
  - **`faq`** — questions and answers, bounds 2–12. Rendered as a `<dl>` rather
    than a run of `<h3>`s, which would inject a second outline competing with the
    section headings around it. Answers are plain text: they feed FAQ structured
    data, where markup is stripped anyway, and a rich-text slot would invite
    formatting that silently vanishes from the search result.

  **The alt-text check now walks repeaters.** It counted `media` and `gallery`
  fields only, so a badge strip — a repeater whose rows each carry an image —
  would have reported a clean pass no matter how many logos were undescribed.
  That is exactly the false pass the check exists to prevent, hiding behind a
  field type rather than a missing asset. Any repeater with a media sub-field now
  contributes one slot per row.

  The seeded About Us page carries both, with one badge deliberately left
  undescribed so the audit demonstrates a real failing check, and the FAQ seeded
  hidden — which also exercises the one structural control a design-owned editor
  keeps.

- 6f2eb6d: Reflow guard foundation: measure real components instead of a class chain

  The editor can now tell you when an edit adds a line of wrapped text at a
  breakpoint you are not looking at — the "it fits on my screen" failure, where
  copy that reads fine on a laptop pushes a hero button below the fold on a
  phone.

  **How it measures.** The guard renders the actual section component off-screen
  and reads the laid-out element, finding fields by the same `data-cms-field`
  marker the click-to-edit bridge uses. The alternative — rebuilding each probe
  from a hardcoded list of wrapper classes — is a second source of truth that has
  to track the renderers by hand, and it cannot describe a consumer's own
  registered renderer at all. Nothing here needs to stay in sync, and a renderer
  refactor cannot silently invalidate it.

  **What guards the guard.** A width cap that applies to the rendered element but
  not to the probe makes the guard lie at exactly the widths where the cap binds
  — so it looks correct on mobile and reports the wrong line count on laptop and
  above. `e2e/reflow-probe.spec.ts` asserts probe width equals rendered width for
  every guarded field on a real page, in a real browser. It has to run in
  Playwright: `happy-dom` and `jsdom` do no layout, so the same assertion there
  would pass while measuring nothing.

  That test has already caught two real defects — a probe mounted outside
  `.prenta-admin` inheriting the document's default font (a button measured 127px
  rendered and 139px probed), and field paths being unique only within a section
  rather than across a page.

  **New in `@prenta/sections-react`:** `paddingClass`, `backgroundClass` and
  `isDark` are now exported. The editor canvas wraps sections itself rather than
  using `<Sections>`, so its padding has to match the site's exactly — a section
  padded differently in the preview than on the site makes every line count taken
  from it wrong. A parity test asserts the two agree.

- 6f2eb6d: Reflow sections on their own width instead of the viewport's.

  Every responsive step in the section renderers moves from Tailwind's `sm:`/`md:`/`lg:`
  viewport breakpoints to `@pv-*` container queries, and each `<section>` (plus
  `PostHeader`) becomes a query container.

  For a visitor nothing changes: a full-width section's width _is_ the viewport. What it
  buys is that the same component renders correctly wherever it is placed — in a sidebar,
  a split view, an embed, and above all in the CMS editor canvas, which shows a page at a
  device's real CSS width scaled down to fit. A viewport query there measures the editor
  window, so picking "Mobile" produced a 390px canvas still laid out for desktop. The
  preview showed a width, not a layout.

  `SiteNav` keeps viewport queries: it is site chrome rather than a section, and is not
  inside a container.

  **This changes canvas rendering**, and the editor-canvas baselines move with it — the
  hero heading now renders at the size its classes specify for a 1920px canvas, where it
  was previously a step smaller. Verified by measurement rather than against the old
  baseline: 48px type in a 768px column, two lines, section 1920px wide.

  Two things worth knowing:
  - `container-type: inline-size` implies `contain: layout`, making a section a containing
    block for `position: fixed` descendants. Relevant only to custom renderers or
    `cssClass` overrides that rely on fixed positioning escaping the section.
  - The `@theme` block declaring the steps must sit **before** the `utilities.css` import.
    Tailwind generates container-query variants while processing that import, so a theme
    block after it contributes nothing and every `@pv-*` class compiles to silence — with
    no error. Moving it to satisfy the usual "@import first" rule breaks every responsive
    step in the package.

- 6f2eb6d: Ship a precompiled stylesheet with the section renderers.

  `@prenta/sections-react` now exports `@prenta/sections-react/styles.css`, built from
  the package's own sources at publish time. Consumers import it instead of configuring
  Tailwind to scan the package.

  **This fixes silently unstyled public pages.** The renderers ship as compiled JS
  styled with Tailwind utilities, so their classes previously had to come from a
  `@source` directive pointing into `node_modules` — which `create-prenta` emitted. That
  directive **emits nothing under pnpm**: pnpm links packages by symlink and Tailwind's
  scanner does not follow symlinks. Verified by scanning the identical files through both
  routes — the package's real path emits every class, the `node_modules` route emits
  none. It is not a gitignore issue; an explicit `@source` into a gitignored directory
  scans fine.

  The result was a public site rendering sections with correct markup and no styling,
  for anyone who installed with pnpm. npm and yarn consumers copy real files and were
  unaffected, which is why it went unnoticed.

  Shipping the CSS removes the failure class rather than moving it: no `@source` to
  forget, no symlink resolution, no dependence on the consumer's Tailwind version, and
  no way for the styles to be silently absent.

  **What the stylesheet contains** — theme variables and utilities, no preflight. It
  loads alongside the consumer's own Tailwind, and a second copy of the base reset would
  re-apply element resets over their styles depending on import order. Theme variables
  are idempotent and safe to duplicate; a reset is not. Utilities are emitted into the
  `utilities` cascade layer so they rank alongside the consumer's rather than above
  everything they wrote. 15KB minified.

  **Scaffolded projects** import it in both the site layout and the admin route — the
  editor canvas renders these same components — and the broken `@source` is gone from
  the generated `globals.css`.

  Verified equivalent: the editor-canvas visual baselines, captured while the classes
  came from content scanning, pass **unchanged** against the shipped stylesheet.

  **Upgrading an existing install** — import the stylesheet and remove the `@source`
  line; optionally pass `DEFAULT_SECTION_RENDERERS` to `<AdminRoot>` so the editor
  canvas renders the same components as the site. Step by step in
  [docs/upgrading-section-styles.md](https://github.com/actuate-media/prenta/blob/main/docs/upgrading-section-styles.md).

  `sideEffects` is declared as `["**/*.css"]` rather than `false`, so bundlers cannot
  tree-shake the CSS import away — under `false` the import is eliminated and sections
  render unstyled again, silently.

### Patch Changes

- 6f2eb6d: Editor canvas paints section backgrounds in the site's palette, not the admin theme's

  The canvas used admin theme tokens (`bg-brand`, `bg-card`, `bg-muted`) for
  section backgrounds. Those follow the editor's light/dark preference; the live
  site does not. So an editor working in dark mode previewed a hero in `#9D71F0`
  sitting on dark body sections, while the published page rendered `violet-600`
  on white — a page no visitor would ever see.

  In light mode this changes nothing visible: `--brand` resolves to `#7C3AED`,
  which is `violet-600`, and `--card` is white. The entire discrepancy lived in
  dark mode, which is where it was least likely to be spotted and most likely to
  mislead. Six dark-mode baselines are updated; no light-mode capture moved.

  It also resolves an inconsistency inside the admin's own implementation, where
  a _gradient_ purple background used the site's raw palette while a _solid_
  purple used a theme token — the two halves of an unfinished migration
  disagreeing with each other.

  **New in `@prenta/core/page-sections`:** `backgroundPreset`, `isDarkBackground`,
  `BACKGROUND_PRESETS` and the `BackgroundPreset` type.

  The two renderers still hold their own class strings, and have to. Tailwind
  generates utilities by scanning source text, and each package's stylesheet
  scans only its own `src/` tree — a literal shared from a third package is
  generated by nobody, so the section renders with correct markup and no
  background at all. (That failure mode is why `@prenta/sections-react` ships a
  precompiled stylesheet in the first place.)

  So core owns the _decision_ and each renderer maps it to its own strings
  through a `Record<BackgroundPreset, string>`. Adding a preset now fails both
  builds until each has handled it — verified by adding one and watching both
  packages refuse to compile — and a parity test asserts the strings still match.

- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
- Updated dependencies [6f2eb6d]
  - @prenta/core@0.119.0

## 0.15.0

### Minor Changes

- 5eea995: Product rename: ActuateCMS is now **Prenta**. Every package moves from the
  `@actuate-media/*` scope to `@prenta/*` with simplified names (`@prenta/core`,
  `@prenta/admin`, `create-prenta`), and version numbers continue from where the
  old scope left off.

  This is a clean break — no backwards-compat aliases:
  - **npm packages**: `@actuate-media/cms-core` → `@prenta/core`,
    `@actuate-media/cms-admin` → `@prenta/admin`, all other packages keep their
    suffix under `@prenta/*`. `create-actuate-cms` → `create-prenta`
    (`npm create prenta`), `create-actuate-astro` → `create-prenta-astro`.
    CLI bins: `actuate`/`actuate-cms` → `prenta`/`prenta-cms`; `actuate-mcp` →
    `prenta-mcp`.
  - **Database**: migration `0012_rename_to_prenta` renames all 36 `actuate_*`
    tables to `prenta_*` in place (plus prefixed constraints, indexes, and
    sequences). Run `prisma migrate deploy` when upgrading.
  - **Runtime identifiers**: `getActuateConfig` → `getPrentaConfig`,
    `handleActuateAPI` → `handlePrentaAPI`, `ActuateClient` → `PrentaClient`,
    `ActuateDB` → `PrentaDB`, config file `actuate.config.ts` →
    `prenta.config.ts`, CSS scopes `.actuate-admin`/`.actuate-canvas` →
    `.prenta-admin`/`.prenta-canvas`, preview protocol `actuate-preview:*` →
    `prenta-preview:*`, webhook headers `X-Actuate-*` → `X-Prenta-*`, session
    cookie `actuate_session` → `prenta_session`, env vars `ACTUATE_*` →
    `PRENTA_*`, purge route `/api/actuate/purge` → `/api/prenta/purge`.
  - **Sessions and webhooks**: all sessions reset on upgrade (cookie + JWT issuer
    changed); webhook consumers must re-verify signatures against the new
    `X-Prenta-*` headers.

  The old `@actuate-media/*` packages are deprecated on npm and will receive no
  further releases.

### Patch Changes

- Updated dependencies [5eea995]
  - @prenta/core@0.118.0

## 0.14.0

### Minor Changes

- 56ebbb9: New `assertSectionCoverage` export in `@actuate-media/sections-react/testing` (issue #775) — the real implementation behind the `verify:sections` check the docs describe. Asserts the three-layer section contract agrees: every declared `sections.types` id has an entry in the shared renderer map, every renderer corresponds to a declared type (catches schema renames that leave a stale renderer), and intentionally renderer-less types are declared via an `allowDataOnly` opt-out list that fails when stale. Aggregates all problems into one actionable error.

## 0.13.1

### Patch Changes

- 02be20a: Visual editing Stage 3: path-aware section content updates, preview bridge edit/mode/request-media protocol with suppress-echo for canvas text edits, and media field-kind annotations on default section renderers.
- Updated dependencies [02be20a]
  - @actuate-media/cms-core@0.117.0

## 0.13.0

### Minor Changes

- abce7a5: Visual editing Phase 2: field selection via `data-cms-field`, unified `insp-field-*` inspector ids, and canvas `fieldPath` → inspector focus.

## 0.12.0

### Minor Changes

- 1216ee7: Visual editing Phase 1: annotate `<Sections>` with `data-cms-section-id` / `data-cms-section-type`, and wire an origin-pinned `actuate-preview:*` postMessage bus so Site preview can select sections into the inspector and highlight the selection.

## 0.11.0

### Minor Changes

- 6ff80a6: Add `@actuate-media/sections-react/testing` (`assertSectionRenderParity`, canvas render options) and wire an Astro-split starter `test:parity` gate so canvas and public orchestrators can't drift into a WYSIWYG lie.

## 0.10.25

### Patch Changes

- Updated dependencies [73854fc]
  - @actuate-media/cms-core@0.116.0

## 0.10.24

### Patch Changes

- Updated dependencies [576f076]
  - @actuate-media/cms-core@0.115.0

## 0.10.23

### Patch Changes

- Updated dependencies [dfffe08]
  - @actuate-media/cms-core@0.114.0

## 0.10.22

### Patch Changes

- Updated dependencies [38aa813]
  - @actuate-media/cms-core@0.113.0

## 0.10.21

### Patch Changes

- Updated dependencies [cf97a40]
  - @actuate-media/cms-core@0.112.0

## 0.10.20

### Patch Changes

- Updated dependencies [90f9801]
- Updated dependencies [90f9801]
  - @actuate-media/cms-core@0.111.0

## 0.10.19

### Patch Changes

- Updated dependencies [56c1309]
  - @actuate-media/cms-core@0.110.2

## 0.10.18

### Patch Changes

- Updated dependencies [f4766d4]
  - @actuate-media/cms-core@0.110.1

## 0.10.17

### Patch Changes

- Updated dependencies [46ea4a7]
  - @actuate-media/cms-core@0.110.0

## 0.10.16

### Patch Changes

- Updated dependencies [e4e55de]
  - @actuate-media/cms-core@0.109.0

## 0.10.15

### Patch Changes

- Updated dependencies [6eaffa5]
  - @actuate-media/cms-core@0.108.0

## 0.10.14

### Patch Changes

- Updated dependencies [1c2afa5]
  - @actuate-media/cms-core@0.107.3

## 0.10.13

### Patch Changes

- Updated dependencies [b305f0c]
  - @actuate-media/cms-core@0.107.2

## 0.10.12

### Patch Changes

- Updated dependencies [a26f61a]
  - @actuate-media/cms-core@0.107.1

## 0.10.11

### Patch Changes

- Updated dependencies [f676bcc]
  - @actuate-media/cms-core@0.107.0

## 0.10.10

### Patch Changes

- Updated dependencies [9d5dad3]
  - @actuate-media/cms-core@0.106.1

## 0.10.9

### Patch Changes

- Updated dependencies [3d1a79f]
  - @actuate-media/cms-core@0.106.0

## 0.10.8

### Patch Changes

- Updated dependencies [78671c4]
  - @actuate-media/cms-core@0.105.0

## 0.10.7

### Patch Changes

- Updated dependencies [258cbc3]
  - @actuate-media/cms-core@0.104.0

## 0.10.6

### Patch Changes

- Updated dependencies [2f2fe29]
  - @actuate-media/cms-core@0.103.0

## 0.10.5

### Patch Changes

- Updated dependencies [f8a2c07]
  - @actuate-media/cms-core@0.102.0

## 0.10.4

### Patch Changes

- Updated dependencies [afceb12]
  - @actuate-media/cms-core@0.101.2

## 0.10.3

### Patch Changes

- Updated dependencies [81aa012]
  - @actuate-media/cms-core@0.101.1

## 0.10.2

### Patch Changes

- Updated dependencies [58f3ca1]
  - @actuate-media/cms-core@0.101.0

## 0.10.1

### Patch Changes

- Updated dependencies [cc6ad93]
- Updated dependencies [cc6ad93]
- Updated dependencies [cc6ad93]
  - @actuate-media/cms-core@0.100.0

## 0.10.0

### Minor Changes

- 80d4e5f: Marketing attribution tracking for forms — captures where every lead came from, automatically.

  **cms-core**: rebuilt `forms/attribution` (now also published at `@actuate-media/cms-core/forms/attribution`) with a first-touch (30-day) / last-touch (24-hour) model merged at submit, the full ad click-ID registry (gclid, fbclid, msclkid, ttclid, li_fat_id, dclid, twclid, rdt_cid + long-tail IDs in `extraIds`), and referrer classification into marketing channels (paid / organic search / social / email / **ai** / referral / direct) with friendly source names ("ChatGPT" instead of "chat.openai.com"). Form entry payloads now expose the full `attribution` blob, CSV exports gained Source / Medium / Campaign / Channel / Referrer / Landing Page columns, and `form.submitted` webhooks forward the merged attribution to CRM consumers. Note: `AttributionData` gained required fields (`channel`, `referrerName`, `extraIds`, `firstTouchAt`, `lastTouchAt`, `version`) and `clickIds` grew from 5 to 8 networks.

  **sections-react**: `<ActuateForm>` now records an attribution touch on mount and attaches the merged first/last-touch payload to every submission — no setup required. Capture is consent-gated when `@actuate-media/plugin-consent` is installed (waits for an analytics grant; proceeds when the plugin is absent). New `<AttributionTracker />` component for the site layout improves first-touch fidelity across multi-page journeys.

  **cms-admin**: the form entry detail pane now shows the full attribution picture — channel with friendly source name, source/medium/campaign/term/content, landing page, referrer, device type, first-touch date, and any ad click IDs — with graceful fallback to the legacy UTM columns for older entries.

### Patch Changes

- Updated dependencies [80d4e5f]
- Updated dependencies [80d4e5f]
  - @actuate-media/cms-core@0.99.0

## 0.9.0

### Minor Changes

- 21aa417: Consolidate the two form-editing surfaces onto the typed Forms API and make GA4 form analytics work end-to-end.

  **cms-admin** — The Edit Form page is rebuilt as the canonical full-page form editor, backed by the same typed Forms API as the Forms list's inline Schema pane (no more legacy `/collections/forms` writes that bypassed schema versioning). It now covers everything in one place: Details (name/description/status + post-submit confirmation), Fields (versioned schema editor), **Notifications** (previously only reachable from the Schema pane), Analytics (GA4), Embed, and Integrations (webhooks). Confirmation settings now persist to `successMessage` / `redirectUrl` — the fields the public submission pipeline actually reads (the old editor wrote a `confirmation` object the runtime ignored). New forms are created through `POST /forms`, gaining slug generation, an initial schema version, and notification defaults, then route into the full editor.

  **cms-core** — `FormDefinition` gains a sanitized `analytics` (GA4) config, persisted via the forms service (`createForm` / `updateForm` / `duplicateForm`) and exposed on the public form schema only when tracking is enabled. New `normalizeAnalyticsConfig` export and a client-safe `@actuate-media/cms-core/forms/analytics` subpath export.

  **sections-react** — `<ActuateForm>` now fires GA4 `form_start` / `form_submit` / `form_error` events (with optional conversion value) when the form has analytics enabled. Also fixes the success state to read the submit endpoint's `confirmation` _object_ (`{ type, message, redirectUrl }`) — previously it was treated as a string, which broke the success message and ignored server-driven redirects. New `resolveSubmitOutcome` helper with regression coverage.

  **plugin-forms** — The `forms` collection field map now declares every key the typed forms service persists (`description`, `notificationSettings`, `embedSettings`, `spamSettings`, `successMessage`, `redirectUrl`, `analytics`, `activeSchemaVersionId`, …). The data layer strips undeclared keys on write, so consumer apps registering this plugin would previously drop notification/embed/spam settings silently. Status options now match the typed `FormStatus` (`active` / `draft` / `archived`).

### Patch Changes

- Updated dependencies [c6b006c]
- Updated dependencies [21aa417]
  - @actuate-media/cms-core@0.98.0

## 0.8.29

### Patch Changes

- Updated dependencies [fb89234]
  - @actuate-media/cms-core@0.97.0

## 0.8.28

### Patch Changes

- Updated dependencies [fdfae4c]
  - @actuate-media/cms-core@0.96.0

## 0.8.27

### Patch Changes

- Updated dependencies [2ccc0a7]
  - @actuate-media/cms-core@0.95.1

## 0.8.26

### Patch Changes

- Updated dependencies [0bd5153]
  - @actuate-media/cms-core@0.95.0

## 0.8.25

### Patch Changes

- Updated dependencies [d9096d9]
  - @actuate-media/cms-core@0.94.0

## 0.8.24

### Patch Changes

- Updated dependencies [fe2b661]
  - @actuate-media/cms-core@0.93.11

## 0.8.23

### Patch Changes

- Updated dependencies [78c6d0f]
  - @actuate-media/cms-core@0.93.10

## 0.8.22

### Patch Changes

- Updated dependencies [06e7fbc]
  - @actuate-media/cms-core@0.93.9

## 0.8.21

### Patch Changes

- Updated dependencies [28dd310]
  - @actuate-media/cms-core@0.93.8

## 0.8.20

### Patch Changes

- Updated dependencies [ac2cf04]
  - @actuate-media/cms-core@0.93.7

## 0.8.19

### Patch Changes

- Updated dependencies [886238a]
  - @actuate-media/cms-core@0.93.6

## 0.8.18

### Patch Changes

- Updated dependencies [430ae43]
  - @actuate-media/cms-core@0.93.5

## 0.8.17

### Patch Changes

- Updated dependencies [899ca9a]
  - @actuate-media/cms-core@0.93.4

## 0.8.16

### Patch Changes

- Updated dependencies [77b3922]
  - @actuate-media/cms-core@0.93.3

## 0.8.15

### Patch Changes

- Updated dependencies [4421c32]
  - @actuate-media/cms-core@0.93.2

## 0.8.14

### Patch Changes

- Updated dependencies [898ec8b]
  - @actuate-media/cms-core@0.93.1

## 0.8.13

### Patch Changes

- Updated dependencies [c231dd0]
  - @actuate-media/cms-core@0.93.0

## 0.8.12

### Patch Changes

- Updated dependencies [cc18833]
  - @actuate-media/cms-core@0.92.0

## 0.8.11

### Patch Changes

- Updated dependencies [f9513f3]
  - @actuate-media/cms-core@0.91.1

## 0.8.10

### Patch Changes

- 041a228: Add the MIT `license` field to every published package (previously missing) and correct `repository.url` to `github.com/actuate-media/actuatecms` (13 packages carried the wrong org).
- Updated dependencies [041a228]
- Updated dependencies [b53b4a4]
- Updated dependencies [041a228]
- Updated dependencies [de419f2]
- Updated dependencies [898a3f8]
- Updated dependencies [4198920]
- Updated dependencies [d6665dd]
  - @actuate-media/cms-core@0.91.0

## 0.8.9

### Patch Changes

- Updated dependencies [3b6d7b3]
  - @actuate-media/cms-core@0.90.1

## 0.8.8

### Patch Changes

- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
- Updated dependencies [4afb589]
  - @actuate-media/cms-core@0.90.0

## 0.8.7

### Patch Changes

- Updated dependencies [41bd56a]
  - @actuate-media/cms-core@0.89.0

## 0.8.6

### Patch Changes

- Updated dependencies [9046381]
  - @actuate-media/cms-core@0.88.0

## 0.8.5

### Patch Changes

- Updated dependencies [163ae1d]
  - @actuate-media/cms-core@0.87.0

## 0.8.4

### Patch Changes

- Updated dependencies [7c105f0]
  - @actuate-media/cms-core@0.86.0

## 0.8.3

### Patch Changes

- Updated dependencies [685958f]
  - @actuate-media/cms-core@0.85.3

## 0.8.2

### Patch Changes

- Updated dependencies [6a6101a]
  - @actuate-media/cms-core@0.85.2

## 0.8.1

### Patch Changes

- Updated dependencies [6d254f3]
  - @actuate-media/cms-core@0.85.1

## 0.8.0

### Minor Changes

- 1a3cf91: Agent-ready public forms (WebMCP): declarative tool metadata on public form schemas, zero-JS `renderPublicFormHtml` / `ActuateFormHtml`, urlencoded submit parsing, HTML confirmation responses, scaffold form prefetch, `actuate doctor` check, and docs.

### Patch Changes

- Updated dependencies [1a3cf91]
  - @actuate-media/cms-core@0.85.0

## 0.7.1

### Patch Changes

- a6360cc: Forms launch kit (Batch 7): Integrations tab with Zapier Catch Hook helper and webhook CRUD in the form schema drawer; marketing seed contact form + /contact page; ActuateForm success focus; actuate doctor forms/CRM checks; forms-and-integrations docs.

## 0.7.0

### Minor Changes

- 6592e09: Add the `widget-embed` section type for lazy-loaded Elfsight, Trustpilot, and Calendly widgets with reserved min-height (CLS-safe) and optional cookie-consent gating via plugin-consent.

### Patch Changes

- Updated dependencies [e1ad198]
- Updated dependencies [a59a501]
- Updated dependencies [e1ad198]
- Updated dependencies [6592e09]
  - @actuate-media/cms-core@0.84.0

## 0.6.17

### Patch Changes

- Updated dependencies [3983016]
  - @actuate-media/cms-core@0.83.0

## 0.6.16

### Patch Changes

- Updated dependencies [c4c41f3]
- Updated dependencies [b22a7bc]
  - @actuate-media/cms-core@0.82.0

## 0.6.15

### Patch Changes

- Updated dependencies [b8b930b]
  - @actuate-media/cms-core@0.81.0

## 0.6.14

### Patch Changes

- Updated dependencies [af18915]
  - @actuate-media/cms-core@0.80.1

## 0.6.13

### Patch Changes

- Updated dependencies [5fce703]
  - @actuate-media/cms-core@0.80.0

## 0.6.12

### Patch Changes

- Updated dependencies [8229d94]
  - @actuate-media/cms-core@0.79.3

## 0.6.11

### Patch Changes

- Updated dependencies [2966293]
  - @actuate-media/cms-core@0.79.2

## 0.6.10

### Patch Changes

- Updated dependencies [0ad67c9]
  - @actuate-media/cms-core@0.79.1

## 0.6.9

### Patch Changes

- Updated dependencies [39e2419]
  - @actuate-media/cms-core@0.79.0

## 0.6.8

### Patch Changes

- Updated dependencies [bc51a1d]
  - @actuate-media/cms-core@0.78.0

## 0.6.7

### Patch Changes

- Updated dependencies [9d4e24c]
- Updated dependencies [869620b]
  - @actuate-media/cms-core@0.77.3

## 0.6.6

### Patch Changes

- Updated dependencies [9875fa3]
  - @actuate-media/cms-core@0.77.2

## 0.6.5

### Patch Changes

- Updated dependencies [4dafd90]
  - @actuate-media/cms-core@0.77.1

## 0.6.4

### Patch Changes

- Updated dependencies [d97e388]
- Updated dependencies [d97e388]
- Updated dependencies [d97e388]
- Updated dependencies [d97e388]
  - @actuate-media/cms-core@0.77.0

## 0.6.3

### Patch Changes

- Updated dependencies [7a6a140]
  - @actuate-media/cms-core@0.76.3

## 0.6.2

### Patch Changes

- Updated dependencies [90b5826]
  - @actuate-media/cms-core@0.76.2

## 0.6.1

### Patch Changes

- Updated dependencies [4899af9]
- Updated dependencies [df1176a]
  - @actuate-media/cms-core@0.76.1

## 0.6.0

### Minor Changes

- c656ddc: Add an `embed` (video) section type for YouTube, Vimeo, and Loom.
  - **cms-core:** new `embed` section in the registry (`url`, `title`, `ratio`, `caption`) plus a shared, deterministic `parseEmbed()` provider allowlist (`@actuate-media/cms-core/page-sections`). Section validation rejects any URL that isn't a recognised YouTube/Vimeo/Loom link, so an arbitrary URL can never reach an iframe.
  - **sections-react:** `EmbedSection` renderer — a lazy-loaded provider iframe inside a fixed aspect-ratio box (16:9 / 4:3 / 1:1 / 9:16) so the embed reserves its space and never causes layout shift (CLS). The URL is re-parsed through the same allowlist at render time.
  - **cms-admin:** matching editor-canvas body for `embed` (provider + ratio placeholder) so the page/post editors preview the section instead of showing a generic "custom section" placeholder.
  - **mcp-server:** `list_section_types` description now lists `embed`.

### Patch Changes

- Updated dependencies [c656ddc]
  - @actuate-media/cms-core@0.76.0

## 0.5.1

### Patch Changes

- Updated dependencies [5446671]
  - @actuate-media/cms-core@0.75.0

## 0.5.0

### Minor Changes

- 2b61b66: Core Web Vitals: give the `hero` section an optional image rendered as the page LCP, and serve AVIF in scaffolded sites.
  - **Hero image (LCP):** the canonical `hero` section now has an optional `imageUrl`/`imageAlt` field. When set, the renderer draws it below the copy as the page's single priority (eager, `fetchpriority=high`) image with a full-width `sizes` hint and intrinsic dimensions — the explicit, dimensioned LCP element the best-practice spec calls for (rather than leaving the LCP to chance). Alt text is required when an image is set (existing accessibility rule). Dimensions are backfilled by the `/resolve` enrichment (hero is now enriched like `feature`).
  - **AVIF delivery:** `create-actuate-cms` now sets `images.formats: ['image/avif', 'image/webp']` so scaffolded sites serve AVIF (≈50% smaller than JPEG) first, then WebP — a direct LCP win via `next/image` format negotiation.

### Patch Changes

- Updated dependencies [2b61b66]
  - @actuate-media/cms-core@0.74.0

## 0.4.1

### Patch Changes

- Updated dependencies [7b7f0be]
  - @actuate-media/cms-core@0.73.0

## 0.4.0

### Minor Changes

- 8a64a6e: Core Web Vitals: enforce the single-priority-image (LCP) policy and ship responsive `sizes` by default.

  The section renderer now guarantees the key LCP/CLS image rules automatically, so an editor (or AI author) can't ship a slow page:
  - **One priority image per page.** A new `LcpController` (exported `createLcpController`) grants the eager, `fetchpriority="high"` slot to the first image on the page; every other image lazy-loads. This replaces `PostHeader`'s hard-coded `priority` (which could collide with a section image). Pass one shared controller via `options.lcp` to both `PostHeader` and `Sections` to share the budget.
  - **Responsive `sizes` everywhere.** Built-in renderers emit contextual `sizes` (full-width header, half-width feature, column-aware gallery via the exported `gallerySizes`) so mobile stops over-downloading the desktop image. `defaultRenderImage` now forwards `sizes` and sets `fetchPriority`.
  - **No raw `<img>`.** The author-bio avatar now renders through `renderImage` (dimensioned, lazy) like every other image.
  - `PostContext` gains `featuredImageWidth`/`featuredImageHeight` so the post header featured image can be dimensioned (CLS).

  `create-actuate-cms`: the generated catch-all creates a per-request shared `LcpController` for the post header + body and forwards `sizes`/`fetchPriority` in its `next/image` fallback.

  Note: `ResolvedRenderOptions` now includes a required `lcp` field — only affects code that constructs that internal type directly; consumers passing `RenderOptions` are unaffected (`lcp` is optional there).

## 0.3.2

### Patch Changes

- Updated dependencies [2fa4ebb]
  - @actuate-media/cms-core@0.72.1

## 0.3.1

### Patch Changes

- Updated dependencies [6f140bc]
  - @actuate-media/cms-core@0.72.0

## 0.3.0

### Minor Changes

- ab216f8: Export the `Img` helper for admin-safe image rendering in section components. Section components that `import next/image` directly break the post/page editor canvas (the admin runs the same components in a plain client bundle with no Next image pipeline). `Img` renders through `options.renderImage` — a `next/image`-backed optimizer on the public site, and Actuate's plain `<img>` renderer in the admin canvas — so one component works in both. Docs gain a "Rendering images in section components" section with the rule and pattern.

## 0.2.25

### Patch Changes

- Updated dependencies [1c81772]
- Updated dependencies [9e0d82b]
  - @actuate-media/cms-core@0.71.1

## 0.2.24

### Patch Changes

- Updated dependencies [9f5e5dc]
  - @actuate-media/cms-core@0.71.0

## 0.2.23

### Patch Changes

- Updated dependencies [0bcc567]
  - @actuate-media/cms-core@0.70.0

## 0.2.22

### Patch Changes

- Updated dependencies [a889054]
  - @actuate-media/cms-core@0.69.0

## 0.2.21

### Patch Changes

- Updated dependencies [4bf16c4]
- Updated dependencies [47bed20]
  - @actuate-media/cms-core@0.68.0

## 0.2.20

### Patch Changes

- Updated dependencies [3ab0649]
- Updated dependencies [5545bfd]
- Updated dependencies [3f7fbf3]
- Updated dependencies [bf00622]
- Updated dependencies [f05c89b]
- Updated dependencies [eb3786d]
- Updated dependencies [88161e8]
- Updated dependencies [2f1f398]
- Updated dependencies [a3c5904]
- Updated dependencies [ee53cfc]
- Updated dependencies [218c5b0]
- Updated dependencies [df5d0de]
  - @actuate-media/cms-core@0.67.0

## 0.2.19

### Patch Changes

- Updated dependencies [a6a17e9]
  - @actuate-media/cms-core@0.66.0

## 0.2.18

### Patch Changes

- Updated dependencies [3e2daa4]
  - @actuate-media/cms-core@0.65.0

## 0.2.17

### Patch Changes

- Updated dependencies [135d0c7]
  - @actuate-media/cms-core@0.64.0

## 0.2.16

### Patch Changes

- Updated dependencies [e3da3bc]
  - @actuate-media/cms-core@0.63.0

## 0.2.15

### Patch Changes

- Updated dependencies [d6b21e7]
  - @actuate-media/cms-core@0.62.0

## 0.2.14

### Patch Changes

- Updated dependencies [0c3a6eb]
  - @actuate-media/cms-core@0.61.0

## 0.2.13

### Patch Changes

- 854152d: Developer experience improvements from external-consumer feedback:
  - Every published package now ships its `CHANGELOG.md` in the npm tarball, and packages that were missing a `README.md` (cli, cms-admin, cms-core, import, platform-vercel, all plugins, sections-react) now have one — so release notes and usage docs are visible on npmjs.com and in `node_modules`.
  - New client-safe form validation entry point: `@actuate-media/cms-core/forms/client` exports `validateFormFieldClient()`, `validateFormValuesClient()`, visibility/conditional helpers, and the exact `FORM_EMAIL_RE` / `FORM_PHONE_RE` regexes the server enforces — zero Node dependencies, so public-site forms can mirror server rules without reimplementing them. The server validator now imports the same primitives, so client and server can never drift.

- Updated dependencies [854152d]
- Updated dependencies [bf5f857]
  - @actuate-media/cms-core@0.60.0

## 0.2.12

### Patch Changes

- Updated dependencies [15b3f45]
  - @actuate-media/cms-core@0.59.1

## 0.2.11

### Patch Changes

- Updated dependencies [f962e2d]
- Updated dependencies [73a117a]
  - @actuate-media/cms-core@0.59.0

## 0.2.10

### Patch Changes

- Updated dependencies [363bb3e]
  - @actuate-media/cms-core@0.58.0

## 0.2.9

### Patch Changes

- Updated dependencies [a5f8bc0]
  - @actuate-media/cms-core@0.57.1

## 0.2.8

### Patch Changes

- Updated dependencies [8e73cf5]
  - @actuate-media/cms-core@0.57.0

## 0.2.7

### Patch Changes

- Updated dependencies [99565a1]
  - @actuate-media/cms-core@0.56.0

## 0.2.6

### Patch Changes

- Updated dependencies [74662c0]
  - @actuate-media/cms-core@0.55.0

## 0.2.5

### Patch Changes

- Updated dependencies [29f32ba]
  - @actuate-media/cms-core@0.54.0

## 0.2.4

### Patch Changes

- Updated dependencies [aa4bd1a]
  - @actuate-media/cms-core@0.53.0

## 0.2.3

### Patch Changes

- Updated dependencies [01eef99]
  - @actuate-media/cms-core@0.52.0

## 0.2.2

### Patch Changes

- Updated dependencies [aeee3a4]
  - @actuate-media/cms-core@0.51.2

## 0.2.1

### Patch Changes

- Updated dependencies [f88a406]
  - @actuate-media/cms-core@0.51.1

## 0.2.0

### Minor Changes

- 204da7a: WS-E (E3): media resolution + responsive images in the public render path.
  - **sections-react:** new pluggable `renderImage` `RenderOptions` hook (with
    exported `RenderImage`/`RenderImageProps` types and a `defaultRenderImage`).
    The default emits a lazy, `decoding="async"` `<img>` and forwards intrinsic
    `width`/`height` when known; a Next.js consumer can pass a `next/image`-backed
    renderer for responsive `srcset` + optimization. The `feature`, `gallery`, and
    post-header images now route through it (`<PostHeader>` gains an optional
    `options` prop). The package stays React-only — no `next` dependency.
  - **cms-core:** the `/resolve` endpoint now enriches section images with
    intrinsic `width`/`height` and library `altText`, matched from `actuate_media`
    by stored URL (`storageKey`/`publicUrl`). Additive and best-effort — the
    stored document is never mutated and a lookup failure never breaks resolve.
    `feature` sections gain `imageWidth`/`imageHeight` (+ backfilled `imageAlt`);
    `gallery` rows gain `width`/`height` (+ backfilled `alt`).
  - **create-actuate-cms:** the generated catch-all page renders CMS images via a
    `next/image`-backed `renderImage` (falling back to a fluid `<img>` when the
    CMS doesn't know dimensions), and `next.config` now allow-lists the storage
    host under `images.remotePatterns` so optimization works out of the box.

- 2f18c56: WS-E (E2): public form rendering — a built site can now render and submit a CMS form end to end.
  - **sections-react:** new `<ActuateForm>` Client Component fetches the public form
    schema (`GET /api/cms/public/forms/:slug`), renders the fields (text/email/
    url/tel/number/date, textarea, select, checkbox, radio), and submits to the
    schema's `submitPath`. Spam protection is wired automatically: a hidden
    honeypot (keyed from `spam.honeypotFieldKey`), an `elapsedMs` "time to submit"
    timer, and an optional consumer-supplied `captchaToken`. Pre-fetch a schema via
    the `form` prop to skip the round-trip (SSR/streaming). Adds a `baseUrl`
    `RenderOptions` field (defaults to same-origin). Exposes pure, tested helpers
    (`coercePublicForm`, `buildSubmitBody`, `fieldControl`, `firstMissingRequired`).
  - **cms-core:** new canonical `form` section type. Set `content.formSlug` (plus
    optional heading/intro) on any page or post and the matching renderer embeds
    the form. The registry-drift guard now covers it (renderer ↔ type).
  - **cms-admin:** the form Embed panel now shows the real public endpoints
    (`/api/cms/public/forms/:slug` + `/submit`) and a copy-paste `<ActuateForm>`
    snippet, replacing the previously-advertised `/api/forms/*`, iframe, and
    `embed.js` routes that were never implemented. Adds a `mail` section icon.

- e2e0bd7: WS-E (E4): public navigation endpoint + navigation rendering in the scaffold.
  - **cms-core:** new unauthenticated `GET /api/cms/public/navigations/:slug` that
    returns a clean `{ name, slug, location, items }` tree. Like globals it is site
    chrome — not status-gated — and safe when the navigation plugin isn't installed
    (no match → 404). A shared, pure `coercePublicNavItems` helper sanitizes the
    open `data.items` JSON: drops unlabeled nodes, strips internal `id`s, caps
    recursion depth, and only passes through `_blank`/`_self` targets. Both
    `createSiteClient` and `createServerSiteClient` gain `getNavigation(slug)`
    returning `PublicNavigation | null`, and `PublicNavigation` / `PublicNavItem`
    are now exported types.
  - **sections-react:** new presentational `<SiteNav>` — an accessible `<nav>`
    landmark whose submenus use native `<details>`/`<summary>` disclosure
    (keyboard-operable, no client JS). New-tab links get
    `rel="noopener noreferrer"`; an empty menu renders nothing. Exports `SiteNav`
    plus `SiteNavProps`/`SiteNavItem`.
  - **create-actuate-cms:** `lib/cms.ts` re-exports `getNavigation`, and the
    generated site layout fetches the `main` menu and renders a header with the
    site name + `<SiteNav>`, degrading gracefully to just the name when no menu
    exists yet.

### Patch Changes

- Updated dependencies [a413616]
- Updated dependencies [204da7a]
- Updated dependencies [2f18c56]
- Updated dependencies [e2e0bd7]
  - @actuate-media/cms-core@0.51.0

## 0.1.2

### Patch Changes

- Updated dependencies [d7a144c]
  - @actuate-media/cms-core@0.50.0

## 0.1.1

### Patch Changes

- Updated dependencies [9ba332c]
  - @actuate-media/cms-core@0.49.0

## 0.1.0

### Minor Changes

- 989bd09: New package: a public React renderer for Actuate's canonical page-content
  contract (ADR 0001 — `data.sections: PageSection[]`).
  - `<Sections sections={...} ctx={...} />` renders all 10 built-in section types
    (hero, text, feature, stats, cta, article-body, quote, author-bio,
    related-posts, gallery) with zero hand-written mapper. SSR-first (safe in
    React Server Components).
  - `<PostHeader config={...} ctx={...} />` renders a post header from its
    template config + the post's own fields.
  - Consumer-controlled renderer registry: override any section type per-render
    via the `renderers` prop, or app-wide via `registerSectionRenderer()`.
    Resolution precedence: prop > global registry > built-in default > skip.
  - Rich-text fields go through an injectable `options.sanitizeHtml` hook
    (default identity; Actuate sanitizes on write, pass a sanitizer for
    defense-in-depth or third-party HTML).
  - A registry-drift guard test keeps the renderer map in lockstep with the
    cms-core section registry, so no section type can ship without a renderer
    (and vice-versa).
