diff --git a/next.config.ts b/next.config.ts
index de1ccf6..b17aaa3 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,9 +1,23 @@
import { withPayload } from '@payloadcms/next/withPayload'
import type { NextConfig } from 'next'
+const ONE_YEAR = 'public, max-age=31536000, immutable'
+
const nextConfig: NextConfig = {
output: 'standalone',
reactStrictMode: true,
+ async headers() {
+ return [
+ {
+ source: '/images/:path*',
+ headers: [{ key: 'Cache-Control', value: ONE_YEAR }],
+ },
+ {
+ source: '/_next/static/:path*',
+ headers: [{ key: 'Cache-Control', value: ONE_YEAR }],
+ },
+ ]
+ },
webpack: (config) => {
config.watchOptions = {
...config.watchOptions,
diff --git a/package.json b/package.json
index 276b311..4f78fab 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cyrillic-to-translit-js": "^3.2.1",
+ "drizzle-kit": "0.31.7",
"graphql": "^16.9.0",
"next": "^16.2.6",
"payload": "^3.33.0",
diff --git a/payload.config.ts b/payload.config.ts
index c9a3cc8..e1eee7e 100644
--- a/payload.config.ts
+++ b/payload.config.ts
@@ -45,7 +45,20 @@ export default buildConfig({
editor: lexicalEditor(),
sharp,
- collections: [Users, Media, Pages, BlogPosts, Categories, Tags, Tariffs, Leads, Orders, Locations, Reviews, BirthdayPackages],
+ collections: [
+ Users,
+ Media,
+ Pages,
+ BlogPosts,
+ Categories,
+ Tags,
+ Tariffs,
+ Leads,
+ Orders,
+ Locations,
+ Reviews,
+ BirthdayPackages,
+ ],
globals: [HomePage, CheckoutPage, ThankYouPage, Header, Footer, SiteSettings],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7de768a..66e79f9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,6 +29,9 @@ importers:
cyrillic-to-translit-js:
specifier: ^3.2.1
version: 3.2.1
+ drizzle-kit:
+ specifier: 0.31.7
+ version: 0.31.7
graphql:
specifier: ^16.9.0
version: 16.14.0
diff --git a/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp b/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp
new file mode 100644
index 0000000..5cd2ce3
Binary files /dev/null and b/public/images/figma/081e52b5-d35a-41d2-b506-a9d751b0b563.webp differ
diff --git a/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp b/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp
new file mode 100644
index 0000000..226d4e5
Binary files /dev/null and b/public/images/figma/109f13b4-25f9-4f94-a06d-77421ff2b4fe.webp differ
diff --git a/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp b/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp
new file mode 100644
index 0000000..d5d548f
Binary files /dev/null and b/public/images/figma/17d78753-cec3-40ca-a1fc-4125c2b79eff.webp differ
diff --git a/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp b/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp
new file mode 100644
index 0000000..dac81fb
Binary files /dev/null and b/public/images/figma/2936ec5e-4f99-441e-9bf2-34f23c283170.webp differ
diff --git a/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp b/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp
new file mode 100644
index 0000000..33b457a
Binary files /dev/null and b/public/images/figma/2c6a3e5e-7346-4c3e-b8a0-fae1facb87ad.webp differ
diff --git a/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp b/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp
new file mode 100644
index 0000000..dc09f88
Binary files /dev/null and b/public/images/figma/455670b9-c89a-4bea-81fe-5a4d93b25483.webp differ
diff --git a/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp b/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp
new file mode 100644
index 0000000..7f522e9
Binary files /dev/null and b/public/images/figma/5088f41f-b898-4958-b7f6-519496b65382.webp differ
diff --git a/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp b/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp
new file mode 100644
index 0000000..7dc46bc
Binary files /dev/null and b/public/images/figma/58218f84-457d-4ff5-8d4e-2ace40b45568.webp differ
diff --git a/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp b/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp
new file mode 100644
index 0000000..35cc975
Binary files /dev/null and b/public/images/figma/640999e1-7096-4623-bf96-12bb8ef62ffc.webp differ
diff --git a/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp b/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp
new file mode 100644
index 0000000..1f0b991
Binary files /dev/null and b/public/images/figma/7a2627b2-b6ce-4325-a0b1-fbc3393aca4c.webp differ
diff --git a/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp b/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp
new file mode 100644
index 0000000..12b737d
Binary files /dev/null and b/public/images/figma/908a8aab-6129-4d7d-b7ac-c43b6fa60044.webp differ
diff --git a/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp b/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp
new file mode 100644
index 0000000..6eeaf2e
Binary files /dev/null and b/public/images/figma/abacda18-57c7-441d-9313-c70d22c6f0f0.webp differ
diff --git a/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp b/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp
new file mode 100644
index 0000000..b109e50
Binary files /dev/null and b/public/images/figma/ac7f3a5e-0e66-4971-bccc-89901a7c314d.webp differ
diff --git a/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp b/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp
new file mode 100644
index 0000000..628e18a
Binary files /dev/null and b/public/images/figma/b5597790-7b54-4ad7-9977-4cb15633286e.webp differ
diff --git a/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp b/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp
new file mode 100644
index 0000000..2a63a1d
Binary files /dev/null and b/public/images/figma/c3053789-6cd0-4dda-9774-d4ae4bc400e1.webp differ
diff --git a/public/images/figma/check-mark.webp b/public/images/figma/check-mark.webp
new file mode 100644
index 0000000..870957d
Binary files /dev/null and b/public/images/figma/check-mark.webp differ
diff --git a/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp b/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp
new file mode 100644
index 0000000..628e18a
Binary files /dev/null and b/public/images/figma/de9ad287-5e83-4a0c-ad06-f2420bff4096.webp differ
diff --git a/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp b/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp
new file mode 100644
index 0000000..cbab9f9
Binary files /dev/null and b/public/images/figma/e42d6611-82e6-47b0-9d84-271f9810ab1a.webp differ
diff --git a/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp b/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp
new file mode 100644
index 0000000..019e09b
Binary files /dev/null and b/public/images/figma/e9a8cee6-6ee5-4c74-b270-1c133a762c0a.webp differ
diff --git a/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp b/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp
new file mode 100644
index 0000000..9a075d4
Binary files /dev/null and b/public/images/figma/f4e2bff2-754c-460c-baba-baa95521bfc9.webp differ
diff --git a/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp b/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp
new file mode 100644
index 0000000..4e561ad
Binary files /dev/null and b/public/images/figma/f5d32fc0-8ea7-4fd1-b193-819d6aa1a68e.webp differ
diff --git a/public/images/figma/footer-bg.webp b/public/images/figma/footer-bg.webp
new file mode 100644
index 0000000..9b930c0
Binary files /dev/null and b/public/images/figma/footer-bg.webp differ
diff --git a/public/images/figma/gallery-1.webp b/public/images/figma/gallery-1.webp
new file mode 100644
index 0000000..5e9c825
Binary files /dev/null and b/public/images/figma/gallery-1.webp differ
diff --git a/public/images/figma/gallery-2.webp b/public/images/figma/gallery-2.webp
new file mode 100644
index 0000000..1580746
Binary files /dev/null and b/public/images/figma/gallery-2.webp differ
diff --git a/public/images/figma/gallery-3.webp b/public/images/figma/gallery-3.webp
new file mode 100644
index 0000000..5e9c825
Binary files /dev/null and b/public/images/figma/gallery-3.webp differ
diff --git a/public/images/figma/gallery-4.webp b/public/images/figma/gallery-4.webp
new file mode 100644
index 0000000..53085a9
Binary files /dev/null and b/public/images/figma/gallery-4.webp differ
diff --git a/public/images/figma/gallery-6.webp b/public/images/figma/gallery-6.webp
new file mode 100644
index 0000000..d4372a9
Binary files /dev/null and b/public/images/figma/gallery-6.webp differ
diff --git a/public/images/figma/gallery-7.webp b/public/images/figma/gallery-7.webp
new file mode 100644
index 0000000..3d64245
Binary files /dev/null and b/public/images/figma/gallery-7.webp differ
diff --git a/public/images/figma/gallery-8.webp b/public/images/figma/gallery-8.webp
new file mode 100644
index 0000000..bcab104
Binary files /dev/null and b/public/images/figma/gallery-8.webp differ
diff --git a/public/images/figma/hero-bg-family.webp b/public/images/figma/hero-bg-family.webp
new file mode 100644
index 0000000..35cc975
Binary files /dev/null and b/public/images/figma/hero-bg-family.webp differ
diff --git a/public/images/figma/hero-bg1.webp b/public/images/figma/hero-bg1.webp
new file mode 100644
index 0000000..5cd2ce3
Binary files /dev/null and b/public/images/figma/hero-bg1.webp differ
diff --git a/public/images/figma/hero-bg2.webp b/public/images/figma/hero-bg2.webp
new file mode 100644
index 0000000..932d741
Binary files /dev/null and b/public/images/figma/hero-bg2.webp differ
diff --git a/public/images/figma/hero-blur-mask.webp b/public/images/figma/hero-blur-mask.webp
new file mode 100644
index 0000000..3af4494
Binary files /dev/null and b/public/images/figma/hero-blur-mask.webp differ
diff --git a/public/images/figma/loc-dinopark.webp b/public/images/figma/loc-dinopark.webp
new file mode 100644
index 0000000..1c7796c
Binary files /dev/null and b/public/images/figma/loc-dinopark.webp differ
diff --git a/public/images/figma/loc-divo-lis.webp b/public/images/figma/loc-divo-lis.webp
new file mode 100644
index 0000000..408efc0
Binary files /dev/null and b/public/images/figma/loc-divo-lis.webp differ
diff --git a/public/images/figma/loc-map.webp b/public/images/figma/loc-map.webp
new file mode 100644
index 0000000..226d4e5
Binary files /dev/null and b/public/images/figma/loc-map.webp differ
diff --git a/public/images/figma/news-bg1.webp b/public/images/figma/news-bg1.webp
new file mode 100644
index 0000000..70cbb8c
Binary files /dev/null and b/public/images/figma/news-bg1.webp differ
diff --git a/public/images/figma/news-bg2.webp b/public/images/figma/news-bg2.webp
new file mode 100644
index 0000000..f2f4c21
Binary files /dev/null and b/public/images/figma/news-bg2.webp differ
diff --git a/public/images/figma/news-bg3.webp b/public/images/figma/news-bg3.webp
new file mode 100644
index 0000000..a766634
Binary files /dev/null and b/public/images/figma/news-bg3.webp differ
diff --git a/public/images/figma/news-bg4.webp b/public/images/figma/news-bg4.webp
new file mode 100644
index 0000000..019e09b
Binary files /dev/null and b/public/images/figma/news-bg4.webp differ
diff --git a/public/images/figma/news-bg5.webp b/public/images/figma/news-bg5.webp
new file mode 100644
index 0000000..a2807bd
Binary files /dev/null and b/public/images/figma/news-bg5.webp differ
diff --git a/public/images/figma/news-bg6.webp b/public/images/figma/news-bg6.webp
new file mode 100644
index 0000000..6eeaf2e
Binary files /dev/null and b/public/images/figma/news-bg6.webp differ
diff --git a/public/images/figma/review-avatar-bg.webp b/public/images/figma/review-avatar-bg.webp
new file mode 100644
index 0000000..fdd12c3
Binary files /dev/null and b/public/images/figma/review-avatar-bg.webp differ
diff --git a/public/images/figma/video-preview.webp b/public/images/figma/video-preview.webp
new file mode 100644
index 0000000..dc09f88
Binary files /dev/null and b/public/images/figma/video-preview.webp differ
diff --git a/public/images/figma/why-parents-1.webp b/public/images/figma/why-parents-1.webp
new file mode 100644
index 0000000..31a5c7e
Binary files /dev/null and b/public/images/figma/why-parents-1.webp differ
diff --git a/public/images/figma/why-parents-2.webp b/public/images/figma/why-parents-2.webp
new file mode 100644
index 0000000..c2a822e
Binary files /dev/null and b/public/images/figma/why-parents-2.webp differ
diff --git a/public/images/figma/why-parents-3.webp b/public/images/figma/why-parents-3.webp
new file mode 100644
index 0000000..bfc390f
Binary files /dev/null and b/public/images/figma/why-parents-3.webp differ
diff --git a/public/images/figma/why-parents-4.webp b/public/images/figma/why-parents-4.webp
new file mode 100644
index 0000000..49dd1e5
Binary files /dev/null and b/public/images/figma/why-parents-4.webp differ
diff --git a/scripts/compress-images.mjs b/scripts/compress-images.mjs
new file mode 100644
index 0000000..6e21be0
--- /dev/null
+++ b/scripts/compress-images.mjs
@@ -0,0 +1,74 @@
+import sharp from 'sharp'
+import { readdir, stat } from 'fs/promises'
+import path from 'path'
+import { fileURLToPath } from 'url'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+const imgDir = path.resolve(__dirname, '../public/images/figma')
+
+const SKIP = /\.(svg|webp|gif)$/i
+
+const SIZE_LIMITS = {
+ // hero / footer / video-preview — full bleed, keep wide
+ 'hero-bg1.png': { width: 1920 },
+ 'hero-bg2.png': { width: 1920 },
+ 'hero-bg-family.png': { width: 1920 },
+ 'footer-bg.png': { width: 1920 },
+ 'video-preview.png': { width: 1920 },
+ // location cards — displayed at ~560px, 2× retina = 1120
+ 'loc-dinopark.jpg': { width: 1200 },
+ 'loc-divo-lis.png': { width: 1200 },
+ // gallery tiles — displayed at ~380px, 2× = 760
+ 'gallery-1.png': { width: 800 },
+ 'gallery-2.png': { width: 800 },
+ 'gallery-3.png': { width: 800 },
+ 'gallery-4.png': { width: 800 },
+ 'gallery-6.png': { width: 800 },
+ 'gallery-7.png': { width: 800 },
+ 'gallery-8.png': { width: 800 },
+ // why-parents side tiles
+ 'why-parents-1.png': { width: 800 },
+ 'why-parents-2.png': { width: 800 },
+ 'why-parents-3.png': { width: 800 },
+ 'why-parents-4.png': { width: 800 },
+ // news / blog thumbnails
+ 'news-bg1.jpg': { width: 800 },
+ 'news-bg2.png': { width: 800 },
+ 'news-bg3.jpg': { width: 800 },
+ // misc
+ 'review-avatar-bg.jpg': { width: 160 },
+ 'check-mark.png': { width: 48 },
+}
+
+const files = await readdir(imgDir)
+
+let totalBefore = 0
+let totalAfter = 0
+
+for (const file of files) {
+ if (SKIP.test(file)) continue
+ if (file.endsWith('.webp')) continue
+
+ const src = path.join(imgDir, file)
+ const dst = path.join(imgDir, file.replace(/\.(png|jpg|jpeg)$/i, '.webp'))
+
+ const srcStat = await stat(src)
+ totalBefore += srcStat.size
+
+ const limit = SIZE_LIMITS[file] ?? { width: 1920 }
+
+ try {
+ const info = await sharp(src)
+ .resize({ width: limit.width, withoutEnlargement: true })
+ .webp({ quality: 82, effort: 4 })
+ .toFile(dst)
+
+ totalAfter += info.size
+ const pct = ((1 - info.size / srcStat.size) * 100).toFixed(1)
+ console.log(`✓ ${file} → ${file.replace(/\.(png|jpg|jpeg)$/i, '.webp')} ${(srcStat.size / 1e6).toFixed(1)}MB → ${(info.size / 1e6).toFixed(1)}MB (${pct}% smaller)`)
+ } catch (err) {
+ console.error(`✗ ${file}: ${err.message}`)
+ }
+}
+
+console.log(`\nTotal: ${(totalBefore / 1e6).toFixed(1)}MB → ${(totalAfter / 1e6).toFixed(1)}MB (${((1 - totalAfter / totalBefore) * 100).toFixed(1)}% reduction)`)
diff --git a/src/app/(frontend)/lokatsii/page.tsx b/src/app/(frontend)/lokatsii/page.tsx
index 0f3f1a4..37a5767 100644
--- a/src/app/(frontend)/lokatsii/page.tsx
+++ b/src/app/(frontend)/lokatsii/page.tsx
@@ -19,7 +19,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [
slug: 'dynopark',
tagline: 'портал у світ динозаврів',
shortDesc: 'Прогуляйтесь серед реалістичних динозаврів у повний зріст. Понад 20 видів доісторичних тварин у природному середовищі.',
- image: '/images/figma/loc-dinopark.jpg',
+ image: '/images/figma/loc-dinopark.webp',
},
{
id: 'dyvolis',
@@ -27,7 +27,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [
slug: 'dyvolis',
tagline: 'зона казкових топіарних фігур',
shortDesc: 'Чарівний ліс з інтерактивними атракціонами, мотузковими парками та пригодами для всіх вікових груп.',
- image: '/images/figma/loc-divo-lis.png',
+ image: '/images/figma/loc-divo-lis.webp',
},
{
id: 'maze',
@@ -35,7 +35,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [
slug: 'maze',
tagline: 'справжній виклик кмітливості',
shortDesc: 'Захоплюючий лабіринт з дзеркалами, оптичними ілюзіями та таємничими кімнатами.',
- image: '/images/figma/gallery-1.png',
+ image: '/images/figma/gallery-1.webp',
},
{
id: 'tir',
@@ -43,7 +43,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [
slug: 'tir',
tagline: 'перемога, яку ви розділите разом',
shortDesc: 'Влаштуйте дружні змагання, дайте малечі декілька уроків та виграйте класний приз.',
- image: '/images/figma/gallery-3.png',
+ image: '/images/figma/gallery-3.webp',
},
{
id: 'playground',
@@ -51,7 +51,7 @@ const STATIC_LOCATIONS: LocationCMS[] = [
slug: 'playground',
tagline: 'територія забав і нових друзів',
shortDesc: 'Поки малеча підкорює гірки та знаходить перших друзів, ви можете нарешті зробити паузу.',
- image: '/images/figma/gallery-8.png',
+ image: '/images/figma/gallery-8.webp',
},
]
@@ -90,7 +90,7 @@ export default async function LocationsPage() {
{locations.map((loc, i) => {
- const imgUrl = getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.jpg'
+ const imgUrl = getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.webp'
const color = COLORS[i % COLORS.length]
const slug = loc.slug
diff --git a/src/collections/Leads.ts b/src/collections/Leads.ts
index 0ef2a78..f942c74 100644
--- a/src/collections/Leads.ts
+++ b/src/collections/Leads.ts
@@ -38,7 +38,12 @@ export const Leads: CollectionConfig = {
},
{ name: 'message', type: 'textarea', label: 'Повідомлення' },
{ name: 'groupSize', type: 'number', label: 'Кількість учасників' },
- { name: 'preferredDate', type: 'date', label: 'Бажана дата', admin: { date: { pickerAppearance: 'dayOnly' } } },
+ {
+ name: 'preferredDate',
+ type: 'date',
+ label: 'Бажана дата',
+ admin: { date: { pickerAppearance: 'dayOnly' } },
+ },
{ name: 'packageSlug', type: 'text', label: 'Пакет (slug)' },
{ name: 'notes', type: 'textarea' },
{
diff --git a/src/components/forms/BirthdayBookingForm.tsx b/src/components/forms/BirthdayBookingForm.tsx
index 2e44bcc..36d5896 100644
--- a/src/components/forms/BirthdayBookingForm.tsx
+++ b/src/components/forms/BirthdayBookingForm.tsx
@@ -46,10 +46,9 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps
packageSlug: packageSlug || undefined,
groupSize: guestCount ? Number(guestCount) : undefined,
preferredDate: preferredDate || undefined,
- message: [
- childAge ? `Вік іменинника: ${childAge}` : '',
- wishes,
- ].filter(Boolean).join('\n') || undefined,
+ message:
+ [childAge ? `Вік іменинника: ${childAge}` : '', wishes].filter(Boolean).join('\n') ||
+ undefined,
...utm,
}),
})
@@ -61,7 +60,7 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps
}
setSuccess(true)
} catch {
- setError('Помилка мережі. Перевірте з\'єднання та спробуйте ще раз.')
+ setError("Помилка мережі. Перевірте з'єднання та спробуйте ще раз.")
}
})
}
@@ -73,7 +72,7 @@ export function BirthdayBookingForm({ defaultPackage }: BirthdayBookingFormProps
Заявку отримано!
-
+
Менеджер зв'яжеться з вами протягом 30 хвилин для уточнення деталей свята.
@@ -220,7 +219,11 @@ function Field({
}) {
return (
-
@@ -198,7 +200,11 @@ function Field({
}) {
return (
-
+
{label}
{children}
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 7cffae9..6d6338d 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -3,7 +3,7 @@ import Link from 'next/link'
import { getGlobal } from '@/lib/payload'
import type { FooterGlobal } from '@/types/globals'
-const IMG_BG = '/images/figma/footer-bg.png'
+const IMG_BG = '/images/figma/footer-bg.webp'
const LOGO_G1 = '/images/figma/logo-g1-lg.svg'
const LOGO_G2 = '/images/figma/logo-g2-lg.svg'
const LOGO_G3 = '/images/figma/logo-g3-lg.svg'
@@ -19,10 +19,14 @@ const STATIC_NAV = [
]
const SOCIAL_ICONS: Record = {
- instagram: 'M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z',
- facebook: 'M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z',
- youtube: 'M23.495 6.205a3.007 3.007 0 0 0-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 0 0 .527 6.205a31.247 31.247 0 0 0-.522 5.805 31.247 31.247 0 0 0 .522 5.783 3.007 3.007 0 0 0 2.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 0 0 2.088-2.088 31.247 31.247 0 0 0 .5-5.783 31.247 31.247 0 0 0-.5-5.805zM9.609 15.601V8.408l6.264 3.602z',
- tiktok: 'M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z',
+ instagram:
+ 'M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z',
+ facebook:
+ 'M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z',
+ youtube:
+ 'M23.495 6.205a3.007 3.007 0 0 0-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 0 0 .527 6.205a31.247 31.247 0 0 0-.522 5.805 31.247 31.247 0 0 0 .522 5.783 3.007 3.007 0 0 0 2.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 0 0 2.088-2.088 31.247 31.247 0 0 0 .5-5.783 31.247 31.247 0 0 0-.5-5.805zM9.609 15.601V8.408l6.264 3.602z',
+ tiktok:
+ 'M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z',
}
export async function Footer() {
@@ -52,13 +56,22 @@ export async function Footer() {
{/* Logo */}
-
+
-
+
-
@@ -78,7 +91,7 @@ export async function Footer() {
{link.label}
- ) : null,
+ ) : null
)}
@@ -132,7 +145,9 @@ export async function Footer() {
) : (
-
{s.platform?.[0]?.toUpperCase()}
+
+ {s.platform?.[0]?.toUpperCase()}
+
)}
)
diff --git a/src/components/sections/BirthdayPricing.tsx b/src/components/sections/BirthdayPricing.tsx
index edf77fc..14f006b 100644
--- a/src/components/sections/BirthdayPricing.tsx
+++ b/src/components/sections/BirthdayPricing.tsx
@@ -2,7 +2,7 @@
import Link from 'next/link'
import type { BirthdayPackageCMS } from '@/types/globals'
-const IMG_CHECK = '/images/figma/check-mark.png'
+const IMG_CHECK = '/images/figma/check-mark.webp'
const STATIC_PACKAGES: BirthdayPackageCMS[] = [
{
diff --git a/src/components/sections/Gallery.tsx b/src/components/sections/Gallery.tsx
index 208d9b6..64a57d3 100644
--- a/src/components/sections/Gallery.tsx
+++ b/src/components/sections/Gallery.tsx
@@ -9,15 +9,15 @@ function getMediaUrl(img: Media | string | null | undefined): string | null {
}
const STATIC_IMAGES: GalleryImage[] = [
- { src: '/images/figma/gallery-1.png', alt: 'Шуміленд — фото 1', width: 320, height: 420, radius: 20 },
- { src: '/images/figma/why-parents-1.png', alt: 'Шуміленд — сімейні враження', width: 380, height: 420, radius: 20 },
- { src: '/images/figma/gallery-2.png', alt: 'Шуміленд — фото 2', width: 320, height: 420, radius: 20 },
- { src: '/images/figma/gallery-3.png', alt: 'Шуміленд — фото 3', width: 380, height: 420, radius: 20 },
- { src: '/images/figma/why-parents-2.png', alt: 'Шуміленд — прогулянка', width: 320, height: 420, radius: 20 },
- { src: '/images/figma/gallery-4.png', alt: 'Шуміленд — фото 4', width: 380, height: 420, radius: 20 },
- { src: '/images/figma/gallery-6.png', alt: 'Шуміленд — атракціони', width: 320, height: 420, radius: 20 },
- { src: '/images/figma/gallery-7.png', alt: 'Шуміленд — фото 5', width: 380, height: 420, radius: 20 },
- { src: '/images/figma/gallery-8.png', alt: 'Шуміленд — фото 6', width: 320, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-1.webp', alt: 'Шуміленд — фото 1', width: 320, height: 420, radius: 20 },
+ { src: '/images/figma/why-parents-1.webp', alt: 'Шуміленд — сімейні враження', width: 380, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-2.webp', alt: 'Шуміленд — фото 2', width: 320, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-3.webp', alt: 'Шуміленд — фото 3', width: 380, height: 420, radius: 20 },
+ { src: '/images/figma/why-parents-2.webp', alt: 'Шуміленд — прогулянка', width: 320, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-4.webp', alt: 'Шуміленд — фото 4', width: 380, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-6.webp', alt: 'Шуміленд — атракціони', width: 320, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-7.webp', alt: 'Шуміленд — фото 5', width: 380, height: 420, radius: 20 },
+ { src: '/images/figma/gallery-8.webp', alt: 'Шуміленд — фото 6', width: 320, height: 420, radius: 20 },
]
interface GalleryProps {
@@ -29,7 +29,7 @@ export function Gallery({ images, title }: GalleryProps) {
const items: GalleryImage[] =
images && images.length > 0
? images.map((img, i) => ({
- src: getMediaUrl(img.image) ?? '/images/figma/gallery-1.png',
+ src: getMediaUrl(img.image) ?? '/images/figma/gallery-1.webp',
alt: img.alt ?? `Шуміленд — фото ${i + 1}`,
width: i % 2 === 0 ? 320 : 380,
height: 420,
diff --git a/src/components/sections/GallerySlider.tsx b/src/components/sections/GallerySlider.tsx
index 58d7b8a..ba95cae 100644
--- a/src/components/sections/GallerySlider.tsx
+++ b/src/components/sections/GallerySlider.tsx
@@ -31,7 +31,8 @@ export function GallerySlider({ images, speed = 60 }: GallerySliderProps) {
if (!el) return
if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return
- // ~1px per tick — tune with speed prop
+ el.scrollLeft = 1
+
const pxPerTick = 1
const intervalMs = Math.max(8, Math.round((speed * 1000) / (el.scrollWidth / 2)))
diff --git a/src/components/sections/Hero.tsx b/src/components/sections/Hero.tsx
index 4717b35..808c973 100644
--- a/src/components/sections/Hero.tsx
+++ b/src/components/sections/Hero.tsx
@@ -2,9 +2,9 @@
import type { HomePageHero, Media } from '@/types/globals'
import { BtnPrimary } from '@/components/ui/BtnPrimary'
-const IMG_BG2 = '/images/figma/hero-bg2.png'
-const IMG_BG1 = '/images/figma/hero-bg1.png'
-const IMG_FAMILY = '/images/figma/hero-bg-family.png'
+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
@@ -28,8 +28,7 @@ export function Hero({ hero }: HeroProps) {
return (
{/* ── Background ──────────────────────────────────────────────── */}
{backgroundVideo ? (
@@ -91,7 +90,7 @@ export function Hero({ hero }: HeroProps) {
alt=""
aria-hidden="true"
className="pointer-events-none absolute inset-0 z-[20] h-full w-full object-cover"
- style={{ objectPosition: 'right center' }}
+ style={{ objectPosition: 'center center' }}
/>
)}
diff --git a/src/components/sections/Locations.tsx b/src/components/sections/Locations.tsx
index 2866776..c10f228 100644
--- a/src/components/sections/Locations.tsx
+++ b/src/components/sections/Locations.tsx
@@ -15,7 +15,7 @@ const STATIC_LOCATIONS: LocationData[] = [
tagline: 'портал у світ динозаврів',
description:
'Ви бачили їх у фільмах та мультиках, а тепер час зустріти в реальному житті та роздивитися їх зблизька! Найбільші динозаври України, які гарчать і рухаються, як справжні.',
- image: '/images/figma/loc-dinopark.jpg',
+ image: '/images/figma/loc-dinopark.webp',
href: '/lokatsii#dynopark',
},
{
@@ -24,7 +24,7 @@ const STATIC_LOCATIONS: LocationData[] = [
tagline: 'зона казкових топіарних фігур',
description:
'Тут на лісових стежках оселилися єдинороги, дракони та добрі лісові звірята. Це ідеальне місце, щоб пофантазувати разом із дитиною.',
- image: '/images/figma/loc-divo-lis.png',
+ image: '/images/figma/loc-divo-lis.webp',
href: '/lokatsii#dyvolis',
},
{
@@ -33,7 +33,7 @@ const STATIC_LOCATIONS: LocationData[] = [
tagline: 'справжній виклик кмітливості',
description:
'Чи зможете ви разом знайти вихід? Це справжній пригодницький виклик для всієї родини! Тут діти вчаться бути уважними та впевненими у собі.',
- image: '/images/figma/gallery-1.png',
+ image: '/images/figma/gallery-1.webp',
href: '/lokatsii#maze',
},
{
@@ -42,7 +42,7 @@ const STATIC_LOCATIONS: LocationData[] = [
tagline: 'перемога, яку ви розділите разом',
description:
'Для дітей це не просто гра, а можливість проявити себе та "заробити" подарунок. Влаштуйте дружні змагання, дайте малечі декілька уроків та виграйте класний приз.',
- image: '/images/figma/gallery-3.png',
+ image: '/images/figma/gallery-3.webp',
href: '/lokatsii#tir',
},
{
@@ -51,7 +51,7 @@ const STATIC_LOCATIONS: LocationData[] = [
tagline: 'територія забав і нових друзів',
description:
'Поки малеча підкорює гірки, випробовує безпечні лазанки та знаходить перших друзів, ви можете нарешті зробити паузу та просто спостерігати.',
- image: '/images/figma/gallery-8.png',
+ image: '/images/figma/gallery-8.webp',
href: '/lokatsii#playground',
},
]
@@ -69,7 +69,7 @@ export function Locations({ data, title }: LocationsProps) {
slug: loc.slug,
tagline: loc.tagline ?? '',
description: loc.shortDesc ?? '',
- image: getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.jpg',
+ image: getMediaUrl(loc.image) ?? '/images/figma/loc-dinopark.webp',
href: loc.href ?? `/lokatsii#${loc.slug}`,
}))
: STATIC_LOCATIONS
diff --git a/src/components/sections/LocationsSlider.tsx b/src/components/sections/LocationsSlider.tsx
index 7436e42..5c4f78b 100644
--- a/src/components/sections/LocationsSlider.tsx
+++ b/src/components/sections/LocationsSlider.tsx
@@ -1,8 +1,7 @@
'use client'
-import { useRef, useState } from 'react'
+import { useRef, useState, useEffect } from 'react'
import { BtnGradient } from '@/components/ui/BtnGradient'
-import { useAutoScroll } from '@/hooks/useAutoScroll'
export interface LocationData {
name: string
@@ -21,7 +20,20 @@ export function LocationsSlider({ locations }: LocationsSliderProps) {
const trackRef = useRef(null)
const pauseTimer = useRef | null>(null)
const [autoPaused, setAutoPaused] = useState(false)
- useAutoScroll(trackRef, { speed: 1.5, intervalMs: 16, disabled: autoPaused })
+ const doubled = [...locations, ...locations]
+
+ useEffect(() => {
+ const el = trackRef.current
+ if (!el) return
+ if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return
+ const id = setInterval(() => {
+ if (autoPaused) return
+ const half = el.scrollWidth / 2
+ el.scrollLeft += 1
+ if (el.scrollLeft >= half) el.scrollLeft = 0
+ }, 16)
+ return () => clearInterval(id)
+ }, [autoPaused])
function scrollByOne(dir: 1 | -1) {
trackRef.current?.scrollBy({ left: dir * 580, behavior: 'smooth' })
@@ -62,9 +74,9 @@ export function LocationsSlider({ locations }: LocationsSliderProps) {
className="flex gap-5 overflow-x-auto scroll-smooth pb-4"
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
>
- {locations.map((loc) => (
+ {doubled.map((loc, idx) => (
diff --git a/src/components/sections/News.tsx b/src/components/sections/News.tsx
index 0247fa0..8b62483 100644
--- a/src/components/sections/News.tsx
+++ b/src/components/sections/News.tsx
@@ -4,19 +4,19 @@ import { BtnDetails } from '@/components/ui/BtnDetails'
const FALLBACK_NEWS = [
{
- image: '/images/figma/news-bg1.jpg',
+ image: '/images/figma/news-bg1.webp',
title: 'Відкриття нового сезону у Шуміленді',
body: 'На території є чисті вбиральні, де можна також помити руку. Поруч розташовані велика паркувальна зона та укриття. Зручний громадський транспорт: метро, маршрутні таксі, що з\'єднують правий та лівий береги.',
href: '/blog',
},
{
- image: '/images/figma/news-bg2.png',
+ image: '/images/figma/news-bg2.webp',
title: 'Нові атракціони у ДиноПарку',
body: 'На території є чисті вбиральні, де можна також помити руку. Поруч розташовані велика паркувальна зона та укриття. Зручний громадський транспорт: метро, маршрутні таксі, що з\'єднують правий та лівий береги.',
href: '/blog',
},
{
- image: '/images/figma/news-bg3.jpg',
+ image: '/images/figma/news-bg3.webp',
title: 'Програма на вихідні в Шуміленді',
body: 'На території є чисті вбиральні, де можна також помити руку. Поруч розташовані велика паркувальна зона та укриття. Зручний громадський транспорт: метро, маршрутні таксі, що з\'єднують правий та лівий береги.',
href: '/blog',
diff --git a/src/components/sections/Reviews.tsx b/src/components/sections/Reviews.tsx
index 7690f71..75b06ac 100644
--- a/src/components/sections/Reviews.tsx
+++ b/src/components/sections/Reviews.tsx
@@ -6,7 +6,7 @@ import { useAutoScroll } from '@/hooks/useAutoScroll'
import type { ReviewCMS, Media } from '@/types/globals'
const IMG_RATE = '/images/figma/rate-stars.svg'
-const IMG_AVATAR_DEFAULT = '/images/figma/review-avatar-bg.jpg'
+const IMG_AVATAR_DEFAULT = '/images/figma/review-avatar-bg.webp'
function getMediaUrl(img: Media | string | null | undefined): string | null {
if (!img) return null
diff --git a/src/components/sections/VideoSection.tsx b/src/components/sections/VideoSection.tsx
index c81ddcc..c68d6d9 100644
--- a/src/components/sections/VideoSection.tsx
+++ b/src/components/sections/VideoSection.tsx
@@ -4,7 +4,7 @@
import { useState } from 'react'
import type { Media } from '@/types/globals'
-const IMG_POSTER_DEFAULT = '/images/figma/video-preview.png'
+const IMG_POSTER_DEFAULT = '/images/figma/video-preview.webp'
const IMG_PLAY = '/images/figma/btn-video-play.svg'
function getMediaUrl(img: Media | string | null | undefined): string | null {
diff --git a/src/components/sections/WhyParents.tsx b/src/components/sections/WhyParents.tsx
index bc5f808..f6a114d 100644
--- a/src/components/sections/WhyParents.tsx
+++ b/src/components/sections/WhyParents.tsx
@@ -11,12 +11,12 @@ function getMediaUrl(img: Media | string | null | undefined): string | null {
}
const STATIC_GALLERY = [
- '/images/figma/why-parents-1.png',
- '/images/figma/why-parents-2.png',
- '/images/figma/why-parents-3.png',
- '/images/figma/why-parents-4.png',
- '/images/figma/gallery-1.png',
- '/images/figma/gallery-3.png',
+ '/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 STATIC_ITEMS: HomePageWhyParentsItem[] = [
@@ -62,7 +62,7 @@ export function WhyParents({ items, sideGallery, title }: WhyParentsProps) {
const galleryUrls: string[] =
sideGallery && sideGallery.length > 0
- ? sideGallery.map((g) => getMediaUrl(g.image) ?? '/images/figma/gallery-1.png')
+ ? sideGallery.map((g) => getMediaUrl(g.image) ?? '/images/figma/gallery-1.webp')
: STATIC_GALLERY
const doubled = [...galleryUrls, ...galleryUrls]
diff --git a/src/components/ui/BtnDetails.tsx b/src/components/ui/BtnDetails.tsx
index 4e798a8..bce76e7 100644
--- a/src/components/ui/BtnDetails.tsx
+++ b/src/components/ui/BtnDetails.tsx
@@ -26,7 +26,7 @@ export function BtnDetails({ children, className = '', variant = 'dark', ...rest
const content = (
<>
{children}
-

+

>
)
diff --git a/src/lib/validateEnv.ts b/src/lib/validateEnv.ts
index f26d7dc..2a004ae 100644
--- a/src/lib/validateEnv.ts
+++ b/src/lib/validateEnv.ts
@@ -4,7 +4,7 @@ export function validateEnv(): void {
const missing = REQUIRED_VARS.filter((key) => !process.env[key])
if (missing.length > 0) {
throw new Error(
- `Missing required environment variables:\n${missing.map((k) => ` - ${k}`).join('\n')}\n\nCheck your .env file.`,
+ `Missing required environment variables:\n${missing.map((k) => ` - ${k}`).join('\n')}\n\nCheck your .env file.`
)
}
}