Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
Use mainwhite.svg on white sections with curvy green transitions into flat green bands, improve text and button contrast, and deploy via OpenNext on Cloudflare Workers. Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
4.6 KiB
TypeScript
131 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { ArrowRight, Ticket } from "lucide-react";
|
|
import type { TicketTier } from "@/content/tickets";
|
|
import { site } from "@/content/site";
|
|
import { TicketInclusionsPopover } from "@/components/tickets/TicketInclusionsPopover";
|
|
import { ScrollReveal } from "@/components/motion/ScrollReveal";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
type Props = {
|
|
tier: TicketTier;
|
|
index: number;
|
|
featured?: boolean;
|
|
/** Lighter layout for /payment tier picker */
|
|
compact?: boolean;
|
|
};
|
|
|
|
/** One-line summary for the card face (not the popover) */
|
|
function ticketTagline(tier: TicketTier): string {
|
|
return tier.features[0] ?? tier.description;
|
|
}
|
|
|
|
export function TicketCard({ tier, index, featured, compact }: Props) {
|
|
const price =
|
|
tier.priceLabel ?? (tier.priceUsd === 0 ? "Free" : `$${tier.priceUsd}`);
|
|
const serial = `GRV-${tier.id.slice(0, 3).toUpperCase()}-${1000 + index}`;
|
|
const schedule = tier.scheduleLabel ?? site.dates.label;
|
|
|
|
return (
|
|
<ScrollReveal
|
|
variant="card"
|
|
delay={index * 100}
|
|
as="article"
|
|
className={cn(
|
|
"flex w-full",
|
|
featured && !compact && "md:-mt-3 md:mb-1 md:scale-[1.02]"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"ticket-admission relative w-full overflow-hidden bg-white text-[#0f0404]",
|
|
"shadow-[0_12px_40px_rgba(0,0,0,0.18)]",
|
|
featured && "ring-2 ring-[#ffb300]/50"
|
|
)}
|
|
data-ticket-notch={compact ? "light" : "inverse"}
|
|
>
|
|
{featured && (
|
|
<span className="absolute right-3 top-3 z-10 rounded-full bg-[#ffb300] px-2.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-[#0f0404]">
|
|
Popular
|
|
</span>
|
|
)}
|
|
|
|
<div
|
|
className={cn(
|
|
"flex flex-col md:flex-row",
|
|
featured && "bg-gradient-to-br from-white via-white to-[#fff9eb]"
|
|
)}
|
|
>
|
|
{/* Stub — price & date only */}
|
|
<div
|
|
className={cn(
|
|
"relative flex shrink-0 md:w-[30%]",
|
|
"border-b border-dashed border-[#1a5c38]/20 md:border-b-0 md:border-r"
|
|
)}
|
|
>
|
|
<div className="flex w-full flex-row items-center justify-between gap-3 p-4 md:flex-col md:items-stretch md:justify-between md:py-5 md:pl-5 md:pr-4">
|
|
<div className="flex items-center gap-2 text-[#1a5c38] md:flex-col md:items-start">
|
|
<Ticket className="size-4 shrink-0" strokeWidth={2} aria-hidden />
|
|
<span className="text-[10px] font-bold uppercase tracking-[0.18em]">
|
|
Admit one
|
|
</span>
|
|
</div>
|
|
<div className="text-right md:text-left">
|
|
<p
|
|
className={cn(
|
|
"font-bold tabular-nums leading-none text-[#1f3d7e]",
|
|
compact ? "text-2xl" : "text-3xl"
|
|
)}
|
|
>
|
|
{price}
|
|
</p>
|
|
<p className="mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
{schedule}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main — name, one line, details popover, CTA */}
|
|
<div className="flex flex-1 flex-col justify-between gap-4 p-4 md:p-5">
|
|
<div className="min-w-0 pr-8 md:pr-0">
|
|
<h3 className="text-lg font-bold leading-tight md:text-xl">{tier.name}</h3>
|
|
<p className="mt-1.5 line-clamp-2 text-sm text-muted-foreground">
|
|
{ticketTagline(tier)}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-3">
|
|
<TicketInclusionsPopover tier={tier} serial={serial} />
|
|
<Button
|
|
className={cn(
|
|
"w-full rounded-full bg-[#ffb300] text-[#0f0404] hover:bg-[#ffb300]/90",
|
|
featured && "ticket-cta-pulse"
|
|
)}
|
|
disabled={tier.soldOut}
|
|
asChild={!tier.soldOut}
|
|
>
|
|
{tier.soldOut ? (
|
|
<span>Sold out</span>
|
|
) : (
|
|
<Link href={compact ? `/payment?ticket=${tier.id}` : "/payment"}>
|
|
{compact ? "Select" : "Get tickets"}
|
|
<ArrowRight className="size-4" />
|
|
</Link>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="h-1 w-full bg-gradient-to-r from-[#1a5c38] via-[#ffb300] to-[#1f3d7e]"
|
|
aria-hidden
|
|
/>
|
|
</div>
|
|
</ScrollReveal>
|
|
);
|
|
}
|