Founding-500 half-off-for-life + standard half-off-first-year + Free-vs-Pro comparison.
How this page works: each card below is a mockup — a choice. Click View handoff to open the card's drawer. Every drawer is complete on its own: full Vue component source, this card's parent invocation, every CSS rule the surface needs, and behavior notes — whichever card gets picked, everything needed to build it is in that one drawer. Each code box is click-to-copy — no separate copy button. the dev doesn't need the HTML; the Vue template + scoped CSS is all he pastes into a new .vue file.
../PLANS-PRICING.md.Lock in half off the annual subscription plan for life! Renews each year at 50% off as long as you stay Pro. The best deal EmojiCopy Pro will ever have.
<PrimaryButton> · catalog<Text> · catalog<Pill> · catalog<OfferCard> — new wrapper (Vue + CSS below)<script setup>
import { computed } from 'vue'
import PrimaryButton from '@/buttons/PrimaryButton.vue'
import Text from '@/components/Text.vue'
import Pill from '@/components/Pill.vue'
const props = defineProps({
variant: {
type: String, required: true,
validator: v => ['founding', 'standard'].includes(v),
},
})
const emit = defineEmits(['go-pro'])
const isFounding = computed(() => props.variant === 'founding')
</script>
<template>
<div class="offer-card" :class="{ 'offer-card--founding': isFounding }">
<Pill v-if="isFounding"
variant="beta-dark"
text="🏆 Founding member · first 500 · annual" />
<Pill v-else
variant="beta-dark"
text="🎉 Beta launch offer" />
<h2 v-if="isFounding" class="offer-card__headline">
50% off — <span class="offer-card__accent">for life</span>
</h2>
<h2 v-else class="offer-card__headline">
<span class="offer-card__strike">$32</span>$16
<small>first year</small>
</h2>
<Text tag="p" class="offer-card__sub"
:text="isFounding
? 'Lock in half off the annual subscription plan for life. Renews each year at 50% off as long as you stay Pro.'
: '50% off your first year of Pro — annual plan only. One-time offer; renews at the standard annual rate.'" />
<div class="offer-card__cta-row">
<PrimaryButton variant="green" size="md"
:label="isFounding
? 'Go Pro — annual, 50% off for life'
: 'Get 50% off — annual'"
@click="emit('go-pro')" />
<Text tag="span" class="offer-card__footnote"
:text="isFounding ? 'annual plan only' : '≈ $1.33/mo first year, billed yearly'" />
</div>
<div class="offer-card__auto">
🔒 Applied automatically at checkout · code
<span class="offer-card__code">
{{ isFounding ? 'FOUNDING50' : 'BETA50' }}
</span>
</div>
</div>
</template>
<OfferCard
variant="founding"
@go-pro="checkout('annual', 'FOUNDING50')"
/>
<style scoped>
.offer-card {
width: 100%;
max-width: 560px;
padding: var(--sp-7);
border-radius: var(--r-xl);
border: 1px solid var(--c-border-primary);
background: var(--c-surface-secondary);
position: relative;
overflow: hidden;
}
.offer-card.offer-card--founding {
background:
radial-gradient(120% 160% at 0% 0%,
color-mix(in srgb, var(--green-500) 35%, transparent),
transparent 55%),
var(--c-surface-secondary);
border-color: color-mix(in srgb, var(--green-600) 40%, var(--c-border-primary));
}
.offer-card__eyebrow {
display: inline-block;
margin-bottom: 14px;
font-family: var(--ff-inter);
font-weight: 700;
font-size: 1.15rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--c-icon-green);
}
.offer-card__headline {
font-family: var(--ff-dm-sans);
font-weight: 900;
font-size: 4.2rem;
line-height: 1;
letter-spacing: -.03em;
color: var(--c-text-primary);
margin: 0 0 10px;
}
.offer-card__strike {
color: var(--c-text-tertiary);
text-decoration: line-through;
font-weight: 700;
margin-right: 8px;
}
.offer-card__accent {
color: var(--c-icon-green);
}
.offer-card__headline small {
font-size: 1.6rem;
font-weight: 700;
color: var(--c-text-tertiary);
margin-left: 6px;
}
.offer-card__sub {
font-size: 1.5rem;
color: var(--c-text-secondary);
max-width: 52ch;
margin: 0 0 var(--sp-5);
line-height: 1.5;
}
.offer-card__sub b {
color: var(--c-text-primary);
}
.offer-card__cta-row {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.offer-card__footnote {
font-size: 1.3rem;
color: var(--c-text-tertiary);
}
.offer-card__auto {
display: inline-flex;
align-items: center;
gap: 8px;
margin-top: 14px;
font-size: 1.3rem;
color: var(--c-text-tertiary);
}
.offer-card__code {
font-family: ui-monospace, monospace;
font-weight: 700;
letter-spacing: .05em;
background: var(--c-surface-tertiary);
color: var(--c-text-primary);
padding: 3px 9px;
border-radius: 6px;
}
</style>
50% off your first year of Pro — annual plan only. One-time offer for new members during the beta; renews at the standard annual rate.
<PrimaryButton> · catalog<Text> · catalog<Pill> · catalog<OfferCard> — new wrapper (Vue + CSS below)<script setup>
import { computed } from 'vue'
import PrimaryButton from '@/buttons/PrimaryButton.vue'
import Text from '@/components/Text.vue'
import Pill from '@/components/Pill.vue'
const props = defineProps({
variant: {
type: String, required: true,
validator: v => ['founding', 'standard'].includes(v),
},
})
const emit = defineEmits(['go-pro'])
const isFounding = computed(() => props.variant === 'founding')
</script>
<template>
<div class="offer-card" :class="{ 'offer-card--founding': isFounding }">
<Pill v-if="isFounding"
variant="beta-dark"
text="🏆 Founding member · first 500 · annual" />
<Pill v-else
variant="beta-dark"
text="🎉 Beta launch offer" />
<h2 v-if="isFounding" class="offer-card__headline">
50% off — <span class="offer-card__accent">for life</span>
</h2>
<h2 v-else class="offer-card__headline">
<span class="offer-card__strike">$32</span>$16
<small>first year</small>
</h2>
<Text tag="p" class="offer-card__sub"
:text="isFounding
? 'Lock in half off the annual subscription plan for life. Renews each year at 50% off as long as you stay Pro.'
: '50% off your first year of Pro — annual plan only. One-time offer; renews at the standard annual rate.'" />
<div class="offer-card__cta-row">
<PrimaryButton variant="green" size="md"
:label="isFounding
? 'Go Pro — annual, 50% off for life'
: 'Get 50% off — annual'"
@click="emit('go-pro')" />
<Text tag="span" class="offer-card__footnote"
:text="isFounding ? 'annual plan only' : '≈ $1.33/mo first year, billed yearly'" />
</div>
<div class="offer-card__auto">
🔒 Applied automatically at checkout · code
<span class="offer-card__code">
{{ isFounding ? 'FOUNDING50' : 'BETA50' }}
</span>
</div>
</div>
</template>
<OfferCard
variant="standard"
@go-pro="checkout('annual', 'BETA50')"
/>
<style scoped>
.offer-card {
width: 100%;
max-width: 560px;
padding: var(--sp-7);
border-radius: var(--r-xl);
border: 1px solid var(--c-border-primary);
background: var(--c-surface-secondary);
position: relative;
overflow: hidden;
}
.offer-card.offer-card--founding {
background:
radial-gradient(120% 160% at 0% 0%,
color-mix(in srgb, var(--green-500) 35%, transparent),
transparent 55%),
var(--c-surface-secondary);
border-color: color-mix(in srgb, var(--green-600) 40%, var(--c-border-primary));
}
.offer-card__eyebrow {
display: inline-block;
margin-bottom: 14px;
font-family: var(--ff-inter);
font-weight: 700;
font-size: 1.15rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--c-icon-green);
}
.offer-card__headline {
font-family: var(--ff-dm-sans);
font-weight: 900;
font-size: 4.2rem;
line-height: 1;
letter-spacing: -.03em;
color: var(--c-text-primary);
margin: 0 0 10px;
}
.offer-card__strike {
color: var(--c-text-tertiary);
text-decoration: line-through;
font-weight: 700;
margin-right: 8px;
}
.offer-card__accent {
color: var(--c-icon-green);
}
.offer-card__headline small {
font-size: 1.6rem;
font-weight: 700;
color: var(--c-text-tertiary);
margin-left: 6px;
}
.offer-card__sub {
font-size: 1.5rem;
color: var(--c-text-secondary);
max-width: 52ch;
margin: 0 0 var(--sp-5);
line-height: 1.5;
}
.offer-card__sub b {
color: var(--c-text-primary);
}
.offer-card__cta-row {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.offer-card__footnote {
font-size: 1.3rem;
color: var(--c-text-tertiary);
}
.offer-card__auto {
display: inline-flex;
align-items: center;
gap: 8px;
margin-top: 14px;
font-size: 1.3rem;
color: var(--c-text-tertiary);
}
.offer-card__code {
font-family: ui-monospace, monospace;
font-weight: 700;
letter-spacing: .05em;
background: var(--c-surface-tertiary);
color: var(--c-text-primary);
padding: 3px 9px;
border-radius: 6px;
}
</style>
<PrimaryButton> · catalog<Text> · catalog<Pill> · catalog<BulletRow> — new atom (CSS below) · catalog<PricingCard> — new wrapper (Vue + CSS below)<script setup>
import PrimaryButton from '@/buttons/PrimaryButton.vue'
import Text from '@/components/Text.vue'
import Pill from '@/components/Pill.vue'
import BulletRow from '@/components/BulletRow.vue'
const props = defineProps({
plan: {
type: String, required: true,
validator: v => ['free', 'pro'].includes(v),
},
name: { type: String, required: true },
price: { type: String, required: true },
priceNote: { type: String, default: '' },
subline: { type: String, default: '' },
badge: { type: Object, default: null }, // { text, variant: 'beta'|'soft' }
bullets: { type: Array, required: true }, // [{ text, included? }]
ctaLabel: { type: String, required: true },
ctaVariant:{ type: String, default: 'gray' }, // 'gray' | 'green'
})
const emit = defineEmits(['cta'])
</script>
<template>
<div class="pricing-card" :class="{ 'pricing-card--pro': plan === 'pro' }">
<div class="pricing-card__head">
<Text tag="span" class="pricing-card__name" :text="name" />
<Pill v-if="badge" :variant="badge.variant" :text="badge.text" />
</div>
<Text tag="h3" class="pricing-card__price" :text="price">
<template #suffix>
<small v-if="priceNote">{{ priceNote }}</small>
</template>
</Text>
<Text v-if="subline" tag="div" class="pricing-card__sub" :text="subline" />
<ul class="pricing-card__bullets">
<BulletRow v-for="b in bullets"
:key="b.text"
color="green"
:text="b.text" />
</ul>
<PrimaryButton :variant="ctaVariant" size="md"
class="button--block"
:label="ctaLabel"
@click="emit('cta')" />
</div>
</template>
<div class="pricing-split">
<PricingCard
plan="free"
name="Free"
price="$0"
subline="Sign up — no guest mode in beta"
:badge="{ variant: 'beta', text: '✨ Ad-free during beta' }"
:bullets="freeBullets"
cta-label="Create free account"
cta-variant="gray"
@cta="signUp"
/>
<PricingCard
plan="pro"
name="Pro"
price="$16"
price-note=" / yr · 50% off for life"
subline="Annual plan only · standard Pro $4/mo or $32/yr · auto-applies at checkout"
:badge="{ variant: 'soft', text: '50% off · founding' }"
:bullets="proBullets"
cta-label="Go Pro"
cta-variant="green"
@cta="checkout"
/>
</div>
<style scoped>
.pricing-card {
padding: var(--sp-6);
border-radius: var(--r-xl);
border: 1px solid var(--c-border-primary);
background: var(--c-surface-secondary);
display: flex;
flex-direction: column;
gap: var(--sp-4);
}
/* `--pro` lives at the top level — native CSS nesting does not support
BEM-element/modifier concatenation like `&--pro`. */
.pricing-card--pro {
border-color: color-mix(in srgb, var(--green-600) 45%, var(--c-border-primary));
box-shadow: 0 16px 72px rgba(48,217,9,.10);
}
.pricing-card__head {
display: flex;
align-items: center;
justify-content: space-between;
}
.pricing-card__name {
font-family: var(--ff-dm-sans);
font-weight: 900;
font-size: 2.2rem;
color: var(--c-text-primary);
letter-spacing: -.03em;
}
.pricing-card__price {
font-family: var(--ff-dm-sans);
font-weight: 900;
font-size: 3.4rem;
letter-spacing: -.04em;
color: var(--c-text-primary);
}
.pricing-card__price small {
font-size: 1.4rem;
font-weight: 500;
color: var(--c-text-tertiary);
letter-spacing: 0;
}
.pricing-card__sub {
font-size: 1.3rem;
color: var(--c-text-tertiary);
margin-bottom: var(--sp-3);
}
.pricing-card__bullets {
display: flex;
flex-direction: column;
gap: var(--sp-2);
list-style: none;
margin: 0;
padding: 0;
}
/* Side-by-side wrapper — used by the parent, not PricingCard itself */
.pricing-split {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--sp-6);
}
@media (max-width: 760px) {
.pricing-split {
grid-template-columns: 1fr;
}
}
</style>
<style scoped>
.bullet-row {
display: flex;
align-items: flex-start;
gap: 10px;
font-family: var(--ff-inter);
font-weight: 400;
font-size: 1.4rem;
line-height: 1.5;
color: var(--c-text-secondary);
/* color override propagates to children — matches the app's `c:<color>/txt` from Text.vue */
&.c\:white\/txt {
color: var(--c-text-white);
}
}
/* Legacy check (check-thick_green.png <img> — same asset as Pill's
leading). New rows should use the `.check-circle` SVG instead; this
stays so older mockups keep rendering. */
.bullet-row__check {
flex: none;
width: 16px;
height: 16px;
margin-top: 2px;
}
/* SVG check inside a bullet row — nudge to sit on the text's optical
center like the <img> version does */
.bullet-row .check-circle {
margin-top: 2px;
}
.bullet-row__text {
flex: 1;
}
</style>