checkout: add shared trust band (guarantee + security) to order flow
High-friction conversion points (payment step, review step, order intro) had almost no trust reinforcement at the moment of payment. Adds a shared, regulator-agnostic CheckoutTrustBand component used across all four verticals: - Payment step: 'full' variant -- money-back-if-we-fail-to-file guarantee + 256-bit TLS / Stripe / SOC 2 / PCI / fixed-price security badges + the right 'not affiliated with <agency>' disclaimer for the vertical. - Review step: 'compact' variant -- guarantee + disclaimer (no badges). - Order intro (VerticalOrderHeader, shared by all 49 order pages): thin green 'Secure checkout / Fixed price / Money-back guarantee' bar. Guarantee copy is a real promise (full refund if we cannot file), worded so it never overpromises a regulatory outcome (agency approval is not ours to give). Vertical is inferred from the intake-step list via slugVertical() (single source of truth, no hand-maintained slug table), with an explicit corporate slug set since corporate services share the generic 'entity' step. Note: the 'dc_agent' step is the telecom D.C. process-agent designation, not corporate. Also fixes two pre-existing mislabeled order-page headers surfaced by an exhaustive header-vs-disclaimer audit: rmd-filing (Robocall Mitigation DB) and new-carrier-bundle (VoIP carrier onboarding) are telecom, not healthcare/ trucking.
This commit is contained in:
parent
ada9e01321
commit
19785629d1
7 changed files with 157 additions and 4 deletions
114
site/src/components/CheckoutTrustBand.astro
Normal file
114
site/src/components/CheckoutTrustBand.astro
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
---
|
||||
// Shared checkout trust band. One source of truth for the security + guarantee
|
||||
// reassurance shown at the high-friction moments of the order flow (payment
|
||||
// step, review step, and the order intro). Used across ALL verticals
|
||||
// (telecom, trucking, healthcare, corporate), so the copy is regulator-agnostic
|
||||
// except for an optional vertical-specific affiliation disclaimer.
|
||||
//
|
||||
// Usage:
|
||||
// <CheckoutTrustBand variant="full" vertical="healthcare" /> // Payment step
|
||||
// <CheckoutTrustBand variant="compact" vertical="telecom" /> // Review step
|
||||
// <CheckoutTrustBand variant="bar" /> // Order H1 bar
|
||||
//
|
||||
// The guarantee is a real money-back-if-we-fail-to-file promise: if we are
|
||||
// unable to file your filing, you get a full refund. Worded so it never
|
||||
// overpromises a regulatory outcome (agency approval is not ours to guarantee),
|
||||
// only our own work.
|
||||
export interface Props {
|
||||
variant?: "full" | "compact" | "bar";
|
||||
vertical?: "trucking" | "telecom" | "healthcare" | "corporate";
|
||||
}
|
||||
const { variant = "full", vertical } = Astro.props;
|
||||
|
||||
// Regulator-agnostic security badges shown on every order.
|
||||
const BADGES = [
|
||||
{ icon: "\u{1F512}", label: "256-bit TLS encrypted" },
|
||||
{ icon: "\u{1F4B3}", label: "Secure payment by Stripe" },
|
||||
{ icon: "\u{1F6E1}\u{FE0F}", label: "SOC 2 hosting \u00b7 PCI compliant" },
|
||||
{ icon: "\u{1F4CB}", label: "Fixed price, no billable hours" },
|
||||
];
|
||||
|
||||
const GUARANTEE_TITLE = "Money-back guarantee";
|
||||
const GUARANTEE_BODY =
|
||||
"If we are unable to file your filing, you get a full refund. Fixed price, no billable hours, no surprises.";
|
||||
|
||||
// Affiliation disclaimer is vertical-specific (only relevant when the order is
|
||||
// tied to a particular agency). Omitted when vertical is unknown.
|
||||
const DISCLAIMERS: Record<NonNullable<Props["vertical"]>, string> = {
|
||||
healthcare: "Performance West is an independent compliance firm, not affiliated with CMS or Medicare.",
|
||||
telecom: "Performance West is an independent compliance firm, not affiliated with the FCC or USAC.",
|
||||
trucking: "Performance West is an independent compliance firm, not affiliated with the FMCSA or DOT.",
|
||||
corporate: "Performance West is an independent compliance firm, not a government agency.",
|
||||
};
|
||||
const disclaimer = vertical ? DISCLAIMERS[vertical] : "";
|
||||
---
|
||||
|
||||
{variant === "bar" && (
|
||||
<div class="pw-trust-bar">
|
||||
<span class="pw-tb-item"><span aria-hidden="true">{"\u{1F512}"}</span> Secure checkout</span>
|
||||
<span class="pw-tb-sep" aria-hidden="true">·</span>
|
||||
<span class="pw-tb-item"><span aria-hidden="true">{"\u{1F4B5}"}</span> Fixed price, no billable hours</span>
|
||||
<span class="pw-tb-sep" aria-hidden="true">·</span>
|
||||
<span class="pw-tb-item"><span aria-hidden="true">{"\u2713"}</span> Money-back guarantee: we file it or your money back</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{variant !== "bar" && (
|
||||
<div class={`pw-trust ${variant === "compact" ? "is-compact" : ""}`}>
|
||||
<div class="pw-trust-guarantee">
|
||||
<span class="pw-guarantee-icon" aria-hidden="true">{"\u{2705}"}</span>
|
||||
<div>
|
||||
<p class="pw-guarantee-title">{GUARANTEE_TITLE}</p>
|
||||
<p class="pw-guarantee-body">{GUARANTEE_BODY}</p>
|
||||
</div>
|
||||
</div>
|
||||
{variant === "full" && (
|
||||
<ul class="pw-trust-badges" aria-label="Security and trust">
|
||||
{BADGES.map((b) => (
|
||||
<li><span class="pw-badge-icon" aria-hidden="true">{b.icon}</span>{b.label}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{disclaimer && <p class="pw-trust-disclaimer">{disclaimer}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style>
|
||||
/* Thin bar (order intro) */
|
||||
.pw-trust-bar {
|
||||
display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
|
||||
margin: 0.75rem 0 0; padding: 0.6rem 0.85rem;
|
||||
background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px;
|
||||
font-size: 0.82rem; color: #166534; line-height: 1.4;
|
||||
}
|
||||
.pw-tb-item { display: inline-flex; align-items: center; gap: 0.3rem; font-weight: 600; }
|
||||
.pw-tb-sep { color: #86efac; }
|
||||
|
||||
/* Card (payment + review) */
|
||||
.pw-trust {
|
||||
margin: 1.5rem 0 0; padding: 1rem 1.15rem;
|
||||
background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px;
|
||||
}
|
||||
.pw-trust.is-compact { margin: 1.25rem 0 0; padding: 0.85rem 1rem; }
|
||||
.pw-trust-guarantee {
|
||||
display: flex; gap: 0.7rem; align-items: flex-start;
|
||||
padding: 0.75rem 0.85rem; background: #ecfdf5;
|
||||
border: 1px solid #6ee7b7; border-radius: 8px;
|
||||
}
|
||||
.pw-guarantee-icon { font-size: 1.25rem; line-height: 1.2; flex: 0 0 auto; }
|
||||
.pw-guarantee-title { margin: 0 0 0.15rem; font-size: 0.9rem; font-weight: 800; color: #047857; }
|
||||
.pw-guarantee-body { margin: 0; font-size: 0.82rem; color: #065f46; line-height: 1.5; }
|
||||
.pw-trust-badges {
|
||||
list-style: none; margin: 0.85rem 0 0; padding: 0;
|
||||
display: flex; flex-wrap: wrap; gap: 0.4rem 1rem;
|
||||
}
|
||||
.pw-trust-badges li {
|
||||
display: inline-flex; align-items: center; gap: 0.35rem;
|
||||
font-size: 0.78rem; font-weight: 600; color: #0f766e;
|
||||
}
|
||||
.pw-badge-icon { font-size: 0.9rem; }
|
||||
.pw-trust-disclaimer {
|
||||
margin: 0.75rem 0 0; font-size: 0.72rem; color: #94a3b8; line-height: 1.4;
|
||||
}
|
||||
.pw-trust.is-compact .pw-trust-disclaimer { margin-top: 0.6rem; }
|
||||
</style>
|
||||
|
|
@ -9,6 +9,7 @@ export interface Props {
|
|||
vertical: "trucking" | "telecom" | "healthcare" | "corporate";
|
||||
}
|
||||
const { vertical } = Astro.props;
|
||||
import CheckoutTrustBand from "./CheckoutTrustBand.astro";
|
||||
|
||||
type Card = { icon: string; h: string; p: string };
|
||||
type Content = {
|
||||
|
|
@ -93,6 +94,7 @@ const c = CONTENT[vertical];
|
|||
))}
|
||||
</div>
|
||||
</section>
|
||||
<CheckoutTrustBand variant="bar" />
|
||||
|
||||
<style>
|
||||
.pw-vhero {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
// order, validates intake, and starts the selected checkout flow.
|
||||
export interface Props { service_slug: string; }
|
||||
const { service_slug } = Astro.props;
|
||||
import { SERVICE_META, formatUSD } from "../../../lib/intake_manifest";
|
||||
import { SERVICE_META, formatUSD, slugVertical } from "../../../lib/intake_manifest";
|
||||
import CheckoutTrustBand from "../../CheckoutTrustBand.astro";
|
||||
const meta = SERVICE_META[service_slug];
|
||||
const vertical = slugVertical(service_slug);
|
||||
---
|
||||
|
||||
<div class="pw-step" data-slug={service_slug}>
|
||||
|
|
@ -35,6 +37,8 @@ const meta = SERVICE_META[service_slug];
|
|||
</select>
|
||||
|
||||
<p class="pw-method-note">Click <strong>Finish</strong> below to continue with the selected method.</p>
|
||||
|
||||
<CheckoutTrustBand variant="full" vertical={vertical} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// ReviewStep — summary panel, server-side validation hook, price display.
|
||||
export interface Props { service_slug: string; }
|
||||
const { service_slug } = Astro.props;
|
||||
import { SERVICE_META, formatUSD } from "../../../lib/intake_manifest";
|
||||
import { SERVICE_META, formatUSD, slugVertical } from "../../../lib/intake_manifest";
|
||||
import CheckoutTrustBand from "../../CheckoutTrustBand.astro";
|
||||
const meta = SERVICE_META[service_slug];
|
||||
const vertical = slugVertical(service_slug);
|
||||
---
|
||||
|
||||
<div class="pw-step" data-slug={service_slug}>
|
||||
|
|
@ -28,6 +30,8 @@ const meta = SERVICE_META[service_slug];
|
|||
|
||||
<div id="pw-review-errors" class="pw-err" hidden></div>
|
||||
<div id="pw-review-warnings" class="pw-warn" hidden></div>
|
||||
|
||||
<CheckoutTrustBand variant="compact" vertical={vertical} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -176,3 +176,32 @@ export function formatUSD(cents: number): string {
|
|||
minimumFractionDigits: 2, maximumFractionDigits: 2,
|
||||
})}`;
|
||||
}
|
||||
|
||||
// ── Vertical inference ──────────────────────────────────────────────────
|
||||
// Derives a service's compliance vertical from its intake-step list, so we
|
||||
// have a single source of truth (no hand-maintained slug->vertical table).
|
||||
// Used by the shared CheckoutTrustBand to show the right "not affiliated with
|
||||
// <agency>" disclaimer at the payment / review steps.
|
||||
export type Vertical = "trucking" | "telecom" | "healthcare" | "corporate";
|
||||
|
||||
export function slugVertical(slug: string): Vertical | undefined {
|
||||
const steps = INTAKE_MANIFEST[slug];
|
||||
if (!steps) return undefined;
|
||||
if (steps.includes("npi-intake")) return "healthcare";
|
||||
if (steps.includes("dot-intake") || steps.includes("state-trucking")) return "trucking";
|
||||
// Corporate services run on the generic `entity` step (same as telecom) and
|
||||
// can't be told apart by step shape (note: the `dc_agent` step is the D.C.
|
||||
// process-agent designation used by telecom 499 filers, NOT corporate). So
|
||||
// corporate slugs are listed explicitly, plus the `foreign_qual` step which
|
||||
// only appears on foreign-qualification orders.
|
||||
if (steps.includes("foreign_qual")) return "corporate";
|
||||
if (CORPORATE_SLUGS.has(slug)) return "corporate";
|
||||
// Everything else is entity/FCC-based telecom.
|
||||
return "telecom";
|
||||
}
|
||||
|
||||
const CORPORATE_SLUGS = new Set<string>([
|
||||
"dc-agent",
|
||||
"foreign-qualification-single",
|
||||
"foreign-qualification-multi",
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const description = "Start-to-finish for a brand-new VoIP carrier: FRN + 499 Ini
|
|||
<p class="pw-desc">{description}</p>
|
||||
</section>
|
||||
|
||||
<VerticalOrderHeader vertical="trucking" />
|
||||
<VerticalOrderHeader vertical="telecom" />
|
||||
|
||||
<Wizard service_slug={slug} steps={steps ?? ["entity", "review", "payment"]} title={meta?.name ?? slug} />
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const description = "Robocall Mitigation Database filing. Annual recertification
|
|||
<p class="pw-desc">{description}</p>
|
||||
</section>
|
||||
|
||||
<VerticalOrderHeader vertical="healthcare" />
|
||||
<VerticalOrderHeader vertical="telecom" />
|
||||
<Wizard service_slug={slug} steps={steps ?? ["entity", "review", "payment"]} title={meta?.name ?? slug} />
|
||||
</main>
|
||||
<script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue