diff --git a/docs/release-control/v6/internal/subsystems/cloud-paid.md b/docs/release-control/v6/internal/subsystems/cloud-paid.md
index 1f8d96a54..a86190f03 100644
--- a/docs/release-control/v6/internal/subsystems/cloud-paid.md
+++ b/docs/release-control/v6/internal/subsystems/cloud-paid.md
@@ -508,6 +508,10 @@ headline price, founding-rate override, compare-at strike-through copy,
campaign badge copy, and annual summary text must come from the shared
plan-definition owners rather than page-local string parsing or hardcoded
retail amounts inside hosted pricing/signup screens.
+The same owner also holds shared hosted commercial copy such as page title,
+introductory description, common Cloud inclusions, and setup-step guidance
+when those facts describe the canonical offer rather than one page's local
+layout.
That same counted-unit boundary also owns the disclosure rule for retail copy:
default billing and pricing surfaces should use concise monitored-system copy,
while the full counted-unit definition appears only behind explicit disclosure
diff --git a/frontend-modern/src/pages/CloudPricing.tsx b/frontend-modern/src/pages/CloudPricing.tsx
index d6b8cee66..74eb02a99 100644
--- a/frontend-modern/src/pages/CloudPricing.tsx
+++ b/frontend-modern/src/pages/CloudPricing.tsx
@@ -5,20 +5,12 @@ import { PageHeader } from '@/components/shared/PageHeader';
import { trackPaywallViewed } from '@/utils/upgradeMetrics';
import { onMount } from 'solid-js';
import {
+ CLOUD_COMMERCIAL_PRESENTATION,
CLOUD_PLAN_DEFINITIONS,
getCloudPlanPricePresentation,
type CloudPlanDefinition,
} from '@/utils/cloudPlans';
-const INCLUDED_IN_ALL = [
- 'All Pro features',
- 'Managed hosting',
- 'Daily backups',
- 'Secure agent connectivity via Relay',
- 'Mobile app access and push notifications',
- 'Dedicated workspace URL',
-];
-
function CloudTierCard(props: { tier: CloudPlanDefinition }) {
const t = props.tier;
const price = getCloudPlanPricePresentation(t);
@@ -95,8 +87,8 @@ export default function CloudPricing() {
return (
{/* Tier cards */}
@@ -106,9 +98,11 @@ export default function CloudPricing() {
{/* What's included in all Cloud plans */}
- Included in every Cloud plan
+
+ {CLOUD_COMMERCIAL_PRESENTATION.includedInAllHeading}
+
-
+
{(item) => (
-
✓
@@ -121,11 +115,11 @@ export default function CloudPricing() {
{/* How it works */}
-
Setup
+
+ {CLOUD_COMMERCIAL_PRESENTATION.setupHeading}
+
- - Create your workspace. No credit card is required for the trial.
- - Install the Pulse agent on any Linux machine.
- - Connect systems, review findings, and configure alerts.
+ {(step) => - {step}
}
diff --git a/frontend-modern/src/pages/__tests__/CloudPricing.test.tsx b/frontend-modern/src/pages/__tests__/CloudPricing.test.tsx
index 6213bd30e..57a599cf4 100644
--- a/frontend-modern/src/pages/__tests__/CloudPricing.test.tsx
+++ b/frontend-modern/src/pages/__tests__/CloudPricing.test.tsx
@@ -37,6 +37,10 @@ describe('CloudPricing', () => {
);
expect(screen.queryByText('Starter founding rate')).not.toBeInTheDocument();
expect(screen.getAllByText('All Pro features')).toHaveLength(1);
+ expect(screen.getByText('Managed hosting')).toBeInTheDocument();
+ expect(
+ screen.getByText('Create your workspace. No credit card is required for the trial.'),
+ ).toBeInTheDocument();
expect(screen.getByText('Setup')).toBeInTheDocument();
expect(
screen.queryByText(/provisioned in under 60 seconds/i),
diff --git a/frontend-modern/src/utils/__tests__/cloudPlans.test.ts b/frontend-modern/src/utils/__tests__/cloudPlans.test.ts
index 840ba0834..a4b4aed99 100644
--- a/frontend-modern/src/utils/__tests__/cloudPlans.test.ts
+++ b/frontend-modern/src/utils/__tests__/cloudPlans.test.ts
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import {
+ CLOUD_COMMERCIAL_PRESENTATION,
CLOUD_PLAN_BY_TIER,
getCloudPlanPricePresentation,
} from '@/utils/cloudPlans';
@@ -25,4 +26,26 @@ describe('cloudPlans', () => {
campaignBadge: undefined,
});
});
+
+ it('keeps shared cloud commercial copy in the common contract', () => {
+ expect(CLOUD_COMMERCIAL_PRESENTATION).toEqual({
+ pageTitle: 'Pulse Cloud',
+ pageDescription: 'Managed Pulse hosting with Pro features included.',
+ includedInAllHeading: 'Included in every Cloud plan',
+ includedInAllItems: [
+ 'All Pro features',
+ 'Managed hosting',
+ 'Daily backups',
+ 'Secure agent connectivity via Relay',
+ 'Mobile app access and push notifications',
+ 'Dedicated workspace URL',
+ ],
+ setupHeading: 'Setup',
+ setupSteps: [
+ 'Create your workspace. No credit card is required for the trial.',
+ 'Install the Pulse agent on any Linux machine.',
+ 'Connect systems, review findings, and configure alerts.',
+ ],
+ });
+ });
});
diff --git a/frontend-modern/src/utils/cloudPlans.ts b/frontend-modern/src/utils/cloudPlans.ts
index e9a8dd22a..ac10fb4f3 100644
--- a/frontend-modern/src/utils/cloudPlans.ts
+++ b/frontend-modern/src/utils/cloudPlans.ts
@@ -21,6 +21,15 @@ export interface CloudPlanPricePresentation {
campaignBadge?: string;
}
+export interface CloudCommercialPresentation {
+ pageTitle: string;
+ pageDescription: string;
+ includedInAllHeading: string;
+ includedInAllItems: readonly string[];
+ setupHeading: string;
+ setupSteps: readonly string[];
+}
+
export const DEFAULT_CLOUD_TIER: CloudTierKey = 'starter';
export const CLOUD_PLAN_DEFINITIONS: readonly CloudPlanDefinition[] = [
@@ -72,6 +81,26 @@ export const CLOUD_PLAN_LABELS: Record = {
msp_scale: 'MSP Scale',
};
+export const CLOUD_COMMERCIAL_PRESENTATION: CloudCommercialPresentation = {
+ pageTitle: 'Pulse Cloud',
+ pageDescription: 'Managed Pulse hosting with Pro features included.',
+ includedInAllHeading: 'Included in every Cloud plan',
+ includedInAllItems: [
+ 'All Pro features',
+ 'Managed hosting',
+ 'Daily backups',
+ 'Secure agent connectivity via Relay',
+ 'Mobile app access and push notifications',
+ 'Dedicated workspace URL',
+ ],
+ setupHeading: 'Setup',
+ setupSteps: [
+ 'Create your workspace. No credit card is required for the trial.',
+ 'Install the Pulse agent on any Linux machine.',
+ 'Connect systems, review findings, and configure alerts.',
+ ],
+} as const;
+
export function parseCloudTier(value?: string | null): CloudTierKey {
switch ((value || '').trim().toLowerCase()) {
case 'power':