diff --git a/src/components/sections/DinoActivities.tsx b/src/components/sections/DinoActivities.tsx index 3cb46b2..2c7e073 100644 --- a/src/components/sections/DinoActivities.tsx +++ b/src/components/sections/DinoActivities.tsx @@ -1,10 +1,13 @@ /* eslint-disable @next/next/no-img-element */ const FONT_MONT = { fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' } +const FONT_POP = { fontFamily: 'var(--font-poppins, Poppins), sans-serif' } interface Activity { name: string + subtitle?: string | null price?: string | null + priceUnit?: string | null description?: string | null imageUrl?: string | null href?: string | null @@ -17,21 +20,48 @@ interface DinoActivitiesProps { } const DEFAULT_ACTIVITIES: Activity[] = [ - { name: 'Звичайна екскурсія', price: '150 грн', href: '#tickets' }, - { name: 'Палеонтологічна екскурсія', price: '300 грн', href: '#tickets' }, - { name: 'ДиноРодео', price: '50 грн', href: '#tickets' }, + { + name: 'Звичайна екскурсія', + subtitle: 'дізнайтеся більше про світ диновелетнів', + price: '150 грн', + priceUnit: 'за 1 людину', + description: + 'На екскурсії ви та дітлахи дізнаєтесь, які динозаври були найбільшими та найнебезпечнішими, чим вони харчувалися, як полювали і чому зникли з лиця Землі.', + imageUrl: '/dynopark/Gemini_Generated_Image_7b985p7b985p7b98_.jpg', + href: '#tickets', + }, + { + name: 'Палеонтологічна екскурсія', + subtitle: 'відчуйте себе першовідкривачами', + price: '300 грн', + priceUnit: 'за 1 людину', + description: + 'Це справжня наукова пригода! Діти візьмуть участь у розкопках, власноруч викопають справжню скамʼянілість і почують захопливі факти про динозаврів.', + imageUrl: '/dynopark/Gemini_Generated_Image_a10736a10736a107_.jpg', + href: '#tickets', + }, + { + name: 'ДиноРодео', + subtitle: 'прокатіться на динозаврі з вітерком', + price: '50 грн', + priceUnit: 'за 1 людину', + description: + 'Безпечна пригода для дітлахів — висота фігури не більше 1,6 м. Але нудьгувати не доведеться: динозавр коливається, рухає хвостом та головою.', + imageUrl: '/dynopark/Gemini_Generated_Image_gc7t4lgc7t4lgc7t_.jpg', + href: '#tickets', + }, ] export function DinoActivities({ - title = 'Додаткові розваги у динопарку', - description = 'Хочете дізнатись ще більше про динозаврів? Замовте екскурсію з гідом, поринь у світ палеонтологічних розкопок або підкорюй справжнього динозавра!', + title = 'Додаткові розваги у ДиноПарку', + description = 'Хочете зробити пригоду ще цікавішою? Замовте екскурсію — і дізнайтесь більше про динозаврів: їхнє походження, спосіб життя та цікаві факти.', activities = DEFAULT_ACTIVITIES, }: DinoActivitiesProps) { if (!activities.length) return null return (
-
+

{description && (

{description}

)} -
+
{activities.map((act, i) => ( ))} @@ -60,9 +90,12 @@ export function DinoActivities({ function ActivityCard({ activity }: { activity: Activity }) { const href = activity.href ?? '#' return ( -
- {/* Photo area */} -
+
+ {/* Full-height photo */} +
{activity.imageUrl ? ( ) : ( -
+
)} - {/* Price badge */} + + {/* Gradient overlay — bottom-heavy for text legibility */} + - {/* Content */} -
-

- {activity.name} -

- {activity.description && ( -

- {activity.description} + {/* Card text — overlaid at bottom */} +

+

+ {activity.name}

- )} - - Замовити екскурсію - - + {activity.subtitle && ( +

+ {activity.subtitle} +

+ )} + {activity.description && ( +

+ {activity.description} +

+ )} + + {/* CTA */} + +
) diff --git a/src/components/sections/DinoGallery.tsx b/src/components/sections/DinoGallery.tsx index 3eaec91..7d73923 100644 --- a/src/components/sections/DinoGallery.tsx +++ b/src/components/sections/DinoGallery.tsx @@ -8,9 +8,10 @@ interface DinoGalleryProps { } const FALLBACK_GALLERY = [ - '/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.jpg', - '/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.jpg', - '/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.png', + '/dynopark/50182754852060_1.jpg', + '/dynopark/4da8605d916401919bbe9cf115d8f8a5_1.jpg', + '/dynopark/Untitled_6_2.jpg', + '/dynopark/if-when-jurassic-world-rebirth-gets-a-se.jpg', ] export function DinoGallery({ images }: DinoGalleryProps) { diff --git a/src/components/sections/DinoHero.tsx b/src/components/sections/DinoHero.tsx index 5a62527..6890dc7 100644 --- a/src/components/sections/DinoHero.tsx +++ b/src/components/sections/DinoHero.tsx @@ -2,6 +2,7 @@ import { BtnPrimary } from '@/components/ui/BtnPrimary' const FONT_MONT = { fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' } +const FONT_INTER = { fontFamily: 'Inter, sans-serif' } const DEFAULT_FEATURES = ['Повнорозмірні анімовані динозавра', 'Реалістичні рухи та звуки'] @@ -15,29 +16,29 @@ interface DinoHeroProps { } export function DinoHero({ - title = 'Динопарк — портал у світ динозаврів', - description = 'Великі динозаври, що рухаються та гарчать, справжнє роздоволлє, цікаві екскурсії та динородео — тут є все, щоб ваша дитина не нудьгувала.', + title = 'ДиноПарк — портал у світ динозаврів', + description = 'Великі динозаври, що рухаються та гарчать, справжні розкопки, цікаві екскурсії та динородео — тут є все, щоб ваша дитина не нудьгувала.', stat = '26', statLabel = 'унікальних експонатів', features = DEFAULT_FEATURES, heroImageUrl, }: DinoHeroProps) { return ( -
+
{/* Left column */} -
-
-
+
+
+
{/* Text */} -
+

{title}

{description} @@ -48,34 +49,35 @@ export function DinoHero({

{/* Stat badge + feature list */} -
+
+ {/* "26 унікальних експонатів" pill */}
{statLabel} @@ -85,11 +87,12 @@ export function DinoHero({ {features.map((f, i) => (
{f} @@ -100,50 +103,50 @@ export function DinoHero({
- {/* Right panel — hero image on yellow circle */} + {/* Right panel — hero image on orange circle */}

+
{loading ? Array.from({ length: 4 }).map((_, i) => ) : dynoTariffs.map((t) => )} @@ -177,16 +179,16 @@ export function DinoTickets({ {/* Combo */} {!loading && comboTariffs.length > 0 && ( <> -
-

+

Комбо -

+

{comboDescription}

diff --git a/src/components/sections/DinoWheel.tsx b/src/components/sections/DinoWheel.tsx index 71928ad..6cfd763 100644 --- a/src/components/sections/DinoWheel.tsx +++ b/src/components/sections/DinoWheel.tsx @@ -6,7 +6,6 @@ import { useState, useEffect, useRef, useCallback } from 'react' const FONT_MONT = { fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' } const FONT_POP = { fontFamily: 'var(--font-poppins, Poppins), sans-serif' } -// ── Types ────────────────────────────────────────────────────────────────── interface DinoSpec { name: string latinName?: string | null @@ -20,160 +19,162 @@ interface DinoWheelProps { dinos?: DinoSpec[] } -// ── Complete dataset extracted from Figma dino_slider (node 2025:672) ───── +// 25 dinosaurs from Figma with actual /dynopark/ image paths const FALLBACK: DinoSpec[] = [ { name: 'Тиранозавр Рекс', latinName: 'Гігантська Версія', length: '20 м.', height: '7 м.', - imageUrl: '/images/figma/dino-trex-giant.png', + imageUrl: '/dynopark/T-Rex_2_1.jpg', }, { name: 'Барионікс', length: '7 м.', height: '2,5 м.', - imageUrl: '/images/figma/dino-baryonyx.png', + imageUrl: null, }, { name: 'Овіраптор', length: '3 м.', - imageUrl: '/images/figma/dino-oviraptor.png', + imageUrl: '/dynopark/Oviraptor_TD_1.jpg', }, { name: 'Спінозавр', length: '15 м.', height: '5,8 м.', - imageUrl: '/images/figma/dino-spinosaurus.png', + imageUrl: '/dynopark/has-anyone-tried-incubating-and-releasin.jpg', }, { name: 'Карнотавр', length: '10 м.', height: '3,2 м.', - imageUrl: '/images/figma/dino-carnotaurus.png', + imageUrl: '/dynopark/carnotaurus_1.jpg', }, { name: 'Ділофозавр на скелі', length: '6 м.', - imageUrl: '/images/figma/dino-dilophosaurus.png', + imageUrl: '/dynopark/dilophosaurus_1.jpg', }, { name: 'Брахіозавр', length: '25 м.', height: '10 м.', - imageUrl: '/images/figma/dino-brachiosaurus.png', + imageUrl: '/dynopark/brachiosaurus_2.jpg', }, { name: 'Пара Брахіозаврів', + latinName: 'Закохані', length: '12 м.', height: '6 м.', - imageUrl: '/images/figma/dino-brachio-pair.png', + imageUrl: '/dynopark/brachiosaurus_2.jpg', }, { name: 'Ураноза́вр', length: '6 м.', height: '2,2 м.', - imageUrl: '/images/figma/dino-ouranosaurus.png', + imageUrl: '/dynopark/ouranosaurus_nigeriensis_1.jpg', }, { name: 'Стиракозавр', length: '6 м.', height: '2,2 м.', - imageUrl: '/images/figma/dino-styracosaurus.png', + imageUrl: '/dynopark/styracosaurus_1.jpg', }, { name: 'Космоцератопс', latinName: 'Пара Закоханих', length: '5 м.', height: '2,5 м.', - imageUrl: '/images/figma/dino-kosmoceratops.png', + imageUrl: null, }, { name: 'Стегозавр', length: '15 м.', height: '7 м.', - imageUrl: '/images/figma/dino-stegosaurus.png', + imageUrl: '/dynopark/stegosaurus_1.jpg', }, { name: 'Анкілозавр', length: '8 м.', height: '2,6 м.', - imageUrl: '/images/figma/dino-ankylosaurus.png', + imageUrl: '/dynopark/ankylosaurus_1.jpg', }, { name: 'Паразавролоф', length: '7 м.', height: '2,2 м.', - imageUrl: '/images/figma/dino-parasaurolophus.png', + imageUrl: '/dynopark/favorite-parasaurolophus-design-v0-0supu.jpg', }, { name: 'Паразавролоф', latinName: 'Сімейство', length: '6 м.', height: '2 м.', - imageUrl: '/images/figma/dino-parasaurolophus-family.png', + imageUrl: '/dynopark/favorite-parasaurolophus-design-v0-0supu.jpg', }, { name: 'Велоцираптор', length: '4 м.', height: '1,7 м.', - imageUrl: '/images/figma/dino-velociraptor.png', + imageUrl: '/dynopark/velociraptor_1_1.jpg', }, { name: 'Кетцалькоатль', height: '6,5 м.', - imageUrl: '/images/figma/dino-quetzalcoatl.png', + imageUrl: null, }, { name: 'Птеродактиль', + latinName: 'з гніздом та яйцями', height: '2 м.', - imageUrl: '/images/figma/dino-pterodactyl.png', + imageUrl: '/dynopark/1697108661_poknok-art-p-pteranodoni-51_1.jpg', }, { name: 'Яйця Динозаврів', height: '1,5 м.', - imageUrl: '/images/figma/dino-dino-eggs.png', + imageUrl: '/dynopark/pngtree-3d-baby-dinosaur-nesting-out-of-.jpg', }, { name: 'Трицератопс', length: '6 м.', height: '2,45 м.', - imageUrl: '/images/figma/dino-triceratops.png', + imageUrl: '/dynopark/202007_Triceratops_horridus.svg_1.jpg', }, { name: 'Тиранозавр', - latinName: 'DinoRodeo', + latinName: 'для ДиноРодео', length: '4 м.', height: '1,6 м.', - imageUrl: '/images/figma/dino-trex-rodeo.png', + imageUrl: '/dynopark/tyrannosaurus_rex_1.jpg', }, { name: 'Карнотавр', - latinName: 'DinoRodeo', + latinName: 'для ДиноРодео', length: '3,5 м.', height: '1,5 м.', - imageUrl: '/images/figma/dino-carno-rodeo.png', + imageUrl: '/dynopark/carnotaurus_1.jpg', }, { name: 'Тиранозавр', latinName: 'Версія 2', length: '4 м.', height: '1,8 м.', - imageUrl: '/images/figma/dino-trex2.png', + imageUrl: '/dynopark/tyrannosaurus_rex_1.jpg', }, { name: 'Велоцираптор', latinName: 'Версія 2', length: '4 м.', height: '1,8 м.', - imageUrl: '/images/figma/dino-velociraptor2.png', + imageUrl: '/dynopark/velociraptor_1.jpg', }, { name: 'Тиранозавр Рекс', latinName: 'Рев до Небес', length: '6 м.', height: '2,8 м.', - imageUrl: '/images/figma/dino-trex3.png', + imageUrl: '/dynopark/1675801348_grizly-club-p-klipart-tiranno.jpg', }, ] @@ -205,19 +206,23 @@ const DINO_EMOJIS = [ '🦖', ] -// ── Orange wheel SVG (matches Figma Group 1966048708) ────────────────────── -// Orange/gold donut with green dividing lines and white border rings +// ── Gallery photos at the bottom of the wheel section ───────────────────── +const GALLERY_PHOTOS = [ + { src: '/dynopark/50182754852060_1.jpg', alt: 'Динопарк — парк динозаврів' }, + { src: '/dynopark/Untitled_6_2.jpg', alt: 'Динопарк — фото парку' }, + { src: '/dynopark/if-when-jurassic-world-rebirth-gets-a-se.jpg', alt: 'Динопарк — відвідувачі' }, +] + +// ── Wheel SVG ────────────────────────────────────────────────────────────── function DinoWheelSVG({ rotation, n, size }: { rotation: number; n: number; size: number }) { const cx = size / 2 const cy = size / 2 - const outerR = size * 0.495 // outer edge of the donut ring - const innerR = size * 0.14 // inner hole of the donut ring + const outerR = size * 0.485 + const innerR = size * 0.295 - // Generate segment divider lines const dividers: { x1: number; y1: number; x2: number; y2: number }[] = [] for (let i = 0; i < n; i++) { - const angleDeg = (i / n) * 360 - const rad = (angleDeg * Math.PI) / 180 + const rad = ((i / n) * 360 * Math.PI) / 180 dividers.push({ x1: cx + innerR * Math.cos(rad), y1: cy + innerR * Math.sin(rad), @@ -259,22 +264,19 @@ function DinoWheelSVG({ rotation, n, size }: { rotation: number; n: number; size - - {/* Outer ring border */} - - - {/* Donut body — using clip-path trick for fillRule evenodd */} + {/* Outer shadow ring */} + + {/* Donut body */} - {/* Inner hole (cover with section background color) */} + {/* Inner hole */} - {/* Segment dividers */} {dividers.map((d, i) => ( - + ))} - - {/* Inner ring border (decorative) */} - + {/* Inner ring accent */} + + ) } @@ -285,43 +287,45 @@ export function DinoWheel({ dinos }: DinoWheelProps) { const n = items.length const [active, setActive] = useState(0) const [visible, setVisible] = useState(true) - - // Track the wheel's CSS rotation angle continuously to avoid back-spinning - const [wheelAngle, setWheelAngle] = useState(() => { - // Place item 0 at bottom (270°). SVG 0° = right, 90° = down. - // Item 0 starts at angle 0° → need to rotate wheel so 0° aligns to 270° (bottom) - return 270 - }) + const [wheelAngle, setWheelAngle] = useState(270) const activeRef = useRef(0) const timerRef = useRef | null>(null) - // ── Compute target wheel rotation to put item i at bottom (270°) ── - const angleForItem = useCallback( - (i: number) => { - // Item i natural angle = (i/n)*360 - // We want it at 270°, so wheel rotation needed = 270 - (i/n)*360 - return 270 - (i / n) * 360 - }, - [n] - ) + // Responsive wheel size + const containerRef = useRef(null) + const [wheelSize, setWheelSize] = useState(820) + + useEffect(() => { + function measure() { + if (containerRef.current) { + const w = containerRef.current.offsetWidth + setWheelSize(Math.min(820, Math.max(340, w))) + } + } + measure() + window.addEventListener('resize', measure) + return () => window.removeEventListener('resize', measure) + }, []) + + const WHEEL_SIZE = wheelSize + const DINO_RADIUS = WHEEL_SIZE * 0.39 + const DINO_ACTIVE_SIZE = Math.round(WHEEL_SIZE * 0.1) + const DINO_INACTIVE_SIZE = Math.round(WHEEL_SIZE * 0.065) + + const angleForItem = useCallback((i: number) => 270 - (i / n) * 360, [n]) - // ── Rotate to item i, going shortest path from current angle ── const goTo = useCallback( (nextIdx: number) => { setVisible(false) - const target = angleForItem(nextIdx) - // Compute current "base" angle (normalized) setWheelAngle((current) => { const currentNorm = ((current % 360) + 360) % 360 const targetNorm = ((target % 360) + 360) % 360 let diff = targetNorm - currentNorm - // Pick shortest arc if (diff > 180) diff -= 360 if (diff < -180) diff += 360 return current + diff }) - setTimeout(() => { setActive(nextIdx) activeRef.current = nextIdx @@ -331,7 +335,6 @@ export function DinoWheel({ dinos }: DinoWheelProps) { [angleForItem] ) - // ── Auto-advance timer ── const startTimer = useCallback(() => { if (timerRef.current) clearInterval(timerRef.current) timerRef.current = setInterval(() => { @@ -351,340 +354,290 @@ export function DinoWheel({ dinos }: DinoWheelProps) { function handleSelect(i: number) { if (timerRef.current) clearInterval(timerRef.current) goTo(i) - // Resume auto-advance after pause setTimeout(startTimer, 8000) } const current = items[active]! - // ── Layout sizing ── - // Wheel is a circle. We show only the TOP half (semicircle) above the info panel. - // Container height = wheelSize/2 so the bottom half is clipped. - const WHEEL_SIZE = 560 // overall circle diameter in px - const DINO_RADIUS = WHEEL_SIZE * 0.37 // radius at which dino icons sit - const DINO_ACTIVE_SIZE = 88 - const DINO_INACTIVE_SIZE = 56 - - // Each dino's position on the rotating wheel: - // Natural angle of item i = (i/n)*360 deg (measured from SVG 0° = right) - // Since wheel rotates by wheelAngle, the screen angle of item i = (i/n)*360 + wheelAngle - // We want the ACTIVE item at screen angle 270° (bottom) — that's enforced by wheelAngle. - // For rendering the dino overlay images, we place them at their natural SVG angles - // (relative to the wheel) and let the wheel rotation carry them. - // But we counter-rotate each dino image so it always faces upright. - return (
- {/* Topographic wave background */} + {/* Topographic wave bg */} - {/* ── Gallery — 4 park photos from Figma node 2004:568 ── */} -
-
- {[ - { src: '/images/figma/dyno-gallery-1.jpg', alt: 'Динопарк фото 1' }, - { src: '/images/figma/dyno-gallery-2.jpg', alt: 'Динопарк фото 2' }, - { src: '/images/figma/dyno-gallery-3.jpg', alt: 'Динопарк фото 3' }, - { src: '/images/figma/dyno-gallery-4.jpg', alt: 'Динопарк фото 4' }, - ].map((photo, i) => ( -
- {photo.alt} -
+ {/* ── Wheel ── */} +
+ {/* Arch container: shows top half of the circle */} +
+ {/* Full circle positioned so center is at bottom of container */} +
+ + + {items.map((dino, i) => { + const naturalDeg = (i / n) * 360 + const isActive = i === active + const iconSize = isActive ? DINO_ACTIVE_SIZE : DINO_INACTIVE_SIZE + + return ( + + ) + })} +
+
+
+ + {/* ── Prev / dots / Next ── */} +
+ + +
+ {items.map((_, i) => ( +
+ + +
+ + {/* ── Photo gallery row ── */} +
+ {GALLERY_PHOTOS.map((photo, i) => ( +
+ {photo.alt} +
+ ))} +
+ + {/* ── Mobile name list ── */} +
+ {items.map((dino, i) => ( + + ))}
) diff --git a/src/components/sections/DinoWhyVisit.tsx b/src/components/sections/DinoWhyVisit.tsx index 58757cb..85689c5 100644 --- a/src/components/sections/DinoWhyVisit.tsx +++ b/src/components/sections/DinoWhyVisit.tsx @@ -1,8 +1,15 @@ 'use client' +/* eslint-disable @next/next/no-img-element */ import { useState, useRef, useEffect } from 'react' const FONT_MONT = { fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' } +const FONT_POP = { fontFamily: 'var(--font-poppins, Poppins), sans-serif' } + +interface ReviewPhoto { + src: string + label?: string | null +} interface ReviewVideo { src: string @@ -14,40 +21,62 @@ interface DinoWhyVisitProps { title?: string items?: Array<{ title: string; description: string }> reviewVideos?: ReviewVideo[] + reviewPhotos?: ReviewPhoto[] } const DEFAULT_ITEMS = [ { title: 'Навчання через гру', description: - 'Дітки дізнаються про стародавніх тварин через захопливі ігри та інтерактивні вправи з гідом.', + "Через гру дитина краще запам'ятовує факти. Вона може вивчати не лише історію про динозаврів, а й нові іноземні слова, розуміти науку та захоплюватись природою.", }, { title: 'Дитячі очі, що палають захватом', description: - 'Реалістичні рухи та звуки динозаврів створюють ефект повного занурення — дитина точно не забуде цього дня.', + "Здивування та вау-ефект, коли дитина побачить цих велетнів, неможливо передати. Це щирі емоції радості та справжнього щастя, які закарбуються у пам'яті назавжди.", }, { title: 'Неймовірні фотографії', description: - 'Сфотографуйтесь поруч із улюбленим динозавром або зробіть фото з екскурсоводом — тепла згадка для всієї родини.', + 'На локації багато зон, де можна зробити красиві фотографії не лише на згадку, а й для соцмереж. Кожен знімок — окремий маленький шедевр.', + }, +] + +const DEFAULT_REVIEW_PHOTOS: ReviewPhoto[] = [ + { + src: '/dynopark/Gemini_Generated_Image_l7a8tql7a8tql7a8_.jpg', + label: 'ЗАРАЗ МИ ДІЗНАЄМОСЯ...', + }, + { + src: '/dynopark/Gemini_Generated_Image_mlbfxbmlbfxbmlbf_.jpg', + label: 'ЗАРАЗ МИ ДІЗНАЄМОСЯ...', + }, + { + src: '/dynopark/Gemini_Generated_Image_novmrqnovmrqnovm_.jpg', + label: 'ЗАРАЗ МИ ДІЗНАЄМОСЯ...', + }, + { + src: '/dynopark/Gemini_Generated_Image_r9d5kbr9d5kbr9d5_.jpg', + label: 'ЗАРАЗ МИ ДІЗНАЄМОСЯ...', }, ] export function DinoWhyVisit({ - title = 'Чому варто відвідати динопарк', + title = 'Чому варто відвідати Динопарк', items = DEFAULT_ITEMS, reviewVideos, + reviewPhotos, }: DinoWhyVisitProps) { - const videos = reviewVideos && reviewVideos.length > 0 ? reviewVideos : [] - const vn = videos.length + const photos = reviewPhotos && reviewPhotos.length > 0 ? reviewPhotos : DEFAULT_REVIEW_PHOTOS + const vn = photos.length const [openIndex, setOpenIndex] = useState(0) - const [videoActive, setVideoActive] = useState(0) - const [playingIndex, setPlayingIndex] = useState(null) - const videoPausedRef = useRef(false) + const [photoActive, setPhotoActive] = useState(0) const accordionTimer = useRef | null>(null) - const videoTimer = useRef | null>(null) - const videoRefs = useRef<(HTMLVideoElement | null)[]>([]) + const photoTimer = useRef | null>(null) + const hovering = useRef(false) + + // Videos take precedence over photos if provided + const hasVideos = reviewVideos && reviewVideos.length > 0 useEffect(() => { accordionTimer.current = setInterval(() => { @@ -59,16 +88,14 @@ export function DinoWhyVisit({ }, [items.length]) useEffect(() => { - if (vn <= 0) return - videoTimer.current = setInterval(() => { - if (!videoPausedRef.current) { - setVideoActive((prev) => (prev + 1) % vn) - } - }, 5000) + if (vn <= 0 || hasVideos) return + photoTimer.current = setInterval(() => { + if (!hovering.current) setPhotoActive((prev) => (prev + 1) % vn) + }, 3800) return () => { - if (videoTimer.current) clearInterval(videoTimer.current) + if (photoTimer.current) clearInterval(photoTimer.current) } - }, [vn]) + }, [vn, hasVideos]) function handleItemClick(i: number) { setOpenIndex(i) @@ -78,74 +105,60 @@ export function DinoWhyVisit({ }, 4000) } - function handlePlayVideo(i: number) { - if (playingIndex === i) return - if (playingIndex !== null && videoRefs.current[playingIndex]) { - videoRefs.current[playingIndex]!.pause() - videoRefs.current[playingIndex]!.currentTime = 0 - } - videoPausedRef.current = true - setPlayingIndex(i) - setVideoActive(i) - setTimeout(() => { - videoRefs.current[i]?.play() - }, 50) - } - - function handleVideoNav(i: number) { - if (playingIndex !== null && videoRefs.current[playingIndex]) { - videoRefs.current[playingIndex]!.pause() - videoRefs.current[playingIndex]!.currentTime = 0 - } - setPlayingIndex(null) - videoPausedRef.current = false - setVideoActive(i) + function handlePhotoNav(i: number) { + setPhotoActive(i) + if (photoTimer.current) clearInterval(photoTimer.current) + photoTimer.current = setInterval(() => { + if (!hovering.current) setPhotoActive((prev) => (prev + 1) % vn) + }, 3800) } return ( -
-
+
+

{title}

-
- {/* Accordion */} +
+ {/* ── Accordion (left) ── */}
-
-
+ {/* Green decorative block behind accordion */} +
+
{items.map((item, i) => { const isOpen = openIndex === i return ( - )} -
- ) - })} -
- -
- -
- {videos.map((_, i) => ( + {photos.map((photo, i) => { + const isActive = i === photoActive + return ( +
+ {isActive + {/* Label overlay */} + {photo.label && ( +
+

+ {photo.label} +

+
+ )} +
+ ) + })} +
+ + {/* Photo nav dots */} +
+ {photos.map((_, i) => (
-
-
- )} + )} +
{/* Bottom quote */}

- Запросіть традицію щоразу знайомитись з новим динозавром або щоразу фотографуватись біля - улюбленого динозавра. З часом ці знімки складуться у захопливий ковток улюблених / назад - — тепла згадка для всієї родини. + Започаткуйте традицію: щотижня знайомтеся з новим диногероєм або щороку фотографуйтесь + біля улюбленого динозавра. З часом ці знімки складуться у захопливу колекцію — тепла + згадка для всієї родини.

) } + +// ── Video carousel (when CMS provides videos) ───────────────────────────── +function VideoCarousel({ + videos, +}: { + videos: { src: string; poster?: string | null; label?: string | null }[] +}) { + const n = videos.length + const [active, setActive] = useState(0) + const [playing, setPlaying] = useState(null) + const refs = useRef<(HTMLVideoElement | null)[]>([]) + const paused = useRef(false) + + useEffect(() => { + if (n <= 0) return + const t = setInterval(() => { + if (!paused.current) setActive((p) => (p + 1) % n) + }, 5000) + return () => clearInterval(t) + }, [n]) + + function handlePlay(i: number) { + if (playing === i) return + if (playing !== null && refs.current[playing]) { + refs.current[playing]!.pause() + refs.current[playing]!.currentTime = 0 + } + paused.current = true + setPlaying(i) + setActive(i) + setTimeout(() => refs.current[i]?.play(), 50) + } + + return ( +
{ + paused.current = true + }} + onMouseLeave={() => { + if (playing === null) paused.current = false + }} + > + {videos.map((v, i) => { + const isActive = i === active + const isPlaying = i === playing + return ( +
+
+ ) + })} +
+ ) +}