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,87 @@
.card {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: var(--spacing-sm) var(--spacing-xs) 0;
background: #ffffff;
border: 1px solid transparent;
border-radius: var(--radius-lg);
cursor: pointer;
text-align: center;
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease,
transform 0.05s ease;
width: 100%;
font: inherit;
color: inherit;
}
.card:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
.card:not(:disabled):active {
transform: scale(0.98);
}
.highlight {
border-color: var(--color-accent);
box-shadow: 0 4px 12px rgba(248, 77, 150, 0.18);
}
.selected {
border-color: var(--color-accent);
border-width: 2px;
box-shadow: 0 6px 16px rgba(248, 77, 150, 0.28);
}
.name {
margin: 0 0 var(--spacing-xs);
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--color-auth-text-primary);
line-height: 1.2;
}
.priceBlock {
display: flex;
align-items: baseline;
justify-content: center;
gap: 2px;
color: #000;
line-height: 1;
}
.currency {
font-size: var(--font-size-md);
font-weight: 400;
color: #000;
}
.price {
font-size: 28px;
font-weight: 300;
color: #000;
line-height: 1;
}
.originalPrice {
margin: 4px 0 var(--spacing-sm);
font-size: var(--font-size-xs);
color: #999999;
text-decoration: line-through;
line-height: 1;
}
.perDayBar {
align-self: stretch;
margin: 0 calc(-1 * var(--spacing-xs)) 0;
padding: 6px 0;
color: #ffffff;
font-size: var(--font-size-xs);
font-weight: 600;
text-align: center;
line-height: 1.2;
}