diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..901321d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,78 @@ +# BLACK HEART DANCE HOUSE — Project Context + +## About +Landing page for "BLACK HEART DANCE HOUSE" — a pole dance school in Minsk, Belarus. +Instagram: @blackheartdancehouse +Content language: Russian + +## Tech Stack +- **Next.js 15** (App Router, TypeScript) +- **Tailwind CSS v4** (light + dark mode, class-based toggle) +- **lucide-react** for icons +- **Fonts**: Inter (body) + Oswald (headings) via `next/font` +- **Hosting**: Vercel (planned) + +## Code Style +- Function declarations for components (not arrow functions) +- PascalCase for component files, camelCase for utils +- `@/` path alias for imports +- Semantic CSS classes via `@apply`: `surface-base`, `surface-muted`, `heading-text`, `body-text`, `nav-link`, `card`, `contact-item`, `contact-icon`, `theme-border` +- Only Header + ThemeToggle are client components (minimal JS shipped) +- `next/image` with `unoptimized` for PNGs that need transparency preserved + +## Project Structure +``` +src/ +├── app/ +│ ├── layout.tsx # Root layout, fonts, metadata +│ ├── page.tsx # Landing: Hero → Team → About → Classes → Contact +│ ├── globals.css # Tailwind imports +│ ├── styles/ +│ │ ├── theme.css # Theme variables, semantic classes +│ │ └── animations.css # Keyframes, scroll reveal, modal animations +│ ├── icon.png # Favicon +│ └── apple-icon.png +├── components/ +│ ├── layout/ +│ │ ├── Header.tsx # Sticky nav, mobile menu, theme toggle ("use client") +│ │ └── Footer.tsx +│ ├── sections/ +│ │ ├── Hero.tsx +│ │ ├── Team.tsx # "use client" — clickable cards + modal +│ │ ├── About.tsx +│ │ ├── Classes.tsx +│ │ └── Contact.tsx +│ └── ui/ +│ ├── Button.tsx +│ ├── SectionHeading.tsx +│ ├── SocialLinks.tsx +│ ├── ThemeToggle.tsx +│ ├── Reveal.tsx # Intersection Observer scroll reveal +│ └── TeamMemberModal.tsx # "use client" — member popup +├── data/ +│ └── content.ts # ALL Russian text, structured for future CMS +├── lib/ +│ └── constants.ts # BRAND constants, NAV_LINKS +└── types/ + ├── index.ts + ├── content.ts # SiteContent, TeamMember, ClassItem, ContactInfo + └── navigation.ts +``` + +## Brand / Styling +- **Accent**: rose/red (`#e11d48`) +- **Dark mode**: bg `#0a0a0a`, surface `#171717` +- **Light mode**: bg `#fafafa`, surface `#ffffff` +- Logo: transparent PNG, uses `dark:invert` + `unoptimized` + +## Content Data +- All text lives in `src/data/content.ts` (type-safe, one file to edit) +- 13 team members with photos, Instagram links, and personal descriptions +- 6 class types (Exotic Pole Dance, Pole Dance, Body Plastic, etc.) +- 2 addresses in Minsk, Yandex Maps embed with markers +- Contact: phone, Instagram + +## Git +- Remote: Gitea at `git.dolgolyov-family.by` +- User: diana.dolgolyova +- Branch: main diff --git a/public/images/classes/body-plastic.webp b/public/images/classes/body-plastic.webp new file mode 100644 index 0000000..cba3ae5 Binary files /dev/null and b/public/images/classes/body-plastic.webp differ diff --git a/public/images/classes/exot-w.webp b/public/images/classes/exot-w.webp new file mode 100644 index 0000000..f7e18e0 Binary files /dev/null and b/public/images/classes/exot-w.webp differ diff --git a/public/images/classes/exot.webp b/public/images/classes/exot.webp new file mode 100644 index 0000000..b781d92 Binary files /dev/null and b/public/images/classes/exot.webp differ diff --git a/public/images/classes/master-class-1.webp b/public/images/classes/master-class-1.webp new file mode 100644 index 0000000..9e99571 Binary files /dev/null and b/public/images/classes/master-class-1.webp differ diff --git a/public/images/classes/master-class-2.webp b/public/images/classes/master-class-2.webp new file mode 100644 index 0000000..cf7e641 Binary files /dev/null and b/public/images/classes/master-class-2.webp differ diff --git a/public/images/classes/master-class-3.webp b/public/images/classes/master-class-3.webp new file mode 100644 index 0000000..9984bcb Binary files /dev/null and b/public/images/classes/master-class-3.webp differ diff --git a/public/images/classes/online-classes.webp b/public/images/classes/online-classes.webp new file mode 100644 index 0000000..3c6d4b7 Binary files /dev/null and b/public/images/classes/online-classes.webp differ diff --git a/public/images/classes/parter-1.webp b/public/images/classes/parter-1.webp new file mode 100644 index 0000000..14d2141 Binary files /dev/null and b/public/images/classes/parter-1.webp differ diff --git a/public/images/classes/parter-2.webp b/public/images/classes/parter-2.webp new file mode 100644 index 0000000..78e2952 Binary files /dev/null and b/public/images/classes/parter-2.webp differ diff --git a/public/images/classes/pole-dance.webp b/public/images/classes/pole-dance.webp new file mode 100644 index 0000000..f00ce16 Binary files /dev/null and b/public/images/classes/pole-dance.webp differ diff --git a/src/app/styles/components.css b/src/app/styles/components.css index ac794af..0ee625e 100644 --- a/src/app/styles/components.css +++ b/src/app/styles/components.css @@ -47,6 +47,26 @@ @apply dark:text-neutral-400 dark:hover:text-white; } +/* ===== Scrollbar ===== */ + +.modal-content { + scrollbar-color: rgb(163 163 163) transparent; +} + +.modal-content * { + scrollbar-color: rgb(163 163 163) transparent; +} + +@variant dark { + .modal-content { + scrollbar-color: rgb(64 64 64) transparent; + } + + .modal-content * { + scrollbar-color: rgb(64 64 64) transparent; + } +} + /* ===== Contact ===== */ .contact-item { diff --git a/src/components/sections/Classes.tsx b/src/components/sections/Classes.tsx index 0959d19..89d41fa 100644 --- a/src/components/sections/Classes.tsx +++ b/src/components/sections/Classes.tsx @@ -1,7 +1,12 @@ +"use client"; + +import { useState } from "react"; import { Flame, Sparkles, Wind, Zap, Star, Monitor } from "lucide-react"; import { siteContent } from "@/data/content"; import { SectionHeading } from "@/components/ui/SectionHeading"; import { Reveal } from "@/components/ui/Reveal"; +import { ClassModal } from "@/components/ui/ClassModal"; +import type { ClassItem } from "@/types"; const iconMap: Record = { flame: , @@ -14,6 +19,7 @@ const iconMap: Record = { export function Classes() { const { classes } = siteContent; + const [selectedClass, setSelectedClass] = useState(null); return (
@@ -24,8 +30,11 @@ export function Classes() {
{classes.items.map((item) => ( - -
+ +
setSelectedClass(item)} + >
{iconMap[item.icon]}

{item.name}

{item.description}

@@ -34,6 +43,11 @@ export function Classes() { ))}
+ + setSelectedClass(null)} + />
); } diff --git a/src/components/sections/Contact.tsx b/src/components/sections/Contact.tsx index ec04429..cd99966 100644 --- a/src/components/sections/Contact.tsx +++ b/src/components/sections/Contact.tsx @@ -1,4 +1,4 @@ -import { MapPin, Phone, Mail, Clock, Instagram } from "lucide-react"; +import { MapPin, Phone, Clock, Instagram } from "lucide-react"; import { siteContent } from "@/data/content"; import { BRAND } from "@/lib/constants"; import { SectionHeading } from "@/components/ui/SectionHeading"; @@ -28,13 +28,6 @@ export function Contact() { -
- - - {contact.email} - -
-

{contact.workingHours}

diff --git a/src/components/ui/ClassModal.tsx b/src/components/ui/ClassModal.tsx new file mode 100644 index 0000000..23ff284 --- /dev/null +++ b/src/components/ui/ClassModal.tsx @@ -0,0 +1,94 @@ +"use client"; + +import { useEffect } from "react"; +import Image from "next/image"; +import { X, Flame, Sparkles, Wind, Zap, Star, Monitor } from "lucide-react"; +import type { ClassItem } from "@/types"; + +const iconMap: Record = { + flame: , + sparkles: , + wind: , + zap: , + star: , + monitor: , +}; + +interface ClassModalProps { + classItem: ClassItem | null; + onClose: () => void; +} + +export function ClassModal({ classItem, onClose }: ClassModalProps) { + useEffect(() => { + if (!classItem) return; + + document.body.style.overflow = "hidden"; + + function handleKeyDown(e: KeyboardEvent) { + if (e.key === "Escape") onClose(); + } + + document.addEventListener("keydown", handleKeyDown); + return () => { + document.body.style.overflow = ""; + document.removeEventListener("keydown", handleKeyDown); + }; + }, [classItem, onClose]); + + if (!classItem) return null; + + return ( +
+
e.stopPropagation()} + > + + +
+
+
{iconMap[classItem.icon]}
+

+ {classItem.name} +

+
+ + {classItem.detailedDescription && ( +
+ {classItem.detailedDescription} +
+ )} + + {classItem.images && classItem.images.length > 0 && ( +
+ {classItem.images.map((src, i) => ( +
+ {`${classItem.name} +
+ ))} +
+ )} +
+
+
+ ); +} diff --git a/src/data/content.ts b/src/data/content.ts index d306695..8fafd8e 100644 --- a/src/data/content.ts +++ b/src/data/content.ts @@ -136,35 +136,53 @@ export const siteContent: SiteContent = { description: "Чувственная хореография с элементами pole dance в каблуках.", icon: "sparkles", + detailedDescription: + "Чувственный, эстетичный, сексуальный вид танца. Он богат на плавные линии, манящие прогибы и развитие вашей женственности.\n\nВы получаете:\n— уверенность в себе,\n— красивую фигуру и развитие всех групп мышц,\n— раскрытие себя с новой стороны и возможность влюбиться заново,\n— вы учитесь наслаждаться собой.", + images: ["/images/classes/exot.webp", "/images/classes/exot-w.webp"], }, { name: "Pole Dance", description: "Сила, грация и пластика на пилоне. Для любого уровня подготовки.", icon: "flame", + detailedDescription: + "Пилон — это отличный тренажер для рук, ног, спины и пресса. Pole Dance учит красиво двигаться, улучшает растяжку, силовые показатели и выдержку.\n\nВы получите:\n— силу и грацию,\n— прекрасную растяжку,\n— правильную осанку,\n— прекрасное настроение.", + images: ["/images/classes/pole-dance.webp"], }, { name: "Body Plastic", description: "Танцевальное направление, раскрывающее женственность и пластику тела.", icon: "wind", + detailedDescription: + "Растяжка — это искусство, которое позволяет вам не только улучшить гибкость, но и раскрыть истинную красоту вашего тела. Это больше, чем просто упражнения — это плавные движения, которые учат вас слушать своё тело и чувствовать его.\n\nЗанимаясь растяжкой, вы получите:\n— уверенность в себе,\n— красивую осанку и гибкость,\n— улучшение общего тонуса тела и расслабление мышц,\n— возможность открыть новые грани своей чувственности и женственности,\n— умение наслаждаться каждым движением и моментом.\n\nРастяжка помогает вам не только достигнуть физического совершенства, но и найти внутреннюю гармонию и любовь к себе.", + images: ["/images/classes/body-plastic.webp"], }, { name: "Партерная акробатика", description: "Акробатические элементы в партере для развития силы и гибкости.", icon: "zap", + detailedDescription: + "Партерная акробатика — это завораживающее сочетание силы, гибкости и грации, которое раскрывает безграничные возможности вашего тела. Этот вид искусства позволяет вам воплотить в жизнь самые смелые акробатические элементы, создавая уникальные и впечатляющие комбинации на полу.\n\nЗанимаясь партерной акробатикой, вы получите:\n— невероятную физическую силу и выносливость,\n— улучшение координации и равновесия,\n— развитие всех групп мышц и повышение гибкости,\n— возможность выразить себя через мощные и динамичные движения,\n— уверенность в своих возможностях и преодоление собственных границ.\n\nПартерная акробатика — это путь к совершенству тела и духа, который дарит ощущение полёта и свободы на земле.", + images: ["/images/classes/parter-1.webp", "/images/classes/parter-2.webp"], }, { name: "Мастер классы", description: "Уникальные занятия с приглашёнными топовыми тренерами.", icon: "star", + detailedDescription: + "Мастер-классы — это уникальная возможность погрузиться в чувственный мир танца, где каждое движение наполнено грацией и страстью. Наши мастер-классы созданы для тех, кто хочет открыть в себе новые грани женственности и научиться выражать свои эмоции через танец.\n\nПриходя на наши мастер-классы, вы получите:\n— уверенность в себе и своих возможностях,\n— возможность раскрыть свою чувственность и сексуальность,\n— умение наслаждаться каждым моментом и каждым движением,\n— опыт от профессиональных тренеров, которые помогут вам достичь новых высот.\n\nНаши мастер-классы — это не просто тренировки, это путь к самопознанию и любви к своему телу. Присоединяйтесь к нам и откройте для себя мир танца, где каждый шаг приносит удовольствие и уверенность.", + images: ["/images/classes/master-class-1.webp", "/images/classes/master-class-2.webp", "/images/classes/master-class-3.webp"], }, { name: "Онлайн занятия", description: "Тренировки в удобное время из любой точки мира.", icon: "monitor", + detailedDescription: + "Если вы находитесь не в Минске, у вас всё равно есть уникальная возможность тренироваться, расти и развиваться с нами! Мы предлагаем занятия онлайн по следующим направлениям: партерная акробатика, Pole Dance, Exotic Pole Dance, Exo-tricks, полёты.\n\nМы предлагаем два способа работы: самостоятельный и VIP. В самостоятельный тариф входит доступ к видеозаписям уроков по выбранному направлению, в VIP-тарифе вы также получите доступ к чату с куратором в Telegram, который подскажет и скорректирует в случае трудностей в процессе изучения материала.", + images: ["/images/classes/online-classes.webp"], }, ], }, @@ -175,7 +193,6 @@ export const siteContent: SiteContent = { "г. Минск, Притыцкого, 62к1", ], phone: "+375 29 389-70-01", - email: "info@blackheartdance.by", instagram: "https://instagram.com/blackheartdancehouse/", mapEmbedUrl: "https://yandex.ru/map-widget/v1/?ll=27.512%2C53.912&z=12&l=map&pt=27.5656%2C53.91583%2Cpm2rdm~27.45974%2C53.90832%2Cpm2rdm", diff --git a/src/types/content.ts b/src/types/content.ts index 996cd17..ee5bd05 100644 --- a/src/types/content.ts +++ b/src/types/content.ts @@ -2,6 +2,8 @@ export interface ClassItem { name: string; description: string; icon: string; + detailedDescription?: string; + images?: string[]; } export interface TeamMember { @@ -16,7 +18,6 @@ export interface ContactInfo { title: string; addresses: string[]; phone: string; - email: string; instagram: string; mapEmbedUrl: string; workingHours: string;