feat(subscription): add VIP subscription page

Add a new /subscription route showing three VIP membership plans (Monthly,
Quarterly, Annual) with a pink→peach banner, per-plan gradient price bars,
a benefits card, a mock activate CTA, and an agreement checkbox.

- New reusable <Checkbox /> in src/app/_components/core for the legal
  agreement toggle
- Hardcoded SUBSCRIPTION_PLANS constant in src/data/constants
- New SubscriptionScreen composed of 7 colocated components (back link,
  user row, banner, plan card, benefits card, CTA button) with CSS
  Modules matching the existing design-token system
- Route registered as protected and reached from /sidebar "Membership"
- Mock CTA: window.alert + router.push(/chat) — no payment calls

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-15 17:48:41 +08:00
committed by chenhang
parent b9315c9f8b
commit e88a99626d
23 changed files with 1001 additions and 0 deletions
@@ -0,0 +1,67 @@
/* 视觉规格:
* - 全宽,52px 高
* - 粉渐变(#f96ADE → #f657A0
* - 半径 24,白字 16px/600
* - 阴影 0 3px 5px rgba(208,12,65,0.20)
*/
.button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 52px;
padding: 0 var(--spacing-lg);
border: none;
border-radius: var(--radius-xxxl);
background: linear-gradient(
90deg,
var(--color-auth-primary-gradient-start),
var(--color-auth-primary-gradient-end)
);
box-shadow: 0 3px 5px var(--color-auth-primary-button-shadow);
color: #ffffff;
font-size: var(--font-size-lg);
font-weight: 600;
cursor: pointer;
transition: filter 0.15s ease, transform 0.05s ease, opacity 0.15s ease;
}
.button:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.button:not(:disabled):active {
transform: scale(0.98);
}
.label {
display: inline-flex;
align-items: center;
gap: var(--spacing-sm);
flex: 1 1 auto;
justify-content: center;
text-align: center;
}
.spinner {
display: inline-block;
width: 18px;
height: 18px;
border-width: 2px;
border-style: solid;
border-color: #ffffff transparent transparent transparent;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}