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

@@ -0,0 +1,116 @@
<script lang="ts" setup>
interface ComparisonRow {
feature: string
ours: string
theirs: string
oursIsWin?: boolean
}
interface Props {
rows: ComparisonRow[]
ourLabel?: string
theirLabel?: string
}
withDefaults(defineProps<Props>(), {
ourLabel: 'EZSCALE',
theirLabel: 'Typical big-cloud VPS',
})
</script>
<template>
<div class="comparison">
<div class="comparison__head" role="rowheader">
<div class="comparison__cell comparison__cell--feature">What you get</div>
<div class="comparison__cell comparison__cell--ours">{{ ourLabel }}</div>
<div class="comparison__cell comparison__cell--theirs">{{ theirLabel }}</div>
</div>
<div
v-for="(row, idx) in rows"
:key="idx"
class="comparison__row"
>
<div class="comparison__cell comparison__cell--feature">{{ row.feature }}</div>
<div class="comparison__cell comparison__cell--ours">
<VIcon icon="tabler-check" size="18" color="success" class="me-2" />
<span>{{ row.ours }}</span>
</div>
<div class="comparison__cell comparison__cell--theirs">
<VIcon icon="tabler-x" size="18" color="error" class="me-2" />
<span>{{ row.theirs }}</span>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.comparison {
max-width: 980px;
margin: 0 auto;
border-radius: 16px;
overflow: hidden;
background: rgba(var(--v-theme-surface), 0.6);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
}
.comparison__head {
display: grid;
grid-template-columns: 1.4fr 1fr 1fr;
gap: 0;
background: rgba(var(--v-theme-on-surface), 0.04);
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.08);
.comparison__cell {
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: rgba(var(--v-theme-on-surface), 0.6);
}
.comparison__cell--ours {
color: rgb(var(--v-theme-primary));
}
}
.comparison__row {
display: grid;
grid-template-columns: 1.4fr 1fr 1fr;
gap: 0;
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.05);
&:last-child {
border-bottom: none;
}
}
.comparison__cell {
display: flex;
align-items: center;
padding: 16px 22px;
font-size: 14px;
color: rgba(var(--v-theme-on-surface), 0.85);
@media (max-width: 600px) {
padding: 12px 14px;
font-size: 13px;
}
}
.comparison__cell--feature {
font-weight: 600;
color: rgba(var(--v-theme-on-surface), 0.95);
border-right: 1px solid rgba(var(--v-theme-on-surface), 0.05);
}
.comparison__cell--ours {
background: rgba(var(--v-theme-primary), 0.05);
border-right: 1px solid rgba(var(--v-theme-on-surface), 0.05);
}
.comparison__cell--theirs {
color: rgba(var(--v-theme-on-surface), 0.6);
}
</style>

View File

@@ -0,0 +1,127 @@
<script lang="ts" setup>
import { ref } from 'vue'
interface FaqItem {
question: string
answer: string
}
interface Props {
items: FaqItem[]
}
defineProps<Props>()
const open = ref<number | null>(null)
function toggle(idx: number): void {
open.value = open.value === idx ? null : idx
}
</script>
<template>
<div class="faq">
<div
v-for="(item, idx) in items"
:key="idx"
class="faq__item"
:class="{ 'faq__item--open': open === idx }"
>
<button
type="button"
class="faq__question"
:aria-expanded="open === idx"
@click="toggle(idx)"
>
<span class="faq__question-text">{{ item.question }}</span>
<VIcon
:icon="open === idx ? 'tabler-minus' : 'tabler-plus'"
size="20"
class="faq__icon"
/>
</button>
<VExpandTransition>
<div v-show="open === idx" class="faq__answer">
<div class="faq__answer-text" v-html="item.answer" />
</div>
</VExpandTransition>
</div>
</div>
</template>
<style lang="scss" scoped>
.faq {
display: flex;
flex-direction: column;
gap: 10px;
max-width: 820px;
margin: 0 auto;
}
.faq__item {
border-radius: 14px;
background: rgba(var(--v-theme-surface), 0.6);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
transition: border-color 0.15s ease;
&:hover {
border-color: rgba(var(--v-theme-on-surface), 0.14);
}
}
.faq__item--open {
border-color: rgba(var(--v-theme-primary), 0.4);
}
.faq__question {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 18px 22px;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
color: rgba(var(--v-theme-on-surface), 0.95);
font-size: 15px;
font-weight: 600;
}
.faq__question-text {
flex-grow: 1;
}
.faq__icon {
color: rgb(var(--v-theme-primary));
flex-shrink: 0;
}
.faq__answer {
padding: 0 22px 18px;
}
.faq__answer-text {
color: rgba(var(--v-theme-on-surface), 0.7);
font-size: 14px;
line-height: 1.6;
:deep(a) {
color: rgb(var(--v-theme-primary));
text-decoration: none;
border-bottom: 1px dotted rgba(var(--v-theme-primary), 0.5);
&:hover {
border-bottom-style: solid;
}
}
:deep(strong) {
color: rgba(var(--v-theme-on-surface), 0.92);
font-weight: 600;
}
}
</style>

