2026-07-27 16:26:19 +02:00
"use client" ;
import Link from "next/link" ;
import { useRef } from "react" ;
import { ScrollReveal } from "@/components/ScrollReveal" ;
import { SectionScrollTitle } from "@/components/SectionScrollTitle" ;
import { SignInButton } from "@/components/SignInButton" ;
import { getVideoAttributionText } from "@/lib/branding" ;
import { SALES_EMAIL } from "@/lib/plans" ;
const FEATURE_LABELS = [
"Infrastructure" ,
"Limit" ,
"Batch mode" ,
"Bulk image matching" ,
"File type" ,
"Playlists" ,
"API support" ,
"ID3 tags" ,
"Support" ,
"Watermark" ,
] as const ;
type PlanFeatures = Record < ( typeof FEATURE_LABELS ) [ number ] , string > ;
type InfrastructureChip = "cloud" | "self-hosted" ;
type PlanConfig = {
name : string ;
badge : string ;
price : string ;
priceNote : string ;
features : PlanFeatures ;
infrastructureChips : InfrastructureChip [ ] ;
watermarkNote? : string ;
cta :
| { type : "signin" }
| { type : "link" ; label : string ; href : string }
| { type : "disabled" ; label : string }
| { type : "mailto" ; label : string ; email : string ; subject : string } ;
highlighted : boolean ;
hover : string ;
accent : string ;
badgeClass : string ;
ctaClass? : string ;
} ;
const PLANS : PlanConfig [ ] = [
{
name : "Bedroom Producer" ,
badge : "Free" ,
price : "€0" ,
priceNote : "/ forever" ,
features : {
Infrastructure : "Cloud" ,
Limit : "10 videos* / month · 720p · buy 1– 15 extras (€0.25 each)" ,
"Batch mode" : "Limited batch · up to 3 files" ,
"Bulk image matching" : "1 static image only" ,
"File type" : "MP3" ,
Playlists : "Not included" ,
"API support" : "Not included" ,
"ID3 tags" : "Auto-fill title & metadata from MP3 tags" ,
Support : "Community" ,
2026-08-03 07:41:11 +02:00
Watermark : "2 clean videos, then Songs2VID badge · bottom-right" ,
2026-07-27 16:26:19 +02:00
} ,
infrastructureChips : [ "cloud" ] ,
watermarkNote :
2026-08-03 07:41:11 +02:00
` First 2 successful Free renders are watermark-free; later Free renders include the " ${ getVideoAttributionText ( ) } " badge (bottom-right). Upgrade to Pro for unwatermarked videos. ` ,
2026-07-27 16:26:19 +02:00
cta : { type : "signin" } ,
highlighted : false ,
hover :
"hover:border-red-500/40 hover:bg-red-500/[0.04] hover:shadow-lg hover:shadow-red-500/15" ,
accent : "text-red-400" ,
badgeClass : "bg-gray-800 text-gray-400" ,
} ,
{
name : "Independent Artist" ,
badge : "Pro" ,
price : "€5" ,
priceNote : "/ mo" ,
features : {
Infrastructure : "Cloud" ,
Limit : "50 videos / month · 1080p" ,
"Batch mode" : "Full batch · up to 5 files" ,
"Bulk image matching" : "Unique image per track (PRO)" ,
"File type" : "MP3 / WAV / FLAC" ,
Playlists : "Create & add uploads to YouTube playlists" ,
"API support" : "REST API · upload, batch & playlists" ,
"ID3 tags" : "Extended metadata support" ,
Support : "E-Mail" ,
Watermark : "Custom text/logo/fonts · art-track layouts + blur (PRO)" ,
} ,
infrastructureChips : [ "cloud" ] ,
cta : { type : "link" , label : "Upgrade to Pro" , href : "/dashboard/settings" } ,
highlighted : true ,
hover :
"hover:border-accent/50 hover:bg-accent/[0.06] hover:shadow-lg hover:shadow-accent/20" ,
accent : "text-accent" ,
badgeClass : "bg-accent/15 text-accent" ,
} ,
{
name : "Record Label / Studio" ,
badge : "Enterprise" ,
price : "Custom" ,
priceNote : "/ contact sales" ,
features : {
Infrastructure : "Cloud or self-hosted" ,
Limit : "Unlimited 4K · zero limit" ,
"Batch mode" : "Unlimited synchronized batch processing" ,
"Bulk image matching" : "Unique image per track" ,
"File type" : "WAV / FLAC / lossless" ,
Playlists : "Org-wide playlist workflows" ,
"API support" : "Full API access · custom integrations & SLAs" ,
"ID3 tags" : "Full metadata · custom mapping" ,
Support : "Top-priority**" ,
Watermark : "Custom branding · position studio" ,
} ,
infrastructureChips : [ "cloud" , "self-hosted" ] ,
cta : {
type : "mailto" ,
label : "Contact Sales" ,
email : SALES_EMAIL ,
subject : "Songs2VID Enterprise Inquiry" ,
} ,
highlighted : false ,
hover :
"hover:border-[#609926]/45 hover:bg-[#609926]/[0.06] hover:shadow-lg hover:shadow-[#609926]/20" ,
accent : "text-[#609926]" ,
badgeClass : "bg-[#609926]/15 text-[#609926]" ,
ctaClass :
"border-[#609926]/40 bg-[#609926]/10 text-[#609926] hover:border-[#609926]/60 hover:bg-[#609926]/20" ,
} ,
] ;
function FeatureValue ( { value } : { value : string } ) {
return < span className = "text-sm text-gray-200" > { value } < / span > ;
}
function CloudIcon ( { className } : { className? : string } ) {
return (
< svg
className = { className }
viewBox = "0 0 24 24"
fill = "none"
stroke = "currentColor"
strokeWidth = "1.5"
aria - hidden = "true"
>
< path
d = "M7.5 18.5h9.75a4.25 4.25 0 0 0 .55-8.46A5.75 5.75 0 0 0 6.9 8.8 4.5 4.5 0 0 0 7.5 18.5Z"
strokeLinecap = "round"
strokeLinejoin = "round"
/ >
< / svg >
) ;
}
function ServerIcon ( { className } : { className? : string } ) {
return (
< svg
className = { className }
viewBox = "0 0 24 24"
fill = "none"
stroke = "currentColor"
strokeWidth = "1.5"
aria - hidden = "true"
>
< rect x = "4" y = "5" width = "16" height = "6" rx = "2" / >
< rect x = "4" y = "13" width = "16" height = "6" rx = "2" / >
< path d = "M7.5 8h.01M7.5 16h.01" strokeLinecap = "round" / >
< / svg >
) ;
}
function InfrastructureChips ( {
chips ,
} : {
chips : InfrastructureChip [ ] ;
} ) {
return (
< div className = "mt-2 flex flex-wrap items-center gap-2" >
{ chips . includes ( "cloud" ) && (
< span className = "inline-flex items-center gap-2 rounded-lg border border-gray-700/60 bg-surface-dark/40 px-3 py-2 text-sm text-gray-200" >
< CloudIcon className = "h-4 w-4 text-gray-300" / >
Cloud
< / span >
) }
{ chips . includes ( "self-hosted" ) && (
< span className = "inline-flex items-center gap-2 rounded-lg border border-gray-700/60 bg-surface-dark/40 px-3 py-2 text-sm text-gray-200" >
< ServerIcon className = "h-4 w-4 text-gray-300" / >
Self - hosted
< / span >
) }
< / div >
) ;
}
function PricingCard ( { plan } : { plan : PlanConfig } ) {
const {
name ,
badge ,
price ,
priceNote ,
features ,
watermarkNote ,
infrastructureChips ,
cta ,
highlighted ,
hover ,
accent ,
badgeClass ,
ctaClass ,
} = plan ;
return (
< div
className = { ` group relative flex h-full flex-col rounded-2xl border bg-surface p-6 transition-all duration-300 sm:p-7 ${
highlighted
? "border-accent/40 shadow-[0_0_32px_rgba(74,158,255,0.12)]"
: "border-gray-700/50"
} $ { hover } ` }
>
{ highlighted && (
< span className = "absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-accent px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-white" >
Most popular
< / span >
) }
< div className = "mb-6" >
< div className = "mb-3 flex items-start justify-between gap-3" >
< h3 className = "text-lg font-semibold text-white" > { name } < / h3 >
< span
className = { ` shrink-0 rounded px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider ${ badgeClass } ` }
>
{ badge }
< / span >
< / div >
< div className = "flex items-end gap-1" >
< span className = { ` text-4xl font-bold ${ accent } ` } > { price } < / span >
< span className = "mb-1 text-sm text-gray-500" > { priceNote } < / span >
< / div >
< / div >
< ul className = "mb-6 flex-1 space-y-4 border-t border-gray-700/50 pt-6" >
{ FEATURE_LABELS . map ( ( label ) = > (
< li key = { label } >
< p className = "text-xs font-medium uppercase tracking-wider text-gray-500" > { label } < / p >
{ label === "Infrastructure" ? (
< InfrastructureChips chips = { infrastructureChips } / >
) : (
< FeatureValue value = { features [ label ] } / >
) }
{ label === "Watermark" && watermarkNote && (
< p className = "mt-1.5 rounded-lg border border-gray-700/60 bg-surface-dark/80 px-3 py-2 text-xs leading-relaxed text-gray-400" >
{ watermarkNote }
< / p >
) }
< / li >
) ) }
< / ul >
< div className = "mt-auto" >
{ cta . type === "signin" && < SignInButton fullWidth / > }
{ cta . type === "link" && (
< Link
href = { cta . href }
className = { ` inline-flex w-full items-center justify-center rounded border px-4 py-3 text-sm font-medium transition-all duration-300 ${ ctaClass ? ? "border-gray-600 text-white hover:border-gray-400" } ` }
>
{ cta . label }
< / Link >
) }
{ cta . type === "disabled" && (
< button
type = "button"
disabled
className = "w-full cursor-not-allowed rounded border border-gray-700 bg-surface-light px-4 py-3 text-sm font-medium text-gray-500"
>
{ cta . label }
< / button >
) }
{ cta . type === "mailto" && (
< a
href = { ` mailto: ${ cta . email } ?subject= ${ encodeURIComponent ( cta . subject ) } ` }
className = { ` inline-flex w-full items-center justify-center rounded border px-4 py-3 text-sm font-medium transition-all duration-300 ${ ctaClass ? ? "" } ` }
>
{ cta . label }
< / a >
) }
< / div >
< / div >
) ;
}
export function PricingSection() {
const sectionRef = useRef < HTMLElement > ( null ) ;
return (
< section
ref = { sectionRef }
id = "pricing"
className = "relative scroll-mt-24 overflow-x-visible overflow-y-hidden px-6 py-24"
>
< SectionScrollTitle sectionRef = { sectionRef } title = "Pricing" / >
< div className = "relative z-10 mx-auto max-w-7xl" >
< ScrollReveal >
< h2 className = "mb-4 text-center text-3xl font-bold text-white" > Pricing < / h2 >
< p className = "mx-auto mb-10 max-w-2xl text-center text-gray-400" >
Free with optional extras , Pro at € 5 / month , or Enterprise for studios .
< / p >
< / ScrollReveal >
< div className = "grid gap-6 sm:grid-cols-2 xl:grid-cols-3 xl:gap-6" >
{ PLANS . map ( ( plan , index ) = > (
< ScrollReveal key = { plan . name } delay = { index * 100 } >
< PricingCard plan = { plan } / >
< / ScrollReveal >
) ) }
< / div >
< ScrollReveal delay = { 360 } >
< p className = "mx-auto mt-10 max-w-2xl text-center text-xs text-gray-500" >
* Free includes 10 videos / month ; buy 1 – 15 extras at € 0.25 each ( max 15 extras ) . After both
are used , Pro ( € 5 / mo · 50 videos ) is required . Total balance capped at 30 . Deduction
2026-08-03 07:41:11 +02:00
order : monthly first , then extras . Free also includes 2 lifetime watermark - free
successful videos ; later Free renders include a Songs2VID watermark unless you upgrade .
2026-07-27 16:26:19 +02:00
< / p >
< p className = "mx-auto mt-4 max-w-2xl text-center text-xs text-gray-500" >
* * Technical support is strictly reserved for Managed Cloud and paid Professional Setup
agreements ; independent self - hosted deployments are community - supported .
< / p >
< / ScrollReveal >
< / div >
< / section >
) ;
}