Skip to content

Build CMS pricing shells: 4 vendor pages + hub - #106

Open
ardeay wants to merge 3 commits into
stagefrom
kaya-cms-pricing-shells
Open

ardeay wants to merge 3 commits into
stagefrom
kaya-cms-pricing-shells

Conversation

@ardeay

@ardeay ardeay commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Short-and-sweet page shells per kaya's 2026-09-11 brief. She said "keep the pages short and sweet until then" — Wordify copy incoming in the next few days. The estimator, JSON-LD, sitemap/llms entries, and nav/footer additions are deferred for follow-up phases.

Files created (folder-based clean URL pattern, matches /solutions/ + /features/)

Hub — webengine/views/page_html/cms-pricing/index.html

  • Intended URL: /cms-pricing/

Vendor pages — webengine/views/page_html/cms-pricing/{sitecore,adobe-experience-manager,contentful,optimizely-cms}.html

  • Intended URLs: /cms-pricing/sitecore/ etc.

Each vendor shell contains

  • Breadcrumb (Home → CMS pricing → Vendor pricing)
  • H1 [Vendor] pricing in 2026 + intro + Last updated: September 2026 <time> line
  • Estimator placeholder card (dashed border, explains what's coming)
  • Empty H2 slots for the 6 planned sections: how vendor prices, cost breakdown, worked example, what pushes the price up, Content.One for the same setup (with 3-logo proof block), FAQ
  • Methodology + Sources placeholder
  • Related links block (Compare vs page, Other pricing guides, Start here)
  • CTA band (content-ops maturity audit + launch.content.one)

Hub shell contains

  • H1 + intro + Last updated
  • 15-row comparison table slot (empty placeholder)
  • "Pick your platform" grid — 4 live vendor cards
  • Two H2 slots (enterprise DXP, headless CMS)
  • Content.One proof block (Salvation Army + Sony + Singlife)
  • FAQ + Sources slots
  • CTA band

What's NOT in this PR

  • Full estimator JS component (brief section 4) — needs Wordify config numbers + is a bigger build. Placeholder card in place.
  • Actual copy in H2 slots (awaits Wordify)
  • FAQ Q/A pairs (awaits Wordify — schema needs visible text to match)
  • JSON-LD structured data (WebPage/BreadcrumbList/FAQPage/ItemList) — waits until FAQ copy arrives
  • Source lists (awaits Wordify)
  • Zoho lead capture + PDF generation (brief 4f)
  • GA4 / LinkedIn event tracking (brief 4h)
  • Nav "Compare" item with pricing-guides sub-group (brief 5a)
  • Footer "Pricing guides" column (brief 5b)
  • Vs page pricing-slot rewrite pointing to vendor pages (brief 5c)
  • Sitemap.xml + llms.txt entries — deliberately held until URLs actually route (see Zesty blocker below); listing them now creates 404s in the crawl

⚠️ Zesty admin blocker (ardeay)

For /cms-pricing/[vendor]/ URLs to route, a parent templateset needs to exist in the Zesty editor named cms-pricing. Same pattern as the solutions/features/industries templatesets.

Once the templateset exists, the 5 shell files auto-create as ajax-json child views via sync-to-zesty on the next stage merge (per the auto-create flow shipped in PR #100). Drop the templateset ZUID here after creation and I'll wire it into zesty.config.json.

Alternative if the templateset takes a beat: I can convert these to root .html files (e.g. /cms-pricing-sitecore.html) so they resolve immediately at slightly different URLs. Just say the word.

Preview

The pages won't preview at their intended URLs until the templateset exists. Once wired, they'll show at:

  • https://hj5v68xn-stage.webengine.zesty.io/cms-pricing/?zpw=bobby
  • https://hj5v68xn-stage.webengine.zesty.io/cms-pricing/sitecore/?zpw=bobby
  • https://hj5v68xn-stage.webengine.zesty.io/cms-pricing/adobe-experience-manager/?zpw=bobby
  • https://hj5v68xn-stage.webengine.zesty.io/cms-pricing/contentful/?zpw=bobby
  • https://hj5v68xn-stage.webengine.zesty.io/cms-pricing/optimizely-cms/?zpw=bobby

Sanity checks (once wired)

  • /cms-pricing/ shows the hub with 4 live vendor cards
  • Each vendor page renders with breadcrumb, H1, placeholder estimator card, and all H2 section slots
  • CTAs anchor to /content-operations-maturity-audit/ and launch.content.one
  • Related links point to correct vs page for each vendor

Short-and-sweet page shells per kaya's ask ('keep the pages short and
sweet until then' — Wordify copy is coming in the next few days).

## Files created (folder-based clean URL pattern like /solutions/ + /features/)

Hub: webengine/views/page_html/cms-pricing/index.html
  Intended URL: /cms-pricing/

Vendor pages:
  webengine/views/page_html/cms-pricing/sitecore.html
  webengine/views/page_html/cms-pricing/adobe-experience-manager.html
  webengine/views/page_html/cms-pricing/contentful.html
  webengine/views/page_html/cms-pricing/optimizely-cms.html
  Intended URLs: /cms-pricing/sitecore/ etc.

## Each vendor shell contains

- Breadcrumb (Home > CMS pricing > Vendor pricing)
- H1 '[Vendor] pricing in 2026' + intro + 'Last updated' <time> line
- Estimator placeholder card (dashed border, explains what's coming)
- Empty H2 slots for the 6 planned sections: how vendor prices, cost
  breakdown, worked example, what pushes the price up, Content.One for
  the same setup (with 3-logo proof block), FAQ
- Methodology + Sources placeholder
- Related links (Compare vs page, Other pricing guides, Start here)
- CTA band (content-ops maturity audit + launch.content.one)

## Hub shell contains

- H1 + intro + 'Last updated'
- 15-row comparison table slot (empty placeholder)
- 'Pick your platform' grid — 4 live vendor cards
- Two H2 slots (enterprise DXP, headless CMS)
- Content.One proof block
- FAQ + Sources slots
- CTA band

## What's NOT in this PR (deferred per kaya's 'short and sweet' scope)

- Full estimator JS component with per-vendor JSON config (brief section 4)
- Actual pricing copy in H2 slots (awaits Wordify)
- Comparison table rows (awaits Wordify)
- FAQ Q/A pairs (awaits Wordify)
- Source lists (awaits Wordify)
- Zoho lead capture + PDF generation (brief 4f)
- GA4 / LinkedIn event tracking (brief 4h)
- Nav 'Compare' item with pricing-guides sub-group (brief 5a)
- Footer 'Pricing guides' column (brief 5b)
- Vs page pricing-slot rewrite to link out to vendor pages (brief 5c)
- JSON-LD schema (WebPage/BreadcrumbList/FAQPage/ItemList — waits until FAQ
  copy arrives since answers must match visible text word-for-word)
- Sitemap.xml + llms.txt entries (URLs will 404 until Zesty templateset
  exists — safer to add after routing works)

## Zesty admin blocker (ardeay)

For /cms-pricing/[vendor]/ URLs to route, a parent templateset needs to
exist in the Zesty editor named 'cms-pricing'. Same pattern as the
solutions/features/industries templatesets. Once the templateset exists,
the vendor page shell files auto-create as ajax-json child views via
sync-to-zesty on stage merge (per the PR #100 auto-create flow).
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

💾 Stage sync preview

New files to create (6):

  • webengine/views/components/cms-pricing-estimator.html
  • webengine/views/page_html/cms-pricing/adobe-experience-manager-pricing.html
  • webengine/views/page_html/cms-pricing/contentful-pricing.html
  • webengine/views/page_html/cms-pricing/index.html
  • webengine/views/page_html/cms-pricing/optimizely-cms-pricing.html
  • webengine/views/page_html/cms-pricing/sitecore-pricing.html

Will add/update — differ from dev (0):
none

Unchanged, skipped: 0

205 mapped resources · zesty-sync

ContentOneBot added 2 commits September 11, 2026 13:59
Kaya (2026-09-11): wants slugs '[vendor]-pricing' not just '[vendor]'.
Renamed the 4 vendor page files + updated the hub's pick-your-platform
grid links + the intended-URL comment headers.

New URLs:
- /cms-pricing/                           (hub, unchanged)
- /cms-pricing/sitecore-pricing/
- /cms-pricing/adobe-experience-manager-pricing/
- /cms-pricing/contentful-pricing/
- /cms-pricing/optimizely-cms-pricing/
…tecore

Per kaya's 2026-09-11 brief section 4 (estimator config + calculation spec)
+ the Sitecore config in section 2.

## Component
webengine/views/components/cms-pricing-estimator.html
- Vanilla JS, no framework, no third-party libs (per brief 4a)
- Reads config from an inline <script type='application/json' id='estimator-config'>
  on the host page — same shape across every vendor page, no vendor-specific
  code paths anywhere in the component
- 7 inputs, in order: situation, edition, visits (slider), sites (slider),
  editors (slider), implementation, add-ons (checkboxes)
- Live recalculates on every input change (60ms debounce), no submit button
- Outputs: three-year total range headline, year 1/2/3 rows, breakdown table
  with source links per line, subscription share percentage
- 'How we got this number' details collapsed by default, expands to show
  formulas with the user's inputs substituted
- Notes + accurate-as-of caption from config
- Content.One panel present as hidden slot (brief 4c 'behind a config flag')
- Query-string state sync (brief 4d): s/e/v/w/u/i/a keys, read on load,
  updated on change via history.replaceState — shareable URLs work today
- Analytics stub for estimate_rendered + estimate_changed via window.gtag
  (brief 4h; other events land in a later pass)

Math verified against kaya's 3 test cases before writing a line of JS:
- Test 1 (new/sitecoreai/500k_2m/12sites/10eds/agency)   → $618k-$1,339k, share 31%/21%  ✓
- Test 2 (renew/sitecoreai/same)                         → $519k-$1,139k                 ✓
- Test 3 (renew/legacy_xp/same)                          → sub $265k-$567k               ✓

## Sitecore page
Replaced the dashed 'coming soon' placeholder card in sitecore-pricing.html
with the config JSON (kaya's Section 2 numbers verbatim) + the include tag.

Wordify edits Sitecore numbers by editing the JSON block on the page — no
code change, no deploy waiting on Mark's side.

## What's not built yet
- 4f 'Email me this estimate' PDF + Zoho lead capture (bigger scope, needs
  Zoho custom-field decisions)
- 4g 'Have an actual quote?' upload form (same)
- 4h remaining events (pdf_requested, quote_submitted, cta_launch_clicked)
- Content.One panel content (flagged in brief as 'pending a decision on your side')

Sanity: JSON parses cleanly on the Sitecore page.
Comment on lines +210 to +211
l.innerHTML = '<input type="radio" name="edition" value="' + ed.id + '"' + (ed.default ? ' checked' : '') + ' />'
+ '<span class="block text-sm px-4 py-3 rounded-lg border border-gray-300 text-gray-700 transition">' + ed.label + '</span>';
Comment on lines +219 to +220
l.innerHTML = '<input type="checkbox" name="addons" value="' + a.id + '" />'
+ '<span class="block text-sm px-4 py-3 rounded-lg border border-gray-300 text-gray-700 transition">' + a.label + priceHint + '</span>';
Comment on lines +364 to +371
breakdown.innerHTML = rows.map(function(r){
return '<tr class="border-t border-gray-100">'
+ '<td class="p-3 font-medium text-ink">' + r.label + '</td>'
+ '<td class="p-3 text-right">' + fmtK(r.lo) + '</td>'
+ '<td class="p-3 text-right">' + fmtK(r.hi) + '</td>'
+ '<td class="p-3 text-xs">' + srcLinks(r.sources) + '</td>'
+ '</tr>';
}).join('');
Comment on lines +377 to +382
formulas.innerHTML = ''
+ '<p class="mb-3"><strong>Subscription (year 1)</strong>: band [' + fmtRange(band.license) + '] × licence multiplier [' + edition.licenseMultiplier.low + '–' + edition.licenseMultiplier.high + '] × editor factor for ' + lo.editorBlocks + ' block(s) = ' + fmtK(lo.lic.y1) + '–' + fmtK(hi.lic.y1) + '. Uplift ' + CFG.upliftPct + '% applied in years 2 and 3.</p>'
+ '<p class="mb-3"><strong>Implementation (year 1)</strong>: ' + (state.situation === 'renew' ? '$0 on renewal.' : state.implementation + ' model at ' + state.sites + ' site(s) = ' + fmtK(lo.impl.y1) + '–' + fmtK(hi.impl.y1) + '.') + '</p>'
+ '<p class="mb-3"><strong>Front-end hosting</strong>: band range ' + fmtRange(band.hosting) + '/yr, no uplift = ' + fmtK(lo.host.total) + '–' + fmtK(hi.host.total) + ' over 3 years.</p>'
+ '<p class="mb-3"><strong>Maintenance</strong>: ' + fmtK(lo.mAnnual) + '–' + fmtK(hi.mAnnual) + '/yr. ' + (state.situation === 'new' ? 'Years 2 and 3 only for new purchases.' : 'All three years for renewals.') + '</p>'
+ (state.addons.length ? '<p class="mb-3"><strong>Add-ons</strong>: ' + state.addons.length + ' selected, priced ones sum to ' + fmtK(lo.addons.y1) + '–' + fmtK(hi.addons.y1) + '/yr.</p>' : '<p class="mb-3"><strong>Add-ons</strong>: none selected.</p>')

// Notes + accurate as of
var notesEl = root.querySelector('[data-cpe-notes]');
notesEl.innerHTML = CFG.notes.map(function(n){ return '<li>' + n + '</li>'; }).join('');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants