diff --git a/.dockerignore b/.dockerignore index 8db092c..09e1c96 100644 --- a/.dockerignore +++ b/.dockerignore @@ -27,3 +27,5 @@ data/ !data/yokatlas.db !data/yokatlas.db-wal !data/yokatlas.db-shm +# Lansman videosu (Remotion) imaja girmez +video/ diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..1637080 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,8 @@ const eslintConfig = defineConfig([ "out/**", "build/**", "next-env.d.ts", + // Lansman videosu ayrı bir Remotion projesi (kendi package.json'ı var) + "video/**", ]), ]); diff --git a/tsconfig.json b/tsconfig.json index cf9c65d..9dbaa4f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,5 +30,5 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": ["node_modules"] + "exclude": ["node_modules", "video"] } diff --git a/video/.gitignore b/video/.gitignore new file mode 100644 index 0000000..9209ef5 --- /dev/null +++ b/video/.gitignore @@ -0,0 +1,2 @@ +node_modules +out diff --git a/video/REMOCN-NOTU.md b/video/REMOCN-NOTU.md new file mode 100644 index 0000000..fda2a46 --- /dev/null +++ b/video/REMOCN-NOTU.md @@ -0,0 +1,30 @@ +# remocn kurulum notu + +remocn bileşenleri shadcn registry'sinden kurulur: + +```sh +pnpm dlx shadcn@latest add @remocn/ +``` + +**Dikkat:** shadcn CLI kurulumda string literal'leri Tailwind sınıf listesi gibi +işleyip tekrar eden "sınıfları" siliyor. Bu, boşlukla ayrılmış ve içinde +yinelenen değer olan her string'i bozar: SVG path'leri, `viewBox="0 0 24 24"`, +`transformOrigin: "0 0"`, `oklch(1 0 0 / 10%)`. İlk kurulumda `cursor.tsx` +(el ve ok imleci path'leri, viewBox) ve `lib/remocn-ui/theme.ts` (iki oklch +değeri) böyle bozuldu ve registry JSON'undan (`https://remocn.dev/r/.json`) +geri yüklendi. + +Yeni bir bileşen kurduktan sonra, içindeki boşluklu string'leri registry +JSON'uyla karşılaştır. + +## Bu projede yerel olarak değiştirilen bileşenler + +shadcn mantığı: kod bizim. Marka yazı tiplerini ve sahne ihtiyaçlarını +karşılamak için şu dosyalar değiştirildi (yeniden `add --overwrite` yapılırsa +değişiklikler gider): + +- `tracking-in.tsx`: `fontFamily`, `background` (şeffaf), `endTracking`, ReactNode metin +- `kinetic-center-build.tsx`: gerçek yazı tipiyle ölçüm, `birim` (harf/kelime), `vurgu`, `gap` +- `focus-blur-resolve.tsx`: `fontFamily` +- `slot-machine-roll.tsx`: `inline`, `fontFamily`, `stagger`, karaktere göre sütun genişliği +- `push-through.tsx`: `origin` (itmenin tıklanan noktadan yapılması) diff --git a/video/components.json b/video/components.json new file mode 100644 index 0000000..bbebf20 --- /dev/null +++ b/video/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { "config": "", "css": "", "baseColor": "slate", "cssVariables": false }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": { + "@remocn": "https://remocn.dev/r/{name}.json" + } +} diff --git a/video/package.json b/video/package.json new file mode 100644 index 0000000..7fffa20 --- /dev/null +++ b/video/package.json @@ -0,0 +1,32 @@ +{ + "name": "kolaytercih-video", + "version": "0.1.0", + "private": true, + "description": "KolayTercih lansman videosu (Remotion + remocn)", + "scripts": { + "studio": "remotion studio src/index.ts", + "render": "remotion render src/index.ts Lansman out/kolaytercih-lansman.mp4", + "render:dikey": "remotion render src/index.ts LansmanDikey out/kolaytercih-lansman-dikey.mp4", + "kare": "remotion still src/index.ts Lansman", + "tip": "tsc --noEmit" + }, + "dependencies": { + "@remotion/cli": "4.0.531", + "@remotion/fonts": "4.0.531", + "@remotion/google-fonts": "4.0.531", + "@remotion/transitions": "4.0.531", + "culori": "^4.0.2", + "lucide-react": "^1.49.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "remotion": "4.0.531", + "zod": "4.5.4" + }, + "devDependencies": { + "@types/culori": "^4.0.1", + "@types/node": "22", + "@types/react": "19", + "@types/react-dom": "19", + "typescript": "^5.9.3" + } +} diff --git a/video/pnpm-lock.yaml b/video/pnpm-lock.yaml new file mode 100644 index 0000000..9910d4b --- /dev/null +++ b/video/pnpm-lock.yaml @@ -0,0 +1,2811 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@remotion/cli': + specifier: 4.0.531 + version: 4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@remotion/fonts': + specifier: 4.0.531 + version: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/google-fonts': + specifier: 4.0.531 + version: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/transitions': + specifier: 4.0.531 + version: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + culori: + specifier: ^4.0.2 + version: 4.0.2 + lucide-react: + specifier: ^1.49.0 + version: 1.49.0(@types/react@19.3.0)(react@19.2.4) + react: + specifier: 19.2.4 + version: 19.2.4 + react-dom: + specifier: 19.2.4 + version: 19.2.4(react@19.2.4) + remotion: + specifier: 4.0.531 + version: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + zod: + specifier: 4.5.4 + version: 4.5.4 + devDependencies: + '@types/culori': + specifier: ^4.0.1 + version: 4.0.1 + '@types/node': + specifier: '22' + version: 22.20.4 + '@types/react': + specifier: '19' + version: 19.3.0 + '@types/react-dom': + specifier: '19' + version: 19.3.0(@types/react@19.3.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.1': + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.0': + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.11.3': + resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.3': + resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mediabunny/aac-encoder@1.56.1': + resolution: {integrity: sha512-a+OY/BctP7zC4G/prHikooOVaoWXMD4+bUyEwHg2dtQiQdIwC6TDJdVYJu5dfrdSNOiAFkv1HKcYEeROdwMezg==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/flac-encoder@1.56.1': + resolution: {integrity: sha512-zbmorMVlF1izwtlz5mWnu7A1rJLM+V26I0pEFr9Td1EDTwE5uAGRSW+/UZlnjwj1t00Woe3Zonfh9kb7lRRBqg==} + peerDependencies: + mediabunny: ^1.0.0 + + '@mediabunny/mp3-encoder@1.56.1': + resolution: {integrity: sha512-p66ovEq6mZt3ejje45NOju84hIJI4v8rHfsmc1D2J34ziV0USE8AdJBzmiwqxl6nZkIAQLNB19HzbVRcQfc51g==} + peerDependencies: + mediabunny: ^1.0.0 + + '@module-federation/error-codes@0.22.0': + resolution: {integrity: sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==} + + '@module-federation/runtime-core@0.22.0': + resolution: {integrity: sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==} + + '@module-federation/runtime-tools@0.22.0': + resolution: {integrity: sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==} + + '@module-federation/runtime@0.22.0': + resolution: {integrity: sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==} + + '@module-federation/sdk@0.22.0': + resolution: {integrity: sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==} + + '@module-federation/webpack-bundler-runtime@0.22.0': + resolution: {integrity: sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==} + + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + + '@remotion/bundler@4.0.531': + resolution: {integrity: sha512-Dv7LjGA/Uhy9H1D7vrrrwZJyL8hT70o8jxW8LsZbBYsrtn+N2Ntzt1mukdj1NM4xFLBjz8qY5flyJyNEdficSA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/canvas@4.0.531': + resolution: {integrity: sha512-VINlJX8nLozdf55lkgFj/hoILPuyBaXIzkwpaoBLh3C+heLDKf4LvfXh8JXorgCNDiwv7uKlkw6gsbkAIgcCGw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/captions@4.0.531': + resolution: {integrity: sha512-/T1VsjEhAM0ShTuRIx2M0cnAkADGeqxqK+A0MyEZefU4p/ddD25qul6Sk73MdSovKy8tx/jD+e6xnkogOLHIOA==} + + '@remotion/cli@4.0.531': + resolution: {integrity: sha512-p99o3iG4NOMTneeqIDVCDEHU61GR6x5VdDSkjpfjHn7oKhp5sPu02mQEWG3lzdYa0r40xkdankQ1Blztqo8zJg==} + hasBin: true + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/codemods@4.0.531': + resolution: {integrity: sha512-rkzC3nda0dsYwElqKxgtzTL6sCqo5K9i0lLEah8CeC0naYOA85w9ySaY+/OdQZfsOWX9LNCZBrR9nwo4hoQ49Q==} + + '@remotion/compositor-darwin-arm64@4.0.531': + resolution: {integrity: sha512-4EUrY22YfNrlXAe2PVg3wJWGLwFnVlHTwmptPJ6ZlYnN8sGYfYZkACTVTLOTO/YmLkRb9I+md1cH2mBX0ugAuQ==} + cpu: [arm64] + os: [darwin] + + '@remotion/compositor-darwin-x64@4.0.531': + resolution: {integrity: sha512-Uhr8cHwYhvYVYS+/Sl+RHyc0QGey+h9lsr1dEVeVKeGVQOESI5L0Xz0enyuj5vO6lgSxhBnXrFRmyOJ/JhtkiQ==} + cpu: [x64] + os: [darwin] + + '@remotion/compositor-linux-arm64-gnu@4.0.531': + resolution: {integrity: sha512-tOzrR5AqWVaQ25uPoYBRmOul1NEtb3CAElMriIfGDJqOEqXJZ+PzcULnjyfyj+B0Q38c27a6sF6UcXVAfyHBTw==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-arm64-musl@4.0.531': + resolution: {integrity: sha512-Q2Pt6p9M9WRXaKmd1jYngSMDac42Kj3Cha4n2coTSxiqvMtAtKq7+I+LerGratYw8HsqGJx2SrUwfRH1UMf54g==} + cpu: [arm64] + os: [linux] + + '@remotion/compositor-linux-x64-gnu@4.0.531': + resolution: {integrity: sha512-o9L1kBAiDVK5SGUpsgXKAeUj1uj2JUR8NoUYSo1nIWFd2Dgt/zRvyMtusp9ojofmiFQhvZszpOSbEB58muMc8Q==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-linux-x64-musl@4.0.531': + resolution: {integrity: sha512-gn9OPNe4VTKwt91EtZWoGdlLjc+FTDwsQxeynfBA6favxkzYZl5DMyi24xwh0blHVrzMuzXY/neywMqKlc9Lig==} + cpu: [x64] + os: [linux] + + '@remotion/compositor-win32-x64-msvc@4.0.531': + resolution: {integrity: sha512-q2/xGTcN+ZLTrPzTXORf+z6GretJEF2Vi2OTgLxV1S3tDOcBx4K16XAUM2KuieKv2E9hYO9VsU1AyQanJOyT5g==} + cpu: [x64] + os: [win32] + + '@remotion/elevenlabs@4.0.531': + resolution: {integrity: sha512-h6rN4dEjaaVqev3Z3vMaqjeKmB9PneiRQRqsuoVrL7TG6YzG4tWub27kri/y6NMGLbDWF49suK0ucfx0aPzJxA==} + + '@remotion/fonts@4.0.531': + resolution: {integrity: sha512-0eemQzfyBPNQYZZ3AumHaMbdFRwnoP+eHX96QeVwk16XXaTxAJ0jkUI8e86BW7/ABvYlFj0iIjtqbgvE9C3cJg==} + + '@remotion/google-fonts@4.0.531': + resolution: {integrity: sha512-Jy1qVNsse6yH9CdFLzZTxHUYfZsx7/igZsW4IQd6hHhjncqpd6krrt7u9O2eFZ3XRsKKVCPGElF02iuNMrsbLw==} + + '@remotion/licensing@4.0.531': + resolution: {integrity: sha512-h17IbJSbD8GmkwJrayVJFUGCjIPTR4uj1AVOWQQYfBfv7/mgPNtCZPXDths9yQOphFXfwPq4IRRIZFPXYJFJZw==} + + '@remotion/media-parser@4.0.531': + resolution: {integrity: sha512-R8z0K2vPACQ5wsxOHnykIN2vbDpfyobGuuScJxtCEfH5IycuS1TL+JGnd1cFJGMg3+XPePeenJjEJPsrrni/TA==} + + '@remotion/media-utils@4.0.531': + resolution: {integrity: sha512-z3Zi8nqAfYrMG3Kkqji5KeAavNmF/af2jNffSsSKLfNoIcg+tgmsKeLy5srtLVrDdfgku8E8Jootnq2IE8emwA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/media@4.0.531': + resolution: {integrity: sha512-MQhJqvn0WC51hBUyqHsR368gYrN+kyZe0HWX652UqxQ601iOHGIJyIHUAx/YNDxOE9qoBt85M+5r0wKULKnrvw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/openai-whisper@4.0.531': + resolution: {integrity: sha512-RUfZ/1EjvvyY6fGpAbYrLaUWmiqys6UCwd5+ozcD/0NkDLsg9vcmyED5MnZnsLRO8eDaLRzjGbL+QuA/o8ePGg==} + + '@remotion/paths@4.0.531': + resolution: {integrity: sha512-fpSoVjz5mSa5whVOy4TvCWwDPHaVDIhf9x1nusxa2faNi/Y6IPzNUq2WHtxoZ5gxd9iExgDu4VGjf1arVnPLOQ==} + + '@remotion/player@4.0.531': + resolution: {integrity: sha512-a2iq13CQvUJMXq2Lf9aJD1TOyQ0Z44n4dEtHjOV1i4P8z6fjnvVDH19n+5ya/NLmakBdLyYmTghF3t0962b92g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/renderer@4.0.531': + resolution: {integrity: sha512-xsPrN0gmweNjEhpt9bOLUDV9odfPlVrUJkn4tOv4TA621X33U/gejfGdvMbOM/kPkZ62Bx6P3rY3eBCgFGQnkw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/shapes@4.0.531': + resolution: {integrity: sha512-0g2TdbC67PibqUv4GgTc6abQzuTBu2Si4R2mDQKxlBHwHIvwLRx4D3UgX6Ccux7kzkVkBnxlHdl4nZv0SxwQJw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/streaming@4.0.531': + resolution: {integrity: sha512-cCXg6HBvgTPCvMGxnpb+2hmwfaBmD+0VuRVX9yjDWzxoDoAG+abYq/JQHsSbJHQRnRkfwHaow73u1H1DOW27qQ==} + + '@remotion/studio-protocol@4.0.531': + resolution: {integrity: sha512-8UewGkNOSRVNwgzKiAYZpHJo6QOdzuHpaP7pR503fIlPn5ZpDb771T8ZqJ91oWzoih6B4V6aHu6/Ny+jUkJoEw==} + + '@remotion/studio-server@4.0.531': + resolution: {integrity: sha512-HqyvUnFNb78j+ht/jEqykxzNVqyrFwr6suDOidOvSrwUhfb+dgorrolSjz7vLZmMGDvlSeo2333UcJ+WJnfGxQ==} + + '@remotion/studio-shared@4.0.531': + resolution: {integrity: sha512-no/I+Tt6df/guYRpBFGEj/JOS532pmyGCQfor6wfLDjW4zWDmhsehny1JZrYHWgg25DVrnl731JlkfVaoZIF8A==} + + '@remotion/studio@4.0.531': + resolution: {integrity: sha512-mFdI2tzKX8MiXDBpqIImV4LqPA3BNEg1jzn++h/vISltJQ6FuwMxXYSWa8QPlTW5zMaFEVODuPLhooGuMU8aWQ==} + peerDependencies: + '@remotion/video-matting': 4.0.531 + '@remotion/whisper-webgpu': 4.0.531 + react: '>=16.8.0' + react-dom: '>=16.8.0' + peerDependenciesMeta: + '@remotion/video-matting': + optional: true + '@remotion/whisper-webgpu': + optional: true + + '@remotion/timeline-utils@4.0.531': + resolution: {integrity: sha512-XE2ikcvYYgxZW57TUE3UEaJVR4zpujOsHq7rmxucqnaDqyu0N6+tGXpJK+MKzW0RVsYik1nQ8Gv0NSp4DjaL6w==} + + '@remotion/transitions@4.0.531': + resolution: {integrity: sha512-WQJCitaF0guxYxZuCujasL6WeFnpOaObmM8nzwduEJizE0HNGq8e8ragRznpt2op7osfri8/eHjKzVRKBsngjw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@remotion/web-renderer@4.0.531': + resolution: {integrity: sha512-fIS7v0ZB97xGEKKKMvFQ/7/puV4ZqJRfoQQwczcGRp9WppJLO65IQYPYnWp206j0KBDo8Qff6CtICRIrd5C2Qw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@remotion/zod-types@4.0.531': + resolution: {integrity: sha512-EWaHyq1i2ATtVRbpNiQlS2DCub6Rc5L0eU5OgYBFfY4TWQ3yihXgt3X3XIOFwLLA63ZAEzBEpZXUk4KBC3inOw==} + + '@rspack/binding-darwin-arm64@1.7.11': + resolution: {integrity: sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.7.11': + resolution: {integrity: sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.7.11': + resolution: {integrity: sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.7.11': + resolution: {integrity: sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.7.11': + resolution: {integrity: sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.7.11': + resolution: {integrity: sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.7.11': + resolution: {integrity: sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.7.11': + resolution: {integrity: sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.7.11': + resolution: {integrity: sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.7.11': + resolution: {integrity: sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.7.11': + resolution: {integrity: sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q==} + + '@rspack/core@1.7.11': + resolution: {integrity: sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + + '@rspack/plugin-react-refresh@1.6.1': + resolution: {integrity: sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw==} + peerDependencies: + react-refresh: '>=0.10.0 <1.0.0' + webpack-hot-middleware: 2.x + peerDependenciesMeta: + webpack-hot-middleware: + optional: true + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@tanstack/react-virtual@3.14.9': + resolution: {integrity: sha512-qZyr0FZDP8rDC4WBhsryIZmAd9bveJvFGUJJtskWaew6/0dTRS6wZxnR6VQ5bY2KwL3LjerrHqQLk3a0GKcPXQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/virtual-core@3.17.7': + resolution: {integrity: sha512-bp+v10y65sp2H7WpWfIMyxTNfl8ZVfxFTLRjPIFRryi6FV/J33z4IS53WO4pTk36KlvJ4iLiQz+oaydDC1xbcA==} + + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + + '@types/culori@4.0.1': + resolution: {integrity: sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ==} + + '@types/dom-mediacapture-transform@0.1.12': + resolution: {integrity: sha512-d7/QsLRwF864A5mgIM/YrfiglHoYn7zgCcAoJgW404r+2DwnNr7EBbLnCWpmOMgH8y0te73L1AV6H1bmauaWFw==} + + '@types/dom-webcodecs@0.1.13': + resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.20.4': + resolution: {integrity: sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + ast-types@0.16.3: + resolution: {integrity: sha512-FvWoWYfSCM6kRxCSH+MGLHIKKGRL6A6AW7Zek2O32REPQRdg131428uRTKMBYAeRd3XXAaHDS60Wpri7CdKDrA==} + engines: {node: '>=4'} + + baseline-browser-mapping@2.11.26: + resolution: {integrity: sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.29.3: + resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001814: + resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-loader@7.1.4: + resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 + webpack: ^5.27.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + culori@4.0.2: + resolution: {integrity: sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@17.3.1: + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + engines: {node: '>=12'} + + electron-to-chromium@1.5.443: + resolution: {integrity: sha512-TDJG36L9A3CWWwZ97HKaE+Iz1sW80pNp6sAU0owzCIV3Zc9eVyRqZK8Vz6NKuMIU8WjP0pIkoqCa0VRf/yFD0Q==} + + enhanced-resolve@5.26.0: + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} + + fs-monkey@1.0.3: + resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kiwi-schema@0.5.0: + resolution: {integrity: sha512-X+FpfU0yTEtc6aTHS7VwbOpvQwRt70+pXXWRI5fd6CvWhe7pSVC854TVo4Zo0x5/wwcWj+/9KUlXpdcP0dY9AA==} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + loader-runner@4.3.2: + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + engines: {node: '>=6.11.5'} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lucide-react@1.49.0: + resolution: {integrity: sha512-ApfaNM4/7jBrVopqLJ84Jz8TMQ1Q1JeZ1PmKh25aeS3E1FTVcMkSh1bEQB7dfzdIrwXRP8p3z4AJjTIHHb5IIA==} + peerDependencies: + '@types/react': '*' + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + mediabunny@1.56.1: + resolution: {integrity: sha512-DNOm0bRBmeOxJgCK/g3LxNWbxY5morTauNdlychujGmJ9L37ezVezkNoI1WtO2Xdb4WlWWJlYfIKnOEzd5phRw==} + + memfs@3.4.3: + resolution: {integrity: sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==} + engines: {node: '>= 4.0.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimist@1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-releases@2.0.57: + resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} + engines: {node: '>=18'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-selector-parser@7.1.6: + resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + recast@0.23.21: + resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==} + engines: {node: '>= 4'} + + remotion@4.0.531: + resolution: {integrity: sha512-GchTilW0M1zk9NsYxK9pIMBeDQMiQ1TrIz3zhPpMiysf9P0rTUYwxkJ9bFANwykz4nlImpWRtUHSxZnhBYeDdw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + schema-utils@4.5.0: + resolution: {integrity: sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA==} + engines: {node: '>= 10.13.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.3: + resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} + engines: {node: '>= 12'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + style-loader@4.0.0: + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.27.0 + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + svg-parser@2.1.0: + resolution: {integrity: sha512-bwLf38YmY+TDYHJw1Ex0Co8c4yeXuJAo8YnXGZrscxrvYoVVIvLeniEkV1Ks/54VteMnL4FtyN1+P+TZJdUPmQ==} + engines: {node: '>=8'} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.6.1: + resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@minify-html/node': '*' + '@swc/core': '*' + '@swc/css': '*' + '@swc/html': '*' + clean-css: '*' + cssnano: '*' + csso: '*' + esbuild: '*' + html-minifier-terser: '*' + lightningcss: '*' + postcss: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@minify-html/node': + optional: true + '@swc/core': + optional: true + '@swc/css': + optional: true + '@swc/html': + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true + esbuild: + optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true + uglify-js: + optional: true + + terser@5.51.2: + resolution: {integrity: sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==} + engines: {node: '>=10'} + hasBin: true + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + watchpack@2.5.2: + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.6.0: + resolution: {integrity: sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA==} + engines: {node: '>=10.13.0'} + + webpack@5.105.0: + resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + zod@4.5.4: + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.24.1': + dependencies: + '@babel/types': 7.29.8 + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.0': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + to-fast-properties: 2.0.0 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@emnapi/core@1.11.3': + dependencies: + '@emnapi/wasi-threads': 1.2.3 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@mediabunny/aac-encoder@1.56.1(mediabunny@1.56.1)': + dependencies: + mediabunny: 1.56.1 + + '@mediabunny/flac-encoder@1.56.1(mediabunny@1.56.1)': + dependencies: + mediabunny: 1.56.1 + + '@mediabunny/mp3-encoder@1.56.1(mediabunny@1.56.1)': + dependencies: + mediabunny: 1.56.1 + + '@module-federation/error-codes@0.22.0': {} + + '@module-federation/runtime-core@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/runtime-tools@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/webpack-bundler-runtime': 0.22.0 + + '@module-federation/runtime@0.22.0': + dependencies: + '@module-federation/error-codes': 0.22.0 + '@module-federation/runtime-core': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@module-federation/sdk@0.22.0': {} + + '@module-federation/webpack-bundler-runtime@0.22.0': + dependencies: + '@module-federation/runtime': 0.22.0 + '@module-federation/sdk': 0.22.0 + + '@napi-rs/wasm-runtime@1.0.7': + dependencies: + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.4 + optional: true + + '@remotion/bundler@4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/media-parser': 4.0.531 + '@remotion/studio': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/timeline-utils': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@rspack/core': 1.7.11 + '@rspack/plugin-react-refresh': 1.6.1(react-refresh@0.18.0) + css-loader: 7.1.4(@rspack/core@1.7.11)(webpack@5.105.0(esbuild@0.28.1)(postcss@8.5.28)) + esbuild: 0.28.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-refresh: 0.18.0 + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + style-loader: 4.0.0(webpack@5.105.0(esbuild@0.28.1)(postcss@8.5.28)) + webpack: 5.105.0(esbuild@0.28.1)(postcss@8.5.28) + transitivePeerDependencies: + - '@minify-html/node' + - '@remotion/video-matting' + - '@remotion/whisper-webgpu' + - '@swc/core' + - '@swc/css' + - '@swc/helpers' + - '@swc/html' + - bufferutil + - clean-css + - cssnano + - csso + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/canvas@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/player': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/captions@4.0.531': {} + + '@remotion/cli@4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + dependencies: + '@babel/parser': 7.24.1 + '@remotion/bundler': 4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/media-utils': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/player': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-server': 4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + dotenv: 17.3.1 + minimist: 1.2.6 + prompts: 2.4.2 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + semver: 7.5.3 + transitivePeerDependencies: + - '@minify-html/node' + - '@remotion/video-matting' + - '@remotion/whisper-webgpu' + - '@swc/core' + - '@swc/css' + - '@swc/helpers' + - '@swc/html' + - bufferutil + - clean-css + - cssnano + - csso + - html-minifier-terser + - lightningcss + - postcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/codemods@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + '@remotion/studio-protocol': 4.0.531 + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ast-types: 0.16.1 + recast: 0.23.21 + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/compositor-darwin-arm64@4.0.531': + optional: true + + '@remotion/compositor-darwin-x64@4.0.531': + optional: true + + '@remotion/compositor-linux-arm64-gnu@4.0.531': + optional: true + + '@remotion/compositor-linux-arm64-musl@4.0.531': + optional: true + + '@remotion/compositor-linux-x64-gnu@4.0.531': + optional: true + + '@remotion/compositor-linux-x64-musl@4.0.531': + optional: true + + '@remotion/compositor-win32-x64-msvc@4.0.531': + optional: true + + '@remotion/elevenlabs@4.0.531': + dependencies: + '@remotion/captions': 4.0.531 + zod: 4.5.4 + + '@remotion/fonts@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/google-fonts@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/licensing@4.0.531': {} + + '@remotion/media-parser@4.0.531': {} + + '@remotion/media-utils@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + mediabunny: 1.56.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/media@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + mediabunny: 1.56.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + zod: 4.5.4 + + '@remotion/openai-whisper@4.0.531': + dependencies: + '@remotion/captions': 4.0.531 + zod: 4.5.4 + + '@remotion/paths@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/player@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/renderer@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/licensing': 4.0.531 + '@remotion/streaming': 4.0.531 + execa: 5.1.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + source-map: 0.8.0 + ws: 8.21.0 + optionalDependencies: + '@remotion/compositor-darwin-arm64': 4.0.531 + '@remotion/compositor-darwin-x64': 4.0.531 + '@remotion/compositor-linux-arm64-gnu': 4.0.531 + '@remotion/compositor-linux-arm64-musl': 4.0.531 + '@remotion/compositor-linux-x64-gnu': 4.0.531 + '@remotion/compositor-linux-x64-musl': 4.0.531 + '@remotion/compositor-win32-x64-msvc': 4.0.531 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@remotion/shapes@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/paths': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/streaming@4.0.531': {} + + '@remotion/studio-protocol@4.0.531': + dependencies: + zod: 4.5.4 + + '@remotion/studio-server@4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + dependencies: + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + '@remotion/bundler': 4.0.531(postcss@8.5.28)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/codemods': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-protocol': 4.0.531 + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + kiwi-schema: 0.5.0 + memfs: 3.4.3 + open: 8.4.2 + recast: 0.23.21 + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + semver: 7.5.3 + zod: 4.5.4 + transitivePeerDependencies: + - '@minify-html/node' + - '@remotion/video-matting' + - '@remotion/whisper-webgpu' + - '@swc/core' + - '@swc/css' + - '@swc/helpers' + - '@swc/html' + - bufferutil + - clean-css + - cssnano + - csso + - html-minifier-terser + - lightningcss + - postcss + - react + - react-dom + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + - webpack-hot-middleware + + '@remotion/studio-shared@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/studio@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@remotion/canvas': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/captions': 4.0.531 + '@remotion/codemods': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/elevenlabs': 4.0.531 + '@remotion/media': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/media-utils': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/openai-whisper': 4.0.531 + '@remotion/paths': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/player': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/renderer': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/studio-protocol': 4.0.531 + '@remotion/studio-shared': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/timeline-utils': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/web-renderer': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/zod-types': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-virtual': 3.14.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + mediabunny: 1.56.1 + memfs: 3.4.3 + open: 8.4.2 + prismjs: 1.30.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + semver: 7.5.3 + zod: 4.5.4 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@remotion/timeline-utils@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + mediabunny: 1.56.1 + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@remotion/transitions@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@remotion/paths': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@remotion/shapes': 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/web-renderer@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@mediabunny/aac-encoder': 1.56.1(mediabunny@1.56.1) + '@mediabunny/flac-encoder': 1.56.1(mediabunny@1.56.1) + '@mediabunny/mp3-encoder': 1.56.1(mediabunny@1.56.1) + '@remotion/licensing': 4.0.531 + mediabunny: 1.56.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@remotion/zod-types@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + remotion: 4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + + '@rspack/binding-darwin-arm64@1.7.11': + optional: true + + '@rspack/binding-darwin-x64@1.7.11': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.7.11': + optional: true + + '@rspack/binding-linux-arm64-musl@1.7.11': + optional: true + + '@rspack/binding-linux-x64-gnu@1.7.11': + optional: true + + '@rspack/binding-linux-x64-musl@1.7.11': + optional: true + + '@rspack/binding-wasm32-wasi@1.7.11': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.7.11': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.7.11': + optional: true + + '@rspack/binding-win32-x64-msvc@1.7.11': + optional: true + + '@rspack/binding@1.7.11': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.7.11 + '@rspack/binding-darwin-x64': 1.7.11 + '@rspack/binding-linux-arm64-gnu': 1.7.11 + '@rspack/binding-linux-arm64-musl': 1.7.11 + '@rspack/binding-linux-x64-gnu': 1.7.11 + '@rspack/binding-linux-x64-musl': 1.7.11 + '@rspack/binding-wasm32-wasi': 1.7.11 + '@rspack/binding-win32-arm64-msvc': 1.7.11 + '@rspack/binding-win32-ia32-msvc': 1.7.11 + '@rspack/binding-win32-x64-msvc': 1.7.11 + + '@rspack/core@1.7.11': + dependencies: + '@module-federation/runtime-tools': 0.22.0 + '@rspack/binding': 1.7.11 + '@rspack/lite-tapable': 1.1.0 + + '@rspack/lite-tapable@1.1.0': {} + + '@rspack/plugin-react-refresh@1.6.1(react-refresh@0.18.0)': + dependencies: + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + react-refresh: 0.18.0 + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-preset@8.1.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.7) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.7) + + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.24.0 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.1.0 + transitivePeerDependencies: + - supports-color + + '@tanstack/react-virtual@3.14.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@tanstack/virtual-core': 3.17.7 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@tanstack/virtual-core@3.17.7': {} + + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/culori@4.0.1': {} + + '@types/dom-mediacapture-transform@0.1.12': + dependencies: + '@types/dom-webcodecs': 0.1.13 + + '@types/dom-webcodecs@0.1.13': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.9 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.20.4': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-phases@1.0.4(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + argparse@2.0.1: {} + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + ast-types@0.16.3: + dependencies: + tslib: 2.8.1 + + baseline-browser-mapping@2.11.26: {} + + browserslist@4.29.3: + dependencies: + baseline-browser-mapping: 2.11.26 + caniuse-lite: 1.0.30001814 + electron-to-chromium: 1.5.443 + node-releases: 2.0.57 + update-browserslist-db: 1.3.3(browserslist@4.29.3) + + buffer-from@1.1.2: {} + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001814: {} + + chrome-trace-event@1.0.4: {} + + commander@2.20.3: {} + + convert-source-map@2.0.0: {} + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.3.2 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-loader@7.1.4(@rspack/core@1.7.11)(webpack@5.105.0(esbuild@0.28.1)(postcss@8.5.28)): + dependencies: + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.28) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.28) + postcss-modules-scope: 3.2.1(postcss@8.5.28) + postcss-modules-values: 4.0.0(postcss@8.5.28) + postcss-value-parser: 4.2.0 + semver: 7.8.5 + optionalDependencies: + '@rspack/core': 1.7.11 + webpack: 5.105.0(esbuild@0.28.1)(postcss@8.5.28) + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + culori@4.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + define-lazy-prop@2.0.0: {} + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv@17.3.1: {} + + electron-to-chromium@1.5.443: {} + + enhanced-resolve@5.26.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-module-lexer@2.3.2: {} + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escalade@3.2.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + esprima@4.0.1: {} + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.8: {} + + fs-monkey@1.0.3: {} + + gensync@1.0.0-beta.2: {} + + get-stream@6.0.1: {} + + glob-to-regexp@0.4.1: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + html-entities@2.6.0: {} + + human-signals@2.1.0: {} + + icss-utils@5.1.0(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + is-arrayish@0.2.1: {} + + is-docker@2.2.1: {} + + is-stream@2.0.1: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isexe@2.0.0: {} + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.20.4 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + js-tokens@4.0.0: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + kiwi-schema@0.5.0: {} + + kleur@3.0.3: {} + + lines-and-columns@1.2.4: {} + + loader-runner@4.3.2: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lucide-react@1.49.0(@types/react@19.3.0)(react@19.2.4): + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.3.0 + + mediabunny@1.56.1: + dependencies: + '@types/dom-mediacapture-transform': 0.1.12 + '@types/dom-webcodecs': 0.1.13 + + memfs@3.4.3: + dependencies: + fs-monkey: 1.0.3 + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + minimist@1.2.6: {} + + ms@2.1.3: {} + + nanoid@3.3.19: {} + + neo-async@2.6.2: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-releases@2.0.57: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-key@3.1.1: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + postcss-modules-extract-imports@3.1.0(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.28): + dependencies: + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 + postcss-selector-parser: 7.1.6 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + postcss-selector-parser: 7.1.6 + + postcss-modules-values@4.0.0(postcss@8.5.28): + dependencies: + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 + + postcss-selector-parser@7.1.6: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + prismjs@1.30.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-refresh@0.18.0: {} + + react@19.2.4: {} + + recast@0.23.21: + dependencies: + ast-types: 0.16.3 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + remotion@4.0.531(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + scheduler@0.27.0: {} + + schema-utils@4.5.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) + + semver@6.3.1: {} + + semver@7.5.3: + dependencies: + lru-cache: 6.0.0 + + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@3.0.7: {} + + sisteransi@1.0.5: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + source-map-js@1.2.2: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.8.0: {} + + stackframe@1.3.4: {} + + strip-final-newline@2.0.0: {} + + style-loader@4.0.0(webpack@5.105.0(esbuild@0.28.1)(postcss@8.5.28)): + dependencies: + webpack: 5.105.0(esbuild@0.28.1)(postcss@8.5.28) + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + svg-parser@2.1.0: {} + + tapable@2.3.3: {} + + terser-webpack-plugin@5.6.1(esbuild@0.28.1)(postcss@8.5.28)(webpack@5.105.0(postcss@8.5.28)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.5.0 + terser: 5.51.2 + webpack: 5.105.0(esbuild@0.28.1)(postcss@8.5.28) + optionalDependencies: + esbuild: 0.28.1 + postcss: 8.5.28 + + terser@5.51.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.18.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + tiny-invariant@1.3.3: {} + + to-fast-properties@2.0.0: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + update-browserslist-db@1.3.3(browserslist@4.29.3): + dependencies: + browserslist: 4.29.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + util-deprecate@1.0.2: {} + + watchpack@2.5.2: + dependencies: + graceful-fs: 4.2.11 + + webpack-sources@3.6.0: {} + + webpack@5.105.0(esbuild@0.28.1)(postcss@8.5.28): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.18.0 + acorn-import-phases: 1.0.4(acorn@8.18.0) + browserslist: 4.29.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.26.0 + es-module-lexer: 2.3.2 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.2 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.5.0 + tapable: 2.3.3 + terser-webpack-plugin: 5.6.1(esbuild@0.28.1)(postcss@8.5.28)(webpack@5.105.0(postcss@8.5.28)) + watchpack: 2.5.2 + webpack-sources: 3.6.0 + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + ws@8.21.0: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + zod@4.5.4: {} diff --git a/video/public/fonts/ClarityCity-Bold.woff2 b/video/public/fonts/ClarityCity-Bold.woff2 new file mode 100644 index 0000000..da8c2fa Binary files /dev/null and b/video/public/fonts/ClarityCity-Bold.woff2 differ diff --git a/video/public/fonts/ClarityCity-ExtraBold.woff2 b/video/public/fonts/ClarityCity-ExtraBold.woff2 new file mode 100644 index 0000000..1a3f07a Binary files /dev/null and b/video/public/fonts/ClarityCity-ExtraBold.woff2 differ diff --git a/video/public/fonts/ClarityCity-Medium.woff2 b/video/public/fonts/ClarityCity-Medium.woff2 new file mode 100644 index 0000000..b1786a8 Binary files /dev/null and b/video/public/fonts/ClarityCity-Medium.woff2 differ diff --git a/video/public/fonts/ClarityCity-Regular.woff2 b/video/public/fonts/ClarityCity-Regular.woff2 new file mode 100644 index 0000000..feedf7f Binary files /dev/null and b/video/public/fonts/ClarityCity-Regular.woff2 differ diff --git a/video/public/fonts/ClarityCity-SemiBold.woff2 b/video/public/fonts/ClarityCity-SemiBold.woff2 new file mode 100644 index 0000000..a7dbc8c Binary files /dev/null and b/video/public/fonts/ClarityCity-SemiBold.woff2 differ diff --git a/video/remotion.config.ts b/video/remotion.config.ts new file mode 100644 index 0000000..ffc9080 --- /dev/null +++ b/video/remotion.config.ts @@ -0,0 +1,19 @@ +import path from "node:path"; +import { Config } from "@remotion/cli/config"; + +Config.setVideoImageFormat("jpeg"); +Config.setJpegQuality(92); +Config.setOverwriteOutput(true); +Config.setConcurrency(4); + +// remocn bileşenleri "@/..." yollarıyla import eder (shadcn alias'ı) +Config.overrideWebpackConfig((config) => ({ + ...config, + resolve: { + ...config.resolve, + alias: { + ...(config.resolve?.alias ?? {}), + "@": path.join(process.cwd(), "src"), + }, + }, +})); diff --git a/video/src/Lansman.tsx b/video/src/Lansman.tsx new file mode 100644 index 0000000..d977b2c --- /dev/null +++ b/video/src/Lansman.tsx @@ -0,0 +1,100 @@ +import { linearTiming, TransitionSeries } from "@remotion/transitions"; +import { AbsoluteFill, useCurrentFrame } from "remotion"; +import { pushThrough } from "@/components/remocn/push-through"; +import { Gren } from "./bilesenler/Gren"; +import "./fontlar"; +import { useFontlarHazir } from "./fontlar-hazir"; +import { pikselDalga } from "./gecisler/piksel-dalga"; +import { R } from "./marka"; +import { S1Gurultu } from "./sahneler/S1Gurultu"; +import { haritaTiklama, S2Harita } from "./sahneler/S2Harita"; +import { S3Siralama, siralamaTiklama } from "./sahneler/S3Siralama"; +import { havuzTiklama, S4Havuz } from "./sahneler/S4Havuz"; +import { danismanDalgaNoktasi, S5Danisman } from "./sahneler/S5Danisman"; +import { degerDalgaNoktasi, S6Deger } from "./sahneler/S6Deger"; +import { S7Kapanis } from "./sahneler/S7Kapanis"; +import { GECIS, SAHNE } from "./zaman"; + +// Sahnelerin küresel başlangıç kareleri (geçişler üst üste biner) +const SIRA = [ + [SAHNE.gurultu, GECIS.g1], + [SAHNE.harita, GECIS.g2], + [SAHNE.siralama, GECIS.g3], + [SAHNE.havuz, GECIS.g4], + [SAHNE.danisman, GECIS.g5], + [SAHNE.deger, GECIS.g6], +] as const; +const BASLAR = SIRA.reduce((acc, [s, g]) => [...acc, acc[acc.length - 1] + s - g], [0]); +const KAPANIS_BASI = BASLAR[6]; + +export const Lansman: React.FC = () => { + useFontlarHazir(); + const f = useCurrentFrame(); + // Koyu sahnelerde gren daha belirgin ve açık zerreli + const koyu = f < SAHNE.gurultu - 14 || f > KAPANIS_BASI + 14; + + return ( + + + + + + {/* Tek piksel patlar: koyudan açığa, merkezden */} + + + + + {/* İmleç logonun turuncu karesine tıklar → dalga oradan */} + + + + + {/* CTA'ya basılır → sahne butonun içinden geçilerek terk edilir */} + + + + + {/* Listedeki satıra tıklanır → satırın içinden danışmana */} + + + + + {/* Trendin son (2026) pikselinden dalga */} + + + + + {/* Son gösterge karesinden koyu dalga: kapanışın CTA bandına */} + + + + + + + + ); +}; diff --git a/video/src/Root.tsx b/video/src/Root.tsx new file mode 100644 index 0000000..982206d --- /dev/null +++ b/video/src/Root.tsx @@ -0,0 +1,15 @@ +import { Composition } from "remotion"; +import { Lansman } from "./Lansman"; +import { FPS, H, W } from "./marka"; +import { TOPLAM } from "./zaman"; + +export const RemotionRoot: React.FC = () => ( + +); diff --git a/video/src/bilesenler/Gren.tsx b/video/src/bilesenler/Gren.tsx new file mode 100644 index 0000000..9866c78 --- /dev/null +++ b/video/src/bilesenler/Gren.tsx @@ -0,0 +1,60 @@ +import { AbsoluteFill, useCurrentFrame } from "remotion"; +import { H, W } from "../marka"; + +/** + * Film greni: her karede tohumu değişen fraktal gürültü (deterministik — + * aynı kare her render'da aynı greni verir). Tüm videonun üstünde durur; + * `guc` koyu sahnelerde artırılır. + */ +export const Gren: React.FC<{ guc: number; koyu?: boolean }> = ({ + guc, + koyu = false, +}) => { + const frame = useCurrentFrame(); + if (guc <= 0) return null; + const id = `gren-${frame}`; + return ( + + + + + {/* Tek kanallı gren: renkli gürültü değil */} + + {/* Açık sahnede yalnız koyu zerreler (multiply), koyuda yalnız açık + zerreler (screen) kalsın diye ortalama kaydırılıp kontrast verilir */} + + + + + + + + + + ); +}; + +/** Koyu sahnelerde kenar kararması */ +export const Vinyet: React.FC<{ guc?: number }> = ({ guc = 0.55 }) => ( + +); diff --git a/video/src/bilesenler/Imlec.tsx b/video/src/bilesenler/Imlec.tsx new file mode 100644 index 0000000..5c35a67 --- /dev/null +++ b/video/src/bilesenler/Imlec.tsx @@ -0,0 +1,84 @@ +import { useCurrentFrame } from "remotion"; +import { Cursor } from "@/components/remocn/cursor"; +import { + cursorPathAt, + type CursorWaypoint, +} from "@/components/remocn/use-cursor-path"; +import { R } from "../marka"; +import { aralik } from "../yardimci"; + +export type Yol = CursorWaypoint[]; + +/** + * remocn Cursor + useCursorPath, sitenin diline göre: koyu ok, beyaz kontur, + * tıklamada turuncu halka. Tıklanabilir bir şeyin üstündeyken el imlecine + * döner (sitede butonlar `cursor-pointer`). + */ +export const Imlec: React.FC<{ + yol: Yol; + /** [görünmeye başladığı kare, kaybolduğu kare] */ + gorunur?: [number, number]; + /** El imleci gösterilecek kare aralıkları */ + el?: [number, number][]; + boy?: number; +}> = ({ yol, gorunur = [0, 1e9], el = [], boy = 46 }) => { + const frame = useCurrentFrame(); + const st = cursorPathAt(yol, frame); + const op = + aralik(frame, gorunur[0], gorunur[0] + 8) * + (1 - aralik(frame, gorunur[1] - 8, gorunur[1])); + const elMi = el.some(([a, b]) => frame >= a && frame <= b); + if (op <= 0) return null; + // El imlecinin sıcak noktası parmak ucunda (viewBox 24'te x≈9.5): tıklama + // noktası ok ucuyla aynı kalsın diye kaydırılır. + const kaydir = elMi ? (9.5 / 24) * boy : (1 / 24) * boy; + return ( +
+ +
+ ); +}; + +export type IsiOrnek = { x: number; y: number; w: number }; + +/** + * Sitenin imleç ısı fırçası (use-pixel-heat.ts) video karşılığı: imleç + * yaklaşınca pikseller anında ısınır, uzaklaşınca yavaş soğur. İmleç yolu saf + * bir fonksiyon olduğu için geçmiş kareler yeniden hesaplanır — iz + * deterministiktir. + */ +export function isiIzi( + yol: Yol, + frame: number, + gorunur: [number, number] = [0, 1e9], + iz = 26, +): IsiOrnek[] { + const out: IsiOrnek[] = []; + for (let k = 0; k <= iz; k += 2) { + const f = frame - k; + if (f < gorunur[0] || f > gorunur[1]) continue; + const p = cursorPathAt(yol, f); + out.push({ x: p.x, y: p.y, w: k === 0 ? 1 : (1 - k / (iz + 2)) ** 1.7 }); + } + return out; +} + +export function isi(x: number, y: number, izler: IsiOrnek[], r: number) { + let h = 0; + const r2 = r * r; + for (const o of izler) { + const dx = x - o.x; + const dy = y - o.y; + const v = Math.exp(-(dx * dx + dy * dy) / r2) * o.w; + if (v > h) h = v; + } + return h; +} + +export { cursorPathAt }; diff --git a/video/src/bilesenler/Kinetik.tsx b/video/src/bilesenler/Kinetik.tsx new file mode 100644 index 0000000..41e8b71 --- /dev/null +++ b/video/src/bilesenler/Kinetik.tsx @@ -0,0 +1,48 @@ +import { useCurrentFrame } from "remotion"; +import { aralik, ease } from "../yardimci"; + +/** + * Metin çıkışı (remocn blur-out-up'ın sarmalayıcı hâli): içerik + * [bas, bas+sure] arasında yukarı kayar, bulanıklaşır ve söner. + * `giris` verilirse aynı yoldan tersine belirir. + */ +export const Cikis: React.FC<{ + bas: number; + sure?: number; + giris?: [number, number]; + yol?: number; + children: React.ReactNode; + style?: React.CSSProperties; +}> = ({ bas, sure = 10, giris, yol = 34, children, style }) => { + const f = useCurrentFrame(); + const c = aralik(f, bas, bas + sure, ease.in); + const g = giris ? aralik(f, giris[0], giris[1], ease.out) : 1; + return ( +
+ {children} +
+ ); +}; + +/** Mutlak konumlu kutu: kinetik bileşenler (inset:0 ortalar) bu kutuya göre ortalanır */ +export const Kutu: React.FC<{ + x?: number; + y: number; + w?: number; + h: number; + children: React.ReactNode; + style?: React.CSSProperties; +}> = ({ x = 0, y, w = 1920, h, children, style }) => ( +
+ {children} +
+); diff --git a/video/src/bilesenler/Logo.tsx b/video/src/bilesenler/Logo.tsx new file mode 100644 index 0000000..7506d7e --- /dev/null +++ b/video/src/bilesenler/Logo.tsx @@ -0,0 +1,94 @@ +import { LOGO_KARELER, R } from "../marka"; +import { FONT } from "../fontlar"; + +/** + * Logo işareti: üç kare (public/kolay-tercih-mark.svg). `kareStil` ile her + * kare ayrı ayrı oynatılabilir (animasyonlu-mark.tsx'teki "rota"/"nabız"). + */ +export const LogoIsaret: React.FC<{ + boy: number; + kareStil?: (i: number) => { dx?: number; dy?: number; olcek?: number; opaklik?: number }; + style?: React.CSSProperties; +}> = ({ boy, kareStil, style }) => ( + + {LOGO_KARELER.map((k, i) => { + const s = kareStil?.(i) ?? {}; + const cx = k.x + k.boy / 2 + (s.dx ?? 0); + const cy = k.y + k.boy / 2 + (s.dy ?? 0); + const o = s.olcek ?? 1; + return ( + + ); + })} + +); + +/** "Kolay" Bricolage 300 + "Tercih" 600, tracking -0.05em (logo svg'si) */ +export const Kelime: React.FC<{ boy: number; renk?: string }> = ({ + boy, + renk = "#0a0a0a", +}) => ( + + Kolay + Tercih + +); + +/** Rozet ikonu PixelMark (pixel-decor.tsx): 2×2 piksel, sağ üst turuncu */ +export const PikselIsaret: React.FC<{ boy: number }> = ({ boy }) => ( + + + + + + +); + +/** + * SectionEyebrow'un video karşılığı (2×): piksel imzalı rozet. + * `inline-flex items-center gap-2 rounded-full border border-slate-200 + * bg-white px-3.5 py-1.5 text-xs font-semibold text-primary` + */ +export const Rozet: React.FC<{ children: React.ReactNode; style?: React.CSSProperties }> = ({ + children, + style, +}) => ( + + + {children} + +); diff --git a/video/src/bilesenler/PikselZemin.tsx b/video/src/bilesenler/PikselZemin.tsx new file mode 100644 index 0000000..fe6d002 --- /dev/null +++ b/video/src/bilesenler/PikselZemin.tsx @@ -0,0 +1,55 @@ +import { useCurrentFrame } from "remotion"; +import { H, PIKSEL, R, W, opaklikFor } from "../marka"; +import { clamp01, hash, karis } from "../yardimci"; +import { isi, type IsiOrnek } from "./Imlec"; + +/** + * Sahne kenarlarında hero dokusu (PikselZemin'in video hâli): ortası boş, + * kenara yaklaştıkça sıklaşan noktalar. İmleç izi verilirse pikseller + * turuncuya ısınır. Yavaş kayma (parallax) derinlik verir. + */ +export const PikselZemin: React.FC<{ + hucre?: number; + tohum?: number; + renk?: string; + /** Kenar bandı: kısa kenarın oranı */ + kenar?: number; + izler?: IsiOrnek[]; + kayma?: number; + guc?: number; +}> = ({ hucre = 40, tohum = 7, renk = R.primary, kenar = 0.26, izler = [], kayma = 0.25, guc = 1 }) => { + const f = useCurrentFrame(); + const bant = Math.min(W, H) * kenar; + const dy = -f * kayma; + const sutun = Math.ceil(W / hucre); + const satir = Math.ceil(H / hucre) + 2; + const rects: React.ReactNode[] = []; + for (let j = 0; j < satir; j++) { + for (let i = 0; i < sutun; i++) { + const cx = i * hucre + hucre / 2; + const cy = j * hucre + hucre / 2 + (dy % hucre); + const kenarUz = Math.min(cx, W - cx, cy, H - cy); + const yogunluk = clamp01(1 - kenarUz / bant) ** 1.6; + if (hash(i, j, tohum) > yogunluk * 0.9) continue; + const h = izler.length ? isi(cx, cy, izler, 110) : 0; + const boy = hucre * PIKSEL.nokta; + rects.push( + , + ); + } + } + return ( + + {rects} + + ); +}; diff --git a/video/src/bilesenler/UI.tsx b/video/src/bilesenler/UI.tsx new file mode 100644 index 0000000..04eeca7 --- /dev/null +++ b/video/src/bilesenler/UI.tsx @@ -0,0 +1,200 @@ +import { ArrowRight } from "lucide-react"; +import { Caret } from "@/components/remocn/caret"; +import { FONT } from "../fontlar"; +import { R } from "../marka"; +import { DILIM_ETIKET, type Dilim } from "../veri"; +import { karis } from "../yardimci"; + +// Arayüz parçaları sitenin 2 katı ölçüde (DESIGN.md §4). Durumlar (hover, +// basma, seçim) 0–1 arası sayı alır ki kareler arasında yumuşak geçsin. + +/** Turuncu funnel CTA (§4.1): metin önce, ArrowRight sonda, yıldız yok */ +export const CtaButon: React.FC<{ + children: React.ReactNode; + hover?: number; + bas?: number; + yukseklik?: number; + style?: React.CSSProperties; +}> = ({ children, hover = 0, bas = 0, yukseklik = 104, style }) => ( +
+ {children} + +
+); + +/** Form alanı hap (§4.6): 48 px → 104 px, tam yuvarlak, slate-200 kenarlık */ +export const GirisHap: React.FC<{ + genislik: number; + deger: string; + placeholder: string; + odak?: number; + yaziyor?: boolean; + ikon?: React.ReactNode; + yukseklik?: number; +}> = ({ genislik, deger, placeholder, odak = 0, yaziyor = false, ikon, yukseklik = 104 }) => ( +
+ {ikon} + + {/* Boş alanda imleç placeholder'ın başında, doluyken metnin sonunda */} + {odak > 0.5 && !deger && ( + + )} + {deger || placeholder} + {odak > 0.5 && deger && ( + + )} + +
+); + +/** Etkileşimli çip (§4.17): pasif beyaz, seçili mavi dolu */ +export const Cip: React.FC<{ + children: React.ReactNode; + secili?: number; + hover?: number; + bas?: number; + ikon?: React.ReactNode; + boy?: number; + style?: React.CSSProperties; +}> = ({ children, secili = 0, hover = 0, bas = 0, ikon, boy = 80, style }) => { + const pasifKenar = karis(R.slate200, R.turuncu, hover); + const pasifYazi = karis(R.slate700, R.turuncu, hover); + return ( + + {ikon} + {children} + + ); +}; + +/** Dilim etiketi (§4.5 düzeni): nokta + metin; renk tek başına anlam taşımaz */ +export const DILIM_RENK: Record = { + hayal: { nokta: R.red500, yazi: R.red600 }, + dengeli: { nokta: R.amber500, yazi: R.amber600 }, + garanti: { nokta: R.emerald500, yazi: R.emerald600 }, +}; + +export const DilimEtiketi: React.FC<{ dilim: Dilim; boy?: number }> = ({ dilim, boy = 26 }) => ( + + + {DILIM_ETIKET[dilim]} + +); + +/** + * Piksel trend: 6 yılın taban sırası, her yıl bir piksel. Üstte = daha iyi + * (küçük) sıra. Son yıl turuncu yanar (PixelSira'nın dili). + */ +export const PikselTrend: React.FC<{ + sira: number[]; + hucre?: number; + yukseklik?: number; + ilerleme?: number; +}> = ({ sira, hucre = 18, yukseklik = 56, ilerleme = 1 }) => { + const min = Math.min(...sira); + const max = Math.max(...sira); + const boy = hucre * 0.74; + return ( + + {sira.map((s, i) => { + const t = max === min ? 0.5 : (s - min) / (max - min); + const gorunur = ilerleme * sira.length > i; + const son = i === sira.length - 1; + return ( + + ); + })} + + ); +}; diff --git a/video/src/components/remocn/caret.tsx b/video/src/components/remocn/caret.tsx new file mode 100644 index 0000000..6afadde --- /dev/null +++ b/video/src/components/remocn/caret.tsx @@ -0,0 +1,69 @@ +"use client"; + +import type { CSSProperties } from "react"; +import { useCurrentFrame, useVideoConfig } from "remotion"; + +export interface CaretProps { + color?: string; + width?: number; + height?: number; + radius?: number; + opacity?: number; + blink?: boolean; + blinkPerSecond?: number; + speed?: number; + marginLeft?: number; + className?: string; + style?: CSSProperties; +} + +export function caretBlinkOpacity( + frame: number, + opts: { fps: number; blinkPerSecond: number; speed: number }, +): number { + const cycles = opts.blinkPerSecond <= 0 ? 1 : opts.blinkPerSecond; + const halfPeriod = opts.fps / cycles / 2; + if (halfPeriod <= 0) return 1; + return Math.floor((frame * opts.speed) / halfPeriod) % 2 === 0 ? 1 : 0; +} + +export function Caret({ + color = "currentColor", + width = 2, + height = 18, + radius = 1, + opacity, + blink = false, + blinkPerSecond = 1, + speed = 1, + marginLeft = 0, + className, + style, +}: CaretProps) { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const resolvedOpacity = + opacity !== undefined + ? opacity + : blink + ? caretBlinkOpacity(frame, { fps, blinkPerSecond, speed }) + : 1; + + return ( + + ); +} diff --git a/video/src/components/remocn/cursor.tsx b/video/src/components/remocn/cursor.tsx new file mode 100644 index 0000000..dfcc13d --- /dev/null +++ b/video/src/components/remocn/cursor.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { type RemocnTheme, useRemocnTheme } from "@/lib/remocn-ui"; + +export type CursorVariant = "arrow" | "pointer"; + +export interface CursorStyle { + x: number; + y: number; + scale: number; + rippleOpacity: number; + rippleScale: number; + pressScale?: number; +} + +export interface CursorProps { + style?: CursorStyle; + variant?: CursorVariant; + size?: number; + theme?: Partial; + rippleColor?: string; + className?: string; +} + +const REST: CursorStyle = { + x: 0, + y: 0, + scale: 1, + rippleOpacity: 0, + rippleScale: 0, +}; + +const ARROW_PATH = + "M1 1 L1 18.5 L5.6 14.4 L8.7 21.6 L11.6 20.4 L8.5 13.2 L14.5 13.2 Z"; +const POINTER_PATH = + "M8 2.2 C8 1.3 8.7 0.7 9.5 0.7 C10.3 0.7 11 1.3 11 2.2 L11 9 " + + "C11 9 11.3 8.4 12.2 8.4 C13 8.4 13.4 9 13.4 9.6 " + + "C13.4 9.6 13.9 9.1 14.7 9.1 C15.5 9.1 15.9 9.7 15.9 10.3 " + + "C15.9 10.3 16.4 9.9 17.1 9.9 C17.9 9.9 18.3 10.5 18.3 11.2 " + + "L18.3 16.8 C18.3 19.8 16.3 22.3 12.8 22.3 L11.4 22.3 " + + "C9 22.3 7.9 21.2 6.6 19.2 L4 15.2 C3.5 14.4 3.7 13.4 4.5 12.9 " + + "C5.1 12.5 5.9 12.6 6.4 13.2 L8 15 Z"; + +export function Cursor({ + style, + variant = "arrow", + size = 28, + theme: themeOverride, + rippleColor, + className, +}: CursorProps) { + const theme = useRemocnTheme(themeOverride, "light"); + const v = style ?? REST; + + const press = v.pressScale ?? 1; + const pressedScale = v.scale * (0.9 + 0.1 * press); + + const ring = rippleColor ?? theme.primary; + const fill = theme.foreground; + const stroke = theme.background; + const rippleSize = size * 1.8; + + return ( +
+ {} +
+ {} + + + +
+ ); +} diff --git a/video/src/components/remocn/focus-blur-resolve.tsx b/video/src/components/remocn/focus-blur-resolve.tsx new file mode 100644 index 0000000..c95722a --- /dev/null +++ b/video/src/components/remocn/focus-blur-resolve.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion"; + +export interface FocusBlurResolveProps { + text: string; + fontFamily?: string; + blur?: number; + fontSize?: number; + color?: string; + fontWeight?: number; + speed?: number; + className?: string; +} + +export function FocusBlurResolve({ + text, + blur = 14, + fontSize = 72, + color = "#171717", + fontWeight = 600, + speed = 1, + className, + fontFamily = "sans-serif", +}: FocusBlurResolveProps) { + const frame = useCurrentFrame() * speed; + const { durationInFrames } = useVideoConfig(); + + const enterDur = 23; + const exitDur = 16; + const exitStart = Math.max(enterDur, durationInFrames - exitDur); + + const enterEasing = Easing.bezier(0.22, 1, 0.36, 1); + const exitEasing = Easing.bezier(0.64, 0, 0.78, 0); + + const enterP = interpolate(frame, [0, enterDur], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: enterEasing, + }); + + const exitP = interpolate(frame, [exitStart, durationInFrames], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: exitEasing, + }); + + const opacity = enterP * (1 - exitP); + + const yEnter = interpolate(frame, [0, enterDur], [14, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: enterEasing, + }); + + const yExit = interpolate(frame, [exitStart, durationInFrames], [0, -10], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: exitEasing, + }); + + const y = yEnter + yExit; + + const blurEnter = interpolate(frame, [0, enterDur], [blur, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: enterEasing, + }); + + const blurExit = interpolate(frame, [exitStart, durationInFrames], [0, 10], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: exitEasing, + }); + + const blurVal = blurEnter + blurExit; + + const scale = interpolate(frame, [0, enterDur], [1.01, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: enterEasing, + }); + + return ( +
+ + {text} + +
+ ); +} diff --git a/video/src/components/remocn/kinetic-center-build.tsx b/video/src/components/remocn/kinetic-center-build.tsx new file mode 100644 index 0000000..dedae6a --- /dev/null +++ b/video/src/components/remocn/kinetic-center-build.tsx @@ -0,0 +1,243 @@ +"use client"; + +import { type CSSProperties, useMemo } from "react"; +import { Easing, interpolate, useCurrentFrame } from "remotion"; + +export interface KineticCenterBuildProps { + text: string; + entryOffset?: number; + fontSize?: number; + color?: string; + fontWeight?: number; + speed?: number; + className?: string; + fontFamily?: string; + letterSpacing?: number; + gap?: number; + /** "harf": harfler sağdan girip satırı iter (upstream davranışı); + * "kelime": uzun cümlelerde kelime kelime */ + birim?: "harf" | "kelime"; + /** Vurgulanan kelimeler (kelime indeksi, iki modda da) ve yazı stili */ + vurgu?: { indices: number[]; style: KineticWordStyle }; +} + +export interface KineticWordStyle { + fontFamily?: string; + fontWeight?: number; + fontStyle?: "normal" | "italic"; + color?: string; + letterSpacing?: number; +} + + +// Ölçüm, kelimenin gerçekte basılacağı yazı tipiyle yapılır (Cal Sans, +// Bricolage…); aksi hâlde itme sırasında kelimeler üst üste biner. +function measureWidths( + words: string[], + fontSize: number, + styleFor: (i: number) => Required, +): number[] { + if (typeof document === "undefined") { + return words.map((w) => w.length * fontSize * 0.55); + } + const ctx = document.createElement("canvas").getContext("2d"); + if (!ctx) return words.map((w) => w.length * fontSize * 0.55); + return words.map((w, i) => { + const st = styleFor(i); + ctx.font = `${st.fontStyle} ${st.fontWeight} ${fontSize}px "${st.fontFamily}"`; + return ctx.measureText(w).width + st.letterSpacing * fontSize * Array.from(w).length; + }); +} + +export function KineticCenterBuild({ + text, + entryOffset = 88, + fontSize = 72, + color = "#171717", + fontWeight = 600, + speed = 1, + className, + fontFamily = "sans-serif", + letterSpacing = -0.03, + gap: gapProp, + vurgu, + birim = "harf", +}: KineticCenterBuildProps) { + const frame = useCurrentFrame() * speed; + + const words = useMemo( + () => (birim === "kelime" ? text.split(" ") : Array.from(text)), + [text, birim], + ); + // Her birimin ait olduğu kelime (vurgu kelime indeksiyle verilir) + const kelimeOf = useMemo(() => { + if (birim === "kelime") return words.map((_, i) => i); + let w = 0; + return words.map((ch) => { + const k = w; + if (ch === " ") w++; + return k; + }); + }, [words, birim]); + const styleFor = (i: number): Required => { + const base = { fontFamily, fontWeight, fontStyle: "normal" as const, color, letterSpacing }; + return vurgu?.indices.includes(kelimeOf[i]) ? { ...base, ...vurgu.style } : base; + }; + const widths = useMemo( + () => measureWidths(words, fontSize, styleFor), + // eslint-disable-next-line react-hooks/exhaustive-deps + [words, fontSize, fontWeight, fontFamily, letterSpacing, vurgu, kelimeOf], + ); + + const gap = gapProp ?? (birim === "kelime" ? fontSize * 0.26 : 0); + const firstDur = 10; + const pushDur = 13; + const travelDur = 7; + const entryScale = 0.992; + const entryBlur = 3.5; + const reflowBlur = 0.8; + const firstWordY = 6; + const easing = Easing.bezier(0.2, 0.8, 0.2, 1); + + const positionsAt = useMemo(() => { + const out: number[][] = []; + for (let k = 1; k <= words.length; k++) { + let total = gap * (k - 1); + for (let j = 0; j < k; j++) total += widths[j]; + let cursor = -total / 2; + const xs: number[] = []; + for (let j = 0; j < k; j++) { + xs.push(cursor + widths[j] / 2); + cursor += widths[j] + gap; + } + out.push(xs); + } + return out; + }, [words, widths]); + + const n = words.length; + + return ( +
+
+ {words.map((word, j) => { + const entryStart = j === 0 ? 0 : firstDur + (j - 1) * pushDur; + const entryEnd = entryStart + (j === 0 ? firstDur : pushDur); + const targetX = positionsAt[j][j]; + const xFrom = j === 0 ? 0 : targetX + entryOffset; + + let x = targetX; + let opacity = 1; + let scale = 1; + let blur = 0; + let y = 0; + + if (frame < entryStart) { + opacity = 0; + x = xFrom; + scale = entryScale; + blur = entryBlur; + y = j === 0 ? firstWordY : 0; + } else if (frame <= entryEnd) { + const range: [number, number] = [entryStart, entryEnd]; + const travelRange: [number, number] = [ + entryStart, + Math.min(entryStart + travelDur, entryEnd), + ]; + const opts = { + extrapolateLeft: "clamp" as const, + extrapolateRight: "clamp" as const, + easing, + }; + x = interpolate(frame, travelRange, [xFrom, targetX], opts); + opacity = interpolate(frame, range, [0, 1], opts); + scale = interpolate(frame, range, [entryScale, 1], opts); + blur = interpolate(frame, range, [entryBlur, 0], opts); + y = + j === 0 + ? interpolate(frame, travelRange, [firstWordY, 0], opts) + : 0; + } else { + for (let w = j + 1; w < n; w++) { + const pushStart = firstDur + (w - 1) * pushDur; + const pushEnd = pushStart + pushDur; + const fromX = positionsAt[w - 1][j]; + const toX = positionsAt[w][j]; + if (frame >= pushEnd) { + x = toX; + } else if (frame >= pushStart) { + x = interpolate( + frame, + [pushStart, Math.min(pushStart + travelDur, pushEnd)], + [fromX, toX], + { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing, + }, + ); + blur = interpolate( + frame, + [pushStart, (pushStart + pushEnd) / 2, pushEnd], + [0, reflowBlur, 0], + { extrapolateLeft: "clamp", extrapolateRight: "clamp" }, + ); + break; + } else { + x = fromX; + break; + } + } + } + + const st = styleFor(j); + const wordStyle: CSSProperties = { + fontFamily: st.fontFamily, + fontWeight: st.fontWeight, + fontStyle: st.fontStyle, + color: st.color, + letterSpacing: `${st.letterSpacing}em`, + }; + return ( + + {word} + + ); + })} +
+
+ ); +} diff --git a/video/src/components/remocn/per-character-rise.tsx b/video/src/components/remocn/per-character-rise.tsx new file mode 100644 index 0000000..e372854 --- /dev/null +++ b/video/src/components/remocn/per-character-rise.tsx @@ -0,0 +1,86 @@ +"use client"; + +import { Easing, interpolate, useCurrentFrame } from "remotion"; + +export interface PerCharacterRiseProps { + text: string; + distance?: number; + fontSize?: number; + color?: string; + fontWeight?: number; + speed?: number; + className?: string; +} + +export function PerCharacterRise({ + text, + distance = 32, + fontSize = 72, + color = "#171717", + fontWeight = 600, + speed = 1, + className, +}: PerCharacterRiseProps) { + const frame = useCurrentFrame() * speed; + + const chars = Array.from(text); + const charDurationFrames = 21; + const charTravelFrames = 10; + const staggerFrames = 1; + + return ( +
+ + {chars.map((char, i) => { + const local = frame - i * staggerFrames; + const easing = Easing.bezier(0.2, 0.8, 0.2, 1); + const travelEasing = Easing.bezier(0.2, 0.8, 0.6, 0.85); + const opacity = interpolate(local, [0, charDurationFrames], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing, + }); + const y = interpolate(local, [0, charTravelFrames], [distance, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: travelEasing, + }); + return ( + + {char} + + ); + })} + +
+ ); +} diff --git a/video/src/components/remocn/push-through.tsx b/video/src/components/remocn/push-through.tsx new file mode 100644 index 0000000..8738da9 --- /dev/null +++ b/video/src/components/remocn/push-through.tsx @@ -0,0 +1,72 @@ +"use client"; + +import type { + TransitionPresentation, + TransitionPresentationComponentProps, +} from "@remotion/transitions"; +import type React from "react"; +import { AbsoluteFill, Easing, interpolate } from "remotion"; + +const clampOpts = { + extrapolateLeft: "clamp" as const, + extrapolateRight: "clamp" as const, +}; + +export type PushThroughProps = { + zoom?: number; + blur?: number; + /** İtmenin merkezi (px). İmlecin tıkladığı nokta verilir: sahne o noktanın + * içinden geçilerek terk edilir, yenisi aynı noktadan açılır. */ + origin?: { x: number; y: number }; +}; + +const PushThroughPresentation: React.FC< + TransitionPresentationComponentProps +> = ({ + children, + presentationProgress, + presentationDirection, + passedProps, +}) => { + const { zoom = 2.4, blur = 14, origin } = passedProps; + const transformOrigin = origin ? `${origin.x}px ${origin.y}px` : "50% 50%"; + const entering = presentationDirection === "entering"; + const p = presentationProgress; + + if (!entering) { + const exitStyle: React.CSSProperties = { + transformOrigin, + opacity: interpolate(p, [0.4, 0.62], [1, 0], { + ...clampOpts, + easing: Easing.bezier(0.42, 0, 0.58, 1), + }), + scale: `${interpolate(p, [0, 0.65], [1, zoom], { + ...clampOpts, + easing: Easing.in(Easing.cubic), + })}`, + filter: `blur(${interpolate(p, [0.15, 0.6], [0, blur], clampOpts)}px)`, + }; + return {children}; + } + + const childStyle: React.CSSProperties = { + transformOrigin, + opacity: interpolate(p, [0.3, 0.5], [0, 1], clampOpts), + scale: `${interpolate(p, [0.3, 0.88, 1], [0.68, 1.02, 1], { + ...clampOpts, + easing: Easing.out(Easing.cubic), + })}`, + filter: `blur(${interpolate(p, [0.3, 0.75], [blur * 0.7, 0], clampOpts)}px)`, + }; + + return {children}; +}; + +export function pushThrough( + props: PushThroughProps = {}, +): TransitionPresentation { + return { + component: PushThroughPresentation, + props, + }; +} diff --git a/video/src/components/remocn/slot-machine-roll.tsx b/video/src/components/remocn/slot-machine-roll.tsx new file mode 100644 index 0000000..99bfa81 --- /dev/null +++ b/video/src/components/remocn/slot-machine-roll.tsx @@ -0,0 +1,112 @@ +"use client"; + +import { spring, useCurrentFrame, useVideoConfig } from "remotion"; + +export interface SlotMachineRollProps { + from?: string; + to?: string; + fontSize?: number; + color?: string; + fontWeight?: number; + speed?: number; + className?: string; + fontFamily?: string; + /** true: tam kare sarmalayıcı ve beyaz zemin olmadan satır içi basar */ + inline?: boolean; + /** Sütunlar arası kademe (kare) */ + stagger?: number; +} + +// Nokta, virgül ve boşluk rakam kadar yer kaplamasın +const charWidth = (ch: string) => + ch === "." || ch === "," ? "0.3em" : ch === " " ? "0.25em" : ch === "%" ? "0.8em" : "0.62em"; + +export function SlotMachineRoll({ + from = "$99", + to = "$199", + fontSize = 120, + color = "#171717", + fontWeight = 700, + speed = 1, + className, + fontFamily = "sans-serif", + inline = false, + stagger = 4, +}: SlotMachineRollProps) { + const frame = useCurrentFrame() * speed; + const { fps } = useVideoConfig(); + + const len = Math.max(from.length, to.length); + const paddedFrom = from.padStart(len, " "); + const paddedTo = to.padStart(len, " "); + + const columns = []; + for (let i = 0; i < len; i++) { + const raw = spring({ + frame: frame - i * stagger, + fps, + config: { damping: 14 }, + }); + // Değişmeyen karakter (ör. "6 yıl"daki "yıl") yerinde durur + const sabit = paddedFrom[i] === paddedTo[i]; + const t = sabit ? 0 : Math.max(0, Math.min(1, raw)); + columns.push( + + + {paddedFrom[i]} + {paddedTo[i]} + + , + ); + } + + const metin = ( + + {columns} + + ); + if (inline) return metin; + + return ( +
+ {metin} +
+ ); +} diff --git a/video/src/components/remocn/tracking-in.tsx b/video/src/components/remocn/tracking-in.tsx new file mode 100644 index 0000000..9215cd3 --- /dev/null +++ b/video/src/components/remocn/tracking-in.tsx @@ -0,0 +1,77 @@ +"use client"; + +import type { ReactNode } from "react"; +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; + +export interface TrackingInProps { + text: ReactNode; + fontFamily?: string; + background?: string; + endTracking?: number; + startTracking?: number; + startBlur?: number; + fontSize?: number; + color?: string; + fontWeight?: number; + speed?: number; + className?: string; +} + +export function TrackingIn({ + text, + startTracking = 0.5, + startBlur = 12, + fontSize = 96, + color = "#171717", + fontWeight = 700, + speed = 1, + className, + fontFamily = "sans-serif", + background = "transparent", + endTracking = -0.03, +}: TrackingInProps) { + const frame = useCurrentFrame() * speed; + const { fps } = useVideoConfig(); + + const t = spring({ + frame, + fps, + config: { damping: 18, stiffness: 90 }, + }); + + const letterSpacing = `${interpolate(t, [0, 1], [startTracking, endTracking])}em`; + const blurAmount = interpolate(t, [0, 1], [startBlur, 0]); + const opacity = interpolate(frame, [0, 15], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + return ( +
+ + {text} + +
+ ); +} diff --git a/video/src/components/remocn/typewriter.tsx b/video/src/components/remocn/typewriter.tsx new file mode 100644 index 0000000..aa119a8 --- /dev/null +++ b/video/src/components/remocn/typewriter.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { Caret } from "@/components/remocn/caret"; +import { useTypewriter } from "@/lib/remocn-ui"; + +export interface TypewriterProps { + text: string; + cursor?: boolean; + charsPerSecond?: number; + speed?: number; + fontSize?: number; + color?: string; + cursorColor?: string; + fontWeight?: number; + className?: string; +} + +export function Typewriter({ + text, + cursor = true, + charsPerSecond = 22, + speed = 1, + fontSize = 48, + color = "#171717", + cursorColor = "#171717", + fontWeight = 600, + className, +}: TypewriterProps) { + const tw = useTypewriter(text, { cps: charsPerSecond, speed }); + + return ( +
+ + {tw.text} + {cursor && ( + + )} + +
+ ); +} diff --git a/video/src/components/remocn/use-cursor-path.ts b/video/src/components/remocn/use-cursor-path.ts new file mode 100644 index 0000000..c52dd7c --- /dev/null +++ b/video/src/components/remocn/use-cursor-path.ts @@ -0,0 +1,133 @@ +"use client"; + +import { useCurrentFrame } from "remotion"; +import type { CursorStyle } from "@/components/remocn/cursor"; +import { clamp01, type EasingName, easings } from "@/lib/remocn-ui"; + +export interface CursorWaypoint { + at: number; + x: number; + y: number; + duration?: number; + click?: boolean; + press?: boolean; + easing?: EasingName; +} + +export const DEFAULT_DURATION = 24; + +export const CLICK_FRAMES = 16; + +export const PRESS_FRAMES = 8; + +export interface CursorPathOptions { + speed?: number; + defaultDuration?: number; +} + +function lerp(a: number, b: number, t: number): number { + return a + (b - a) * t; +} + +export function ripplePhase(framesSinceClick: number): { + rippleOpacity: number; + rippleScale: number; +} { + if (framesSinceClick < 0 || framesSinceClick >= CLICK_FRAMES) { + return { rippleOpacity: 0, rippleScale: 0 }; + } + const p = clamp01(framesSinceClick / CLICK_FRAMES); + return { + rippleOpacity: 0.5 * (1 - p), + rippleScale: 2.5 * easings.out(p), + }; +} + +export function clickPress(framesSinceClick: number): number { + if (framesSinceClick < 0 || framesSinceClick >= PRESS_FRAMES) return 1; + const half = PRESS_FRAMES / 2; + const p = + framesSinceClick < half + ? framesSinceClick / half + : 1 - (framesSinceClick - half) / half; + return 1 - clamp01(p); +} + +export function useCursorPath( + waypoints: CursorWaypoint[], + opts: CursorPathOptions = {}, +): CursorStyle { + const raw = useCurrentFrame(); + return cursorPathAt(waypoints, raw, opts); +} + +export function cursorPathAt( + waypoints: CursorWaypoint[], + raw: number, + opts: CursorPathOptions = {}, +): CursorStyle { + const { speed = 1, defaultDuration = DEFAULT_DURATION } = opts; + const effectiveFrame = raw * speed; + + if (waypoints.length === 0) { + return { x: 0, y: 0, scale: 1, rippleOpacity: 0, rippleScale: 0 }; + } + + const first = waypoints[0]; + + if (effectiveFrame <= first.at) { + return { + x: first.x, + y: first.y, + scale: 1, + rippleOpacity: 0, + rippleScale: 0, + pressScale: 1, + }; + } + + let toIndex = waypoints.length - 1; + for (let i = 1; i < waypoints.length; i++) { + if (waypoints[i].at > effectiveFrame) { + toIndex = i; + break; + } + } + const pastLast = effectiveFrame >= waypoints[waypoints.length - 1].at; + const to = pastLast ? waypoints[waypoints.length - 1] : waypoints[toIndex]; + const from = pastLast + ? waypoints[waypoints.length - 1] + : waypoints[toIndex - 1]; + + const dur = to.duration ?? defaultDuration; + const ease = easings[to.easing ?? "inOut"]; + const start = to.at - dur; + const t = + pastLast || dur <= 0 ? 1 : ease(clamp01((effectiveFrame - start) / dur)); + const x = lerp(from.x, to.x, t); + const y = lerp(from.y, to.y, t); + + let lastClickAt = -Infinity; + for (const wp of waypoints) { + if (wp.click && wp.at <= effectiveFrame && wp.at > lastClickAt) + lastClickAt = wp.at; + } + const sinceClick = + lastClickAt === -Infinity ? -1 : effectiveFrame - lastClickAt; + const ripple = ripplePhase(sinceClick); + const clickDip = clickPress(sinceClick); + + const holdWp = pastLast ? waypoints[waypoints.length - 1] : from; + const heldPress = holdWp.press ? 0 : 1; + + const pressScale = Math.min(clickDip, heldPress); + + return { + x, + y, + scale: 1, + rippleOpacity: ripple.rippleOpacity, + rippleScale: ripple.rippleScale, + pressScale, + }; +} diff --git a/video/src/fontlar-hazir.ts b/video/src/fontlar-hazir.ts new file mode 100644 index 0000000..eb9537b --- /dev/null +++ b/video/src/fontlar-hazir.ts @@ -0,0 +1,22 @@ +import { useEffect, useState } from "react"; +import { continueRender, delayRender } from "remotion"; + +/** + * Kinetik yerleşimler metni gerçek yazı tipiyle ölçer. Yazı tipleri + * yüklenmeden ölçülürse kelimeler kayar; bu kanca yazı tipleri hazır olana + * kadar render'ı bekletir ve hazır olunca ağacı yeniden çizdirir. + */ +export function useFontlarHazir() { + const [hazir, setHazir] = useState( + () => typeof document !== "undefined" && document.fonts.status === "loaded", + ); + const [handle] = useState(() => (hazir ? null : delayRender("yazı tipleri"))); + useEffect(() => { + if (hazir) return; + document.fonts.ready.then(() => { + setHazir(true); + if (handle !== null) continueRender(handle); + }); + }, [hazir, handle]); + return hazir; +} diff --git a/video/src/fontlar.ts b/video/src/fontlar.ts new file mode 100644 index 0000000..774ea9c --- /dev/null +++ b/video/src/fontlar.ts @@ -0,0 +1,38 @@ +import { loadFont as yerelFont } from "@remotion/fonts"; +import { loadFont as bricolageYukle } from "@remotion/google-fonts/BricolageGrotesque"; +import { loadFont as calSansYukle } from "@remotion/google-fonts/CalSans"; +import { staticFile } from "remotion"; + +// Sitenin dört yazı tipinden üçü (DESIGN.md §3): Cal Sans başlık ve iri sayı, +// Clarity City gövde/buton, Bricolage vurgu. Geist Mono arayüzde yok. +const cal = calSansYukle("normal", { + weights: ["400"], + subsets: ["latin", "latin-ext"], +}); +const bricolage = bricolageYukle("normal", { + weights: ["300", "400", "600", "700", "800"], + subsets: ["latin", "latin-ext"], +}); + +const CLARITY = "Clarity City"; +for (const [dosya, agirlik] of [ + ["Regular", "400"], + ["Medium", "500"], + ["SemiBold", "600"], + ["Bold", "700"], + ["ExtraBold", "800"], +] as const) { + yerelFont({ + family: CLARITY, + url: staticFile(`fonts/ClarityCity-${dosya}.woff2`), + weight: agirlik, + }); +} + +export const FONT = { + /** Cal Sans yalnız 400; site `font-bold` ile sentetik kalınlık kullanıyor, + * video da aynı görünsün diye başlıklarda fontWeight 700 verilir. */ + baslik: cal.fontFamily, + govde: CLARITY, + vurgu: bricolage.fontFamily, +} as const; diff --git a/video/src/gecisler/piksel-dalga.tsx b/video/src/gecisler/piksel-dalga.tsx new file mode 100644 index 0000000..f193495 --- /dev/null +++ b/video/src/gecisler/piksel-dalga.tsx @@ -0,0 +1,137 @@ +import type { + TransitionPresentation, + TransitionPresentationComponentProps, +} from "@remotion/transitions"; +import { AbsoluteFill, Easing, interpolate } from "remotion"; +import { H, LOGO_KARELER, PIKSEL, W } from "../marka"; +import { clamp01, hash } from "../yardimci"; + +/** + * Piksel dalgası: yeni sahne, bir noktadan (çoğunlukla imlecin tıkladığı + * yer) yayılan iri piksellerle açılır. Dalganın önünde logonun üç rengi + * (mavi, şeftali, turuncu) yanıp söner — hero haritasındaki ısınan + * piksellerin sahne ölçeğindeki hâli. Hücreler sitenin nokta oranını + * (%74, rx %20) taşır; dalga geçince birleşip kesintisiz yüzeye döner. + */ +export type PikselDalgaProps = { + origin?: { x: number; y: number }; + /** Hücre kenarı (px) */ + hucre?: number; + /** Dalga önünün kalınlığı (px) */ + bant?: number; + /** Dalga önündeki piksellerin renkleri */ + renkler?: string[]; + /** Çıkan sahne hafifçe geri çekilsin (derinlik) */ + geriCekil?: number; + tohum?: number; +}; + +const VARSAYILAN_RENKLER = LOGO_KARELER.map((k) => k.renk); + +const koseUzakligi = (o: { x: number; y: number }) => + Math.max( + Math.hypot(o.x, o.y), + Math.hypot(W - o.x, o.y), + Math.hypot(o.x, H - o.y), + Math.hypot(W - o.x, H - o.y), + ); + +/** + * Dalga önünün yarıçapı. Gelen sahne kendi öğelerini dalgayla eş zamanlı + * belirtmek isterse (ör. haritanın noktaları) aynı fonksiyonu kullanır. + */ +export const dalgaYaricapi = ( + p: number, + origin: { x: number; y: number } = { x: W / 2, y: H / 2 }, + bant = 420, +) => + interpolate(clamp01(p), [0, 1], [0, koseUzakligi(origin) + bant * 1.25], { + easing: Easing.bezier(0.45, 0.05, 0.4, 1), + }); + +const PikselDalgaSunum: React.FC< + TransitionPresentationComponentProps +> = ({ children, presentationProgress, presentationDirection, passedProps }) => { + const { + origin = { x: W / 2, y: H / 2 }, + hucre = 64, + bant = 420, + renkler = VARSAYILAN_RENKLER, + geriCekil = 0.05, + tohum = 3, + } = passedProps; + const p = presentationProgress; + + if (presentationDirection === "exiting") { + return ( + + {children} + + ); + } + + const sutun = Math.ceil(W / hucre); + const satir = Math.ceil(H / hucre); + const r = dalgaYaricapi(p, origin, bant); + + let maske = ""; + let renkli = ""; + for (let j = 0; j < satir; j++) { + for (let i = 0; i < sutun; i++) { + const cx = i * hucre + hucre / 2; + const cy = j * hucre + hucre / 2; + const d = Math.hypot(cx - origin.x, cy - origin.y) + (hash(i, j, tohum) - 0.5) * bant * 0.55; + const t = clamp01((r - d) / bant); + if (t <= 0) continue; + // Önde renkli piksel belirip söner… + if (t < 0.72) { + const c = Math.sin(Math.PI * (t / 0.72)); + const boy = hucre * PIKSEL.nokta * c; + const renk = renkler[Math.floor(hash(i, j, tohum + 9) * renkler.length)]; + renkli += ``; + } + // …arkasından yeni sahne piksel piksel açılır + const m = Easing.bezier(0.22, 1, 0.36, 1)(clamp01((t - 0.38) / 0.62)); + if (m > 0) { + const boy = hucre * (PIKSEL.nokta + 0.3 * m); + const rx = hucre * PIKSEL.rx * (1 - m); + maske += ``; + } + } + } + + const maskeUrl = `url("data:image/svg+xml;utf8,${encodeURIComponent( + `${maske}`, + )}")`; + + return ( + + + {children} + + + + + + ); +}; + +export function pikselDalga( + props: PikselDalgaProps = {}, +): TransitionPresentation { + return { component: PikselDalgaSunum, props }; +} diff --git a/video/src/index.ts b/video/src/index.ts new file mode 100644 index 0000000..f31c790 --- /dev/null +++ b/video/src/index.ts @@ -0,0 +1,4 @@ +import { registerRoot } from "remotion"; +import { RemotionRoot } from "./Root"; + +registerRoot(RemotionRoot); diff --git a/video/src/lib/remocn-ui/color.ts b/video/src/lib/remocn-ui/color.ts new file mode 100644 index 0000000..f92074d --- /dev/null +++ b/video/src/lib/remocn-ui/color.ts @@ -0,0 +1,87 @@ +import { + clampChroma, + converter, + formatRgb, + interpolate, + type Oklch, + parse, + type Rgb, +} from "culori"; + +const toRgb = converter("rgb"); +const toOklch = converter("oklch"); + +const BLACK: Rgb = { mode: "rgb", r: 0, g: 0, b: 0, alpha: 1 }; + +export function parseColor(c: string): Rgb { + const s = c.trim(); + + if (s.startsWith("var(")) { + if (process.env.NODE_ENV !== "production") { + console.warn( + `[remocn-ui] parseColor cannot resolve CSS variable "${s}" under Remotion's per-frame render. ` + + "Animated colors must be concrete oklch/hex/rgb values supplied via the theme." + + "Falling back to the JS default.", + ); + } + return { ...BLACK }; + } + + const rgb = toRgb(parse(s)); + if (!rgb) { + if (process.env.NODE_ENV !== "production") { + console.warn( + `[remocn-ui] parseColor could not parse "${s}"; using black.`, + ); + } + return { ...BLACK }; + } + + return { mode: "rgb", r: rgb.r, g: rgb.g, b: rgb.b, alpha: rgb.alpha ?? 1 }; +} + +export function oklchToRgb(l: number, c: number, h: number): Rgb { + const mapped = clampChroma({ mode: "oklch", l, c, h }, "oklch", "rgb"); + return toRgb(mapped); +} + +export function rgbToOklch(rgb: Rgb): Oklch { + const { l, c, h } = toOklch(rgb); + return { mode: "oklch", l, c, h: Number.isFinite(h) ? h : 0 }; +} + +function resolveColorString(s: string): string { + const trimmed = s.trim(); + if (trimmed.startsWith("var(")) { + if (process.env.NODE_ENV !== "production") { + console.warn( + `[remocn-ui] mixOklch cannot resolve CSS variable "${trimmed}" under Remotion's per-frame render. ` + + "Animated colors must be concrete oklch/hex/rgb values supplied via the theme." + + "Falling back to the JS default.", + ); + } + return "#000"; + } + if (!parse(trimmed)) { + if (process.env.NODE_ENV !== "production") { + console.warn( + `[remocn-ui] mixOklch could not parse "${trimmed}"; using black.`, + ); + } + return "#000"; + } + return trimmed; +} + +export function mixOklch(a: string, b: string, t: number): string { + const mixed = clampChroma( + interpolate([resolveColorString(a), resolveColorString(b)], "oklch")(t), + "oklch", + "rgb", + ); + return toCss(toRgb(mixed)); +} + +export function toCss(color: Rgb): string { + return formatRgb(color); +} diff --git a/video/src/lib/remocn-ui/index.ts b/video/src/lib/remocn-ui/index.ts new file mode 100644 index 0000000..ecaa3bd --- /dev/null +++ b/video/src/lib/remocn-ui/index.ts @@ -0,0 +1,27 @@ +export { + mixOklch, + oklchToRgb, + parseColor, + rgbToOklch, + toCss, +} from "./color"; +export type { EasingName, SpringName } from "./motion"; +export { easings, springs } from "./motion"; +export type { RemocnTheme, RemocnUIProviderProps } from "./theme"; +export { + defaultDarkTheme, + defaultLightTheme, + RemocnUIProvider, + useRemocnTheme, +} from "./theme"; +export type { TypewriterOptions, TypewriterState } from "./timeline"; +export { + clamp01, + framesFor, + revealCount, + revealedText, + useCurrentState, + useStateTransition, + useTypewriter, +} from "./timeline"; +export type { Step } from "./types"; diff --git a/video/src/lib/remocn-ui/motion.ts b/video/src/lib/remocn-ui/motion.ts new file mode 100644 index 0000000..e42e135 --- /dev/null +++ b/video/src/lib/remocn-ui/motion.ts @@ -0,0 +1,17 @@ +export const easings = { + linear: (t: number): number => t, + out: (t: number): number => 1 - (1 - t) ** 3, + in: (t: number): number => t * t * t, + inOut: (t: number): number => + t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2, +} as const; + +export type EasingName = keyof typeof easings; + +export const springs = { + snappy: { damping: 18, stiffness: 220, mass: 0.7 }, + soft: { damping: 14, stiffness: 120, mass: 0.9 }, + bouncy: { damping: 10, stiffness: 180, mass: 0.8 }, +} as const; + +export type SpringName = keyof typeof springs; diff --git a/video/src/lib/remocn-ui/theme.ts b/video/src/lib/remocn-ui/theme.ts new file mode 100644 index 0000000..40435f3 --- /dev/null +++ b/video/src/lib/remocn-ui/theme.ts @@ -0,0 +1,108 @@ +"use client"; + +import type { ReactNode } from "react"; +import { createContext, createElement, useContext } from "react"; + +export interface RemocnTheme { + background: string; + foreground: string; + card: string; + cardForeground: string; + popover: string; + popoverForeground: string; + primary: string; + primaryForeground: string; + secondary: string; + secondaryForeground: string; + muted: string; + mutedForeground: string; + accent: string; + accentForeground: string; + destructive: string; + destructiveForeground: string; + border: string; + input: string; + ring: string; + radius: number; +} + +export const defaultLightTheme: RemocnTheme = { + background: "oklch(1 0 0)", + foreground: "oklch(0.145 0 0)", + card: "oklch(1 0 0)", + cardForeground: "oklch(0.145 0 0)", + popover: "oklch(1 0 0)", + popoverForeground: "oklch(0.145 0 0)", + primary: "oklch(0.205 0 0)", + primaryForeground: "oklch(0.985 0 0)", + secondary: "oklch(0.97 0 0)", + secondaryForeground: "oklch(0.205 0 0)", + muted: "oklch(0.97 0 0)", + mutedForeground: "oklch(0.556 0 0)", + accent: "oklch(0.97 0 0)", + accentForeground: "oklch(0.205 0 0)", + destructive: "oklch(0.577 0.245 27.325)", + destructiveForeground: "oklch(0.985 0 0)", + border: "oklch(0.922 0 0)", + input: "oklch(0.922 0 0)", + ring: "oklch(0.708 0 0)", + radius: 10, +}; + +export const defaultDarkTheme: RemocnTheme = { + background: "oklch(0.145 0 0)", + foreground: "oklch(0.985 0 0)", + card: "oklch(0.205 0 0)", + cardForeground: "oklch(0.985 0 0)", + popover: "oklch(0.205 0 0)", + popoverForeground: "oklch(0.985 0 0)", + primary: "oklch(0.922 0 0)", + primaryForeground: "oklch(0.205 0 0)", + secondary: "oklch(0.269 0 0)", + secondaryForeground: "oklch(0.985 0 0)", + muted: "oklch(0.269 0 0)", + mutedForeground: "oklch(0.708 0 0)", + accent: "oklch(0.269 0 0)", + accentForeground: "oklch(0.985 0 0)", + destructive: "oklch(0.704 0.191 22.216)", + destructiveForeground: "oklch(0.985 0 0)", + border: "oklch(1 0 0 / 10%)", + input: "oklch(1 0 0 / 15%)", + ring: "oklch(0.556 0 0)", + radius: 10, +}; + +interface RemocnThemeContextValue { + theme?: Partial; + mode?: "light" | "dark"; +} + +const RemocnThemeContext = createContext({}); + +export interface RemocnUIProviderProps { + theme?: Partial; + mode?: "light" | "dark"; + children: ReactNode; +} + +export function RemocnUIProvider({ + theme, + mode, + children, +}: RemocnUIProviderProps) { + return createElement( + RemocnThemeContext.Provider, + { value: { theme, mode } }, + children, + ); +} + +export function useRemocnTheme( + override?: Partial, + modeOverride?: "light" | "dark", +): RemocnTheme { + const ctx = useContext(RemocnThemeContext); + const mode = modeOverride ?? ctx.mode ?? "light"; + const base = mode === "dark" ? defaultDarkTheme : defaultLightTheme; + return { ...base, ...ctx.theme, ...override }; +} diff --git a/video/src/lib/remocn-ui/timeline.ts b/video/src/lib/remocn-ui/timeline.ts new file mode 100644 index 0000000..f815976 --- /dev/null +++ b/video/src/lib/remocn-ui/timeline.ts @@ -0,0 +1,96 @@ +import { useCurrentFrame, useVideoConfig } from "remotion"; +import type { Step } from "./types"; + +export function framesFor( + d: number | { seconds: number }, + fps: number, +): number { + return typeof d === "number" ? d : Math.round(d.seconds * fps); +} + +export function revealCount( + localFrame: number, + fps: number, + len: number, + cps: number, +): number { + const over = (len / cps) * fps; + if (over <= 0) return len; + return Math.max(0, Math.min(len, Math.floor((localFrame / over) * len))); +} + +export function clamp01(t: number): number { + return Math.max(0, Math.min(1, t)); +} + +export function revealedText(full: string, count: number): string { + const c = Math.max(0, Math.min(full.length, Math.floor(count))); + return full.slice(0, c); +} + +export interface TypewriterOptions { + cps?: number; + speed?: number; + startFrame?: number; +} + +export interface TypewriterState { + text: string; + count: number; + done: boolean; + typing: boolean; +} + +export function useTypewriter( + full: string, + options: TypewriterOptions = {}, +): TypewriterState { + const { cps = 20, speed = 1, startFrame = 0 } = options; + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const local = frame * speed - startFrame; + const count = local <= 0 ? 0 : revealCount(local, fps, full.length, cps); + return { + text: revealedText(full, count), + count, + done: count >= full.length, + typing: count > 0 && count < full.length, + }; +} + +export function useCurrentState( + steps: Step[], + defaultState: S, + speed = 1, +): S { + const effectiveFrame = useCurrentFrame() * speed; + let current = defaultState; + let bestAt = -Infinity; + steps.forEach((step) => { + if (step.at <= effectiveFrame && step.at >= bestAt) { + bestAt = step.at; + current = step.state; + } + }); + return current; +} + +export function useStateTransition( + steps: Step[], + defaultState: S, + speed = 1, + defaultDuration = 8, +): { from: S; to: S; progress: number } { + const effectiveFrame = useCurrentFrame() * speed; + const started = steps + .map((step, index) => ({ step, index })) + .sort((a, b) => a.step.at - b.step.at || a.index - b.index) + .filter((e) => e.step.at <= effectiveFrame); + if (started.length === 0) + return { from: defaultState, to: defaultState, progress: 1 }; + const to = started[started.length - 1].step; + const from = started.length >= 2 ? started[started.length - 2].step : null; + const dur = to.duration ?? defaultDuration; + const progress = dur > 0 ? clamp01((effectiveFrame - to.at) / dur) : 1; + return { from: from ? from.state : defaultState, to: to.state, progress }; +} diff --git a/video/src/lib/remocn-ui/types.ts b/video/src/lib/remocn-ui/types.ts new file mode 100644 index 0000000..dea316e --- /dev/null +++ b/video/src/lib/remocn-ui/types.ts @@ -0,0 +1,5 @@ +export interface Step { + at: number; + state: S; + duration?: number; +} diff --git a/video/src/marka.ts b/video/src/marka.ts new file mode 100644 index 0000000..7c89865 --- /dev/null +++ b/video/src/marka.ts @@ -0,0 +1,67 @@ +/** + * Lansman videosunun görsel dili — docs/tasarim/DESIGN.md'nin video karşılığı. + * Renkler sitenin Tailwind v4 tonlarıyla birebir (oklch); logo renkleri + * public/kolay-tercih-mark.svg'den. Video 1080p olduğu için arayüz ölçüleri + * sitenin 2 katıdır (h-12 = 48 px → 96–104 px). + */ + +export const W = 1920; +export const H = 1080; +export const FPS = 30; + +export const R = { + // Bilgi mavisi (--primary) ve logo mavisi + primary: "oklch(0.623 0.188 259.8)", + mavi: "#3b82f6", + mavi400: "oklch(0.707 0.165 254.624)", + // Logo: şeftali + turuncu kare + seftali: "#fdc7a7", + turuncuLogo: "#ff5a00", + // Aksiyon turuncusu (orange-500) ve hover'ı + turuncu: "oklch(0.705 0.213 47.604)", + turuncu600: "oklch(0.646 0.222 41.116)", + turuncu50: "oklch(0.98 0.016 73.684)", + + slate50: "oklch(0.984 0.003 247.858)", + slate100: "oklch(0.968 0.007 247.896)", + slate200: "oklch(0.929 0.013 255.508)", + slate300: "oklch(0.869 0.022 252.894)", + slate400: "oklch(0.704 0.04 256.788)", + slate500: "oklch(0.554 0.046 257.417)", + slate600: "oklch(0.446 0.043 257.281)", + slate700: "oklch(0.372 0.044 257.287)", + slate800: "oklch(0.279 0.041 260.031)", + slate900: "oklch(0.208 0.042 265.755)", + slate950: "oklch(0.129 0.042 264.695)", + beyaz: "#ffffff", + + // Risk / dilim renkleri — her zaman metin etiketiyle birlikte + emerald500: "oklch(0.696 0.17 162.48)", + emerald600: "oklch(0.596 0.145 163.225)", + amber500: "oklch(0.769 0.188 70.08)", + amber600: "oklch(0.666 0.179 58.318)", + red500: "oklch(0.637 0.237 25.331)", + red600: "oklch(0.577 0.245 27.325)", + + // Gösterge kareleri (§4.11) + indigo500: "oklch(0.585 0.233 277.117)", + green600: "oklch(0.627 0.194 149.214)", +} as const; + +/** Piksel imzası: hero ızgarası (hücre 10, nokta 7.4, rx 2) oranları */ +export const PIKSEL = { + nokta: 0.74, + rx: 0.2, + // Hero doku paleti (turkey-pixel-map.tsx) + opakliklar: [0.3, 0.16, 0.24, 0.12, 0.27, 0.19], +} as const; + +export const opaklikFor = (x: number, y: number) => + PIKSEL.opakliklar[(x * 7 + y * 11) % PIKSEL.opakliklar.length]; + +/** Logo işareti (viewBox 180) */ +export const LOGO_KARELER = [ + { x: 27, y: 56, boy: 57, renk: "#3b82f6" }, + { x: 91, y: 29, boy: 56, renk: "#fdc7a7" }, + { x: 91, y: 95, boy: 56, renk: "#ff5a00" }, +] as const; diff --git a/video/src/sahneler/S1Gurultu.tsx b/video/src/sahneler/S1Gurultu.tsx new file mode 100644 index 0000000..a4d59a7 --- /dev/null +++ b/video/src/sahneler/S1Gurultu.tsx @@ -0,0 +1,183 @@ +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { Vinyet } from "../bilesenler/Gren"; +import { Cikis } from "../bilesenler/Kinetik"; +import { FONT } from "../fontlar"; +import { H, PIKSEL, R, W } from "../marka"; +import { GURULTU_SAYILARI } from "../veri"; +import { aralik, clamp01, ease, hash, lerp } from "../yardimci"; + +/** + * 1 · Gürültü. Koyu zemin, derinlikten üstüne gelen gerçek taban sıraları + * (tercih dönemindeki tablo kalabalığı). Üç kinetik satır; sonunda bütün + * sayılar merkeze çöker ve geriye **tek bir turuncu piksel** kalır. Bir + * sonraki geçiş o pikselden yayılan dalgadır. + */ + +const ADET = 60; +const DERINLIK = 3200; +const YAKIN = 70; +const ODAK = 720; +const COKUS: [number, number] = [104, 122]; + +const tunel = Array.from({ length: ADET }, (_, i) => { + let x = (hash(i, 1) - 0.5) * 2800; + let y = (hash(i, 2) - 0.5) * 1600; + // Başlık alanının içinden geçmesin: merkeze yakın olanlar dışarı itilir + if (Math.abs(x) < 520 && Math.abs(y) < 260) { + x += Math.sign(x || 1) * 520; + y += Math.sign(y || 1) * 180; + } + const r = hash(i, 4); + return { + metin: GURULTU_SAYILARI[i % GURULTU_SAYILARI.length], + x, + y, + z0: hash(i, 3) * DERINLIK, + renk: r > 0.9 ? R.turuncu : r > 0.78 ? R.mavi400 : R.slate400, + }; +}); + +// Tünelde kat edilen yol: sabit hız, çöküşe doğru hızlanır +const yol = (f: number) => f * 24 + Math.max(0, f - 70) ** 2 * 0.9; + +export const S1Gurultu: React.FC = () => { + const f = useCurrentFrame(); + const cokus = aralik(f, COKUS[0], COKUS[1], ease.in); + const merkezPiksel = aralik(f, 116, 122, ease.geriTep); + + return ( + + {/* Derinlik tüneli */} + {tunel.map((t, i) => { + const z = YAKIN + ((((t.z0 - yol(f)) % DERINLIK) + DERINLIK) % DERINLIK); + const s = ODAK / z; + let X = W / 2 + t.x * s; + let Y = H / 2 + t.y * s; + let olcek = s; + // Çöküş: her sayı merkeze çekilip piksele küçülür + X = lerp(X, W / 2, cokus); + Y = lerp(Y, H / 2, cokus); + olcek = lerp(olcek, 0.02, cokus); + const uzak = aralik(z, DERINLIK, DERINLIK * 0.72); + const yakin = aralik(z, YAKIN, 220); + const op = uzak * yakin * 0.7 * (1 - aralik(f, COKUS[1] - 4, COKUS[1])); + const bulanik = Math.min(7, Math.abs(z - ODAK) / 420) + (1 - yakin) * 6; + if (op <= 0.01) return null; + return ( +
+ {t.metin} +
+ ); + })} + + {/* Başlığın arkasını sakinleştiren karartma */} + + + {/* Kinetik satırlar */} + + + + + + + + + + + + + + + {/* Geriye kalan tek piksel — dalga buradan doğar */} + {merkezPiksel > 0 && ( +
+ )} + + + ); +}; + +/** "Hangisi senin?" — belirir, sonra harf aralığı sıfırın altına çöküp piksele küçülür */ +const HangisiSenin: React.FC = () => { + const f = useCurrentFrame(); + const cok = aralik(f, 12, 26, ease.in); + return ( + + + Hangisi{" "} + + senin? + + + } + fontFamily={FONT.baslik} + fontSize={176} + color={R.beyaz} + startTracking={0.7} + endTracking={lerp(0.015, -0.2, cok)} + startBlur={12} + speed={1.4} + /> + + ); +}; diff --git a/video/src/sahneler/S2Harita.tsx b/video/src/sahneler/S2Harita.tsx new file mode 100644 index 0000000..34dfda4 --- /dev/null +++ b/video/src/sahneler/S2Harita.tsx @@ -0,0 +1,355 @@ +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { Imlec, isi, isiIzi, type Yol } from "../bilesenler/Imlec"; +import { Cikis, Kutu } from "../bilesenler/Kinetik"; +import { LogoIsaret } from "../bilesenler/Logo"; +import { FONT } from "../fontlar"; +import { dalgaYaricapi } from "../gecisler/piksel-dalga"; +import { H, LOGO_KARELER, PIKSEL, R, W, opaklikFor } from "../marka"; +import { IZGARA_SATIR, IZGARA_SUTUN, SEHIRLER, TURKIYE_HUCRELER } from "../turkiye"; +import { IL_PROGRAM, TOPLAM_PROGRAM } from "../veri"; +import { aralik, clamp01, ease, hash, karis, lerp, olc, tr } from "../yardimci"; +import { GECIS, SAHNE, gecisBasi } from "../zaman"; + +/** + * 2 · Harita. Dalga geçerken hero'nun dot-matrix Türkiye'si piksel piksel + * belirir; harita derinlikte yatık durur, illerin program sayısı kadar + * piksel kuleleri yükselir. İmleç haritanın üstünden geçer, sitedeki gibi + * pikseller turuncuya ısınıp yavaş soğur. Sonra bütün pikseller logonun üç + * karesine toplanır; "KolayTercih" harf aralığı daralarak (tracking) yerine + * oturur. İmleç turuncu kareye tıklar → bir sonraki dalga oradan. + */ + +const HUCRE = 24; +const PERSPEKTIF = 2600; +const DALGA_BANT = 420; +const MORF: [number, number] = [110, 146]; +const LOGO_BOY = 260; +const LOGO_MERKEZ_Y = 470; + +// Kelime markanın logo svg'sindeki oranlarıyla: yazı 46.7/44 işaret boyu +// sağda başlar, punto işaret boyunun ~0.63'ü +const KELIME_BOY = 164; +const KELIME_OFSET = (46.7 / 44) * LOGO_BOY; + +/** Logonun son yerleşimi (işaret + kelime ortalı) */ +export const logoYerlesimi = () => { + const ww = + olc("Kolay", { family: FONT.vurgu, size: KELIME_BOY, weight: 300, tracking: -0.05 }) + + olc("Tercih", { family: FONT.vurgu, size: KELIME_BOY, weight: 600, tracking: -0.05 }); + const toplam = KELIME_OFSET + ww; + const sol = W / 2 - toplam / 2; + return { sol, ww, ust: LOGO_MERKEZ_Y - LOGO_BOY / 2 }; +}; + +/** İmlecin tıkladığı nokta: turuncu karenin merkezi */ +export const haritaTiklama = () => { + const { sol, ust } = logoYerlesimi(); + const k = LOGO_KARELER[2]; + return { + x: sol + ((k.x + k.boy / 2) / 180) * LOGO_BOY, + y: ust + ((k.y + k.boy / 2) / 180) * LOGO_BOY, + }; +}; + +const TIKLAMA_KARESI = gecisBasi(SAHNE.harita, GECIS.g2); + +// ——— Harita noktaları ve morf hedefleri (bir kez hesaplanır) ——— + +const bolgeOf = (x: number, y: number) => (x < 30 ? 0 : y < 12 ? 1 : 2); + +const noktalar = (() => { + const ham = TURKIYE_HUCRELER.map(({ x, y }) => ({ + gx: x, + gy: y, + mx: (x - IZGARA_SUTUN / 2 + 0.5) * HUCRE, + my: (y - IZGARA_SATIR / 2 + 0.5) * HUCRE, + op: 0.2 + opaklikFor(x, y) * 1.7, + bolge: bolgeOf(x, y), + })); + const sinir = [0, 1, 2].map((b) => { + const n = ham.filter((p) => p.bolge === b); + return { + n: n.length, + x0: Math.min(...n.map((p) => p.gx)), + x1: Math.max(...n.map((p) => p.gx)), + y0: Math.min(...n.map((p) => p.gy)), + y1: Math.max(...n.map((p) => p.gy)), + }; + }); + const mSol = W / 2 - LOGO_BOY / 2; + const mUst = LOGO_MERKEZ_Y - LOGO_BOY / 2; + return ham.map((p, i) => { + const s = sinir[p.bolge]; + const k = LOGO_KARELER[p.bolge]; + const u = (p.gx - s.x0) / Math.max(1, s.x1 - s.x0); + const v = (p.gy - s.y0) / Math.max(1, s.y1 - s.y0); + const kk = Math.ceil(Math.sqrt(s.n)); + const kenar = (k.boy / 180) * LOGO_BOY; + const hc = kenar / kk; + return { + ...p, + hedefX: mSol + (k.x / 180) * LOGO_BOY + (Math.min(kk - 1, Math.floor(u * kk)) + 0.5) * hc, + hedefY: mUst + (k.y / 180) * LOGO_BOY + (Math.min(kk - 1, Math.floor(v * kk)) + 0.5) * hc, + hedefBoy: hc * 1.08, + hedefRenk: k.renk, + // Soldan sağa süpürülen toplanma + biraz dağınıklık + gecikme: (p.gx / IZGARA_SUTUN) * 10 + hash(i, 5) * 6, + }; + }); +})(); + +const kuleler = SEHIRLER.filter((s) => IL_PROGRAM[s.name]).map((s, i) => ({ + ...s, + mx: (s.x - IZGARA_SUTUN / 2 + 0.5) * HUCRE, + my: (s.y - IZGARA_SATIR / 2 + 0.5) * HUCRE, + n: IL_PROGRAM[s.name], + yukseklik: 16 + 170 * Math.sqrt(IL_PROGRAM[s.name] / IL_PROGRAM["İstanbul"]), + i, +})); +const ETIKETLI = ["İstanbul", "Ankara", "İzmir"]; + +// ——— Kamera ——— + +type Kamera = { egim: number; donus: number; zoom: number; cx: number; cy: number }; + +const kameraAt = (f: number): Kamera => { + const t = aralik(f, 0, 130, ease.inOut); + return { + egim: (lerp(56, 44, t) * Math.PI) / 180, + donus: (lerp(-9, -3, t) * Math.PI) / 180, + zoom: lerp(0.94, 1.06, t), + cx: W / 2, + cy: lerp(560, 540, t), + }; +}; + +/** Harita uzayından ekrana: Z çevresinde dönüş, X çevresinde eğim, perspektif */ +const izdus = (mx: number, my: number, mz: number, k: Kamera) => { + const x1 = mx * Math.cos(k.donus) - my * Math.sin(k.donus); + const y1 = mx * Math.sin(k.donus) + my * Math.cos(k.donus); + const y2 = y1 * Math.cos(k.egim) - mz * Math.sin(k.egim); + const z2 = y1 * Math.sin(k.egim) + mz * Math.cos(k.egim); + const s = (PERSPEKTIF / (PERSPEKTIF - z2)) * k.zoom; + return { x: k.cx + x1 * s, y: k.cy + y2 * s, s }; +}; + +// ——— İmleç ——— + +const tik = () => haritaTiklama(); +const imlecYolu = (): Yol => { + const t = tik(); + return [ + { at: 44, x: 1860, y: 1010 }, + { at: 72, x: 1340, y: 700, duration: 28 }, + { at: 96, x: 920, y: 540, duration: 24 }, + { at: 120, x: 520, y: 620, duration: 24 }, + { at: 156, x: 1240, y: 880, duration: 36 }, + { at: TIKLAMA_KARESI - 4, x: t.x, y: t.y, duration: 34 }, + { at: TIKLAMA_KARESI, x: t.x, y: t.y, click: true, duration: 4 }, + ]; +}; +const IMLEC_GORUNUR: [number, number] = [42, SAHNE.harita]; + +export const S2Harita: React.FC = () => { + const f = useCurrentFrame(); + const kam = kameraAt(f); + const yol = imlecYolu(); + const izler = isiIzi(yol, f, IMLEC_GORUNUR); + const r = dalgaYaricapi(f / GECIS.g1, { x: W / 2, y: H / 2 }, DALGA_BANT); + const cos = Math.cos(kam.egim); + + const kuleCokus = 1 - aralik(f, MORF[0] - 10, MORF[0] + 6, ease.in); + const noktaSonus = 1 - aralik(f, MORF[1] + 2, MORF[1] + 10); + const logoBelir = aralik(f, MORF[1] - 4, MORF[1] + 6, ease.out); + + const { sol, ww } = logoYerlesimi(); + const kay = aralik(f, 150, 178, ease.inOut); + const logoSol = lerp(W / 2 - LOGO_BOY / 2, sol, kay); + + // Noktalar + const rects: React.ReactNode[] = []; + if (noktaSonus > 0) { + noktalar.forEach((p, i) => { + const e = ease.inOut(clamp01((f - MORF[0] - p.gecikme) / 22)); + const q = izdus(p.mx, p.my, 0, kam); + const d = Math.hypot(q.x - W / 2, q.y - H / 2); + const pop = ease.geriTep(clamp01((r - d - 0.36 * DALGA_BANT) / (0.42 * DALGA_BANT))); + if (pop <= 0) return; + const h = e < 1 ? isi(q.x, q.y, izler, 130) * (1 - e) : 0; + const boy0 = HUCRE * PIKSEL.nokta * q.s * pop; + const x = lerp(q.x, p.hedefX, e); + const y = lerp(q.y, p.hedefY, e); + const w = lerp(boy0, p.hedefBoy, e); + const hh = lerp(boy0 * cos, p.hedefBoy, e); + const renk = e > 0 ? karis(karis(R.primary, R.turuncu, h), p.hedefRenk, e) : karis(R.primary, R.turuncu, h); + const op = lerp(lerp(p.op, 0.95, h), 1, e) * noktaSonus; + rects.push( + , + ); + }); + } + + // Piksel kuleleri: il başına program sayısı kadar yükselen plakalar + const kuleRects: React.ReactNode[] = []; + const etiketler: React.ReactNode[] = []; + if (kuleCokus > 0) { + kuleler.forEach((k) => { + const buyu = aralik(f, 30 + k.i * 3, 70 + k.i * 3, ease.out) * kuleCokus; + const yuk = k.yukseklik * buyu; + const adim = HUCRE * 0.55; + const adet = Math.floor(yuk / adim); + for (let j = 0; j <= adet; j++) { + const q = izdus(k.mx, k.my, j * adim, kam); + const boy = HUCRE * PIKSEL.nokta * q.s; + const ust = j === adet; + kuleRects.push( + , + ); + } + if (ETIKETLI.includes(k.name)) { + const op = aralik(f, 62 + k.i * 5, 72 + k.i * 5) * (1 - aralik(f, 100, 110)); + if (op > 0) { + const q = izdus(k.mx, k.my, yuk, kam); + etiketler.push( +
+ {k.name} + · + {tr(k.n)} + program +
, + ); + } + } + }); + } + + return ( + + + + {rects} + {kuleRects} + + {etiketler} + + {/* Logo: morfun sonunda katı kareler, sonra sola kayar */} + {logoBelir > 0 && ( + + )} + + {/* Kelime marka: harf aralığı genişten daralarak yerine oturur */} + + + + Kolay + Tercih + + } + fontFamily={FONT.vurgu} + fontSize={KELIME_BOY} + fontWeight={300} + color="#0a0a0a" + startTracking={0.55} + endTracking={-0.05} + startBlur={14} + /> + + + + {/* Alt yazı: harita beliren veriyi söyler */} + + + + + + + + + {/* Slogan (hero h1) */} + + + + + + + + + ); +}; diff --git a/video/src/sahneler/S3Siralama.tsx b/video/src/sahneler/S3Siralama.tsx new file mode 100644 index 0000000..298b9ba --- /dev/null +++ b/video/src/sahneler/S3Siralama.tsx @@ -0,0 +1,155 @@ +import { Trophy } from "lucide-react"; +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { useTypewriter } from "@/lib/remocn-ui"; +import { Imlec, isiIzi, type Yol } from "../bilesenler/Imlec"; +import { Kutu } from "../bilesenler/Kinetik"; +import { Rozet } from "../bilesenler/Logo"; +import { PikselZemin } from "../bilesenler/PikselZemin"; +import { Cip, CtaButon, GirisHap } from "../bilesenler/UI"; +import { FONT } from "../fontlar"; +import { R, W } from "../marka"; +import { ORNEK_SIRA_METNI } from "../veri"; +import { aralik, ease, lerp, olc } from "../yardimci"; +import { GECIS, SAHNE, gecisBasi } from "../zaman"; + +/** + * 3 · Sıralamanı gir. Hero formunun kendisi (hero-form.tsx): puan türü + * çipleri, sıralama alanı, turuncu CTA. Sahne dalgayla açılırken form + * yatık bir düzlemden doğrulur (derinlik). İmleç alana tıklar, sıralama + * yazılır, imleç CTA'ya gider ve basar → sahne butonun içinden geçilerek + * terk edilir. + */ + +const CTA_METNI = "Yapay Zeka listemi kur"; +const FORM_Y = 610; +const FORM_H = 104; +const GIRIS_W = 860; +const ARA = 24; +const PUAN_TURLERI = ["Sayısal", "Eşit Ağırlık", "Sözel", "Dil", "TYT"]; + +const ctaGenislik = () => + 2 * 0.52 * FORM_H + + olc(CTA_METNI, { family: FONT.govde, size: FORM_H * 0.31, weight: 600 }) + + 16 + + FORM_H * 0.31; + +const formSol = () => W / 2 - (GIRIS_W + ARA + ctaGenislik()) / 2; + +export const siralamaTiklama = () => ({ + x: formSol() + GIRIS_W + ARA + ctaGenislik() / 2, + y: FORM_Y + FORM_H / 2, +}); + +const TIK = gecisBasi(SAHNE.siralama, GECIS.g3); +const YAZ_BAS = 34; + +export const S3Siralama: React.FC = () => { + const f = useCurrentFrame(); + const cta = siralamaTiklama(); + const sol = formSol(); + const girisX = sol + 150; + const yol: Yol = [ + { at: 2, x: 1620, y: 980 }, + { at: 28, x: girisX, y: cta.y + 6, duration: 26 }, + { at: 30, x: girisX, y: cta.y + 6, click: true, duration: 2 }, + { at: TIK - 10, x: cta.x - 40, y: cta.y + 4, duration: 30 }, + { at: TIK, x: cta.x - 40, y: cta.y + 4, click: true, duration: 10 }, + ]; + const izler = isiIzi(yol, f); + const yazi = useTypewriter(ORNEK_SIRA_METNI, { cps: 10, startFrame: YAZ_BAS }); + const odak = aralik(f, 30, 36); + const hover = aralik(f, TIK - 18, TIK - 10); + const bas = f >= TIK && f < TIK + 8 ? 1 - Math.abs(f - TIK - 4) / 4 : 0; + + // Form yatık düzlemden doğrulur + const dogrul = aralik(f, 0, 30, ease.out); + + return ( + + + + + + + Nasıl çalışır? · 1/3 + + + + + + + + {/* Puan türü çipleri: Sayısal seçili */} + + {PUAN_TURLERI.map((t, i) => ( +
+ {t} +
+ ))} +
+ +
+ } + /> + + {CTA_METNI} + +
+ + + + Veri: YÖK Atlas · 2026 yerleştirme sonuçları + + +
+
+ +
+ ); +}; diff --git a/video/src/sahneler/S4Havuz.tsx b/video/src/sahneler/S4Havuz.tsx new file mode 100644 index 0000000..cd81a7d --- /dev/null +++ b/video/src/sahneler/S4Havuz.tsx @@ -0,0 +1,707 @@ +import { Building2, Landmark, Scale } from "lucide-react"; +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { FocusBlurResolve } from "@/components/remocn/focus-blur-resolve"; +import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { Imlec, type Yol } from "../bilesenler/Imlec"; +import { Cikis, Kutu } from "../bilesenler/Kinetik"; +import { Rozet } from "../bilesenler/Logo"; +import { Cip, DILIM_RENK, DilimEtiketi, PikselTrend } from "../bilesenler/UI"; +import { FONT } from "../fontlar"; +import { PIKSEL, R, W, opaklikFor } from "../marka"; +import { TURKIYE_HUCRELER } from "../turkiye"; +import { DILIM_ETIKET, HUNI, ISKELET, LISTE, SORULAN, type Dilim } from "../veri"; +import { aralik, clamp01, ease, hash, karis, lerp, olc, tr } from "../yardimci"; +import { GECIS, SAHNE, gecisBasi } from "../zaman"; + +/** + * 4 · Havuz → 24'lük liste. + * + * A) Sihirbaz (alan → il → devlet/vakıf). Sağda aday havuzu: sayı her + * seçimde küçülür, altındaki piksel alanından o kadar piksel düşer. + * Son adımda kalan her piksel **bir gerçek programdır** (179). + * B) Kart derinliğe çekilir; 179 piksel havuz sınırının içine toplanır. + * Dışarıdaki gri pikseller sınıra çarpıp geri döner (yapay zekâ havuzun + * dışına çıkamaz). 24 piksel seçilir, dilim renklerini alır ve soldaki + * 24'lük şeride uçar; sağda liste yatık bir düzlemden doğrulur. İmleç + * bir satıra tıklar → sahne o satırın içinden terk edilir. + */ + +const TIK = gecisBasi(SAHNE.havuz, GECIS.g4); + +// ——— Sihirbaz kartı yerleşimi ——— +const KART = { x: 150, y: 170, w: 860, h: 740, ic: 64 }; +const ICERIK_X = KART.x + KART.ic; +const ICERIK_W = KART.w - KART.ic * 2; +const CIP_BOY = 72; + +const ALANLAR = [ + "Mühendislik & Teknoloji", + "Sağlık & Tıp", + "Fen & Temel Bilimler", + "Mimarlık & Tasarım", + "İktisat & İşletme", + "Eğitim & Öğretmenlik", + "Hukuk & Siyasal", + "Tarım & Doğa", +]; +const ILLER = ["İstanbul", "Ankara", "İzmir", "Bursa", "Antalya", "Eskişehir"]; +const IL_HUCRE: Record = { + İstanbul: [8, 4], + Ankara: [23, 9], + İzmir: [4, 16], +}; + +/** Çipleri kart genişliğinde satırlara dizer (gerçek yazı genişliğiyle) */ +const cipDiz = (metinler: string[], x0: number, y0: number, ikonlu = false) => { + const ara = 16; + let x = x0; + let y = y0; + return metinler.map((m) => { + const w = + CIP_BOY * 0.7 + + olc(m, { family: FONT.govde, size: CIP_BOY * 0.35, weight: 600 }) + + (ikonlu ? CIP_BOY * 0.35 + CIP_BOY * 0.15 : 0); + if (x + w > x0 + ICERIK_W) { + x = x0; + y += CIP_BOY + ara; + } + const k = { metin: m, x, y, w, cx: x + w / 2, cy: y + CIP_BOY / 2 }; + x += w + ara; + return k; + }); +}; + +// Adım pencereleri: [giriş, çıkış] +const ADIM: [number, number][] = [ + [-10, 88], + [86, 154], + [152, 214], +]; + +// Tıklama kareleri +const T_ALAN = 46; +const T_IL = [106, 120, 134]; +const T_TUR = 174; + +// ——— Havuz sayacı ve piksel alanı ——— +const SAYAC: { a: number; b: number; n: number }[] = [ + { a: 8, b: 34, n: HUNI[1].n }, + { a: 48, b: 70, n: HUNI[2].n }, + { a: 136, b: 158, n: HUNI[3].n }, + { a: 176, b: 198, n: HUNI[4].n }, +]; +const sayacAt = (f: number) => { + let n: number = HUNI[0].n; + let adim = 0; + for (const [i, s] of SAYAC.entries()) { + if (f >= s.a) { + n = lerp(n, s.n, aralik(f, s.a, s.b, ease.out)); + adim = i + 1; + } + } + return { n, adim }; +}; + +const ALAN = { x: 1100, y: 560, sutun: 30, satir: 14, hucre: 22 }; +const HAVUZ = HUNI[4].n; // 179 +const EN_COK = ALAN.sutun * ALAN.satir; // 420 +const yanan = (n: number) => + HAVUZ + (EN_COK - HAVUZ) * (Math.log10(n / HAVUZ) / Math.log10(HUNI[0].n / HAVUZ)); + +// Kümelenme (B): havuz sınırı içinde 17×11 +const KUME = { sutun: 17, hucre: 34, cx: 960, cy: 590 }; +const KUME_SATIR = Math.ceil(HAVUZ / KUME.sutun); +const KUME_X0 = KUME.cx - (KUME.sutun * KUME.hucre) / 2; +const KUME_Y0 = KUME.cy - (KUME_SATIR * KUME.hucre) / 2; +const SINIR = { + x: KUME_X0 - 30, + y: KUME_Y0 - 30, + w: KUME.sutun * KUME.hucre + 60, + h: KUME_SATIR * KUME.hucre + 60, +}; + +// Şerit (B4): seçilen 24 piksel +const SERIT = { x: 140, y: 700, hucre: 30 }; + +const pikseller = (() => { + const hucreler = Array.from({ length: EN_COK }, (_, i) => ({ + i, + gx: i % ALAN.sutun, + gy: Math.floor(i / ALAN.sutun), + h: hash(i % ALAN.sutun, Math.floor(i / ALAN.sutun), 41), + })); + const sirali = [...hucreler].sort((a, b) => a.h - b.h); + const rutbe = new Map(sirali.map((c, r) => [c.i, r])); + const havuzdakiler = hucreler.filter((c) => (rutbe.get(c.i) ?? 0) < HAVUZ); // alan sırasına göre + const havuzSira = new Map(havuzdakiler.map((c, p) => [c.i, p])); + // 24 seçim: havuzdan dağınık + const secimSirasi = [...havuzdakiler].sort((a, b) => hash(a.i, 3, 77) - hash(b.i, 3, 77)).slice(0, 24); + const dilimler: Dilim[] = ISKELET.flatMap((d) => Array(d.adet).fill(d.dilim)); + const secim = new Map(secimSirasi.map((c, k) => [c.i, { k, dilim: dilimler[k] }])); + return hucreler.map((c) => ({ + ...c, + rutbe: rutbe.get(c.i) ?? 0, + p: havuzSira.get(c.i), + secim: secim.get(c.i), + })); +})(); + +// Havuz dışındaki "hayalet" programlar: sınıra çarpıp geri döner +const HAYALETLER = [ + { bx: SINIR.x, by: SINIR.y + 90, nx: -1, ny: 0 }, + { bx: SINIR.x, by: SINIR.y + 300, nx: -1, ny: 0 }, + { bx: SINIR.x + SINIR.w, by: SINIR.y + 150, nx: 1, ny: 0 }, + { bx: SINIR.x + SINIR.w, by: SINIR.y + 340, nx: 1, ny: 0 }, + { bx: SINIR.x + 180, by: SINIR.y + SINIR.h, nx: 0, ny: 1 }, + { bx: SINIR.x + 420, by: SINIR.y, nx: 0, ny: -1 }, +]; + +// ——— Liste (B4) ——— +const LISTE_X = 950; +const LISTE_W = 860; +const SATIR_H = 84; +const BASLIK_H = 58; +const DAHA_H = 42; +const BOLUMLER = ISKELET.map((d) => ({ + ...d, + satirlar: LISTE.map((s, i) => ({ s, i })).filter(({ s }) => s.dilim === d.dilim), +})); +const listeUst = (f: number) => lerp(150, 12, aralik(f, 330, 392, ease.inOut)); +// Tıklanan satırın liste içindeki konumu +const HEDEF_SATIR = LISTE.indexOf(SORULAN); +const hedefOfset = (() => { + let y = 0; + for (const b of BOLUMLER) { + y += BASLIK_H; + for (const { i } of b.satirlar) { + if (i === HEDEF_SATIR) return y; + y += SATIR_H; + } + y += DAHA_H; + } + return y; +})(); + +export const havuzTiklama = () => ({ + x: LISTE_X + LISTE_W * 0.42, + y: listeUst(TIK) + hedefOfset + SATIR_H / 2, +}); + +export const S4Havuz: React.FC = () => { + const f = useCurrentFrame(); + + const alanCip = cipDiz(ALANLAR, ICERIK_X, 430); + const ilCip = cipDiz(ILLER, ICERIK_X, 744); + const turCip = cipDiz(["Devlet", "Vakıf", "Fark etmez"], ICERIK_X, 494, true); + const hedef = havuzTiklama(); + + const yol: Yol = [ + { at: 16, x: 1480, y: 1010 }, + { at: T_ALAN, x: alanCip[0].cx, y: alanCip[0].cy, duration: 26, click: true }, + { at: T_IL[0], x: ilCip[0].cx, y: ilCip[0].cy, duration: 30, click: true }, + { at: T_IL[1], x: ilCip[1].cx, y: ilCip[1].cy, duration: 12, click: true }, + { at: T_IL[2], x: ilCip[2].cx, y: ilCip[2].cy, duration: 12, click: true }, + { at: T_TUR, x: turCip[0].cx, y: turCip[0].cy, duration: 28, click: true }, + { at: 206, x: 1180, y: 1060, duration: 26 }, + { at: 350, x: 1880, y: 1040, duration: 4 }, + { at: 380, x: 1560, y: 760, duration: 28 }, + { at: TIK - 2, x: hedef.x, y: hedef.y, duration: 16 }, + { at: TIK, x: hedef.x, y: hedef.y, click: true, duration: 2 }, + ]; + const imlecGorunur = f < 230 ? ([12, 228] as [number, number]) : ([352, SAHNE.havuz] as [number, number]); + + const { n, adim } = sayacAt(f); + const yananF = yanan(n); + + // ——— Faz geçişleri ——— + const kartCikis = aralik(f, 198, 232, ease.in); + const panelCikis = aralik(f, 198, 214); + const kume = (p: number) => ease.inOut(clamp01((f - 204 - p * 0.1) / 26)); + const secimBas = 262; + const seritBas = 300; + const sinirOp = aralik(f, 222, 238) * (1 - aralik(f, 300, 316)); + + // ——— Pikseller ——— + const rects: React.ReactNode[] = []; + for (const c of pikseller) { + let x = ALAN.x + c.gx * ALAN.hucre + ALAN.hucre / 2; + let y = ALAN.y + c.gy * ALAN.hucre + ALAN.hucre / 2; + let boy = ALAN.hucre * PIKSEL.nokta; + let renk: string = R.primary; + let op = 0.25 + opaklikFor(c.gx, c.gy) * 1.9; + // Belirme (kart ile birlikte) + const belir = aralik(f, 4 + (c.gx + c.gy) * 0.35, 14 + (c.gx + c.gy) * 0.35, ease.out); + op *= belir; + // Havuzdan düşenler: sayaç indikçe sırası gelen piksel griye döner ve düşer + if (c.rutbe >= yananF) { + const dus = clamp01((c.rutbe + 1 - yananF) / 20); + if (dus >= 1) continue; + y += dus * dus * 150; + renk = karis(R.primary, R.slate300, clamp01(dus * 3)); + op *= 1 - dus; + } + if (c.p !== undefined) { + // B: kümeye toplanma + const e = kume(c.p); + const kx = KUME_X0 + (c.p % KUME.sutun) * KUME.hucre + KUME.hucre / 2; + const ky = KUME_Y0 + Math.floor(c.p / KUME.sutun) * KUME.hucre + KUME.hucre / 2; + x = lerp(x, kx, e); + y = lerp(y, ky, e); + boy = lerp(boy, KUME.hucre * PIKSEL.nokta, e); + op = lerp(op, 0.55 + opaklikFor(c.gx, c.gy), e); + if (c.secim) { + const s = aralik(f, secimBas + c.secim.k * 1.3, secimBas + 8 + c.secim.k * 1.3, ease.out); + renk = karis(renk, DILIM_RENK[c.secim.dilim].nokta, s); + op = lerp(op, 1, s); + // seçilirken kısa bir halka + const halka = Math.sin(Math.PI * s); + if (halka > 0.02) { + const hb = boy * (1 + 0.7 * halka); + rects.push( + , + ); + } + // şeride uçuş + const u = ease.inOut(clamp01((f - seritBas - c.secim.k * 0.9) / 26)); + x = lerp(x, SERIT.x + c.secim.k * SERIT.hucre + SERIT.hucre / 2, u); + y = lerp(y, SERIT.y + SERIT.hucre / 2, u); + boy = lerp(boy, SERIT.hucre * PIKSEL.nokta, u); + } else { + op *= 1 - 0.72 * aralik(f, secimBas, secimBas + 26); + op *= 1 - aralik(f, seritBas, seritBas + 14); + if (op <= 0.01) continue; + } + } + rects.push( + , + ); + } + + // Hayaletler + const hayaletler = HAYALETLER.map((g, i) => { + const u = (f - (240 + i * 5)) / 26; + if (u < 0 || f > 316) return null; + const bosluk = + u < 1 + ? lerp(170, 10, ease.in(u)) + : 10 + 46 * Math.abs(Math.sin((u - 1) * Math.PI * 1.2)) * Math.exp(-(u - 1) * 2.4); + const boy = KUME.hucre * PIKSEL.nokta; + const x = g.bx + g.nx * (bosluk + boy / 2); + const y = g.by + g.ny * (bosluk + boy / 2); + const op = aralik(u, 0, 0.3) * (1 - aralik(f, 300, 316)); + return ( + + ); + }); + // Çarpma anında sınır kısa süre turuncuya döner + const carpma = Math.max( + ...HAYALETLER.map((_, i) => { + const u = (f - (240 + i * 5)) / 26 - 1; + return u >= 0 && u < 0.4 ? 1 - u / 0.4 : 0; + }), + ); + + // Kartın adımları + const adimStil = (k: number): React.CSSProperties => { + const [a, b] = ADIM[k]; + const g = aralik(f, a, a + 10, ease.out); + const c = aralik(f, b - 8, b, ease.in); + return { + position: "absolute", + inset: 0, + opacity: g * (1 - c), + translate: `${(1 - g) * 90 - c * 90}px 0`, + filter: `blur(${(1 - g) * 6 + c * 6}px)`, + }; + }; + const secildi = (t: number) => aralik(f, t, t + 5); + const uzerinde = (t: number) => aralik(f, t - 7, t - 2) * (1 - aralik(f, t, t + 3)); + const aktifAdim = f < ADIM[1][0] + 4 ? 0 : f < ADIM[2][0] + 4 ? 1 : 2; + + const basliklar = ["Hangi alanlara ilgi duyuyorsun?", "Nerede okumak istersin?", "Üniversite tipi & öncelikler"]; + const aciklamalar = [ + "Sıralamanla ulaşabileceğin alanlar. En az bir tane seç.", + "En fazla 5 il seçebilirsin. İstersen boş bırak, fark etmez.", + "Bunlar listenin dengesini belirler.", + ]; + + return ( + + {/* ——— A: sihirbaz kartı (derinliğe çekilerek çıkar) ——— */} + +
+
+ {/* İlerleme */} +
+ {[0, 1, 2].map((k) => ( +
+ ))} + + {aktifAdim + 1} / 3 + +
+ {[0, 1, 2].map((k) => ( +
+
+ {basliklar[k]} +
+
+ {aciklamalar[k]} +
+ {k === 0 && + alanCip.map((c, i) => ( + + {c.metin} + + ))} + {k === 1 && ( + <> + + {ilCip.map((c, i) => ( + + {c.metin} + + ))} + + )} + {k === 2 && ( + <> +
+ Üniversite tipi +
+ {turCip.map((c, i) => { + const Ikon = [Landmark, Building2, Scale][i]; + const sec = i === 0 ? secildi(T_TUR) : 0; + return ( + } + style={{ position: "absolute", left: c.x, top: c.y }} + > + {c.metin} + + ); + })} +
+ Öncelikler +
+ {cipDiz(["Burslu", "Öğretim dili", "Evime yakın"], ICERIK_X, 672).map((c) => ( + + {c.metin} + + ))} + + )} +
+ ))} +
+
+ + + {/* ——— A: aday havuzu paneli ——— */} +
+ + Nasıl çalışır? · 2/3 + +
+ Aday havuzu +
+
+ + {tr(n)} + + program +
+ {HUNI.map((h, i) => { + const op = i === adim ? aralik(f, i === 0 ? 0 : SAYAC[i - 1].a, (i === 0 ? 0 : SAYAC[i - 1].a) + 8) : 0; + return ( +
+ {h.etiket} +
+ ); + })} +
+ + {/* ——— B: havuz sınırı ——— */} + {sinirOp > 0 && ( + <> +
+
+ Aday havuzu · 179 gerçek program +
+ + )} + + + {hayaletler} + {rects} + + + + + + + + + + + + + + + + {/* ——— B4: sol başlık + 24'lük şerit + dilimler ——— */} + {f >= 316 && } + + {/* ——— B4: liste ——— */} + {f >= 318 && } + + + + ); +}; + +/** İl adımındaki dot-matrix harita; seçilen iller turuncu yanar */ +const MiniHarita: React.FC<{ x: number; y: number; f: number }> = ({ x, y, f }) => { + const hucre = 10.6; + const boy = hucre * PIKSEL.nokta; + const secim = Object.entries(IL_HUCRE).map(([ad, [cx, cy]], i) => ({ ad, cx, cy, s: aralik(f, T_IL[i], T_IL[i] + 8, ease.geriTep) })); + return ( + + {TURKIYE_HUCRELER.map(({ x: gx, y: gy }) => { + let isi = 0; + for (const s of secim) { + const d = Math.hypot(gx - s.cx, gy - s.cy); + if (d <= 1.5) isi = Math.max(isi, s.s * (d < 0.5 ? 1 : 0.75)); + } + const b = boy * (1 + 0.25 * isi); + return ( + + ); + })} + + ); +}; + +const SolaYasli: React.FC<{ x: number; y: number; metin: React.ReactNode; genislik: number; bas: number; boy: number; family: string; renk?: string }> = ({ x, y, metin, genislik, bas, boy, family, renk = R.slate900 }) => ( + + + + + +); + +const ListeBasligi: React.FC<{ f: number }> = ({ f }) => { + const boy = 118; + const w1 = olc("Dengeli", { family: FONT.baslik, size: boy, weight: 700, tracking: 0.015 }); + const w2 = olc("24'lük", { family: FONT.vurgu, size: boy, weight: 700, italic: true, tracking: -0.05 }); + const w3 = olc("listen hazır.", { family: FONT.baslik, size: boy, weight: 700, tracking: 0.015 }); + return ( + <> + + Nasıl çalışır? · 3/3 + + + 24'lük} + /> + +
+ {ISKELET.map((d) => ( + + + {d.adet} + + ))} +
+
+ Örnek: SAY 45.000 · Mühendislik & Teknoloji · İstanbul, Ankara, İzmir · Devlet. + Satırlar 2026 YÖK Atlas kayıtlarıdır. +
+ + ); +}; + +const Liste: React.FC<{ f: number; hover: number }> = ({ f, hover }) => { + const duz = aralik(f, 330, 390, ease.inOut); + const ust = listeUst(f); + let y = 0; + let sira = 0; + const ogeler: React.ReactNode[] = []; + BOLUMLER.forEach((b, bi) => { + const by = y; + ogeler.push( +
+ + · {b.adet} tercih +
, + ); + y += BASLIK_H; + b.satirlar.forEach(({ s, i }) => { + sira++; + const hedef = i === HEDEF_SATIR; + const sy = y; + ogeler.push( +
+ + {LISTE_SIRASI[i]} + +
+
0.5 ? "underline" : "none", + textUnderlineOffset: 5, + whiteSpace: "nowrap", + }} + > + {s.program} +
+
+ {s.universite} · {s.il} +
+
+ +
+
{tr(s.sira[5])}
+
taban 2026
+
+
, + ); + y += SATIR_H; + }); + const kalan = b.adet - b.satirlar.length; + ogeler.push( +
+ +{kalan} {DILIM_ETIKET[b.dilim].toLocaleLowerCase("tr-TR")} tercih daha +
, + ); + y += DAHA_H; + }); + + return ( + +
+ {ogeler} +
+
+ ); +}; + +// Listede her satırın 24'lük listedeki sırası (hayal 1–5, dengeli 6–18, güvenli 19–24) +const LISTE_SIRASI: Record = (() => { + const out: Record = {}; + let bas = 1; + for (const b of BOLUMLER) { + b.satirlar.forEach(({ i }, k) => (out[i] = bas + k)); + bas += b.adet; + } + return out; +})(); + +/** Satır girişi: derinlikten öne, bulanıktan nete */ +const giris = (f: number, bas: number): React.CSSProperties => { + const g = aralik(f, bas, bas + 14, ease.out); + return { + opacity: g, + transform: `translateZ(${(1 - g) * -260}px) translateY(${(1 - g) * 24}px)`, + filter: `blur(${(1 - g) * 8}px)`, + }; +}; diff --git a/video/src/sahneler/S5Danisman.tsx b/video/src/sahneler/S5Danisman.tsx new file mode 100644 index 0000000..4e11a54 --- /dev/null +++ b/video/src/sahneler/S5Danisman.tsx @@ -0,0 +1,242 @@ +import { ArrowUp } from "lucide-react"; +import { AbsoluteFill, useCurrentFrame } from "remotion"; +import { useTypewriter } from "@/lib/remocn-ui"; +import { Imlec, type Yol } from "../bilesenler/Imlec"; +import { LogoIsaret } from "../bilesenler/Logo"; +import { PikselZemin } from "../bilesenler/PikselZemin"; +import { DilimEtiketi, GirisHap, PikselTrend } from "../bilesenler/UI"; +import { FONT } from "../fontlar"; +import { R } from "../marka"; +import { SORULAN, ORNEK_SIRA_METNI } from "../veri"; +import { aralik, ease, karis, lerp, olc, tr } from "../yardimci"; +import { GECIS, SAHNE, gecisBasi } from "../zaman"; + +/** + * 5 · Danışman. Tıklanan satır sohbetin konusu olur. Soru yazılır, gönder + * düğmesine basılır; yanıt beklenirken logonun üç karesi nabız atar + * (animasyonlu-mark.tsx "nabız"). Yanıt yalnızca gerçek taban geçmişini + * söyler ve altında 6 yılın piksel trendi belirir; son yılın turuncu pikseli + * bir sonraki dalganın çıkış noktasıdır. + */ + +const PANEL = { x: 330, y: 64, w: 1260, h: 952 }; +const SORU = "Bu tercih neden dengeli dilimde?"; +const YANIT = `2026'da taban sırası ${tr(SORULAN.sira[5])}, seninki ${ORNEK_SIRA_METNI}. Ama son 6 yılda taban ${tr(Math.min(...SORULAN.sira))} ile ${tr(Math.max(...SORULAN.sira))} arasında gidip geldi — bu kadar dalgalı olduğu için dengeli dilimde.`; + +const YAZ_BAS = 12; +const GONDER = 58; +const BEKLE: [number, number] = [64, 86]; +const TREND_BAS = 150; + +const GIRIS_Y = PANEL.y + PANEL.h - 132; +const GONDER_X = PANEL.x + PANEL.w - 64 - 42; +const GONDER_Y = GIRIS_Y + 52; + +// Yanıt balonu ve içindeki trend (satır sayısı gerçek yazı genişliğiyle) +const BALON = { x: PANEL.x + 56, y: PANEL.y + 330, w: 960, pad: 40, ust: 30, punto: 32, satir: 48 }; +const TREND_H = { hucre: 34, yukseklik: 90 }; +const satirSayisi = () => { + const bosluk = olc(" ", { family: FONT.govde, size: BALON.punto }); + let n = 1; + let x = 0; + for (const k of YANIT.split(" ")) { + const w = olc(k, { family: FONT.govde, size: BALON.punto }); + if (x > 0 && x + bosluk + w > BALON.w - BALON.pad * 2) { + n++; + x = w; + } else x += (x > 0 ? bosluk : 0) + w; + } + return n; +}; +const trendKonumu = () => ({ + x: BALON.x + BALON.pad, + y: BALON.y + BALON.ust + satirSayisi() * BALON.satir + 28, +}); +/** Trendin son (2026) pikseli: bir sonraki dalga buradan yayılır */ +export const danismanDalgaNoktasi = () => { + const t = trendKonumu(); + const son = SORULAN.sira[5]; + const min = Math.min(...SORULAN.sira); + const max = Math.max(...SORULAN.sira); + return { + x: t.x + 5 * TREND_H.hucre + TREND_H.hucre / 2, + y: t.y + ((son - min) / (max - min)) * TREND_H.yukseklik + (TREND_H.hucre * 0.74) / 2, + }; +}; + +export const S5Danisman: React.FC = () => { + const f = useCurrentFrame(); + const soru = useTypewriter(SORU, { cps: 24, startFrame: YAZ_BAS }); + const yanit = useTypewriter(YANIT, { cps: 70, startFrame: BEKLE[1] }); + const gonderildi = f >= GONDER; + + const yol: Yol = [ + { at: 8, x: 1700, y: 1050 }, + { at: GONDER - 4, x: GONDER_X, y: GONDER_Y, duration: 30 }, + { at: GONDER, x: GONDER_X, y: GONDER_Y, click: true, duration: 4 }, + { at: 100, x: 1740, y: 1010, duration: 30 }, + ]; + const bas = f >= GONDER && f < GONDER + 8 ? 1 - Math.abs(f - GONDER - 4) / 4 : 0; + const soruBalon = aralik(f, GONDER + 2, GONDER + 14, ease.geriTep); + const bekliyor = f >= BEKLE[0] && f < BEKLE[1] + 4; + const yanitBalon = aralik(f, BEKLE[1] - 4, BEKLE[1] + 8, ease.out); + const trend = trendKonumu(); + + return ( + + +
+ {/* Başlık */} +
+ { + const faz = ((f / 30) * 2 * Math.PI) / 1.8 - (i * 2 * Math.PI) / 3; + const nabiz = bekliyor ? 0.5 + 0.5 * Math.sin(faz) : 1; + return { olcek: lerp(0.78, 1, nabiz) }; + }} + /> +
+
Danışman
+
+ {SORULAN.program} · {SORULAN.universite} +
+
+
+
+ +
+
+ + {/* Soru balonu */} + {soruBalon > 0 && ( +
+ {SORU} +
+ )} + + {/* Bekleme: üç kare nabız */} + {bekliyor && ( +
+ {[0, 1, 2].map((i) => { + const s = 0.55 + 0.45 * Math.sin((f / 30) * 2 * Math.PI * 1.4 - i * 1.1); + return
; + })} +
+ )} + + {/* Yanıt balonu */} + {yanitBalon > 0 && ( +
+ {/* Yer tutucu: balon baştan tam boyda dursun, metin akarken zıplamasın */} +
+ {YANIT} + {yanit.text} +
+
+
+ )} +
+ + {/* Trend: 6 yılın taban sırası piksel piksel */} +
+ +
+
+
+ Taban sırası, 2021–2026 +
+
Kaynak: YÖK Atlas · yapay zekâ yorumu değil, gerçek geçmiş
+
+ + {/* Giriş */} +
+ +
+ +
+
+ + + + ); +}; + +export const DANISMAN_DALGA_BASI = gecisBasi(SAHNE.danisman, GECIS.g5); diff --git a/video/src/sahneler/S6Deger.tsx b/video/src/sahneler/S6Deger.tsx new file mode 100644 index 0000000..3edc7da --- /dev/null +++ b/video/src/sahneler/S6Deger.tsx @@ -0,0 +1,120 @@ +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { FocusBlurResolve } from "@/components/remocn/focus-blur-resolve"; +import { SlotMachineRoll } from "@/components/remocn/slot-machine-roll"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { Kutu } from "../bilesenler/Kinetik"; +import { Rozet } from "../bilesenler/Logo"; +import { FONT } from "../fontlar"; +import { R } from "../marka"; +import { aralik, ease, lerp, olc } from "../yardimci"; + +/** + * 6 · Değer. Solda "Tahmin değil, veri." ve dürüstlük notu; sağda sitenin + * gösterge kareleri (DESIGN.md §4.11): logodaki üç kare gibi iki sütun, + * sağdaki aşağı kaymış, boylar farklı. Kareler derinlikten gelip oturur, + * sayılar slot makinesi gibi yerine döner. + */ + +type Kare = { renk: string; sayi: [string, string]; etiket: string; x: number; y: number; w: number; h: number; bas: number }; + +const SUTUN_W = 400; +const SOL_X = 990; +const SAG_X = SOL_X + SUTUN_W + 36; +const UST = 96; + +// Renk sırası: en önemli turuncu, sonra mavi, indigo, yeşil (§4.11) +export const KARELER: Kare[] = [ + { renk: R.turuncu, sayi: ["00.000+", "21.000+"], etiket: "program tarandı", x: SOL_X, y: UST, w: SUTUN_W, h: 420, bas: 8 }, + { renk: R.primary, sayi: ["0 yıl", "6 yıl"], etiket: "taban sıralaması trendi", x: SAG_X, y: UST + 130, w: SUTUN_W, h: 330, bas: 15 }, + { renk: R.indigo500, sayi: ["%000", "%100"], etiket: "resmî YÖK Atlas verisi", x: SOL_X, y: UST + 460, w: SUTUN_W, h: 300, bas: 22 }, + { renk: R.green600, sayi: ["00", "24"], etiket: "tercihlik dengeli liste", x: SAG_X, y: UST + 500, w: SUTUN_W, h: 390, bas: 29 }, +]; + +/** Son karenin merkezi: kapanışa giden koyu dalga buradan */ +export const degerDalgaNoktasi = () => { + const k = KARELER[3]; + return { x: k.x + k.w / 2, y: k.y + k.h / 2 }; +}; + +export const S6Deger: React.FC = () => { + const f = useCurrentFrame(); + const boy = 132; + const w1 = olc("Tahmin değil,", { family: FONT.baslik, size: boy, weight: 700, tracking: 0.015 }); + const w2 = olc("veri.", { family: FONT.vurgu, size: boy, weight: 700, italic: true, tracking: -0.05 }); + const n1 = "Yerleşme garantisi vermiyoruz."; + const n2 = "Riski satır satır gösteriyoruz."; + const nw1 = olc(n1, { family: FONT.govde, size: 38, weight: 600 }); + const nw2 = olc(n2, { family: FONT.govde, size: 38, weight: 600 }); + + return ( + + + Neden KolayTercih? + + + + + + + + + veri.} + fontFamily={FONT.vurgu} + fontSize={boy} + color={R.primary} + startTracking={0.9} + endTracking={-0.05} + /> + + + + + + + + + + + + + + {/* Gösterge kareleri — derinlikten gelip oturur */} + + {KARELER.map((k, i) => { + const g = aralik(f, k.bas, k.bas + 22, ease.out); + const yuz = Math.sin((f + i * 17) / 22) * 6 * g; + return ( +
+ + + + {k.etiket} +
+ ); + })} +
+
+ ); +}; diff --git a/video/src/sahneler/S7Kapanis.tsx b/video/src/sahneler/S7Kapanis.tsx new file mode 100644 index 0000000..8ce5453 --- /dev/null +++ b/video/src/sahneler/S7Kapanis.tsx @@ -0,0 +1,205 @@ +import { AbsoluteFill, Sequence, useCurrentFrame } from "remotion"; +import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build"; +import { TrackingIn } from "@/components/remocn/tracking-in"; +import { Vinyet } from "../bilesenler/Gren"; +import { Imlec, isi, isiIzi, type Yol } from "../bilesenler/Imlec"; +import { Kutu } from "../bilesenler/Kinetik"; +import { LogoIsaret } from "../bilesenler/Logo"; +import { CtaButon } from "../bilesenler/UI"; +import { FONT } from "../fontlar"; +import { dalgaYaricapi } from "../gecisler/piksel-dalga"; +import { H, LOGO_KARELER, PIKSEL, R, W } from "../marka"; +import { aralik, clamp01, ease, hash, karis, lerp, olc } from "../yardimci"; +import { GECIS } from "../zaman"; +import { degerDalgaNoktasi } from "./S6Deger"; + +/** + * 7 · Kapanış. Sitenin tek koyu yüzeyi olan CTA bandının dili: kenarlarda + * büyüklü küçüklü, hafif kaymış kareler (PixelMasonry). Logo kareleri + * "rota" hareketiyle yer değiştirip oturur, kelime marka harf aralığı + * daralarak gelir, slogan kinetik kurulur. İmleç CTA'ya gelirken yolundaki + * kareler turuncuya ısınır; tıklar. + */ + +const LOGO_BOY = 200; +const LOGO_Y = 330; +const KELIME_BOY = 126; +const KELIME_OFSET = (46.7 / 44) * LOGO_BOY; +const CTA_Y = 690; +const CTA_H = 112; +const TIK = 138; + +// PixelMasonry: 4×4 hücrelik makro hücrelere 2–4 hücrelik tek kare, kenar bandında +const MASONRY = (() => { + const hucre = 22; + const makro = hucre * 4; + const bant = Math.min(W, H) * 0.3; + const out: { x: number; y: number; boy: number; op: number; vurgu: string | null }[] = []; + for (let j = 0; j * makro < H; j++) { + for (let i = 0; i * makro < W; i++) { + const cx = i * makro + makro / 2; + const cy = j * makro + makro / 2; + const kenar = Math.min(cx, W - cx, cy, H - cy); + const yog = clamp01(1 - kenar / bant); + if (hash(i, j, 7) > yog * 0.95) continue; + const n = 2 + Math.floor(hash(i, j, 8) * 3); + const boy = n * hucre; + const bos = makro - boy; + const r = hash(i, j, 9); + out.push({ + x: i * makro + hash(i, j, 10) * bos + boy / 2, + y: j * makro + hash(i, j, 11) * bos + boy / 2, + boy: boy * 0.86, + op: 0.05 + hash(i, j, 12) * 0.08, + vurgu: r > 0.93 ? R.turuncu : r > 0.87 ? R.mavi : null, + }); + } + } + return out; +})(); + +export const S7Kapanis: React.FC = () => { + const f = useCurrentFrame(); + const kok = degerDalgaNoktasi(); + const r = dalgaYaricapi(f / GECIS.g6, kok, 440); + + const ww = + olc("Kolay", { family: FONT.vurgu, size: KELIME_BOY, weight: 300, tracking: -0.05 }) + + olc("Tercih", { family: FONT.vurgu, size: KELIME_BOY, weight: 600, tracking: -0.05 }); + const sol = W / 2 - (KELIME_OFSET + ww) / 2; + const ctaW = + 2 * 0.52 * CTA_H + olc("kolaytercih.com", { family: FONT.govde, size: CTA_H * 0.31, weight: 600 }) + 16 + CTA_H * 0.31; + const cta = { x: W / 2, y: CTA_Y + CTA_H / 2 }; + + const yol: Yol = [ + { at: 96, x: 1860, y: 1050 }, + { at: 120, x: 1500, y: 900, duration: 24 }, + { at: TIK - 2, x: cta.x + ctaW * 0.28, y: cta.y + 8, duration: 16 }, + { at: TIK, x: cta.x + ctaW * 0.28, y: cta.y + 8, click: true, duration: 2 }, + ]; + const izler = isiIzi(yol, f, [96, 999]); + + // Logo "rota": her kare sıradakinin yerine süzülür, bir tur sonra evine döner + const rota = (i: number) => { + const konum = (k: number) => { + const q = LOGO_KARELER[(i + k) % 3]; + const ev = LOGO_KARELER[i]; + return { dx: q.x + q.boy / 2 - (ev.x + ev.boy / 2), dy: q.y + q.boy / 2 - (ev.y + ev.boy / 2) }; + }; + const adim = [aralik(f, 14 + i * 3, 30 + i * 3, ease.inOut), aralik(f, 34 + i * 3, 50 + i * 3, ease.inOut), aralik(f, 54 + i * 3, 70 + i * 3, ease.inOut)]; + const a = konum(0); + const b = konum(1); + const c = konum(2); + let dx = lerp(a.dx, b.dx, adim[0]); + let dy = lerp(a.dy, b.dy, adim[0]); + dx = lerp(dx, c.dx, adim[1]); + dy = lerp(dy, c.dy, adim[1]); + dx = lerp(dx, 0, adim[2]); + dy = lerp(dy, 0, adim[2]); + const ucus = Math.max(...adim.map((t) => Math.sin(Math.PI * t))); + return { dx, dy, olcek: 1 - 0.16 * ucus, opaklik: 1 - 0.4 * ucus }; + }; + const kay = aralik(f, 66, 90, ease.inOut); + const logoSol = lerp(W / 2 - LOGO_BOY / 2, sol, kay); + const hover = aralik(f, TIK - 10, TIK - 4); + const bas = f >= TIK && f < TIK + 8 ? 1 - Math.abs(f - TIK - 4) / 4 : 0; + const son = aralik(f, 168, 180); + + return ( + + + {MASONRY.map((m, i) => { + const d = Math.hypot(m.x - kok.x, m.y - kok.y); + const pop = ease.geriTep(clamp01((r - d - 0.3 * 440) / (0.4 * 440))); + if (pop <= 0) return null; + const h = isi(m.x, m.y, izler, 150); + const boy = m.boy * pop; + const taban = m.vurgu ?? R.beyaz; + return ( + + ); + })} + + + + + + + + Kolay + Tercih + + } + fontFamily={FONT.vurgu} + fontSize={KELIME_BOY} + fontWeight={300} + color={R.beyaz} + startTracking={0.55} + endTracking={-0.05} + /> + + + + + + + +
+ + kolaytercih.com + +
+
+ Bağımsız bir karar destek aracıdır. ÖSYM ya da YÖK ile bağlantılı değildir; yerleşme garantisi vermez. +
+ + + + + ); +}; diff --git a/video/src/turkiye.ts b/video/src/turkiye.ts new file mode 100644 index 0000000..be87e5c --- /dev/null +++ b/video/src/turkiye.ts @@ -0,0 +1,51 @@ +/** + * Dot-matrix Türkiye silüeti — src/features/pazarlama/components/turkey-pixel-map.tsx + * ile aynı ızgara (64×26 hücre, gerçek sınır poligonlarından rasterize). + */ +export const TURKIYE_IZGARA = [ + "..######..............##########................................", + "..#######...........##############..............................", + "..#######.........#################................#####........", + ".########...###########################...........########......", + ".######.....###############################################.....", + ".###........###############################################.....", + ".##.......##################################################....", + ".###########################################################....", + ".#############################################################..", + "..#############################################################.", + "...############################################################.", + "...##########################################################...", + "...##########################################################...", + "...###########################################################..", + "..############################################################..", + "..############################################################..", + "...###########################################################..", + "....##########################################################..", + "....##########################################################..", + ".....#########################################################..", + ".....###################################################..##....", + ".....############################################...............", + "......##########..######################...###..................", + "...........#####....#########....#####..........................", + "............###.......#####......####...........................", + ".................................###............................",]; + +/** Hero'daki vurgu şehirleri (sütun, satır) */ +export const SEHIRLER = [ + { name: "İstanbul", x: 8, y: 4 }, + { name: "Ankara", x: 23, y: 9 }, + { name: "İzmir", x: 4, y: 16 }, + { name: "Antalya", x: 16, y: 22 }, + { name: "Kayseri", x: 32, y: 14 }, + { name: "Trabzon", x: 46, y: 5 }, + { name: "Diyarbakır", x: 47, y: 18 }, + { name: "Van", x: 58, y: 15 },]; + +export const IZGARA_SUTUN = TURKIYE_IZGARA[0].length; +export const IZGARA_SATIR = TURKIYE_IZGARA.length; + +/** Dolu hücreler (x, y) */ +export const TURKIYE_HUCRELER: { x: number; y: number }[] = TURKIYE_IZGARA.flatMap( + (satir, y) => + Array.from(satir).flatMap((c, x) => (c === "#" ? [{ x, y }] : [])), +); diff --git a/video/src/veri.ts b/video/src/veri.ts new file mode 100644 index 0000000..4154575 --- /dev/null +++ b/video/src/veri.ts @@ -0,0 +1,88 @@ +/** + * Videodaki örnek öğrenci ve liste — data/yokatlas.db'den (2026 kılavuzu) + * alınmış gerçek kayıtlar, uydurma satır yok. + * + * Örnek: SAY 45.000 · Mühendislik · İstanbul, Ankara, İzmir · Devlet. + * + * Havuz sayıları sadeleştirilmiş bir sorgudan (taban sırası sıralamanın + * 0,7–3 katı arasındaki SAY programları); ürünün gerçek havuz kuralı daha + * ayrıntılıdır. Sorgu: + * son_kilavuz_yili=2026 AND tur='SAYISAL' + * AND COALESCE(sira2026,sira2025) BETWEEN 31500 AND 135000 + * → 1.443 · +isim LIKE '%Mühendisliği%' → 704 + * · +il IN (İSTANBUL, ANKARA, İZMİR) → 484 · +unitur='DEVLET' → 179 + */ + +export const ORNEK_SIRA = 45000; +export const ORNEK_SIRA_METNI = "45.000"; + +/** 2026 kılavuzundaki toplam program (lisans + önlisans) */ +export const TOPLAM_PROGRAM = 21493; + +export const HUNI = [ + { n: TOPLAM_PROGRAM, etiket: "Tüm programlar" }, + { n: 1443, etiket: "Sıralamanla açılan · Sayısal" }, + { n: 704, etiket: "Mühendislik & Teknoloji" }, + { n: 484, etiket: "İstanbul · Ankara · İzmir" }, + { n: 179, etiket: "Devlet üniversiteleri" }, +] as const; + +export type Dilim = "hayal" | "dengeli" | "garanti"; + +export interface Satir { + program: string; + universite: string; + il: string; + dilim: Dilim; + /** Taban başarı sırası 2021 → 2026 */ + sira: number[]; +} + +// Görünen dilim adı "Güvenli"; iç anahtar "garanti" (src/lib/risk.ts DILIM_ETIKET) +export const DILIM_ETIKET: Record = { + hayal: "Hayal", + dengeli: "Dengeli", + garanti: "Güvenli", +}; + +export const LISTE: Satir[] = [ + { program: "Elektrik-Elektronik Mühendisliği", universite: "Dokuz Eylül Üniversitesi", il: "İzmir", dilim: "hayal", sira: [45295, 41759, 36137, 40164, 39190, 38626] }, + { program: "Elektrik-Elektronik Mühendisliği", universite: "Ankara Yıldırım Beyazıt Üniversitesi", il: "Ankara", dilim: "hayal", sira: [53586, 47520, 40031, 45027, 42086, 40221] }, + { program: "Elektrik-Elektronik Mühendisliği", universite: "İstanbul Üniversitesi-Cerrahpaşa", il: "İstanbul", dilim: "hayal", sira: [58073, 54911, 49623, 52063, 47099, 42633] }, + { program: "Endüstri Mühendisliği", universite: "İstanbul Üniversitesi-Cerrahpaşa", il: "İstanbul", dilim: "dengeli", sira: [51496, 46297, 43436, 46957, 46877, 45099] }, + { program: "Bilgisayar Mühendisliği", universite: "Dokuz Eylül Üniversitesi", il: "İzmir", dilim: "dengeli", sira: [26098, 15816, 14246, 23271, 36435, 52071] }, + { program: "Endüstri Mühendisliği", universite: "Dokuz Eylül Üniversitesi", il: "İzmir", dilim: "dengeli", sira: [58559, 52933, 49684, 54049, 54049, 53587] }, + { program: "Elektrik-Elektronik Mühendisliği", universite: "İzmir Katip Çelebi Üniversitesi", il: "İzmir", dilim: "dengeli", sira: [71760, 64426, 59156, 68341, 60857, 53699] }, + { program: "Bilgisayar Mühendisliği", universite: "Ankara Yıldırım Beyazıt Üniversitesi", il: "Ankara", dilim: "dengeli", sira: [37831, 27534, 21602, 30797, 42108, 55158] }, + { program: "Elektrik-Elektronik Mühendisliği", universite: "İstanbul Medeniyet Üniversitesi", il: "İstanbul", dilim: "garanti", sira: [76034, 74632, 70514, 74989, 67761, 59417] }, + { program: "Endüstri Mühendisliği", universite: "İstanbul Medeniyet Üniversitesi", il: "İstanbul", dilim: "garanti", sira: [79628, 73208, 67215, 70600, 68739, 65169] }, + { program: "Bilgisayar Mühendisliği", universite: "İstanbul Medeniyet Üniversitesi", il: "İstanbul", dilim: "garanti", sira: [50828, 43992, 38865, 48481, 56616, 66119] }, + { program: "Bilgisayar Mühendisliği", universite: "İzmir Katip Çelebi Üniversitesi", il: "İzmir", dilim: "garanti", sira: [55869, 41281, 36708, 47803, 56980, 71833] }, +]; + +/** Danışman sahnesinde sorulan satır: DEÜ Bilgisayar Mühendisliği */ +export const SORULAN = LISTE[4]; + +/** 24'lük iskelet hedefi (huni-semasi.tsx: hayal 5 · dengeli 13 · güvenli 6) */ +export const ISKELET: { dilim: Dilim; adet: number }[] = [ + { dilim: "hayal", adet: 5 }, + { dilim: "dengeli", adet: 13 }, + { dilim: "garanti", adet: 6 }, +]; + +/** Açılıştaki "gürültü" tüneli: listedeki gerçek taban sıraları */ +export const GURULTU_SAYILARI = [ + ...new Set(LISTE.flatMap((s) => s.sira)), +].map((n) => n.toLocaleString("tr-TR")); + +/** İl program sayıları (2026 kılavuzu) — harita kuleleri */ +export const IL_PROGRAM: Record = { + İstanbul: 6442, + Ankara: 1831, + İzmir: 793, + Antalya: 427, + Trabzon: 317, + Kayseri: 235, + Van: 123, + Diyarbakır: 111, +}; diff --git a/video/src/yardimci.ts b/video/src/yardimci.ts new file mode 100644 index 0000000..a50cda7 --- /dev/null +++ b/video/src/yardimci.ts @@ -0,0 +1,53 @@ +import { Easing } from "remotion"; + +export const clamp01 = (t: number) => Math.max(0, Math.min(1, t)); +export const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +/** Sitedeki pixel-decor.tsx ile aynı deterministik sözde-rastgele */ +export const hash = (x: number, y: number, seed = 0) => { + const n = Math.sin(x * 127.1 + y * 311.7 + seed * 74.7) * 43758.5453; + return n - Math.floor(n); +}; + +export const ease = { + out: Easing.bezier(0.22, 1, 0.36, 1), + inOut: Easing.bezier(0.65, 0, 0.35, 1), + in: Easing.bezier(0.64, 0, 0.78, 0), + // Sitenin yay hissi: bounce 0 (Apple damping 1) + yay: Easing.bezier(0.2, 0.8, 0.2, 1), + geriTep: Easing.bezier(0.34, 1.56, 0.64, 1), +}; + +/** f, [a, b] aralığında 0→1 ilerleme (kırpılmış, isteğe bağlı eğri) */ +export const aralik = ( + f: number, + a: number, + b: number, + egri: (t: number) => number = (t) => t, +) => egri(clamp01((f - a) / (b - a))); + +/** İki CSS rengi arasında oklab karışımı (oklch token'larla çalışır) */ +export const karis = (a: string, b: string, t: number) => { + const p = Math.round(clamp01(t) * 1000) / 10; + if (p <= 0) return a; + if (p >= 100) return b; + return `color-mix(in oklab, ${b} ${p}%, ${a})`; +}; + +/** Sayıyı Türkçe binlik ayraçla yazar: 45000 → "45.000" */ +export const tr = (n: number) => Math.round(n).toLocaleString("tr-TR"); + +/** Metin ölçümü: kinetik yerleşimler için gerçek yazı tipiyle genişlik */ +export const olc = ( + metin: string, + font: { family: string; size: number; weight?: number; italic?: boolean; tracking?: number }, +) => { + if (typeof document === "undefined") return metin.length * font.size * 0.55; + const ctx = document.createElement("canvas").getContext("2d"); + if (!ctx) return metin.length * font.size * 0.55; + ctx.font = `${font.italic ? "italic " : ""}${font.weight ?? 400} ${font.size}px "${font.family}"`; + return ( + ctx.measureText(metin).width + + (font.tracking ?? 0) * font.size * Array.from(metin).length + ); +}; diff --git a/video/src/zaman.ts b/video/src/zaman.ts new file mode 100644 index 0000000..52d4df0 --- /dev/null +++ b/video/src/zaman.ts @@ -0,0 +1,41 @@ +/** + * Zaman çizelgesi (30 fps). Sahneler TransitionSeries'te art arda; geçişler + * iki sahneyi üst üste bindirir, yani toplam süre = sahneler − geçişler. + * + * Storyboard omurgası: her geçiş ya bir **piksel** (dalga, tek piksele + * çöküş, havuzdan seçilen 24 piksel) ya da **imlecin tıkladığı nokta** + * (o noktanın içinden itme / o noktadan yayılan piksel dalgası) üstüne + * kurulu. Geçişin başladığı kare, imlecin tıkladığı karedir. + */ + +export const SAHNE = { + gurultu: 150, + harita: 250, + siralama: 126, + havuz: 420, + danisman: 200, + deger: 170, + kapanis: 180, +} as const; + +export const GECIS = { + /** gürültü → harita: tek piksel patlar, koyudan açığa piksel dalgası */ + g1: 30, + /** harita → sıralama: imleç logonun turuncu karesine tıklar, dalga oradan */ + g2: 26, + /** sıralama → havuz: CTA'ya tıklanır, sahne butonun içinden geçer */ + g3: 22, + /** havuz → danışman: listedeki satıra tıklanır, satırın içinden geçilir */ + g4: 22, + /** danışman → değer: gönder düğmesinden piksel dalgası */ + g5: 26, + /** değer → kapanış: son gösterge karesinden koyu piksel dalgası */ + g6: 28, +} as const; + +/** Bir sahnenin, kendisinden sonraki geçişin başladığı yerel karesi */ +export const gecisBasi = (sahne: number, gecis: number) => sahne - gecis; + +export const TOPLAM = + Object.values(SAHNE).reduce((a, b) => a + b, 0) - + Object.values(GECIS).reduce((a, b) => a + b, 0); diff --git a/video/tsconfig.json b/video/tsconfig.json new file mode 100644 index 0000000..0b576b9 --- /dev/null +++ b/video/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noEmit": true, + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noUnusedLocals": true, + "baseUrl": ".", + "paths": { "@/*": ["./src/*"] } + }, + "include": ["src", "remotion.config.ts"] +}