initial site

This commit is contained in:
merrow committed 2026-10-11 22:24:54 +05:00
commit da5b259d06
167 files changed
+34157

No files matched your search

+257
View File
@@ -0,0 +1,257 @@
import { useCallback, useEffect, useRef, useState } from "react";
import useEmblaCarousel from "embla-carousel-react";
import Autoplay from "embla-carousel-autoplay";
import { ArrowRight, Bot, ChevronLeft, ChevronRight, Coffee, Flame, Sparkles } from "lucide-react";
import { cn } from "../utils/cn";
import { Reveal, SectionHeading } from "./shared";
import { signatureMixes, strengthLabels, type SignatureMix } from "../data/catalog";
const items = signatureMixes;
function MenuCard({ item, index }: { item: SignatureMix; index: number }) {
return (
<article className="group relative h-full rounded-3xl bg-gradient-to-br from-neon-violet/50 via-white/10 to-neon-cyan/40 p-[1px] transition-all duration-500 hover:-translate-y-2 hover:shadow-[0_24px_70px_-18px_rgba(176,38,255,0.4)]">
<div className="holo-sheen relative flex h-full flex-col rounded-[calc(1.5rem-1px)] bg-panel/85 p-5 backdrop-blur-xl sm:p-7">
<span className="pointer-events-none absolute right-5 top-4 select-none font-display text-6xl font-extrabold text-white/[0.05]">
0{index + 1}
</span>
{item.badge && (
<span
className={cn(
"inline-flex w-fit items-center rounded-full border px-3 py-1 text-[10px] font-bold uppercase tracking-[0.2em]",
item.badge.hot
? "border-neon-violet/60 bg-neon-violet/15 text-fuchsia-200 shadow-[0_0_18px_rgba(176,38,255,0.35)]"
: "border-white/15 bg-white/5 text-mist/60"
)}
>
{item.badge.label}
</span>
)}
<h3 className="mt-5 font-display text-lg font-bold leading-snug text-white transition-colors duration-300 group-hover:text-cyan-100 sm:text-xl">
{item.name}
</h3>
<p className="mt-3 flex-1 text-sm leading-relaxed text-mist/65">{item.desc}</p>
{/* strength */}
<div className="mt-6 flex items-center gap-2.5">
<span className="text-[10px] font-bold uppercase tracking-[0.25em] text-mist/45">
Крепость · {strengthLabels[item.strength]}
</span>
<span className="flex items-center gap-1.5">
{[1, 2, 3].map((d) => (
<span
key={d}
className={cn(
"h-1.5 w-1.5 rounded-full",
d <= item.strength
? "bg-neon-cyan shadow-[0_0_8px_rgba(0,240,255,0.9)]"
: "bg-white/15"
)}
/>
))}
</span>
</div>
<div className="mt-5 h-px bg-gradient-to-r from-neon-violet/40 via-white/10 to-neon-cyan/40" />
<div className="mt-5 flex items-center justify-between">
<span className="font-display text-2xl font-bold text-white text-glow-cyan">
{item.price}
</span>
<a
href="#contacts"
aria-label={`Забронировать и попробовать ${item.name}`}
className="flex h-11 w-11 items-center justify-center rounded-full border border-white/15 text-mist/70 transition-all duration-300 group-hover:border-neon-cyan/60 group-hover:text-neon-cyan group-hover:shadow-[0_0_20px_rgba(0,240,255,0.45)]"
>
<ArrowRight size={18} />
</a>
</div>
</div>
</article>
);
}
export default function MenuCarousel() {
const autoplay = useRef(
Autoplay({ delay: 5200, stopOnInteraction: false, stopOnMouseEnter: true })
);
const [emblaRef, emblaApi] = useEmblaCarousel(
{ loop: true, align: "start" },
[autoplay.current]
);
const [canPrev, setCanPrev] = useState(true);
const [canNext, setCanNext] = useState(true);
const prev = useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);
const next = useCallback(() => emblaApi?.scrollNext(), [emblaApi]);
useEffect(() => {
if (!emblaApi) return;
const update = () => {
setCanPrev(emblaApi.canScrollPrev());
setCanNext(emblaApi.canScrollNext());
};
update();
emblaApi.on("select", update);
return () => {
emblaApi.off("select", update);
};
}, [emblaApi]);
return (
<section id="prices" className="relative overflow-hidden py-24 sm:py-32">
{/* smoky backdrop */}
<div className="absolute inset-0">
<img
src="./images/menu-bg.jpg"
alt=""
aria-hidden
className="h-full w-full object-cover opacity-25"
/>
<div className="absolute inset-0 bg-gradient-to-b from-void via-void/70 to-void" />
</div>
<div className="relative mx-auto max-w-7xl px-5 sm:px-8">
<div className="flex flex-col gap-8 sm:flex-row sm:items-end sm:justify-between">
<Reveal>
<SectionHeading
align="left"
tag="Меню"
title={
<>
Что <span className="text-glow-cyan text-cyan-300">попробовать</span>
</>
}
desc="Авторские миксы от наших кальянных мастеров. Не нашёл своё — соберём любой вкус под тебя. Цена единая — без сюрпризов в чеке."
/>
</Reveal>
<Reveal delay={120} className="hidden sm:block">
<div className="flex gap-3">
<button
onClick={prev}
disabled={!canPrev}
aria-label="Назад"
className="glass flex h-12 w-12 items-center justify-center rounded-full text-white transition-all duration-300 hover:border-neon-violet/60 hover:shadow-[0_0_24px_rgba(176,38,255,0.45)] disabled:opacity-40 disabled:hover:shadow-none"
>
<ChevronLeft size={22} />
</button>
<button
onClick={next}
disabled={!canNext}
aria-label="Вперёд"
className="glass flex h-12 w-12 items-center justify-center rounded-full text-white transition-all duration-300 hover:border-neon-cyan/60 hover:shadow-[0_0_24px_rgba(0,240,255,0.45)] disabled:opacity-40 disabled:hover:shadow-none"
>
<ChevronRight size={22} />
</button>
</div>
</Reveal>
</div>
<Reveal delay={150}>
<div className="mt-10 grid gap-3 sm:grid-cols-2 sm:gap-4">
<div className="relative overflow-hidden rounded-3xl border border-neon-violet/40 bg-gradient-to-br from-neon-violet/25 via-panel/90 to-panel/90 p-5 shadow-[0_0_45px_-12px_rgba(176,38,255,0.6)] sm:p-7">
<div className="pointer-events-none absolute -right-12 -top-12 h-40 w-40 rounded-full bg-neon-violet/30 blur-3xl" />
<div className="relative flex flex-wrap items-center gap-x-4 gap-y-3">
<div className="flex flex-1 basis-[190px] items-center gap-3 sm:gap-4">
<span className="flex shrink-0 items-center justify-center rounded-2xl border border-neon-violet/50 bg-neon-violet/20 p-2.5 text-fuchsia-200 shadow-[0_0_22px_rgba(176,38,255,0.5)] sm:p-3.5">
<Flame size={20} className="sm:hidden" />
<Flame size={24} className="hidden sm:block" />
</span>
<div>
<p className="font-display text-base font-bold text-white sm:text-xl">
Кальян от
</p>
<p className="mt-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-mist/55 sm:text-xs sm:tracking-[0.2em]">
от 1 400 ₽
</p>
</div>
</div>
<span className="text-glow-violet ml-auto shrink-0 whitespace-nowrap font-display text-2xl font-extrabold text-white sm:text-4xl">
1 400 ₽
</span>
</div>
</div>
<div className="relative overflow-hidden rounded-3xl border border-neon-cyan/35 bg-gradient-to-br from-neon-cyan/15 via-panel/90 to-panel/90 p-5 shadow-[0_0_45px_-12px_rgba(0,240,255,0.45)] sm:p-7">
<div className="pointer-events-none absolute -right-12 -top-12 h-40 w-40 rounded-full bg-neon-cyan/20 blur-3xl" />
<div className="relative flex flex-wrap items-center gap-x-4 gap-y-3">
<div className="flex flex-1 basis-[190px] items-center gap-3 sm:gap-4">
<span className="flex shrink-0 items-center justify-center rounded-2xl border border-neon-cyan/50 bg-neon-cyan/10 p-2.5 text-neon-cyan shadow-[0_0_22px_rgba(0,240,255,0.4)] sm:p-3.5">
<Coffee size={20} className="sm:hidden" />
<Coffee size={24} className="hidden sm:block" />
</span>
<div>
<p className="font-display text-base font-bold text-white sm:text-xl">
Авторский чай
</p>
<p className="mt-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-mist/55 sm:text-xs sm:tracking-[0.2em]">
Чайник на компанию
</p>
</div>
</div>
<span className="text-glow-cyan ml-auto shrink-0 whitespace-nowrap font-display text-2xl font-extrabold text-white sm:text-4xl">
350 ₽
</span>
</div>
</div>
</div>
</Reveal>
<Reveal delay={180}>
<div ref={emblaRef} className="mt-10 cursor-grab overflow-hidden active:cursor-grabbing">
<div className="-ml-4 flex pb-3">
{items.map((item, i) => (
<div
key={i}
className="min-w-0 shrink-0 grow-0 basis-[88%] pl-4 pr-6 sm:basis-[58%] lg:basis-[36%] xl:basis-[31%]"
>
<MenuCard item={item} index={i} />
</div>
))}
</div>
</div>
</Reveal>
<Reveal delay={260}>
<div className="mt-10 flex flex-col items-center justify-between gap-5 sm:flex-row">
<p className="max-w-md text-center text-xs leading-relaxed text-mist/45 sm:text-left">
* Кальян от <span className="font-bold text-mist/70">1 400 ₽</span>,
чай — <span className="font-bold text-mist/70">350 ₽</span>.
</p>
<div className="flex flex-wrap justify-center gap-3 sm:justify-end">
<a
href="#/ai-mix"
className="inline-flex items-center gap-2 rounded-full border border-neon-cyan/60 bg-gradient-to-r from-neon-violet/25 via-neon-cyan/25 to-neon-violet/20 px-6 py-3.5 text-sm font-bold text-white shadow-[0_0_25px_rgba(0,240,255,0.35)] transition-all duration-300 hover:-translate-y-0.5 hover:border-neon-cyan hover:shadow-[0_0_38px_rgba(0,240,255,0.6)]"
>
<Bot size={16} className="text-neon-cyan" />
Автоподбор микса
<Sparkles size={14} className="text-cyan-200" />
</a>
<a
href="#/mixes"
className="inline-flex items-center gap-2.5 rounded-full border border-neon-violet/50 bg-neon-violet/10 px-6 py-3.5 text-sm font-bold text-white transition-all duration-300 hover:-translate-y-0.5 hover:border-neon-cyan hover:shadow-[0_0_28px_rgba(0,240,255,0.4)]"
>
Все миксы
<ArrowRight size={16} />
</a>
<a
href="#/menu"
className="inline-flex items-center gap-2.5 rounded-full bg-gradient-to-r from-neon-violet to-[#7a1fd6] px-7 py-3.5 text-sm font-bold text-white shadow-[0_0_25px_rgba(176,38,255,0.4)] transition-all duration-300 hover:-translate-y-0.5 hover:shadow-[0_0_40px_rgba(176,38,255,0.65)]"
>
Карта меню
<ArrowRight size={16} />
</a>
<a
href="#contacts"
className="inline-flex items-center gap-2.5 rounded-full border border-neon-violet/50 bg-neon-violet/10 px-7 py-3.5 text-sm font-bold text-white transition-all duration-300 hover:-translate-y-0.5 hover:border-neon-cyan hover:shadow-[0_0_28px_rgba(0,240,255,0.4)]"
>
Забронировать
</a>
</div>
</div>
</Reveal>
</div>
</section>
);
}