feat(dyvolis): visual overhaul — bg color, 3D gallery, why-visit, compact tickets
Some checks are pending
CI / Type Check (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Unit Tests (push) Waiting to run
Deploy / Build & Push Image (push) Waiting to run
Deploy / Deploy to VPS (push) Blocked by required conditions

- Change all #fdf2e8 → #f1fbeb (matches homepage light-green background)
- Hero: fix badge border color, add orange decorative glow ellipse behind cat
- Gallery: replace static grid with 3D coverflow carousel (auto-rotate + nav arrows)
- WhyVisit: redesign to match WhyParents layout (auto-rotate accordion + RAF scroll gallery)
- Tickets: compact cards with flex justify-between so CTA button is always at card bottom

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-13 11:18:18 +01:00
parent 91d11c1009
commit 3ae7ff4bad
5 changed files with 296 additions and 136 deletions

View file

@ -12,7 +12,7 @@ export const metadata: Metadata = {
export default function DyvoLisPage() {
return (
<div style={{ background: '#fdf2e8' }}>
<div style={{ background: '#f1fbeb' }}>
<DyvoLisHero />
<DyvoLisGallery />
<DyvoLisWhyVisit />

View file

@ -1,5 +1,8 @@
'use client'
/* eslint-disable @next/next/no-img-element */
import { useState, useEffect } from 'react'
const QUOTE =
'Це місце де малеча зустрічає героїв улюблених казок. Простір справжнього дитинства.'
@ -11,42 +14,134 @@ const GALLERY = [
]
export function DyvoLisGallery() {
const [active, setActive] = useState(0)
const n = GALLERY.length
useEffect(() => {
const t = setInterval(() => setActive((p) => (p + 1) % n), 3500)
return () => clearInterval(t)
}, [n])
function getOffset(i: number): number {
const d = ((i - active) % n + n) % n
return d > n / 2 ? d - n : d
}
return (
<section className="flex flex-col items-center overflow-hidden">
<section className="flex flex-col items-center overflow-hidden" style={{ background: '#f1fbeb' }}>
{/* Quote banner */}
<div
className="relative w-full overflow-hidden py-10 lg:rounded-[20px] lg:py-14"
style={{ background: '#396817' }}
>
<div className="relative w-full py-10 lg:py-14" style={{ background: '#396817' }}>
<p
className="relative mx-auto max-w-[900px] px-8 text-center text-[20px] font-bold leading-[1.4] text-[#fdf2e8] lg:text-[28px]"
style={{
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
}}
className="relative mx-auto max-w-[900px] px-8 text-center text-[20px] leading-[1.4] font-bold text-white lg:text-[28px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
{QUOTE}
</p>
</div>
{/* Gallery row — overlaps banner on desktop */}
<div className="relative z-10 -mt-0 w-full lg:-mt-[80px]">
{/* 3D Coverflow */}
<div className="relative w-full overflow-hidden py-14 lg:py-20">
<div
className="mx-auto flex max-w-[1204px] gap-4 overflow-x-auto px-8 pb-4 pt-4 lg:overflow-x-visible lg:pb-0 lg:pt-0"
style={{ scrollbarWidth: 'none' }}
className="relative mx-auto h-[260px] max-w-[1204px] lg:h-[400px]"
style={{ perspective: '1100px' }}
>
{GALLERY.map((src, i) => (
<div
key={i}
className="h-[220px] w-[240px] flex-none overflow-hidden rounded-[16px] shadow-[0_8px_40px_rgba(57,104,23,0.18)] lg:h-[380px] lg:flex-1"
>
<img
src={src}
alt={`ДивоЛіс — фото ${i + 1}`}
className="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
loading="lazy"
{GALLERY.map((src, i) => {
const d = getOffset(i)
const absD = Math.abs(d)
const sign = d >= 0 ? 1 : -1
const cfg =
absD === 0
? { tx: 0, ry: 0, scale: 1, opacity: 1, z: 20 }
: absD === 1
? { tx: sign * 270, ry: -sign * 48, scale: 0.82, opacity: 0.62, z: 10 }
: { tx: sign * 440, ry: -sign * 62, scale: 0.6, opacity: 0.18, z: 1 }
return (
<div
key={i}
role={d !== 0 ? 'button' : undefined}
tabIndex={d !== 0 ? 0 : undefined}
aria-label={d !== 0 ? `Показати фото ${i + 1}` : undefined}
onClick={() => d !== 0 && setActive(i)}
onKeyDown={(e) => e.key === 'Enter' && d !== 0 && setActive(i)}
style={{
position: 'absolute',
top: '50%',
left: '50%',
width: 'clamp(200px, 30vw, 370px)',
aspectRatio: '4/3',
transform: `translate(calc(-50% + ${cfg.tx}px), -50%) rotateY(${cfg.ry}deg) scale(${cfg.scale})`,
opacity: cfg.opacity,
zIndex: cfg.z,
transition: 'transform 0.65s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.65s ease',
borderRadius: '16px',
overflow: 'hidden',
cursor: d !== 0 ? 'pointer' : 'default',
boxShadow:
d === 0
? '0 20px 60px rgba(57,104,23,0.28)'
: '0 8px 24px rgba(0,0,0,0.14)',
}}
>
<img
src={src}
alt={d === 0 ? `ДивоЛіс — фото ${i + 1}` : ''}
aria-hidden={d !== 0 ? true : undefined}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
</div>
)
})}
</div>
{/* Navigation */}
<div className="mt-10 flex items-center justify-center gap-6">
<button
onClick={() => setActive((p) => (p - 1 + n) % n)}
className="flex h-10 w-10 items-center justify-center rounded-full bg-[#396817] text-white transition-all hover:scale-110 hover:bg-[#2d5414]"
aria-label="Попереднє фото"
>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
<path
d="M11 4L6 9L11 14"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</div>
))}
</svg>
</button>
<div className="flex gap-2.5">
{GALLERY.map((_, i) => (
<button
key={i}
onClick={() => setActive(i)}
className="h-2.5 w-2.5 rounded-full transition-all duration-300"
style={{ background: i === active ? '#396817' : '#b8d8a0' }}
aria-label={`Фото ${i + 1}`}
aria-current={i === active ? true : undefined}
/>
))}
</div>
<button
onClick={() => setActive((p) => (p + 1) % n)}
className="flex h-10 w-10 items-center justify-center rounded-full bg-[#396817] text-white transition-all hover:scale-110 hover:bg-[#2d5414]"
aria-label="Наступне фото"
>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
<path
d="M7 4L12 9L7 14"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
</div>
</div>
</section>

View file

@ -10,29 +10,29 @@ const TIPS = [
export function DyvoLisHero() {
return (
<section
className="relative overflow-hidden"
style={{ background: '#fdf2e8' }}
>
<section className="relative overflow-hidden" style={{ background: '#f1fbeb' }}>
<div className="mx-auto max-w-[1204px] px-8">
<div className="relative flex min-h-[600px] flex-col gap-10 pt-12 pb-16 lg:min-h-[700px] lg:flex-row lg:items-start lg:gap-0 lg:pt-16 lg:pb-20">
{/* Left: text + info tips */}
<div className="relative z-10 flex flex-col gap-10 lg:w-[608px] lg:flex-none">
{/* Text block */}
<div className="flex flex-col gap-6">
<h1
className="text-[36px] font-bold uppercase leading-[1.2] text-[#272727] lg:text-[56px]"
className="text-[36px] leading-[1.2] font-bold text-[#272727] uppercase lg:text-[56px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
ДивоЛіс територія магії та фантазії
</h1>
<p
className="text-[16px] font-medium leading-[1.6] text-[#272727] lg:text-[20px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif', fontWeight: 500 }}
className="text-[16px] leading-[1.6] font-medium text-[#272727] lg:text-[20px]"
style={{
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
fontWeight: 500,
}}
>
Топіарні фігури зроблені з урахуванням важливих деталей, тому ви одразу впізнаєте в них улюблених казкових героїв. Тут можна бігати, стрибати, лазити по фігурках і ставати героями власної казки.
Топіарні фігури зроблені з урахуванням важливих деталей, тому ви одразу впізнаєте в
них улюблених казкових героїв. Тут можна бігати, стрибати, лазити по фігурках і
ставати героями власної казки.
</p>
<BtnPrimary href="/kvytky?category=dyvolis" className="self-start">
Купити квиток
@ -41,11 +41,10 @@ export function DyvoLisHero() {
{/* Info tips */}
<div className="flex flex-col gap-4">
{/* Main tip with X badge */}
<div className="relative flex items-center">
<div
className="z-10 flex h-[100px] w-[100px] flex-none items-center justify-center rounded-full border-[12px] border-[#fdf2e8] text-[56px] font-extrabold text-white"
className="z-10 flex h-[100px] w-[100px] flex-none items-center justify-center rounded-full border-[12px] border-[#f1fbeb] text-[56px] font-extrabold text-white"
style={{
background: '#396817',
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
@ -57,18 +56,26 @@ export function DyvoLisHero() {
Х
</div>
<div
className="flex-1 rounded-[20px] py-6 pl-14 pr-6 text-[16px] font-medium leading-[1.4] text-white lg:text-[20px]"
style={{ background: '#396817', fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
className="flex-1 rounded-[20px] py-6 pr-6 pl-14 text-[16px] leading-[1.4] font-medium text-white lg:text-[20px]"
style={{
background: '#396817',
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
}}
>
атракціонів з безпечних<br />для дітей матеріалів
атракціонів з безпечних
<br />
для дітей матеріалів
</div>
</div>
{TIPS.map((tip, i) => (
<div
key={i}
className="rounded-[20px] px-8 py-6 text-[16px] font-medium leading-[1.4] text-white lg:text-[20px]"
style={{ background: '#396817', fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
className="rounded-[20px] px-8 py-6 text-[16px] leading-[1.4] font-medium text-white lg:text-[20px]"
style={{
background: '#396817',
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
}}
>
{tip}
</div>
@ -78,9 +85,14 @@ export function DyvoLisHero() {
{/* Right: hero image (desktop) */}
<div
className="pointer-events-none absolute right-0 top-0 hidden h-full w-[54%] lg:block"
className="pointer-events-none absolute top-0 right-0 hidden h-full w-[54%] lg:block"
aria-hidden="true"
>
{/* Decorative orange glow behind the cat */}
<div
className="absolute top-[15%] right-[10%] h-[380px] w-[380px] rounded-full"
style={{ background: '#f28b4a', opacity: 0.28, filter: 'blur(80px)' }}
/>
<img
src={HERO_IMG}
alt="Топіарна фігура ДивоЛісу"

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element */
import { BtnPrimary } from '@/components/ui/BtnPrimary'
const FONT_MONT = { fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }
@ -47,40 +46,39 @@ const COMBO_TICKETS: TicketCard[] = [
function Ticket({ label, price, per, note }: TicketCard) {
return (
<div
className="flex flex-col gap-6 rounded-[20px] p-5 shadow-[0_4px_30px_rgba(242,139,74,0.25)]"
style={{ background: '#fdf2e8' }}
className="flex flex-col rounded-[20px] p-5 shadow-[0_4px_30px_rgba(242,139,74,0.25)]"
style={{ background: '#f1fbeb' }}
>
<div className="flex flex-col gap-3 text-center">
<div className="flex flex-col gap-2 text-center">
{label && (
<p
className="text-[14px] font-bold uppercase leading-[1.5] text-[#f28b4a] lg:text-[16px]"
className="text-[13px] leading-[1.5] font-bold text-[#f28b4a] uppercase"
style={FONT_MONT}
>
{label}
</p>
)}
<div className="border-t border-[#e8d5c0]" />
<div className="border-t border-[#c8e8b0]" />
<p
className="text-[40px] font-black leading-[1.4] text-[#272727] lg:text-[56px]"
className="text-[32px] leading-[1.3] font-black text-[#272727] lg:text-[42px]"
style={FONT_MONT}
>
{price}
</p>
<p className="text-[14px] font-bold leading-[1.5] text-[#272727] lg:text-[16px]" style={FONT_MONT}>
<p className="text-[13px] leading-[1.5] font-bold text-[#272727]" style={FONT_MONT}>
{per}
</p>
{note && (
<p
className="text-[13px] leading-[1.5] text-[#272727] lg:text-[14px]"
style={FONT_MONT}
>
<p className="text-[12px] leading-[1.5] text-[#4a4a4a]" style={FONT_MONT}>
{note}
</p>
)}
</div>
<BtnPrimary href="/kvytky?category=dyvolis" className="w-full justify-center">
Забронювати пригоду
</BtnPrimary>
<div className="mt-auto pt-5">
<BtnPrimary href="/kvytky?category=dyvolis" className="w-full justify-center !text-[16px]">
Забронювати пригоду
</BtnPrimary>
</div>
</div>
)
}
@ -94,23 +92,22 @@ export function DyvoLisTickets() {
style={{ background: '#396817' }}
/>
<div className="relative z-10 mx-auto max-w-[1204px] px-8 pb-20 pt-0">
<div className="relative z-10 mx-auto max-w-[1204px] px-8 pt-0 pb-16">
{/* Working hours banner */}
<div
className="mb-14 flex flex-col items-center justify-center gap-2 overflow-hidden rounded-b-[20px] px-8 py-6 text-center lg:flex-row lg:gap-6"
className="mb-10 flex flex-col items-center justify-center gap-2 overflow-hidden rounded-b-[20px] px-8 py-5 text-center lg:flex-row lg:gap-6"
style={{
background: 'linear-gradient(90deg, #f28b4a 0%, #fdcf54 50%, #f28b4a 100%)',
}}
>
<p
className="text-[22px] font-bold uppercase leading-[1.4] text-[#272727] lg:text-[28px]"
className="text-[20px] leading-[1.4] font-bold text-[#272727] uppercase lg:text-[26px]"
style={FONT_MONT}
>
Час роботи
</p>
<p
className="text-[18px] font-bold leading-[1.4] text-[#272727] lg:text-[24px]"
className="text-[16px] leading-[1.4] font-bold text-[#272727] lg:text-[22px]"
style={FONT_MONT}
>
п&apos;ятницясуботанеділя з 11:00 до 20:00
@ -119,33 +116,33 @@ export function DyvoLisTickets() {
{/* Single tickets */}
<h3
className="mb-8 text-[24px] font-bold uppercase leading-[1.4] text-[#fdf2e8] lg:text-[32px]"
className="mb-6 text-[22px] leading-[1.4] font-bold text-white uppercase lg:text-[28px]"
style={FONT_MONT}
>
Вартість квитка
</h3>
<div className="mb-14 grid grid-cols-1 gap-5 sm:grid-cols-2">
<div className="mb-10 grid grid-cols-1 gap-4 sm:grid-cols-2">
{SINGLE_TICKETS.map((t, i) => (
<Ticket key={i} {...t} />
))}
</div>
{/* Combo header */}
<div className="mb-8 flex flex-col gap-2">
<div className="mb-6 flex flex-col gap-2">
<h3
className="text-[24px] font-bold uppercase leading-[1.4] text-[#fdf2e8] lg:text-[32px]"
className="text-[22px] leading-[1.4] font-bold text-white uppercase lg:text-[28px]"
style={FONT_MONT}
>
Комбо
</h3>
<p
className="text-[18px] font-bold leading-[1.4] text-[#fdf2e8] lg:text-[24px]"
className="text-[16px] leading-[1.4] font-semibold text-white/80 lg:text-[20px]"
style={FONT_MONT}
>
ДивоЛіс із казковими топіарними фігурами + ДиноПарк + Дзеркальний лабіринт
</p>
</div>
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
{COMBO_TICKETS.map((t, i) => (
<Ticket key={i} {...t} />
))}

View file

@ -1,14 +1,13 @@
'use client'
/* eslint-disable @next/next/no-img-element */
import { useState } from 'react'
const VIDEO_THUMB = '/images/dyvolis/video-thumb.jpg'
import { useState, useRef, useEffect } from 'react'
const ITEMS = [
{
title: 'Простір для спільної фантазії',
description: '',
description:
'Вигадуйте казки та пригоди разом із дітьми — кожна топіарна фігурка стає новою сторінкою вашої власної чарівної історії.',
},
{
title: 'Казковий ліс у справжньому лісі',
@ -17,62 +16,95 @@ const ITEMS = [
},
{
title: 'Магічні кадри для сімейного альбому',
description: '',
description:
'Унікальні топіарні декорації та яскраві персонажі — ідеальний фон для незабутніх сімейних фотографій, які захочеться переглядати знову і знову.',
},
]
const VIDEOS = [
{ src: VIDEO_THUMB, label: 'Відео 1' },
{ src: VIDEO_THUMB, label: 'Відео 2' },
{ src: VIDEO_THUMB, label: 'Відео 3' },
const GALLERY = [
'/images/dyvolis/gallery-1.jpg',
'/images/dyvolis/gallery-2.jpg',
'/images/dyvolis/gallery-3.jpg',
'/images/dyvolis/gallery-4.jpg',
]
const PLAY_BTN = (
<div
aria-hidden="true"
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex h-[72px] w-[72px] items-center justify-center rounded-full bg-white/90 shadow-[0_4px_40px_rgba(0,0,0,0.4)]"
>
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" aria-hidden="true">
<path d="M8 5.5l16 8.5-16 8.5V5.5z" fill="#396817" />
</svg>
</div>
)
export function DyvoLisWhyVisit() {
const [openIndex, setOpenIndex] = useState<number>(1)
const [openIndex, setOpenIndex] = useState(0)
const galleryRef = useRef<HTMLDivElement>(null)
const galleryPausedRef = useRef(false)
const galleryRafRef = useRef<number | null>(null)
const autoTimer = useRef<ReturnType<typeof setInterval> | null>(null)
const doubled = [...GALLERY, ...GALLERY]
useEffect(() => {
autoTimer.current = setInterval(() => {
setOpenIndex((prev) => (prev + 1) % ITEMS.length)
}, 4000)
return () => {
if (autoTimer.current) clearInterval(autoTimer.current)
}
}, [])
function handleItemClick(i: number) {
setOpenIndex(i)
if (autoTimer.current) clearInterval(autoTimer.current)
autoTimer.current = setInterval(() => {
setOpenIndex((prev) => (prev + 1) % ITEMS.length)
}, 4000)
}
useEffect(() => {
const el = galleryRef.current
if (!el) return
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
let last = performance.now()
const tick = (now: number) => {
const dt = now - last
last = now
if (!galleryPausedRef.current) {
const half = el.scrollWidth / 2
if (half > 0) {
el.scrollLeft += dt * 0.04
if (el.scrollLeft >= half) el.scrollLeft -= half
}
}
galleryRafRef.current = requestAnimationFrame(tick)
}
galleryRafRef.current = requestAnimationFrame(tick)
return () => {
if (galleryRafRef.current) cancelAnimationFrame(galleryRafRef.current)
}
}, [])
return (
<section className="py-12 lg:py-16" style={{ background: '#fdf2e8' }}>
<section className="py-[20px] md:py-[60px] lg:py-[40px]" style={{ background: '#f1fbeb' }}>
<div className="mx-auto max-w-[1204px] px-8">
<h2
className="mb-10 text-[24px] font-bold uppercase leading-[1.4] text-[#272727] lg:mb-14 lg:text-[32px]"
className="mb-[40px] text-[24px] font-bold text-[#272727] uppercase md:mb-[60px] md:text-[32px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
Чому варто відвідати ДивоЛіс
</h2>
<div className="flex flex-col gap-12 lg:flex-row lg:items-start lg:gap-16">
<div className="flex flex-col items-start gap-16 lg:flex-row lg:items-stretch">
{/* Left: accordion */}
<div className="relative w-full flex-none lg:w-auto">
<div className="absolute top-8 left-0 hidden h-[488px] w-[333px] rounded-[30px] bg-[#396817] lg:block" />
{/* Accordion column */}
<div className="relative flex-none lg:w-auto">
{/* Green vertical decoration bar */}
<div className="absolute left-0 top-8 hidden h-[420px] w-[280px] rounded-[30px] bg-[#396817] lg:block" />
<div className="relative flex flex-col gap-5 lg:ml-[76px]">
<div className="relative flex flex-col gap-6 lg:ml-[76px] lg:min-h-[560px]">
{ITEMS.map((item, i) => {
const isOpen = openIndex === i
return (
<button
key={i}
type="button"
onClick={() => handleItemClick(i)}
className="flex w-full flex-col gap-2.5 rounded-[10px] bg-[#f1fbeb] px-[50px] py-[20px] text-left shadow-[0_4px_60px_0_rgba(242,139,74,0.25)] transition-all duration-200 lg:w-[628px]"
aria-expanded={isOpen}
onClick={() => setOpenIndex(i)}
className="flex w-full flex-col gap-2.5 rounded-[10px] bg-[#fdf2e8] px-10 py-5 text-left shadow-[0_4px_60px_0_rgba(242,139,74,0.25)] transition-all duration-200 lg:w-[628px]"
>
<div className="flex items-center gap-5">
<span
className="flex-1 text-[18px] font-bold leading-[1.5] text-[#272727] lg:text-[20px]"
className="flex-1 text-[20px] leading-tight font-bold text-[#272727]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
{item.title}
@ -86,60 +118,84 @@ export function DyvoLisWhyVisit() {
style={{ transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)' }}
aria-hidden="true"
>
<path d="M1 1L9.5 9L18 1" stroke="#f28b4a" strokeWidth="2" strokeLinecap="round" />
<path
d="M1 1L9.5 9L18 1"
stroke="#f28b4a"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</div>
{item.description && (
<div
className={`grid transition-[grid-template-rows] duration-300 ease-out ${isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'}`}
>
<div className="overflow-hidden">
<p
className="pt-1 text-[15px] leading-[1.6] font-light text-[#272727] lg:text-[16px]"
style={{ fontFamily: 'var(--font-poppins, Poppins), sans-serif' }}
>
{item.description}
</p>
</div>
<div
className={`grid transition-[grid-template-rows] duration-300 ease-out ${isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'}`}
>
<div className="overflow-hidden">
<p
className="pt-2 text-[16px] leading-[1.6] font-light text-[#272727]"
style={{ fontFamily: 'var(--font-poppins, Poppins), sans-serif' }}
>
{item.description}
</p>
</div>
)}
</div>
</button>
)
})}
</div>
</div>
{/* Video cards */}
{/* Right: auto-scroll gallery — desktop */}
<div className="hidden flex-1 overflow-hidden lg:block">
<div
ref={galleryRef}
className="flex gap-5 overflow-x-auto"
style={{ scrollbarWidth: 'none' }}
onMouseEnter={() => {
galleryPausedRef.current = true
}}
onMouseLeave={() => {
galleryPausedRef.current = false
}}
>
{doubled.map((src, i) => (
<div
key={i}
className="relative h-[488px] w-[340px] flex-none overflow-hidden rounded-[20px]"
>
<img
src={src}
alt=""
aria-hidden="true"
className="h-full w-full object-cover transition-transform duration-700 ease-out hover:scale-110"
loading={i < 2 ? 'eager' : 'lazy'}
/>
</div>
))}
</div>
</div>
{/* Mobile: horizontal scroll */}
<div
className="flex gap-4 overflow-x-auto pb-2 lg:flex-1 lg:overflow-x-visible lg:pb-0"
className="flex w-full gap-5 overflow-x-auto pb-2 lg:hidden"
style={{ scrollbarWidth: 'none' }}
>
{VIDEOS.map((v, i) => (
{GALLERY.map((src, i) => (
<div
key={i}
className="relative h-[260px] w-[260px] flex-none cursor-pointer overflow-hidden rounded-[20px] bg-[#d6f2c0] shadow-[0_4px_30px_rgba(57,104,23,0.15)] transition-transform duration-300 hover:scale-[1.02] lg:h-[480px] lg:flex-1 lg:w-auto"
className="flex-none overflow-hidden rounded-[20px]"
style={{ width: '280px', height: '200px' }}
>
<img
src={v.src}
alt={v.label}
src={src}
alt=""
aria-hidden="true"
className="h-full w-full object-cover"
loading="lazy"
/>
{PLAY_BTN}
</div>
))}
</div>
</div>
{/* Description paragraph */}
<div className="mt-12 rounded-[20px] shadow-[0_4px_30px_rgba(242,139,74,0.25)] lg:mt-16">
<p
className="px-8 py-6 text-[16px] font-medium leading-[1.6] text-[#272727] lg:text-[20px]"
style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }}
>
Це простір, де ви разом із дитиною створюєте власний магічний світ, вчитеся помічати дива у звичайних речах та розвиваєте творчу уяву через спільну гру. Хто знає, можлива саме ця прогулянка спонукає вже дорослу дитину написати казкову історію, яка стане бестселером.
</p>
</div>
</div>
</section>
)