View File

@@ -0,0 +1,235 @@
<script lang="ts" setup>
import { computed } from 'vue'
interface PlanFeatures {
cpu?: string | number
ram?: string | number
storage?: string | number
bandwidth?: string | number
[key: string]: string | number | undefined
}
interface Plan {
id: number
name: string
slug: string
price: string
features: PlanFeatures | null
}
interface Props {
plan: Plan
popular?: boolean
orderUrl: string
variant?: 'standard' | 'storage'
}
const props = withDefaults(defineProps<Props>(), {
popular: false,
variant: 'standard',
})
const emit = defineEmits<{
estimate: [planId: number]
}>()
const formattedPrice = computed<string>(() => {
const price = parseFloat(props.plan.price) || 0
return price % 1 === 0 ? `${price}` : price.toFixed(2)
})
const cpu = computed<string>(() => String(props.plan.features?.cpu ?? '—'))
const ram = computed<string>(() => String(props.plan.features?.ram ?? '—'))
const storage = computed<string>(() => String(props.plan.features?.storage ?? '—'))
const bandwidth = computed<string>(() => String(props.plan.features?.bandwidth ?? 'Unmetered'))
</script>
<template>
<div
class="plan-card"
:class="{ 'plan-card--popular': popular, 'plan-card--storage': variant === 'storage' }"
>
<div v-if="popular" class="plan-card__badge">Most popular</div>
<div class="plan-card__head">
<div class="plan-card__name">{{ plan.name }}</div>
<div class="plan-card__price-row">
<span class="plan-card__currency">$</span>
<span class="plan-card__price">{{ formattedPrice }}</span>
<span class="plan-card__period">/mo</span>
</div>
</div>
<div class="plan-card__specs">
<div class="plan-card__spec">
<VIcon icon="tabler-cpu" size="18" />
<span>{{ cpu }}</span>
</div>
<div class="plan-card__spec">
<VIcon icon="tabler-device-sd-card" size="18" />
<span>{{ ram }} RAM</span>
</div>
<div class="plan-card__spec">
<VIcon icon="tabler-database" size="18" />
<span>{{ storage }}</span>
</div>
<div class="plan-card__spec">
<VIcon icon="tabler-arrows-right-left" size="18" />
<span>{{ bandwidth }}</span>
</div>
</div>
<div class="plan-card__actions">
<a :href="orderUrl" class="text-decoration-none">
<VBtn block color="primary" size="default" rounded="lg">
Order Now
<VIcon icon="tabler-arrow-right" end size="small" />
</VBtn>
</a>
<button
type="button"
class="plan-card__estimate"
@click="emit('estimate', plan.id)"
>
Estimate this plan
</button>
</div>
</div>
</template>
<style lang="scss" scoped>
.plan-card {
position: relative;
display: flex;
flex-direction: column;
padding: 24px 22px 22px;
border-radius: 16px;
background: rgba(var(--v-theme-surface), 0.6);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
&:hover {
transform: translateY(-3px);
border-color: rgba(var(--v-theme-primary), 0.4);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
}
}
.plan-card--popular {
border-color: rgba(var(--v-theme-primary), 0.5);
background: linear-gradient(180deg, rgba(var(--v-theme-primary), 0.10), rgba(var(--v-theme-surface), 0.6));
box-shadow: 0 8px 28px rgba(var(--v-theme-primary), 0.18);
}
.plan-card--storage {
border-color: rgba(var(--v-theme-success), 0.25);
&:hover {
border-color: rgba(var(--v-theme-success), 0.5);
}
}
.plan-card__badge {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
padding: 4px 14px;
border-radius: 999px;
background: rgb(var(--v-theme-primary));
color: rgb(var(--v-theme-on-primary));
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(var(--v-theme-primary), 0.4);
}
.plan-card__head {
margin-bottom: 18px;
}
.plan-card__name {
font-size: 18px;
font-weight: 700;
letter-spacing: -0.01em;
color: rgba(var(--v-theme-on-surface), 0.95);
margin-bottom: 6px;
}
.plan-card__price-row {
display: flex;
align-items: baseline;
gap: 2px;
color: rgb(var(--v-theme-primary));
}
.plan-card__currency {
font-size: 20px;
font-weight: 600;
}
.plan-card__price {
font-size: 38px;
font-weight: 800;
line-height: 1;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
}
.plan-card__period {
font-size: 14px;
font-weight: 500;
color: rgba(var(--v-theme-on-surface), 0.6);
margin-left: 4px;
}
.plan-card__specs {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 22px;
padding: 14px 0;
border-top: 1px solid rgba(var(--v-theme-on-surface), 0.06);
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.06);
}
.plan-card__spec {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
color: rgba(var(--v-theme-on-surface), 0.85);
.v-icon {
color: rgba(var(--v-theme-on-surface), 0.55);
flex-shrink: 0;
}
}
.plan-card__actions {
margin-top: auto;
display: flex;
flex-direction: column;
gap: 10px;
}
.plan-card__estimate {
background: transparent;
border: none;
color: rgb(var(--v-theme-primary));
font-size: 13px;
font-weight: 500;
cursor: pointer;
padding: 6px;
border-radius: 6px;
transition: background 0.15s ease;
&:hover {
background: rgba(var(--v-theme-primary), 0.06);
}
}
</style>

