refactor: switch to free-trial/private-deploy/buyout pricing, fix feature gaps, add SEO landing + deploy config

This commit is contained in:
TradeMate Dev
2026-07-12 08:09:28 +08:00
parent 9ca5d79d8a
commit 04924e3bc4
36 changed files with 1026 additions and 975 deletions
+2
View File
@@ -135,6 +135,8 @@ export function subscribeCreditPlan(planId, payType = 'alipay') {
}
export function cancelCreditSubscription() { return http.post('/credits/cancel-subscription') }
export function submitLead(data) { return http.post('/leads', data) }
export function startAgentPipeline(data) { return http.post('/agent/start', data, { timeout: 300000 }) }
export function listAgentPipelines(params) { return http.get('/agent/pipelines', { params }) }
export function getAgentPipeline(id) { return http.get(`/agent/${id}`) }
+21 -121
View File
@@ -9,51 +9,15 @@
<button class="modal-x" @click="close">&times;</button>
</div>
<div class="modal-plans" v-loading="loading">
<div
v-for="p in displayPlans"
:key="p.id"
class="plan-card"
:class="{ featured: p.featured, current: p.isCurrent }"
>
<div v-if="p.badge" class="plan-badge">{{ p.badge }}</div>
<div class="plan-name">{{ p.name }}</div>
<div class="plan-name-en">{{ p.name_en }}</div>
<div class="plan-price">
<template v-if="p.price > 0">
¥{{ p.price }}<small>/</small>
</template>
<span v-else class="plan-free">免费</span>
</div>
<div v-if="p.credits" class="plan-credits">{{ p.credits }} <small>积分/</small></div>
<ul class="plan-features">
<li v-for="f in p.features" :key="f">{{ f }}</li>
</ul>
<el-button
v-if="p.isCurrent"
type="default"
disabled
class="plan-btn"
>当前套餐</el-button>
<el-button
v-else-if="p.price === 0"
type="default"
disabled
class="plan-btn"
>当前套餐</el-button>
<el-button
v-else
type="primary"
class="plan-btn"
:loading="payingId === p.id"
@click="handleUpgrade(p)"
>升级</el-button>
</div>
<div class="modal-summary">
<div class="summary-row"><strong>免费试用</strong><span>30 积分 + 每日 1000 字翻译零门槛体验</span></div>
<div class="summary-row"><strong>私有化部署</strong><span>独立部署数据私有不限账号年付授权</span></div>
<div class="summary-row"><strong>买断源码</strong><span>完整源码可二次开发永久授权</span></div>
</div>
<div class="modal-foot">
<span class="hint">订阅后网页端浏览器插件Skills 通用</span>
<el-button text size="small" @click="goCreditsPage">购买积分包低至 ¥2.9</el-button>
<span class="hint">网页端浏览器插件Skills 通用</span>
<el-button type="primary" size="small" @click="goUpgradePage">查看完整方案</el-button>
</div>
</div>
</div>
@@ -62,10 +26,8 @@
</template>
<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getSubscriptionPlans, subscribeCreditPlan } from '@/api'
const props = defineProps({
visible: { type: Boolean, default: false },
@@ -76,93 +38,26 @@ const props = defineProps({
const emit = defineEmits(['update:visible'])
const router = useRouter()
const loading = ref(false)
const plans = ref([])
const currentPlanId = ref(null)
const payingId = ref(null)
const FEATURES_FALLBACK = {
'free': { name: 'Free', name_en: 'Free', credits: 30, price: 0, features: ['30 积分(一次性)', '每日 1000 字免费翻译', '基本功能体验'], badge: '' },
'starter': { name: 'Starter', name_en: 'Starter', credits: 200, price: 9.9, features: ['200 积分/月', '无每日限制', '翻译 + 客户发现 + 营销'], badge: '入门' },
'pro': { name: 'Professional', name_en: 'Professional', credits: 1000, price: 49, features: ['1000 积分/月', 'AI 数字员工', '团队协作(3 人)', '优先支持'], badge: '推荐' },
'enterprise': { name: 'Enterprise', name_en: 'Enterprise', credits: 2500, price: 99, features: ['2500 积分/月', '不限团队人数', 'API 调用权限', 'SLA 保障'], badge: '旗舰' },
}
const displayPlans = computed(() => {
if (plans.value.length) {
return plans.value.map(p => ({
...p,
isCurrent: p.id === currentPlanId.value,
featured: p.credits_per_month >= 500 && p.credits_per_month < 2000,
badge: p.credits_per_month >= 500 && p.credits_per_month < 2000 ? '推荐' : '',
}))
}
// Fallback display when API not loaded yet
return Object.entries(FEATURES_FALLBACK).map(([key, v]) => ({
id: key,
...v,
isCurrent: key === 'free',
featured: key === 'pro',
badge: key === 'pro' ? '推荐' : (key === 'enterprise' ? '旗舰' : ''),
}))
})
async function loadPlans() {
loading.value = true
try {
const res = await getSubscriptionPlans()
plans.value = Array.isArray(res) ? res : (res.data || res.items || res.plans || [])
} catch { /* fallback to hardcoded */ }
loading.value = false
}
async function handleUpgrade(plan) {
payingId.value = plan.id
// For Free/current, no action
if (plan.price === 0 || plan.isCurrent) {
payingId.value = null
return
}
try {
// Use credits subscribe endpoint
const res = await subscribeCreditPlan(plan.id, 'alipay')
if (res.pay_url) {
window.open(res.pay_url, '_blank')
} else {
ElMessage.success('订阅成功!')
}
close()
} catch (e) {
const detail = e?.detail || e?.message || '订阅失败'
ElMessage.error(detail)
}
payingId.value = null
}
function goCreditsPage() {
close()
router.push('/workspace/profile/credits')
}
function close() {
emit('update:visible', false)
}
function goUpgradePage() {
close()
router.push('/workspace/upgrade')
}
// Listen for global upgrade event (from 402 interceptor or other components)
function onUpgradeEvent(e) {
// Don't auto-show if we're on the credits page already
if (router.currentRoute?.value?.path?.includes('/credits')) return
if (router.currentRoute?.value?.path?.includes('/upgrade')) return
emit('update:visible', true)
}
watch(() => props.visible, (v) => {
if (v) loadPlans()
})
watch(() => props.visible, () => {})
onMounted(() => {
window.addEventListener('trademate:upgrade', onUpgradeEvent)
if (props.visible) loadPlans()
})
onUnmounted(() => {
@@ -195,10 +90,15 @@ onUnmounted(() => {
background: none; border: none; font-size: 24px; color: #94a3b8; cursor: pointer;
}
.modal-x:hover { color: #64748b; }
.modal-plans {
padding: 20px 24px; display: flex; gap: 12px;
min-height: 260px;
.modal-summary {
padding: 16px 24px; display: flex; flex-direction: column; gap: 10px;
}
.summary-row {
display: flex; justify-content: space-between; gap: 12px;
font-size: 13px; color: #475569; padding: 8px 12px;
background: #f8faff; border-radius: 8px;
}
.summary-row strong { color: #1e293b; white-space: nowrap; }
.plan-card {
flex: 1; border: 1px solid #e5e7eb; border-radius: 12px;
padding: 16px; text-align: center; position: relative;
+1
View File
@@ -22,6 +22,7 @@
@select="showMobileMenu = false"
>
<el-menu-item index="/workspace"><el-icon><Odometer /></el-icon><span>{{ $t('nav.home') }}</span></el-menu-item>
<el-menu-item index="/workspace/agent"><el-icon><MagicStick /></el-icon><span>{{ $t('nav.agent') || 'AI数字员工' }}</span></el-menu-item>
<el-menu-item index="/workspace/customers"><el-icon><User /></el-icon><span>{{ $t('nav.customers') }}</span></el-menu-item>
<el-menu-item index="/workspace/biz"><el-icon><Goods /></el-icon><span>{{ $t('nav.biz') }}</span></el-menu-item>
<el-menu-item index="/workspace/analytics"><el-icon><DataAnalysis /></el-icon><span>{{ $t('nav.analytics') }}</span></el-menu-item>
+2 -1
View File
@@ -13,6 +13,7 @@ const routes = [
{ path: 'customers', name: 'Customers', component: () => import('@/views/WorkspaceCustomer.vue'), meta: { title: '客户工作台' } },
{ path: 'biz', name: 'Biz', component: () => import('@/views/WorkspaceBiz.vue'), meta: { title: '业务工作台' } },
{ path: 'analytics', name: 'Analytics', component: () => import('@/views/Analytics.vue'), meta: { title: '数据分析' } },
{ path: 'agent', name: 'Agent', component: () => import('@/views/Agent.vue'), meta: { title: 'AI数字员工' } },
{ path: 'team', name: 'Team', component: () => import('@/views/Team.vue'), meta: { title: '团队协作' } },
{ path: 'profile', name: 'Profile', component: () => import('@/views/Profile.vue'), meta: { title: '个人中心' } },
{ path: 'profile/credits', name: 'Credits', component: () => import('@/views/Credits.vue'), meta: { title: '购买次数' } },
@@ -35,7 +36,7 @@ const routes = [
{ path: '/invoice', redirect: '/workspace/profile/invoice' },
{ path: '/notifications', redirect: '/workspace/profile/notifications' },
{ path: '/feedback', redirect: '/workspace/profile/feedback' },
{ path: '/agent', redirect: '/workspace' },
{ path: '/agent', redirect: '/workspace/agent' },
{ path: '/discovery', redirect: '/workspace/customers' },
{ path: '/followup', redirect: '/workspace/customers' },
{ path: '/marketing', redirect: '/workspace/biz' },
+96 -239
View File
@@ -1,254 +1,134 @@
<template>
<div class="upgrade-page">
<div class="page-head">
<h1>选择适合你的套餐</h1>
<p class="page-sub">订阅后所有产品线通用 网页工作台浏览器插件Agent Skills</p>
<div class="billing-toggle">
<el-radio-group v-model="billingPeriod" size="small">
<el-radio-button value="monthly">月付</el-radio-button>
<el-radio-button value="yearly">年付 <span class="save-tag" v-if="billingPeriod === 'yearly'"> 2 个月</span></el-radio-button>
</el-radio-group>
</div>
<h1>选择适合你的方案</h1>
<p class="page-sub">TradeMate 提供免费试用私有化部署与源码买断三种方式按需选择</p>
</div>
<div class="plans-grid" v-loading="loading">
<!-- Free Tier -->
<div class="plan-card" :class="{ current: currentPlan === 'free' }">
<div class="plan-name">Free</div>
<div class="plan-price free">免费</div>
<div class="plan-credits">30 积分一次性</div>
<!-- Free Trial -->
<div class="plan-card free">
<div class="plan-badge">推荐体验</div>
<div class="plan-name">免费试用</div>
<div class="plan-price free">¥0</div>
<div class="plan-credits">30 积分一次性+ 每日 1000 字免费翻译</div>
<ul class="plan-features">
<li>每日 1000 字免费翻译</li>
<li>基本功能体验</li>
<li>用完即止</li>
<li>AI 翻译 / 智能回复</li>
<li>客户发现 / 营销生成</li>
<li>报价单 / 客户健康度</li>
<li>网页端浏览器插件通用</li>
</ul>
<el-button type="default" disabled class="plan-btn" v-if="currentPlan === 'free'">当前套餐</el-button>
<el-button type="default" disabled class="plan-btn" v-else>当前套餐</el-button>
<el-button type="primary" class="plan-btn" @click="startTrial">免费使用</el-button>
</div>
<!-- Dynamically loaded plan cards -->
<div
v-for="p in planCards"
:key="p.id"
class="plan-card"
:class="{
featured: p.featured,
current: p.isCurrent,
'yearly-active': billingPeriod === 'yearly'
}"
>
<div v-if="p.badge" class="plan-badge">{{ p.badge }}</div>
<div class="plan-name">{{ p.name }}</div>
<div class="plan-name-en">{{ p.name_en }}</div>
<!-- Private Deployment -->
<div class="plan-card featured">
<div class="plan-badge">企业首选</div>
<div class="plan-name">私有化部署</div>
<div class="plan-price">
¥{{ billingPeriod === 'yearly' ? p.yearlyPrice : p.price }}
<small>/{{ billingPeriod === 'yearly' ? '年' : '月' }}</small>
¥{{ privatePlan.price }}<small>/{{ privatePlan.unit }}</small>
</div>
<div v-if="billingPeriod === 'yearly' && p.yearlyOriginal" class="plan-original">
<del>¥{{ p.yearlyOriginal }}/</del>
<span class="plan-discount">{{ p.discountPct }}% 优惠</span>
</div>
<div class="plan-credits">{{ p.credits }} <small>积分/</small></div>
<div class="plan-credits">{{ privatePlan.credits }}</div>
<ul class="plan-features">
<li v-for="f in p.features" :key="f">{{ f }}</li>
<li>独立部署到你的服务器</li>
<li>数据完全私有安全合规</li>
<li>不限账号数与调用量</li>
<li>支持对接自有 AI 模型</li>
<li>一年技术支持与升级</li>
</ul>
<el-button
v-if="p.isCurrent"
type="default"
disabled
class="plan-btn"
>当前套餐</el-button>
<el-button
v-else
type="primary"
class="plan-btn"
:loading="payingId === p.id"
@click="handleUpgrade(p)"
>升级到 {{ p.name }}</el-button>
<el-button type="primary" class="plan-btn" @click="openContact('private')">申请私有化部署</el-button>
</div>
<!-- Source Buyout -->
<div class="plan-card">
<div class="plan-badge">一次买断</div>
<div class="plan-name">买断源码</div>
<div class="plan-price">
¥{{ buyoutPlan.price }}<small>/一次性</small>
</div>
<div class="plan-credits">{{ buyoutPlan.credits }}</div>
<ul class="plan-features">
<li>完整前端 + 后端源码</li>
<li>可自由二次开发</li>
<li>永久授权无后续费用</li>
<li>社区与文档支持</li>
</ul>
<el-button type="primary" class="plan-btn" @click="openContact('buyout')">咨询源码买断</el-button>
</div>
</div>
<!-- Feature Comparison Table -->
<el-card class="comparison-card" v-if="planCards.length">
<template #header><strong>完整功能对比</strong></template>
<el-table :data="comparisonRows" border stripe>
<el-table-column prop="feature" label="功能" width="160" />
<el-table-column prop="free" label="Free" width="120" align="center" />
<el-table-column v-for="p in planCards" :key="p.id" :prop="p.id" :label="p.name" width="130" align="center" />
</el-table>
</el-card>
<!-- Package section -->
<el-card class="package-card">
<template #header><strong>积分包无需订阅按需购买</strong></template>
<div class="package-grid">
<div v-for="pkg in packages" :key="pkg.id" class="package-item">
<div class="pkg-name">{{ pkg.name }}</div>
<div class="pkg-credits">{{ pkg.credits }} <small>积分</small></div>
<div class="pkg-price">¥{{ pkg.price }}</div>
<div class="pkg-unit"> ¥{{ (pkg.price / pkg.credits).toFixed(2) }}/积分</div>
<el-button size="small" type="primary" @click="buyPackage(pkg)">购买</el-button>
</div>
</div>
</el-card>
<!-- Purchase Dialog -->
<el-dialog v-model="payDialog.visible" title="选择支付方式" width="360px">
<p style="margin-bottom:12px;text-align:center" v-if="payDialog.type === 'subscription'">
订阅 <strong>{{ payDialog.plan?.name }}</strong>
({{ payDialog.plan?.credits }} 积分/)
</p>
<p style="margin-bottom:12px;text-align:center" v-else>
购买 <strong>{{ payDialog.pkg?.name }}</strong> ({{ payDialog.pkg?.credits }} 积分)
</p>
<p style="font-size:22px;font-weight:bold;color:#e6a23c;text-align:center;margin-bottom:16px">
¥{{ payDialog.type === 'subscription' ? payDialog.plan?.price : payDialog.pkg?.price }}
</p>
<el-radio-group v-model="payDialog.payType" style="display:flex;gap:16px;justify-content:center;margin-bottom:16px">
<el-radio-button value="alipay">支付宝</el-radio-button>
<el-radio-button value="wechat">微信支付</el-radio-button>
</el-radio-group>
<!-- Contact dialog for enterprise offerings -->
<el-dialog v-model="contact.visible" :title="contactTitle" width="420px">
<el-form :model="contact.form" label-width="80px">
<el-form-item label="称呼" required>
<el-input v-model="contact.form.name" placeholder="您的称呼" />
</el-form-item>
<el-form-item label="公司">
<el-input v-model="contact.form.company" placeholder="公司名称(选填)" />
</el-form-item>
<el-form-item label="手机号" required>
<el-input v-model="contact.form.phone" placeholder="用于商务联系" />
</el-form-item>
<el-form-item label="需求">
<el-input v-model="contact.form.message" type="textarea" :rows="3" placeholder="部署规模 / 定制需求(选填)" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="payDialog.visible = false">取消</el-button>
<el-button type="primary" @click="confirmPay" :loading="payDialog.loading">确认支付</el-button>
<el-button @click="contact.visible = false">取消</el-button>
<el-button type="primary" :loading="contact.loading" @click="submitContact">提交申请</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
getSubscriptionPlans, getCreditPackages, getCreditBalance,
subscribeCreditPlan, purchaseCreditPackage,
} from '@/api'
import { submitLead } from '@/api'
const billingPeriod = ref('monthly')
const router = useRouter()
const loading = ref(false)
const plans = ref([])
const packages = ref([])
const currentPlan = ref('free')
const payingId = ref(null)
const payDialog = ref({
const PRICING = {
private: { price: '39,800', unit: '年', credits: '不限账号 · 不限调用' },
buyout: { price: '98,000', unit: '一次性', credits: '永久授权 · 可二开' },
}
const privatePlan = ref(PRICING.private)
const buyoutPlan = ref(PRICING.buyout)
const contact = ref({
visible: false,
type: 'subscription', // 'subscription' | 'package'
plan: null,
pkg: null,
payType: 'alipay',
type: 'private',
loading: false,
form: { name: '', company: '', phone: '', message: '' },
})
const contactTitle = computed(() => contact.value.type === 'private' ? '申请私有化部署' : '咨询源码买断')
const PLAN_META = {
starter: { badge: '入门', yearlyDiscount: 0.17, features: ['200 积分/月', '无每日限制', '翻译 + 客户发现 + 营销生成', '智能回复 + 报价单'] },
pro: { badge: '推荐', featured: true, yearlyDiscount: 0.15, features: ['1000 积分/月', 'AI 数字员工', '团队协作(3 人)', '优先技术支持'] },
enterprise: { badge: '旗舰', yearlyDiscount: 0.16, features: ['2500 积分/月', '不限团队人数', 'API 调用权限', 'SLA 保障'] },
function startTrial() {
router.push('/workspace')
}
const planCards = computed(() => {
return plans.value.map(p => {
const meta = PLAN_META[p.id] || {}
const yearlyOriginal = Math.round(p.price * 12)
const yearlyPrice = Math.round(p.price * 12 * (1 - (meta.yearlyDiscount || 0)))
return {
...p,
credits: p.credits_per_month || p.credits || 0,
badge: meta.badge || '',
featured: meta.featured || false,
isCurrent: p.id === currentPlan.value,
yearlyPrice,
yearlyOriginal,
discountPct: Math.round((meta.yearlyDiscount || 0) * 100),
features: meta.features || [],
}
}).filter(p => p.price > 0) // exclude free
})
const comparisonRows = computed(() => {
const features = [
{ feature: '积分/月', free: '30(一次性)', ...Object.fromEntries(planCards.value.map(p => [p.id, p.credits])) },
{ feature: 'AI 翻译', free: '1000字/天', ...Object.fromEntries(planCards.value.map(p => [p.id, '✓'])) },
{ feature: '智能回复', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, '✓'])) },
{ feature: '客户发现', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, '✓'])) },
{ feature: '营销生成', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, '✓'])) },
{ feature: '报价单', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, '✓'])) },
{ feature: 'AI 数字员工', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, p.credits >= 1000 ? '✓' : '—'])) },
{ feature: '团队协作', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, p.credits >= 1000 ? '3 人' : p.credits >= 500 ? '3 人' : '—'])) },
{ feature: 'API 调用', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, p.credits >= 2000 ? '✓' : '—'])) },
{ feature: '技术支持', free: '—', ...Object.fromEntries(planCards.value.map(p => [p.id, p.credits >= 1000 ? '优先' : '—'])) },
]
return features
})
async function loadData() {
loading.value = true
try {
const [plansRes, pkgsRes, balanceRes] = await Promise.all([
getSubscriptionPlans().catch(() => []),
getCreditPackages().catch(() => []),
getCreditBalance().catch(() => null),
])
plans.value = Array.isArray(plansRes) ? plansRes : (plansRes.data || plansRes.items || [])
packages.value = Array.isArray(pkgsRes) ? pkgsRes : (pkgsRes.data || pkgsRes.items || [])
if (balanceRes?.subscription?.plan_id) {
currentPlan.value = balanceRes.subscription.plan_id
}
} catch { /* ignore */ }
loading.value = false
function openContact(type) {
contact.value.type = type
contact.value.form = { name: '', company: '', phone: '', message: '' }
contact.value.visible = true
}
async function handleUpgrade(plan) {
if (plan.isCurrent) return
payDialog.value = {
visible: true,
type: 'subscription',
plan,
pkg: null,
payType: 'alipay',
loading: false,
async function submitContact() {
const f = contact.value.form
if (!f.name.trim() || !f.phone.trim()) {
ElMessage.warning('请填写称呼与手机号')
return
}
}
async function confirmPay() {
const d = payDialog.value
d.loading = true
contact.value.loading = true
try {
if (d.type === 'subscription') {
const res = await subscribeCreditPlan(d.plan.id, d.payType)
if (res.pay_url) window.open(res.pay_url, '_blank')
else ElMessage.success('订阅成功!')
} else {
const res = await purchaseCreditPackage(d.pkg.id, d.payType)
if (res.code_url || res.pay_url) {
if (res.pay_url) window.open(res.pay_url, '_blank')
// QR code handling
if (res.code_url) {
ElMessage.info('请在新页面扫码支付')
}
} else {
ElMessage.success('购买成功!')
}
}
d.visible = false
await submitLead({ type: contact.value.type, ...f })
ElMessage.success('提交成功,商务会尽快联系您')
contact.value.visible = false
} catch (e) {
ElMessage.error(e?.detail || e?.message || '支付失败')
ElMessage.error(e?.detail || e?.message || '提交失败')
}
d.loading = false
contact.value.loading = false
}
function buyPackage(pkg) {
payDialog.value = {
visible: true,
type: 'package',
plan: null,
pkg,
payType: 'alipay',
loading: false,
}
}
onMounted(loadData)
</script>
<style scoped>
@@ -256,9 +136,7 @@ onMounted(loadData)
.page-head { text-align: center; margin-bottom: 32px; }
.page-head h1 { font-size: 28px; color: #1e293b; margin: 0 0 8px; }
.page-sub { font-size: 14px; color: #64748b; margin: 0 0 20px; }
.billing-toggle { display: inline-flex; align-items: center; gap: 8px; }
.save-tag { background: #52c41a; color: #fff; font-size: 10px; padding: 1px 6px; border-radius: 8px; margin-left: 4px; }
.plans-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; margin-bottom: 32px; }
.plans-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.plan-card {
background: #fff; border: 1px solid #e5e7eb; border-radius: 16px;
padding: 24px 20px; text-align: center; position: relative;
@@ -266,40 +144,19 @@ onMounted(loadData)
}
.plan-card:hover { border-color: #2563eb; box-shadow: 0 4px 16px rgba(37,99,235,0.1); transform: translateY(-2px); }
.plan-card.featured { border-color: #2563eb; border-width: 2px; background: #f8faff; }
.plan-card.current { border-color: #52c41a; background: #f6ffed; }
.plan-card.yearly-active.featured { border-color: #2563eb; box-shadow: 0 4px 20px rgba(37,99,235,0.15); }
.plan-badge {
position: absolute; top: -10px; left: 50%; transform: translateX(-50%);
background: #2563eb; color: #fff; font-size: 11px; padding: 2px 14px;
border-radius: 10px; font-weight: 600;
}
.plan-card.current .plan-badge { background: #52c41a; }
.plan-card.free .plan-badge { background: #52c41a; }
.plan-name { font-size: 16px; font-weight: 700; color: #1e293b; margin-bottom: 2px; }
.plan-name-en { font-size: 12px; color: #94a3b8; margin-bottom: 8px; }
.plan-price { font-size: 28px; font-weight: 800; color: #2563eb; margin: 8px 0 2px; }
.plan-price.free { color: #64748b; font-size: 20px; }
.plan-price { font-size: 30px; font-weight: 800; color: #2563eb; margin: 8px 0 2px; }
.plan-price.free { color: #64748b; font-size: 22px; }
.plan-price small { font-size: 14px; font-weight: 400; color: #64748b; }
.plan-original { font-size: 12px; color: #94a3b8; margin-bottom: 4px; }
.plan-discount { color: #52c41a; font-weight: 600; margin-left: 6px; }
.plan-credits { font-size: 13px; color: #64748b; margin-bottom: 12px; }
.plan-features { list-style: none; padding: 0; margin: 0 0 16px; }
.plan-features li { font-size: 13px; color: #475569; line-height: 2; }
.plan-features li::before { content: '✓ '; color: #52c41a; font-weight: 700; }
.plan-btn { width: 100%; }
.comparison-card { margin-bottom: 24px; }
.comparison-card :deep(td) { font-size: 13px; }
.package-card { margin-bottom: 24px; }
.package-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.package-item {
border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; text-align: center;
transition: border-color 0.2s;
}
.package-item:hover { border-color: #2563eb; }
.pkg-name { font-size: 15px; font-weight: 600; color: #1e293b; }
.pkg-credits { font-size: 20px; font-weight: 700; color: #2563eb; margin: 6px 0; }
.pkg-credits small { font-size: 12px; font-weight: 400; }
.pkg-price { font-size: 18px; font-weight: 700; color: #e6a23c; margin-bottom: 2px; }
.pkg-unit { font-size: 11px; color: #94a3b8; margin-bottom: 10px; }
</style>