@@ -12,6 +12,7 @@ import { cn } from "@/lib/utils"
1212import NumberFlow from "@number-flow/react"
1313import { IconCheck , IconX } from "@tabler/icons-react"
1414import { BorderBeam } from "border-beam"
15+ import { AnimatePresence , m as motion } from "motion/react"
1516import { useState } from "react"
1617
1718type PricingPeriod = "monthly" | "quarterly" | "yearly"
@@ -27,6 +28,16 @@ const popularPillColorClasses = {
2728 magenta : "border-magenta/10! bg-magenta/10! text-magenta!" ,
2829} as const
2930
31+ const highlightedCardColors = {
32+ brand : "var(--bg-brand)" ,
33+ pink : "var(--bg-pink)" ,
34+ yellow : "var(--bg-yellow)" ,
35+ aqua : "var(--bg-aqua)" ,
36+ blue : "var(--bg-blue)" ,
37+ lime : "var(--bg-lime)" ,
38+ magenta : "var(--bg-magenta)" ,
39+ } as const
40+
3041interface PricingSectionProps {
3142 content ?: PricingLayoutBlock | null
3243 locale : AppLocale
@@ -105,6 +116,7 @@ export function PricingSection({ content, locale, packages, paymentPeriod }: Pri
105116 const buttonUrl = pricingPackage . content ?. button ?. url ?. trim ( )
106117 const highlighted = pricingPackage . key === "max"
107118 const popularPillColor = content . popularPill ?. color ?? "brand"
119+ const highlightedCardColor = content . highlightedCardColor ? highlightedCardColors [ content . highlightedCardColor ] : null
108120 const periodMonths = selectedPeriod === "quarterly" ? 3 : selectedPeriod === "yearly" ? 12 : 1
109121 const configuredDiscount = selectedPeriod === "quarterly" ? paymentPeriod . quarterlyDiscount : selectedPeriod === "yearly" ? paymentPeriod . yearlyDiscount : 0
110122 const priceWithoutPeriodDiscount = pricingPackage . price !== null && configuredDiscount > 0 && configuredDiscount < 1 ? pricingPackage . price / ( 1 - configuredDiscount ) : null
@@ -120,6 +132,13 @@ export function PricingSection({ content, locale, packages, paymentPeriod }: Pri
120132 y = { 14 }
121133 duration = { 0.42 }
122134 className = "relative flex h-full min-w-0 flex-col overflow-hidden rounded-3xl border border-white/5 bg-[linear-gradient(160deg,rgba(255,255,255,0.08),rgba(255,255,255,0.02)_28%,rgba(8,10,20,0.6)_100%)] p-6 md:p-8"
135+ style = {
136+ highlighted && highlightedCardColor
137+ ? {
138+ background : `linear-gradient(160deg, color-mix(in oklch, ${ highlightedCardColor } 8%, transparent), color-mix(in oklch, ${ highlightedCardColor } 2%, transparent) 28%, rgba(8, 10, 20, 0.6) 100%)` ,
139+ }
140+ : undefined
141+ }
123142 >
124143 { highlighted && < div aria-hidden = "true" className = "pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,oklch(1_0_0/0.1),transparent_36%)]" /> }
125144 < div className = "pointer-events-none absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-white/30 to-transparent" />
@@ -133,21 +152,35 @@ export function PricingSection({ content, locale, packages, paymentPeriod }: Pri
133152 </ StableBadge >
134153 </ div >
135154 ) }
136- < h3 className = { cn ( "relative z-10 text-2xl font-semibold text-white" , pricingPackage . price !== null && "pr-20" ) } > { pricingPackage . title } </ h3 >
155+ < h3
156+ className = { cn ( "relative z-10 text-2xl font-semibold text-white" , pricingPackage . price !== null && "pr-20" ) }
157+ style = { highlighted && content . titleColor ? { color : content . titleColor } : undefined }
158+ >
159+ { pricingPackage . title }
160+ </ h3 >
137161 { pricingPackage . description && < p className = "relative z-10 mt-1 leading-6 text-secondary" > { pricingPackage . description } </ p > }
138162
139163 { pricingPackage . price !== null && (
140164 < div className = "relative z-10 mt-6 flex flex-col items-start gap-0" >
141- { hasDiscount && (
142- < span className = "relative -mb-1 inline-flex text-sm leading-none font-medium text-tertiary after:absolute after:top-1/2 after:right-0 after:left-0 after:z-10 after:h-px after:bg-current after:content-['']" >
143- { regularPrice . toLocaleString ( locale === "de" ? "de-DE" : "en-US" , {
144- style : "currency" ,
145- currency : "EUR" ,
146- minimumFractionDigits : 0 ,
147- maximumFractionDigits : 2 ,
148- } ) }
149- </ span >
150- ) }
165+ < AnimatePresence initial = { false } >
166+ { hasDiscount && (
167+ < motion . span
168+ key = { selectedPeriod }
169+ initial = { { opacity : 0 , y : 4 } }
170+ animate = { { opacity : 1 , y : 0 } }
171+ exit = { { opacity : 0 , y : 4 } }
172+ transition = { { duration : 0.2 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
173+ className = "relative -mb-1 inline-flex text-sm leading-none font-medium text-tertiary after:absolute after:top-1/2 after:right-0 after:left-0 after:z-10 after:h-px after:bg-current after:content-['']"
174+ >
175+ { regularPrice . toLocaleString ( locale === "de" ? "de-DE" : "en-US" , {
176+ style : "currency" ,
177+ currency : "EUR" ,
178+ minimumFractionDigits : 0 ,
179+ maximumFractionDigits : 2 ,
180+ } ) }
181+ </ motion . span >
182+ ) }
183+ </ AnimatePresence >
151184 < div className = "flex flex-wrap items-center gap-2" >
152185 < NumberFlow
153186 value = { pricingPackage . price }
0 commit comments