View File

@@ -6,6 +6,9 @@ import SectionHeader from '@/Components/Marketing/SectionHeader.vue'
import HeroSection from '@/Components/Marketing/HeroSection.vue' import HeroSection from '@/Components/Marketing/HeroSection.vue'
import VpsHero from '@/Components/Marketing/VpsHero.vue' import VpsHero from '@/Components/Marketing/VpsHero.vue'
import EstimatorSection from '@/Components/Marketing/Estimator/EstimatorSection.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 { useEstimatorStore, type EstimatorPlan, type EstimatorAddOnGroup, type WorkloadEntry, type AppExample } from '@/stores/estimator'
import { crossDomainUrl } from '@/utils/resolvers' import { crossDomainUrl } from '@/utils/resolvers'
@@ -19,7 +22,7 @@ interface PageProps {
domains: { marketing: string; account: string; admin: string } domains: { marketing: string; account: string; admin: string }
} }
interface Feature { interface Differentiator {
icon: string icon: string
title: string title: string
description: string description: string
@@ -46,13 +49,51 @@ const startingPrice = computed<string>(() => {
return lowest % 1 === 0 ? lowest.toString() : lowest.toFixed(2) return lowest % 1 === 0 ? lowest.toString() : lowest.toFixed(2)
}) })
const features: Feature[] = [ // Slug of the plan we want to flag as "Most popular" in the plan grid.
{ icon: 'tabler-database', title: 'RAID 10 SSD Storage', description: 'Redundant SSD arrays for fast read/write speeds and data protection.' }, const popularSlug = 'vps-4'
{ 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.' }, // Plans split into standard VPS (1-32) and storage-focused tiers, so the grid
{ icon: 'tabler-refresh', title: 'Free ZFS Snapshots', description: 'Built-in snapshots for quick rollbacks. Off-site backup add-ons available.' }, // reads as a clean ladder of one shape rather than a jumble.
{ icon: 'tabler-terminal', title: 'Full Root Access', description: 'Complete control over your server environment.' }, const standardPlans = computed<EstimatorPlan[]>(() =>
{ icon: 'tabler-server', title: 'VirtFusion Panel', description: 'Out-of-band console + VNC access included.' }, 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[] = [ const includedFeatures: IncludedItem[] = [
@@ -72,30 +113,74 @@ const includedFeatures: IncludedItem[] = [
{ text: 'DDoS protection', comingSoon: true }, { text: 'DDoS protection', comingSoon: true },
] ]
// Keys from features JSON that should not be shown as table columns const comparisonRows = [
const internalKeys = new Set([ {
'control_panel', feature: 'Free snapshots & rollbacks',
'os', ours: 'ZFS-backed, included',
'ipv4', theirs: '$0.05/GB add-on',
'ipv6', },
'tier', {
]) 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 { const faqs = [
return String(plan.features?.[key] ?? '-') {
} 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.',
function formatPrice(plan: EstimatorPlan): string { },
const price = parseFloat(plan.price) || 0 {
return price % 1 === 0 ? `$${price}` : `$${price.toFixed(2)}` 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 { function prefillEstimator(planId: number): void {
estimator.setPlan(planId) estimator.setPlan(planId)
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const url = estimator.shareUrl const url = estimator.shareUrl
window.history.replaceState({}, '', url) window.history.replaceState({}, '', url)
// Smooth-scroll up to the estimator
const el = document.querySelector('.estimator-section') const el = document.querySelector('.estimator-section')
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }) 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> Virtual Private <span class="hero-gradient-text">Servers</span>
</h1> </h1>
<p class="text-h6 text-medium-emphasis font-weight-regular mb-4" style="max-width: 600px;"> <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>
<p class="text-body-1 text-medium-emphasis mb-8"> <p class="text-body-1 text-medium-emphasis mb-8">
Starting at just <span class="text-primary font-weight-bold">${{ startingPrice }}/mo</span> 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" :account-url="accountUrl"
/> />
<!-- Features --> <!-- Differentiators -->
<VContainer class="marketing-section"> <VContainer class="marketing-section">
<SectionHeader <SectionHeader
label="Features" label="Why EZSCALE"
title="Why Choose EZSCALE VPS?" title="Built different on purpose"
highlight-word="EZSCALE" highlight-word="different"
subtitle="Enterprise-grade infrastructure with the simplicity you deserve." subtitle="Six things that separate us from the multi-region mega-clouds — and from the race-to-the-bottom budget hosts."
/> />
<VRow> <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}`]"> <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"> <VAvatar color="primary" variant="tonal" size="44">
<VIcon :icon="feature.icon" size="22" /> <VIcon :icon="item.icon" size="22" />
</VAvatar> </VAvatar>
<div> <div>
<h3 class="text-subtitle-1 font-weight-bold">{{ feature.title }}</h3> <h3 class="text-subtitle-1 font-weight-bold">{{ item.title }}</h3>
<p class="text-body-2 text-medium-emphasis mb-0">{{ feature.description }}</p> <p class="text-body-2 text-medium-emphasis mb-0">{{ item.description }}</p>
</div> </div>
</div> </div>
</VCol> </VCol>
</VRow> </VRow>
</VContainer> </VContainer>
<!-- Plans Table --> <!-- Plans grid -->
<div class="section-alt-bg marketing-section"> <div class="section-alt-bg marketing-section">
<VContainer> <VContainer>
<SectionHeader <SectionHeader
label="Pricing" label="Pricing"
title="VPS Plans" title="VPS Plans"
highlight-word="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"> <h3 class="text-h6 font-weight-bold mb-4 plan-grid-heading">Standard VPS</h3>
<div style="overflow-x: auto; -webkit-overflow-scrolling: touch;"> <VRow class="mb-8">
<VTable> <VCol
<thead> v-for="plan in standardPlans"
<tr> :key="plan.id"
<th>Plan</th> cols="12"
<th>CPU</th> sm="6"
<th>RAM</th> md="4"
<th>Storage</th> lg="3"
<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 <PlanCard
</VBtn> :plan="plan"
</div> :popular="plan.slug === popularSlug"
</td> :order-url="accountUrl + '/checkout/' + plan.id"
</tr> @estimate="prefillEstimator"
</tbody> />
</VTable> </VCol>
</div> </VRow>
</VCard>
<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 --> <!-- Included with all plans -->
<VCard class="mt-8 pa-6 feature-card-hover"> <VCard class="mt-8 pa-6 feature-card-hover">
@@ -252,12 +337,36 @@ function prefillEstimator(planId: number): void {
</VContainer> </VContainer>
</div> </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 --> <!-- CTA -->
<div class="marketing-section" style="background: linear-gradient(135deg, rgb(var(--v-theme-primary), 0.12), rgb(var(--v-theme-surface)));"> <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"> <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"> <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> </p>
<div class="d-flex ga-3 justify-center flex-wrap"> <div class="d-flex ga-3 justify-center flex-wrap">
<a :href="plans.length ? accountUrl + '/checkout/' + plans[0].id : '/pricing'" class="text-decoration-none"> <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>
<a href="/contact" class="text-decoration-none"> <a href="/contact" class="text-decoration-none">
<VBtn color="primary" variant="outlined" size="large" rounded="lg"> <VBtn color="primary" variant="outlined" size="large" rounded="lg">
Contact Sales Talk to a Human
</VBtn> </VBtn>
</a> </a>
</div> </div>
@@ -276,3 +385,10 @@ function prefillEstimator(planId: number): void {
</div> </div>
</div> </div>
</template> </template>
<style lang="scss" scoped>
.plan-grid-heading {
letter-spacing: -0.01em;
color: rgba(var(--v-theme-on-surface), 0.85);
}
</style>