feat: UI improvements — scrollbar, multi-filters, pricing fix, routing, modals

- Global page scrollbar styled with gold theme
- Schedule: multi-select for class types and status tags
- Pricing: fix tab switch blink (display toggle vs conditional render)
- OpenDay: trainer name more prominent, section divider added
- Team: browser back button closes trainer bio (history API)
- Modals: block scroll + compensate scrollbar width to prevent layout shift
- Header: remove booking button from desktop nav
This commit is contained in:
2026-03-26 13:23:03 +03:00
parent 228e547e10
commit 8088b99a43
17 changed files with 275 additions and 229 deletions
+111 -119
View File
@@ -53,136 +53,128 @@ export function Pricing({ data: pricing }: PricingProps) {
</Reveal>
{/* Prices tab */}
{activeTab === "prices" && (
<Reveal>
<div className="mx-auto mt-10 max-w-4xl">
<p className="mb-8 text-center text-sm text-neutral-500 dark:text-neutral-400">
{pricing.subtitle}
</p>
<div className={activeTab === "prices" ? "block" : "hidden"}>
<div className="mx-auto mt-10 max-w-4xl">
<p className="mb-8 text-center text-sm text-neutral-500 dark:text-neutral-400">
{pricing.subtitle}
</p>
{/* Cards grid */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
{regularItems.map((item, i) => {
const isPopular = item.popular ?? false;
return (
<div
key={i}
className={`group relative rounded-2xl border p-5 transition-all duration-300 ${
isPopular
? "border-gold/40 bg-gradient-to-br from-gold/10 via-transparent to-gold/5 dark:from-gold/[0.07] dark:to-gold/[0.02] shadow-lg shadow-gold/10"
: "border-neutral-200 bg-white dark:border-white/[0.06] dark:bg-[#0a0a0a]"
}`}
>
{/* Popular badge */}
{isPopular && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
<span className="inline-flex items-center gap-1 rounded-full bg-gold px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-black shadow-md shadow-gold/30">
<Sparkles size={10} />
Популярный
</span>
</div>
{/* Cards grid */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
{regularItems.map((item, i) => {
const isPopular = item.popular ?? false;
return (
<div
key={i}
className={`group relative rounded-2xl border p-5 transition-all duration-300 ${
isPopular
? "border-gold/40 bg-gradient-to-br from-gold/10 via-transparent to-gold/5 dark:from-gold/[0.07] dark:to-gold/[0.02] shadow-lg shadow-gold/10"
: "border-neutral-200 bg-white dark:border-white/[0.06] dark:bg-[#0a0a0a]"
}`}
>
{/* Popular badge */}
{isPopular && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
<span className="inline-flex items-center gap-1 rounded-full bg-gold px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-black shadow-md shadow-gold/30">
<Sparkles size={10} />
Популярный
</span>
</div>
)}
<div className={isPopular ? "mt-1" : ""}>
{/* Name */}
<p className={`text-sm font-medium ${isPopular ? "text-gold-dark dark:text-gold-light" : "text-neutral-700 dark:text-neutral-300"}`}>
{item.name}
</p>
{/* Note */}
{item.note && (
<p className="mt-1 text-xs text-neutral-400 dark:text-neutral-500">
{item.note}
</p>
)}
<div className={isPopular ? "mt-1" : ""}>
{/* Name */}
<p className={`text-sm font-medium ${isPopular ? "text-gold-dark dark:text-gold-light" : "text-neutral-700 dark:text-neutral-300"}`}>
{item.name}
</p>
{/* Note */}
{item.note && (
<p className="mt-1 text-xs text-neutral-400 dark:text-neutral-500">
{item.note}
</p>
)}
{/* Price */}
<p className={`mt-3 font-display text-2xl font-bold ${isPopular ? "text-gold" : "text-neutral-900 dark:text-white"}`}>
{item.price}
</p>
</div>
{/* Price */}
<p className={`mt-3 font-display text-2xl font-bold ${isPopular ? "text-gold" : "text-neutral-900 dark:text-white"}`}>
{item.price}
</p>
</div>
);
})}
</div>
{/* Featured — big card */}
{featuredItem && (
<div className="mt-6 w-full team-card-glitter rounded-2xl border border-gold/30 bg-gradient-to-r from-gold/10 via-gold/5 to-gold/10 dark:from-gold/[0.06] dark:via-transparent dark:to-gold/[0.06] p-6 sm:p-8">
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
<div className="text-center sm:text-left">
<div className="flex items-center justify-center gap-2 sm:justify-start">
<Crown size={18} className="text-gold" />
<p className="text-lg font-bold text-neutral-900 dark:text-white">
{featuredItem.name}
</p>
</div>
{featuredItem.note && (
<p className="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
{featuredItem.note}
</p>
)}
</div>
<p className="shrink-0 font-display text-3xl font-bold text-gold">
{featuredItem.price}
</p>
</div>
</div>
)}
);
})}
</div>
</div>
</Reveal>
)}
{/* Rental tab */}
{activeTab === "rental" && (
<Reveal>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rentalItems.map((item, i) => (
<div
key={i}
className="flex items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white px-6 py-5 dark:border-white/[0.06] dark:bg-[#0a0a0a]"
>
<div>
<p className="font-medium text-neutral-900 dark:text-white">
{item.name}
</p>
{item.note && (
<p className="mt-0.5 text-sm text-neutral-500 dark:text-neutral-400">
{item.note}
{/* Featured — big card */}
{featuredItem && (
<div className="mt-6 w-full team-card-glitter rounded-2xl border border-gold/30 bg-gradient-to-r from-gold/10 via-gold/5 to-gold/10 dark:from-gold/[0.06] dark:via-transparent dark:to-gold/[0.06] p-6 sm:p-8">
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
<div className="text-center sm:text-left">
<div className="flex items-center justify-center gap-2 sm:justify-start">
<Crown size={18} className="text-gold" />
<p className="text-lg font-bold text-neutral-900 dark:text-white">
{featuredItem.name}
</p>
</div>
{featuredItem.note && (
<p className="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
{featuredItem.note}
</p>
)}
</div>
<span className="shrink-0 font-display text-xl font-bold text-gold-dark dark:text-gold-light">
{item.price}
</span>
</div>
))}
</div>
</Reveal>
)}
{/* Rules tab */}
{activeTab === "rules" && (
<Reveal>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rules.map((rule, i) => (
<div
key={i}
className="flex gap-4 rounded-2xl border border-neutral-200 bg-white px-5 py-4 dark:border-white/[0.06] dark:bg-[#0a0a0a]"
>
<span className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gold/10 text-xs font-bold text-gold-dark dark:bg-gold/10 dark:text-gold-light">
{i + 1}
</span>
<p className="text-sm leading-relaxed text-neutral-700 dark:text-neutral-300">
{rule}
<p className="shrink-0 font-display text-3xl font-bold text-gold">
{featuredItem.price}
</p>
</div>
))}
</div>
</Reveal>
)}
</div>
)}
</div>
</div>
{/* Rental tab */}
<div className={activeTab === "rental" ? "block" : "hidden"}>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rentalItems.map((item, i) => (
<div
key={i}
className="flex items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white px-6 py-5 dark:border-white/[0.06] dark:bg-[#0a0a0a]"
>
<div>
<p className="font-medium text-neutral-900 dark:text-white">
{item.name}
</p>
{item.note && (
<p className="mt-0.5 text-sm text-neutral-500 dark:text-neutral-400">
{item.note}
</p>
)}
</div>
<span className="shrink-0 font-display text-xl font-bold text-gold-dark dark:text-gold-light">
{item.price}
</span>
</div>
))}
</div>
</div>
{/* Rules tab */}
<div className={activeTab === "rules" ? "block" : "hidden"}>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rules.map((rule, i) => (
<div
key={i}
className="flex gap-4 rounded-2xl border border-neutral-200 bg-white px-5 py-4 dark:border-white/[0.06] dark:bg-[#0a0a0a]"
>
<span className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gold/10 text-xs font-bold text-gold-dark dark:bg-gold/10 dark:text-gold-light">
{i + 1}
</span>
<p className="text-sm leading-relaxed text-neutral-700 dark:text-neutral-300">
{rule}
</p>
</div>
))}
</div>
</div>
</div>
</section>
);