GRV-Summit-Site/components/tickets/TicketCard.tsx
“kirukib” 2b419883eb
Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
Add data consent prompt, shorten privacy copy, and fix ticket text colors.
Replace hero pan animations with gentle opacity breathing, add bottom-right consent modal with accept/decline, condense privacy policy with contact for full details, and ensure ticket card descriptions read green on white cards in the tickets section.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 15:38:23 +03:00

133 lines
4.7 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(
"topo-card-surface 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-[#3d5248]">
{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 text-[#0d3d26] md:text-xl">
{tier.name}
</h3>
<p className="mt-1.5 line-clamp-2 text-sm text-[#3d5248]">
{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>
);
}