feat(home): apply Figma design — new hero + location photos, WhyParents gallery
- Hero: "Започаткуйте традицію:" title at 96px, subtitle 48px/Black (щороку фотографуйтесь біля улюбленого динозавра); removed separate T-Rex/family overlays — T-Rex now baked into hero-bg2 - WhyParents: replaced desktop auto-scroll photo gallery with 3 static video thumbnail cards with play buttons (Figma Gallery_holder layout) - Assets: replace hero + 5 location images with real 2026 park photos from Figma; add why-parents-video.webp thumbnail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 351 KiB After Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 381 KiB After Width: | Height: | Size: 943 KiB |
BIN
public/images/figma/why-parents-video.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
|
@ -13,9 +13,8 @@ import type { HomePageHero } from '@/types/globals'
|
|||
export const revalidate = 60
|
||||
|
||||
const STATIC_HERO: NonNullable<HomePageHero> = {
|
||||
title: 'Шуміленд — світ, де казка оживає',
|
||||
subtitle:
|
||||
'Сімейний тематичний парк, де гра допомагає пізнавати світ, а кожна прогулянка перетворюється на незабутню пригоду.',
|
||||
title: 'Започаткуйте традицію:',
|
||||
subtitle: 'щороку фотографуйтесь біля улюбленого динозавра',
|
||||
ctaLabel: 'Купити квиток',
|
||||
ctaHref: '/kvytky',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import type { HomePageHero, Media } from '@/types/globals'
|
|||
import { BtnPrimary } from '@/components/ui/BtnPrimary'
|
||||
|
||||
const IMG_BG2 = '/images/figma/hero-bg2.webp'
|
||||
const IMG_BG1 = '/images/figma/hero-bg1.webp'
|
||||
const IMG_FAMILY = '/images/figma/hero-bg-family.webp'
|
||||
|
||||
interface HeroProps {
|
||||
hero?: HomePageHero | null
|
||||
|
|
@ -24,7 +22,6 @@ export function Hero({ hero }: HeroProps) {
|
|||
const { title, subtitle, ctaLabel, ctaHref, backgroundImage, backgroundVideo } = hero
|
||||
const showCta = Boolean(ctaLabel && ctaHref && isSafeHref(ctaHref))
|
||||
const bgMedia = isMedia(backgroundImage) ? backgroundImage : null
|
||||
const useDefaultLayers = !bgMedia && !backgroundVideo
|
||||
|
||||
return (
|
||||
<section className="relative mx-[10px] -mt-[60px] h-[clamp(480px,calc(56.25vw+60px),calc(100vh+60px))] overflow-hidden rounded-b-[20px] bg-black lg:-mt-[120px] lg:h-[clamp(600px,calc(56.25vw+120px),calc(100vh+120px))]">
|
||||
|
|
@ -46,14 +43,12 @@ export function Hero({ hero }: HeroProps) {
|
|||
className="pointer-events-none absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<img
|
||||
src={IMG_BG2}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
</>
|
||||
<img
|
||||
src={IMG_BG2}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Gradient: mobile ── */}
|
||||
|
|
@ -75,16 +70,16 @@ export function Hero({ hero }: HeroProps) {
|
|||
}}
|
||||
/>
|
||||
|
||||
{/* ── Text content (above T-Rex z-[20]) ── */}
|
||||
{/* ── Text content ── */}
|
||||
<div className="relative z-[25] flex flex-col px-6 pt-[100px] pb-[60px] lg:px-0 lg:pt-[228px] lg:pb-[80px]">
|
||||
<div className="lg:mx-auto lg:w-full lg:max-w-[1504px] lg:px-0">
|
||||
<div className="flex max-w-[85vw] flex-col gap-5 md:gap-6 lg:max-w-[1032px] lg:pl-[154px]">
|
||||
<div className="flex max-w-[85vw] flex-col gap-5 md:gap-6 lg:max-w-[1032px] lg:gap-[38px] lg:pl-[154px]">
|
||||
{title && (
|
||||
<h1
|
||||
className="font-bold text-white uppercase"
|
||||
style={{
|
||||
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
|
||||
fontSize: 'clamp(28px, 6.25vw, 120px)',
|
||||
fontSize: 'clamp(28px, 5vw, 96px)',
|
||||
lineHeight: 1.2,
|
||||
fontWeight: 700,
|
||||
whiteSpace: 'pre-line',
|
||||
|
|
@ -98,10 +93,9 @@ export function Hero({ hero }: HeroProps) {
|
|||
className="text-white"
|
||||
style={{
|
||||
fontFamily: 'var(--font-montserrat, Montserrat), sans-serif',
|
||||
fontSize: 'clamp(14px, 1.25vw, 24px)',
|
||||
fontWeight: 500,
|
||||
fontSize: 'clamp(18px, 2.5vw, 48px)',
|
||||
fontWeight: 900,
|
||||
lineHeight: 1.5,
|
||||
maxWidth: '629px',
|
||||
}}
|
||||
>
|
||||
{subtitle}
|
||||
|
|
@ -115,24 +109,6 @@ export function Hero({ hero }: HeroProps) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── T-Rex overlay — above text, desktop only ── */}
|
||||
{useDefaultLayers && (
|
||||
<>
|
||||
<img
|
||||
src={IMG_BG1}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 z-[20] hidden h-full w-full object-cover lg:block"
|
||||
/>
|
||||
<img
|
||||
src={IMG_FAMILY}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 z-[30] hidden h-full w-full object-cover lg:block"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,7 @@
|
|||
import { useState, useRef, useEffect } from 'react'
|
||||
import type { HomePageWhyParentsItem, Media } from '@/types/globals'
|
||||
|
||||
function getMediaUrl(img: Media | string | null | undefined): string | null {
|
||||
if (!img) return null
|
||||
if (typeof img === 'string') return img
|
||||
return img.url ?? null
|
||||
}
|
||||
|
||||
const STATIC_GALLERY = [
|
||||
'/images/figma/why-parents-1.webp',
|
||||
'/images/figma/why-parents-2.webp',
|
||||
'/images/figma/why-parents-3.webp',
|
||||
'/images/figma/why-parents-4.webp',
|
||||
'/images/figma/gallery-1.webp',
|
||||
'/images/figma/gallery-3.webp',
|
||||
]
|
||||
const VIDEO_THUMBNAIL = '/images/figma/why-parents-video.webp'
|
||||
|
||||
const STATIC_ITEMS: HomePageWhyParentsItem[] = [
|
||||
{
|
||||
|
|
@ -58,11 +45,8 @@ interface WhyParentsProps {
|
|||
title?: string
|
||||
}
|
||||
|
||||
export function WhyParents({ items, sideGallery, title }: WhyParentsProps) {
|
||||
export function WhyParents({ items, sideGallery: _sideGallery, title }: WhyParentsProps) {
|
||||
const [openIndex, setOpenIndex] = useState<number>(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 activeItems = items && items.length > 0 ? items : STATIC_ITEMS
|
||||
|
|
@ -84,36 +68,6 @@ export function WhyParents({ items, sideGallery, title }: WhyParentsProps) {
|
|||
}, 4000)
|
||||
}
|
||||
|
||||
const galleryUrls: string[] =
|
||||
sideGallery && sideGallery.length > 0
|
||||
? sideGallery.map((g) => getMediaUrl(g.image) ?? '/images/figma/gallery-1.webp')
|
||||
: STATIC_GALLERY
|
||||
|
||||
const doubled = [...galleryUrls, ...galleryUrls]
|
||||
|
||||
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-[20px] md:py-[60px] lg:py-[40px]">
|
||||
<div className="mx-auto max-w-[1204px] px-8">
|
||||
|
|
@ -180,44 +134,56 @@ export function WhyParents({ items, sideGallery, title }: WhyParentsProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Horizontal auto-scroll gallery — desktop */}
|
||||
<div className="hidden flex-1 overflow-hidden lg:block">
|
||||
<div className="relative">
|
||||
{/* Desktop: 3 video thumbnail cards — Figma Gallery_holder */}
|
||||
<div className="hidden flex-1 overflow-hidden lg:flex lg:items-start lg:gap-5">
|
||||
{(
|
||||
[
|
||||
{ h: 546, mt: 0 },
|
||||
{ h: 488, mt: 29 },
|
||||
{ h: 488, mt: 29 },
|
||||
] as { h: number; mt: number }[]
|
||||
).map(({ h, mt }, i) => (
|
||||
<div
|
||||
ref={galleryRef}
|
||||
className="flex gap-5 overflow-x-auto"
|
||||
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
|
||||
onMouseEnter={() => {
|
||||
galleryPausedRef.current = true
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
galleryPausedRef.current = false
|
||||
}}
|
||||
key={i}
|
||||
className="relative flex-none overflow-hidden rounded-[20px] bg-[#d6f2c0]"
|
||||
style={{ width: 505, height: h, marginTop: mt }}
|
||||
>
|
||||
{doubled.map((src, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`relative h-[488px] w-[505px] flex-none overflow-hidden rounded-[20px] ${i === 0 ? 'bg-[#d6f2c0]' : ''}`}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
alt=""
|
||||
<img
|
||||
src={VIDEO_THUMBNAIL}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="h-full w-full object-cover"
|
||||
loading={i === 0 ? 'eager' : 'lazy'}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="flex h-[100px] w-[100px] items-center justify-center rounded-full bg-white/80 shadow-[0_4px_125px_0_rgba(23,27,36,0.6)]">
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
className="h-full w-full object-cover transition-transform duration-700 ease-out hover:scale-110"
|
||||
loading={i < 2 ? 'eager' : 'lazy'}
|
||||
/>
|
||||
className="ml-1"
|
||||
>
|
||||
<path d="M5 3L19 12L5 21V3Z" fill="#396817" />
|
||||
</svg>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Mobile: horizontal scroll */}
|
||||
{/* Mobile: horizontal scroll with video thumbnails */}
|
||||
<div
|
||||
className="flex w-full gap-5 overflow-x-auto pb-2 lg:hidden"
|
||||
style={{ scrollbarWidth: 'none' }}
|
||||
>
|
||||
{galleryUrls.map((src, i) => (
|
||||
{[
|
||||
'/images/figma/why-parents-1.webp',
|
||||
'/images/figma/why-parents-2.webp',
|
||||
'/images/figma/why-parents-3.webp',
|
||||
'/images/figma/why-parents-4.webp',
|
||||
].map((src, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex-none overflow-hidden rounded-[20px]"
|
||||
|
|
|
|||