feat(vps): rewrite Why Choose section + 5 page improvements

The old "Why Choose EZSCALE VPS?" grid was duplicating the
Included With All Plans card right below it (RAID 10, ZFS Snapshots,
VirtFusion Panel, Full Root Access appeared in both). Replaced with
6 actual differentiators that answer "why us, not Linode/Hetzner/
Vultr": real US humans, ZFS-backed reliability, predictable billing,
optional Pilot tier, 14-day money-back, owner-operated.

Other improvements bundled in this pass:
- Hero subtitle rewritten from feature dump to identity-first one-liner.
- Plans table → responsive PlanCard grid with a "Most popular" badge
  on VPS-4 and a separate "Storage-focused" subgrid for stor-500/1tb.
  Each card carries Order Now + Estimate-this-plan link to the
  estimator (still wired to the prefillEstimator handler).
- Added a 5-row "EZSCALE vs typical big-cloud" comparison table —
  honest differences only, no specific competitor names to avoid
  liability.
- Added an 8-question FAQ section (provisioning time, OS list,
  upgrades, refunds, DDoS roadmap, rDNS, location, migrations) using
  a new reusable Faq accordion component.
- Bottom CTA copy fixed (it was still mentioning DDoS protection as
  current; it's coming-soon upstream): now reads "Real humans on
  support. 14-day money-back if it's not the right fit." Heading
  flipped to "Ready to spin one up?" / "Talk to a Human."

New shared components: Components/Marketing/PlanCard.vue,
Components/Marketing/Faq.vue, Components/Marketing/ComparisonTable.vue.
All three are reusable for the dedicated/web/game pages later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 16:35:43 -04:00
parent 66a65263c3
commit 28a4ca8d32
4 changed files with 677 additions and 83 deletions

View File

@@ -6,6 +6,9 @@ import SectionHeader from '@/Components/Marketing/SectionHeader.vue'
import HeroSection from '@/Components/Marketing/HeroSection.vue'
import VpsHero from '@/Components/Marketing/VpsHero.vue'
import EstimatorSection from '@/Components/Marketing/Estimator/EstimatorSection.vue'
import PlanCard from '@/Components/Marketing/PlanCard.vue'
import ComparisonTable from '@/Components/Marketing/ComparisonTable.vue'
import Faq from '@/Components/Marketing/Faq.vue'
import { useEstimatorStore, type EstimatorPlan, type EstimatorAddOnGroup, type WorkloadEntry, type AppExample } from '@/stores/estimator'
import { crossDomainUrl } from '@/utils/resolvers'
@@ -19,7 +22,7 @@ interface PageProps {
domains: { marketing: string; account: string; admin: string }
}
interface Feature {
interface Differentiator {
icon: string
title: string
description: string
@@ -46,13 +49,51 @@ const startingPrice = computed<string>(() => {
return lowest % 1 === 0 ? lowest.toString() : lowest.toFixed(2)
})
const features: Feature[] = [
{ icon: 'tabler-database', title: 'RAID 10 SSD Storage', description: 'Redundant SSD arrays for fast read/write speeds and data protection.' },
{ icon: 'tabler-server', title: 'KVM Virtualization', description: 'Full hardware virtualization for predictable, dedicated performance.' },
{ icon: 'tabler-rocket', title: 'Near-Instant Provisioning', description: 'Your VPS is deployed seconds after ordering.' },
{ icon: 'tabler-refresh', title: 'Free ZFS Snapshots', description: 'Built-in snapshots for quick rollbacks. Off-site backup add-ons available.' },
{ icon: 'tabler-terminal', title: 'Full Root Access', description: 'Complete control over your server environment.' },
{ icon: 'tabler-server', title: 'VirtFusion Panel', description: 'Out-of-band console + VNC access included.' },
// Slug of the plan we want to flag as "Most popular" in the plan grid.
const popularSlug = 'vps-4'
// Plans split into standard VPS (1-32) and storage-focused tiers, so the grid
// reads as a clean ladder of one shape rather than a jumble.
const standardPlans = computed<EstimatorPlan[]>(() =>
plans.value.filter(p => !p.slug.startsWith('stor-')),
)
const storagePlans = computed<EstimatorPlan[]>(() =>
plans.value.filter(p => p.slug.startsWith('stor-')),
)
// Differentiators answer "why pick EZSCALE over Linode/Hetzner/Vultr" — distinct
// from the Included With All Plans card below which lists *what's in the box*.
const differentiators: Differentiator[] = [
{
icon: 'tabler-headset',
title: 'Real US-based humans',
description: 'Atlanta-based ops team. Tickets answered by engineers, not chatbots. Same hands fix the server and reply to your ticket.',
},
{
icon: 'tabler-history',
title: 'ZFS-backed reliability',
description: 'Free snapshots and instant rollbacks built into the storage layer — no bolt-on services to wire up or pay extra for.',
},
{
icon: 'tabler-receipt-2',
title: 'Predictable billing',
description: 'No surprise egress fees on fair-use traffic. Monthly, quarterly, or annual — your call. The price you see is the price you pay.',
},
{
icon: 'tabler-plane-tilt',
title: 'Optional Pilot tier',
description: 'Hand off the ops work at $99/mo. We run patching, monitoring, and deploys; you keep root access on a co-admin model.',
},
{
icon: 'tabler-shield-check',
title: '14-day money-back',
description: 'Try it for two weeks. If it isnt the right fit, we refund the first cycle, no questions asked.',
},
{
icon: 'tabler-users',
title: 'Owner-operated',
description: 'Small US team. No 3-tier outsourced support maze. Direct line to the people who built it and run it.',
},
]
const includedFeatures: IncludedItem[] = [
@@ -72,30 +113,74 @@ const includedFeatures: IncludedItem[] = [
{ text: 'DDoS protection', comingSoon: true },
]
// Keys from features JSON that should not be shown as table columns
const internalKeys = new Set([
'control_panel',
'os',
'ipv4',
'ipv6',
'tier',
])
const comparisonRows = [
{
feature: 'Free snapshots & rollbacks',
ours: 'ZFS-backed, included',
theirs: '$0.05/GB add-on',
},
{
feature: 'Talk to engineers, not chatbots',
ours: 'Direct line to ops team',
theirs: 'Tier-1 outsourced',
},
{
feature: 'Predictable monthly bill',
ours: 'No egress fees on fair-use',
theirs: 'Per-GB egress overages',
},
{
feature: 'Optional managed (you keep root)',
ours: 'Pilot tier at $99/mo',
theirs: 'Often $200+/mo or unavailable',
},
{
feature: 'Money-back guarantee',
ours: '14 days, no questions',
theirs: '7 days or none',
},
]
function getFeature(plan: EstimatorPlan, key: string): string {
return String(plan.features?.[key] ?? '-')
}
function formatPrice(plan: EstimatorPlan): string {
const price = parseFloat(plan.price) || 0
return price % 1 === 0 ? `$${price}` : `$${price.toFixed(2)}`
}
const faqs = [
{
question: 'How quickly is my VPS provisioned?',
answer: 'Within seconds of completing checkout. Youll get the root password (or your SSH key gets installed) on the same screen, and the VPS is bootable immediately. No queue, no manual approval.',
},
{
question: 'Which operating systems can I install?',
answer: 'Ubuntu (20.04, 22.04, 24.04), Debian (11, 12), Rocky Linux 9, AlmaLinux 9, CentOS Stream, and Windows Server 2019/2022 with your own license. Custom ISO uploads are supported via the VirtFusion panel.',
},
{
question: 'Can I upgrade or downgrade my plan later?',
answer: 'Yes. Upgrades resize on the next reboot; downgrades happen at the next billing cycle. Resizes preserve your data and IP. <a href="/contact">Contact us</a> if you need a custom build between tiers.',
},
{
question: 'Whats your refund policy?',
answer: '14-day money-back guarantee on the first cycle, no questions asked. After that, refunds are pro-rated against unused time on annual plans.',
},
{
question: 'Do you offer DDoS protection?',
answer: 'Volumetric DDoS protection is on the roadmap and will be free for all VPS plans when it ships. In the meantime, the network has standard upstream filtering and you can request null-routing of attack targets via support.',
},
{
question: 'Can I get rDNS / PTR control on my IPv4?',
answer: 'Yes — every VPS plan includes self-service rDNS / PTR record control through the VirtFusion panel. Useful if youre running a mail server or anything that needs forward-confirmed DNS.',
},
{
question: 'Where are the servers physically located?',
answer: 'All VPS plans are hosted in our Atlanta, GA datacenter. EU and West Coast regions are on the roadmap.',
},
{
question: 'Do you help with migrations from another provider?',
answer: 'Yes. <a href="/contact">Open a ticket before you order</a> and well help size the right plan and transfer your existing setup. Pilot-tier customers get migration assistance included; Self/Basic/Pro plans get free guidance, paid hands-on labor.',
},
]
function prefillEstimator(planId: number): void {
estimator.setPlan(planId)
if (typeof window !== 'undefined') {
const url = estimator.shareUrl
window.history.replaceState({}, '', url)
// Smooth-scroll up to the estimator
const el = document.querySelector('.estimator-section')
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
@@ -113,7 +198,7 @@ function prefillEstimator(planId: number): void {
Virtual Private <span class="hero-gradient-text">Servers</span>
</h1>
<p class="text-h6 text-medium-emphasis font-weight-regular mb-4" style="max-width: 600px;">
High-performance VPS hosting with RAID 10 SSD storage, dedicated resources, and full root access from our Atlanta, GA datacenter.
Atlanta-based VPS hosting from a small team that picks up the phone. ZFS-backed reliability, predictable billing, and real root access.
</p>
<p class="text-body-1 text-medium-emphasis mb-8">
Starting at just <span class="text-primary font-weight-bold">${{ startingPrice }}/mo</span>
@@ -140,82 +225,82 @@ function prefillEstimator(planId: number): void {
:account-url="accountUrl"
/>
<!-- Features -->
<!-- Differentiators -->
<VContainer class="marketing-section">
<SectionHeader
label="Features"
title="Why Choose EZSCALE VPS?"
highlight-word="EZSCALE"
subtitle="Enterprise-grade infrastructure with the simplicity you deserve."
label="Why EZSCALE"
title="Built different on purpose"
highlight-word="different"
subtitle="Six things that separate us from the multi-region mega-clouds — and from the race-to-the-bottom budget hosts."
/>
<VRow>
<VCol v-for="(feature, index) in features" :key="feature.title" cols="12" sm="6" md="4">
<VCol
v-for="(item, index) in differentiators"
:key="item.title"
cols="12"
sm="6"
md="4"
>
<div :class="['d-flex ga-3 mb-4 feature-card-hover pa-3 rounded-lg', `fade-in-up stagger-${index + 1}`]">
<VAvatar color="primary" variant="tonal" size="44">
<VIcon :icon="feature.icon" size="22" />
<VIcon :icon="item.icon" size="22" />
</VAvatar>
<div>
<h3 class="text-subtitle-1 font-weight-bold">{{ feature.title }}</h3>
<p class="text-body-2 text-medium-emphasis mb-0">{{ feature.description }}</p>
<h3 class="text-subtitle-1 font-weight-bold">{{ item.title }}</h3>
<p class="text-body-2 text-medium-emphasis mb-0">{{ item.description }}</p>
</div>
</div>
</VCol>
</VRow>
</VContainer>
<!-- Plans Table -->
<!-- Plans grid -->
<div class="section-alt-bg marketing-section">
<VContainer>
<SectionHeader
label="Pricing"
title="VPS Plans"
highlight-word="Plans"
subtitle="All plans hosted in our Atlanta, GA datacenter. DDoS protection, full root access, and VirtFusion panel included."
subtitle="All plans hosted in our Atlanta, GA datacenter. Free ZFS snapshots, full root access, and the VirtFusion panel are included on every tier."
/>
<VCard class="feature-card-hover">
<div style="overflow-x: auto; -webkit-overflow-scrolling: touch;">
<VTable>
<thead>
<tr>
<th>Plan</th>
<th>CPU</th>
<th>RAM</th>
<th>Storage</th>
<th>Bandwidth</th>
<th>Price</th>
<th />
</tr>
</thead>
<tbody>
<tr v-for="plan in plans" :key="plan.id">
<td class="font-weight-bold">{{ plan.name }}</td>
<td>{{ getFeature(plan, 'cpu') }}</td>
<td>{{ getFeature(plan, 'ram') }}</td>
<td>{{ getFeature(plan, 'storage') }}</td>
<td>{{ getFeature(plan, 'bandwidth') }}</td>
<td class="text-primary font-weight-bold">{{ formatPrice(plan) }}/mo</td>
<td>
<div class="d-flex align-center ga-2">
<a :href="accountUrl + '/checkout/' + plan.id" class="text-decoration-none">
<VBtn color="primary" size="small" variant="tonal">Order Now</VBtn>
</a>
<VBtn
size="small"
variant="text"
color="primary"
class="text-caption"
@click="prefillEstimator(plan.id)"
>
Estimate
</VBtn>
</div>
</td>
</tr>
</tbody>
</VTable>
</div>
</VCard>
<h3 class="text-h6 font-weight-bold mb-4 plan-grid-heading">Standard VPS</h3>
<VRow class="mb-8">
<VCol
v-for="plan in standardPlans"
:key="plan.id"
cols="12"
sm="6"
md="4"
lg="3"
>
<PlanCard
:plan="plan"
:popular="plan.slug === popularSlug"
:order-url="accountUrl + '/checkout/' + plan.id"
@estimate="prefillEstimator"
/>
</VCol>
</VRow>
<h3 v-if="storagePlans.length" class="text-h6 font-weight-bold mb-4 plan-grid-heading">Storage-focused</h3>
<VRow v-if="storagePlans.length" class="mb-4">
<VCol
v-for="plan in storagePlans"
:key="plan.id"
cols="12"
sm="6"
md="4"
lg="3"
>
<PlanCard
:plan="plan"
:order-url="accountUrl + '/checkout/' + plan.id"
variant="storage"
@estimate="prefillEstimator"
/>
</VCol>
</VRow>
<!-- Included with all plans -->
<VCard class="mt-8 pa-6 feature-card-hover">
@@ -252,12 +337,36 @@ function prefillEstimator(planId: number): void {
</VContainer>
</div>
<!-- Comparison -->
<VContainer class="marketing-section">
<SectionHeader
label="Comparison"
title="EZSCALE vs the big-cloud playbook"
highlight-word="EZSCALE"
subtitle="A few honest differences — the kind that show up on a bill at the end of the month."
/>
<ComparisonTable :rows="comparisonRows" />
</VContainer>
<!-- FAQ -->
<div class="section-alt-bg marketing-section">
<VContainer>
<SectionHeader
label="FAQ"
title="Common questions"
highlight-word="questions"
subtitle="The stuff people ask before signing up. Don't see yours? Hit us at /contact."
/>
<Faq :items="faqs" />
</VContainer>
</div>
<!-- CTA -->
<div class="marketing-section" style="background: linear-gradient(135deg, rgb(var(--v-theme-primary), 0.12), rgb(var(--v-theme-surface)));">
<VContainer class="text-center">
<h2 class="text-h4 font-weight-bold mb-3">Ready to Get Started?</h2>
<h2 class="text-h4 font-weight-bold mb-3">Ready to spin one up?</h2>
<p class="text-body-1 text-medium-emphasis mb-6">
Deploy your VPS in seconds with full root access, DDoS protection, and expert support.
Deploy in seconds. Real humans on support. 14-day money-back if it's not the right fit.
</p>
<div class="d-flex ga-3 justify-center flex-wrap">
<a :href="plans.length ? accountUrl + '/checkout/' + plans[0].id : '/pricing'" class="text-decoration-none">
@@ -268,7 +377,7 @@ function prefillEstimator(planId: number): void {
</a>
<a href="/contact" class="text-decoration-none">
<VBtn color="primary" variant="outlined" size="large" rounded="lg">
Contact Sales
Talk to a Human
</VBtn>
</a>
</div>
@@ -276,3 +385,10 @@ function prefillEstimator(planId: number): void {
</div>
</div>
</template>
<style lang="scss" scoped>
.plan-grid-heading {
letter-spacing: -0.01em;
color: rgba(var(--v-theme-on-surface), 0.85);
}
</style>