diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 83c3322..154840e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -131,180 +131,7 @@ importers:
specifier: 5.9.3
version: 5.9.3
- videos/cuabench-launch:
- dependencies:
- '@launchpad/assets':
- specifier: workspace:*
- version: link:../../packages/assets
- '@launchpad/shared':
- specifier: workspace:*
- version: link:../../packages/shared
- '@remotion/bundler':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/cli':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/google-fonts':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/paths':
- specifier: 4.0.407
- version: 4.0.407
- '@remotion/player':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/shapes':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/tailwind-v4':
- specifier: 4.0.407
- version: 4.0.407(@remotion/bundler@4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(webpack@5.96.1)
- clsx:
- specifier: 2.1.1
- version: 2.1.1
- next:
- specifier: 16.0.10
- version: 16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- react:
- specifier: 19.2.3
- version: 19.2.3
- react-dom:
- specifier: 19.2.3
- version: 19.2.3(react@19.2.3)
- remotion:
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- tailwind-merge:
- specifier: 3.0.1
- version: 3.0.1
- zod:
- specifier: 3.22.3
- version: 3.22.3
- devDependencies:
- '@remotion/eslint-plugin':
- specifier: 4.0.407
- version: 4.0.407(eslint@9.19.0(jiti@2.6.1))(typescript@5.9.3)
- '@tailwindcss/postcss':
- specifier: 4.1.1
- version: 4.1.1
- '@types/node':
- specifier: 20.12.14
- version: 20.12.14
- '@types/react':
- specifier: 19.2.3
- version: 19.2.3
- '@types/react-dom':
- specifier: 19.2.3
- version: 19.2.3(@types/react@19.2.3)
- autoprefixer:
- specifier: 10.4.20
- version: 10.4.20(postcss@8.4.47)
- eslint:
- specifier: 9.19.0
- version: 9.19.0(jiti@2.6.1)
- postcss:
- specifier: 8.4.47
- version: 8.4.47
- prettier:
- specifier: 3.6.0
- version: 3.6.0
- tailwindcss:
- specifier: 4.0.3
- version: 4.0.3
- typescript:
- specifier: 5.9.3
- version: 5.9.3
-
- videos/cuabenchnew:
- dependencies:
- '@launchpad/assets':
- specifier: workspace:*
- version: link:../../packages/assets
- '@launchpad/shared':
- specifier: workspace:*
- version: link:../../packages/shared
- '@remotion/bundler':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/cli':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/google-fonts':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/paths':
- specifier: 4.0.407
- version: 4.0.407
- '@remotion/player':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/shapes':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- '@remotion/tailwind-v4':
- specifier: 4.0.407
- version: 4.0.407(@remotion/bundler@4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(webpack@5.96.1)
- '@remotion/transitions':
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- clsx:
- specifier: 2.1.1
- version: 2.1.1
- next:
- specifier: 16.0.10
- version: 16.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- react:
- specifier: 19.2.3
- version: 19.2.3
- react-dom:
- specifier: 19.2.3
- version: 19.2.3(react@19.2.3)
- remotion:
- specifier: 4.0.407
- version: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- tailwind-merge:
- specifier: 3.0.1
- version: 3.0.1
- zod:
- specifier: 3.22.3
- version: 3.22.3
- devDependencies:
- '@remotion/eslint-plugin':
- specifier: 4.0.407
- version: 4.0.407(eslint@9.19.0(jiti@2.6.1))(typescript@5.9.3)
- '@tailwindcss/postcss':
- specifier: 4.1.1
- version: 4.1.1
- '@types/node':
- specifier: 20.12.14
- version: 20.12.14
- '@types/react':
- specifier: 19.2.7
- version: 19.2.7
- '@types/react-dom':
- specifier: 19.2.3
- version: 19.2.3(@types/react@19.2.7)
- autoprefixer:
- specifier: 10.4.20
- version: 10.4.20(postcss@8.4.47)
- eslint:
- specifier: 9.19.0
- version: 9.19.0(jiti@2.6.1)
- postcss:
- specifier: 8.4.47
- version: 8.4.47
- prettier:
- specifier: 3.6.0
- version: 3.6.0
- tailwindcss:
- specifier: 4.0.3
- version: 4.0.3
- typescript:
- specifier: 5.9.3
- version: 5.9.3
-
- videos/skills-announcement:
+ videos/laudos-launch:
dependencies:
'@launchpad/assets':
specifier: workspace:*
@@ -1356,12 +1183,6 @@ packages:
peerDependencies:
'@remotion/bundler': 4.0.407
- '@remotion/transitions@4.0.407':
- resolution: {integrity: sha512-XnRoBM/IEjQ3BHjTlJS/b3o8uxq9CH/s8qAC9n1PWkhoWx4I1/JaUqFbkunc6LQIAPC7286up2Edtx2zAOP1rA==}
- peerDependencies:
- react: '>=16.8.0'
- react-dom: '>=16.8.0'
-
'@remotion/web-renderer@4.0.407':
resolution: {integrity: sha512-wO3IE+opVoT6mdcrZUzvD9urdglOr91qXO4uJqbTgzcVzRCpRp2iOPKQCjPmI/RxWQ6+fWN4VjZJP1E0iQIgGw==}
peerDependencies:
@@ -1481,9 +1302,6 @@ packages:
peerDependencies:
'@types/react': ^19.2.0
- '@types/react@19.2.3':
- resolution: {integrity: sha512-k5dJVszUiNr1DSe8Cs+knKR6IrqhqdhpUwzqhkS8ecQTSf3THNtbfIp/umqHMpX2bv+9dkx3fwDv/86LcSfvSg==}
-
'@types/react@19.2.7':
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
@@ -3508,14 +3326,14 @@ snapshots:
'@remotion/media-parser': 4.0.407
'@remotion/studio': 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@remotion/studio-shared': 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- css-loader: 5.2.7(webpack@5.96.1)
+ css-loader: 5.2.7(webpack@5.96.1(esbuild@0.25.0))
esbuild: 0.25.0
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
react-refresh: 0.9.0
remotion: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
source-map: 0.7.3
- style-loader: 4.0.0(webpack@5.96.1)
+ style-loader: 4.0.0(webpack@5.96.1(esbuild@0.25.0))
webpack: 5.96.1(esbuild@0.25.0)
transitivePeerDependencies:
- '@swc/core'
@@ -3692,25 +3510,17 @@ snapshots:
dependencies:
'@remotion/bundler': 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@tailwindcss/postcss': 4.1.1
- css-loader: 5.2.7(webpack@5.96.1)
+ css-loader: 5.2.7(webpack@5.96.1(esbuild@0.25.0))
postcss: 8.5.1
postcss-loader: 8.1.1(postcss@8.5.1)(typescript@5.9.3)(webpack@5.96.1)
postcss-preset-env: 10.1.3(postcss@8.5.1)
- style-loader: 4.0.0(webpack@5.96.1)
+ style-loader: 4.0.0(webpack@5.96.1(esbuild@0.25.0))
tailwindcss: 4.1.1
transitivePeerDependencies:
- '@rspack/core'
- typescript
- webpack
- '@remotion/transitions@4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
- dependencies:
- '@remotion/paths': 4.0.407
- '@remotion/shapes': 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- react: 19.2.3
- react-dom: 19.2.3(react@19.2.3)
- remotion: 4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
-
'@remotion/web-renderer@4.0.407(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@remotion/licensing': 4.0.407
@@ -3821,18 +3631,10 @@ snapshots:
dependencies:
undici-types: 5.26.5
- '@types/react-dom@19.2.3(@types/react@19.2.3)':
- dependencies:
- '@types/react': 19.2.3
-
'@types/react-dom@19.2.3(@types/react@19.2.7)':
dependencies:
'@types/react': 19.2.7
- '@types/react@19.2.3':
- dependencies:
- csstype: 3.2.3
-
'@types/react@19.2.7':
dependencies:
csstype: 3.2.3
@@ -4105,7 +3907,7 @@ snapshots:
postcss-selector-parser: 7.1.1
postcss-value-parser: 4.2.0
- css-loader@5.2.7(webpack@5.96.1):
+ css-loader@5.2.7(webpack@5.96.1(esbuild@0.25.0)):
dependencies:
icss-utils: 5.1.0(postcss@8.4.47)
loader-utils: 2.0.4
@@ -5102,7 +4904,7 @@ snapshots:
strip-json-comments@3.1.1: {}
- style-loader@4.0.0(webpack@5.96.1):
+ style-loader@4.0.0(webpack@5.96.1(esbuild@0.25.0)):
dependencies:
webpack: 5.96.1(esbuild@0.25.0)
@@ -5127,7 +4929,7 @@ snapshots:
tapable@2.3.0: {}
- terser-webpack-plugin@5.3.16(esbuild@0.25.0)(webpack@5.96.1(esbuild@0.25.0)):
+ terser-webpack-plugin@5.3.16(esbuild@0.25.0)(webpack@5.96.1):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
@@ -5249,7 +5051,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.3.0
- terser-webpack-plugin: 5.3.16(esbuild@0.25.0)(webpack@5.96.1(esbuild@0.25.0))
+ terser-webpack-plugin: 5.3.16(esbuild@0.25.0)(webpack@5.96.1)
watchpack: 2.5.1
webpack-sources: 3.3.3
transitivePeerDependencies:
diff --git a/videos/laudos-launch/next.config.js b/videos/laudos-launch/next.config.js
new file mode 100644
index 0000000..2b227a8
--- /dev/null
+++ b/videos/laudos-launch/next.config.js
@@ -0,0 +1,6 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ transpilePackages: ["@launchpad/shared", "@launchpad/assets"],
+};
+
+module.exports = nextConfig;
diff --git a/videos/laudos-launch/package.json b/videos/laudos-launch/package.json
new file mode 100644
index 0000000..333891e
--- /dev/null
+++ b/videos/laudos-launch/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "@launchpad/LaudosLaunch",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint .",
+ "remotion": "remotion studio",
+ "render": "remotion render LaudosLaunchFull",
+ "render:preview": "remotion render LaudosLaunchFull --scale=0.5"
+ },
+ "dependencies": {
+ "@launchpad/shared": "workspace:*",
+ "@launchpad/assets": "workspace:*",
+ "@remotion/bundler": "4.0.407",
+ "@remotion/cli": "4.0.407",
+ "@remotion/google-fonts": "4.0.407",
+ "@remotion/paths": "4.0.407",
+ "@remotion/player": "4.0.407",
+ "@remotion/shapes": "4.0.407",
+ "@remotion/tailwind-v4": "4.0.407",
+ "clsx": "2.1.1",
+ "next": "16.0.10",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
+ "remotion": "4.0.407",
+ "tailwind-merge": "3.0.1",
+ "zod": "3.22.3"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "4.1.1",
+ "@remotion/eslint-plugin": "4.0.407",
+ "@types/node": "20.12.14",
+ "@types/react": "19.2.7",
+ "@types/react-dom": "19.2.3",
+ "autoprefixer": "10.4.20",
+ "eslint": "9.19.0",
+ "postcss": "8.4.47",
+ "prettier": "3.6.0",
+ "tailwindcss": "4.0.3",
+ "typescript": "5.9.3"
+ }
+}
diff --git a/videos/laudos-launch/postcss.config.mjs b/videos/laudos-launch/postcss.config.mjs
new file mode 100644
index 0000000..d5c96e4
--- /dev/null
+++ b/videos/laudos-launch/postcss.config.mjs
@@ -0,0 +1,8 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ autoprefixer: {},
+ },
+};
+
+export default config;
diff --git a/videos/laudos-launch/public/.gitkeep b/videos/laudos-launch/public/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/videos/laudos-launch/public/ascending-ticks.wav b/videos/laudos-launch/public/ascending-ticks.wav
new file mode 100644
index 0000000..ce99e88
Binary files /dev/null and b/videos/laudos-launch/public/ascending-ticks.wav differ
diff --git a/videos/laudos-launch/public/background-music.wav b/videos/laudos-launch/public/background-music.wav
new file mode 100644
index 0000000..8f68e18
Binary files /dev/null and b/videos/laudos-launch/public/background-music.wav differ
diff --git a/videos/laudos-launch/public/deep-whoosh.wav b/videos/laudos-launch/public/deep-whoosh.wav
new file mode 100644
index 0000000..f6932a4
Binary files /dev/null and b/videos/laudos-launch/public/deep-whoosh.wav differ
diff --git a/videos/laudos-launch/public/descending-ticks.wav b/videos/laudos-launch/public/descending-ticks.wav
new file mode 100644
index 0000000..92dc2af
Binary files /dev/null and b/videos/laudos-launch/public/descending-ticks.wav differ
diff --git a/videos/laudos-launch/public/pop.wav b/videos/laudos-launch/public/pop.wav
new file mode 100644
index 0000000..9f8fbe5
Binary files /dev/null and b/videos/laudos-launch/public/pop.wav differ
diff --git a/videos/laudos-launch/public/selection-tap.wav b/videos/laudos-launch/public/selection-tap.wav
new file mode 100644
index 0000000..9a4d6f3
Binary files /dev/null and b/videos/laudos-launch/public/selection-tap.wav differ
diff --git a/videos/laudos-launch/public/typing.wav b/videos/laudos-launch/public/typing.wav
new file mode 100644
index 0000000..12c3fe8
Binary files /dev/null and b/videos/laudos-launch/public/typing.wav differ
diff --git a/videos/laudos-launch/public/unscramble.wav b/videos/laudos-launch/public/unscramble.wav
new file mode 100644
index 0000000..14c075b
Binary files /dev/null and b/videos/laudos-launch/public/unscramble.wav differ
diff --git a/videos/laudos-launch/public/whoosh.wav b/videos/laudos-launch/public/whoosh.wav
new file mode 100644
index 0000000..a927647
Binary files /dev/null and b/videos/laudos-launch/public/whoosh.wav differ
diff --git a/videos/laudos-launch/remotion.config.ts b/videos/laudos-launch/remotion.config.ts
new file mode 100644
index 0000000..07a9aa5
--- /dev/null
+++ b/videos/laudos-launch/remotion.config.ts
@@ -0,0 +1,5 @@
+import { Config } from "@remotion/cli/config";
+import { webpackOverride } from "./src/remotion/webpack-override.mjs";
+
+Config.setVideoImageFormat("jpeg");
+Config.overrideWebpackConfig(webpackOverride);
diff --git a/videos/laudos-launch/src/app/layout.tsx b/videos/laudos-launch/src/app/layout.tsx
new file mode 100644
index 0000000..29e92ac
--- /dev/null
+++ b/videos/laudos-launch/src/app/layout.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from "next";
+import "../styles/global.css";
+
+export const metadata: Metadata = {
+ title: "Laudos.AI Launch Video Preview",
+ description: "Preview and render Remotion videos",
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/videos/laudos-launch/src/app/page.tsx b/videos/laudos-launch/src/app/page.tsx
new file mode 100644
index 0000000..bab85e1
--- /dev/null
+++ b/videos/laudos-launch/src/app/page.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import { Player } from "@remotion/player";
+import { FullVideo, FULL_VIDEO_DURATION } from "../remotion/scenes/FullVideo";
+import { VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_FPS } from "../../types/constants";
+
+export default function Home() {
+ return (
+
+ Laudos.AI Launch Video
+
+
+
+
+
Use Remotion Studio for full editing: pnpm remotion
+
Render video: pnpm render
+
+
+ );
+}
diff --git a/videos/laudos-launch/src/remotion/Root.tsx b/videos/laudos-launch/src/remotion/Root.tsx
new file mode 100644
index 0000000..8653149
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/Root.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+import { Composition } from "remotion";
+import { VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_FPS } from "../../types/constants";
+import { IntroScene, INTRO_SCENE_DURATION } from "./scenes/IntroScene";
+import { ProblemScene, PROBLEM_SCENE_DURATION } from "./scenes/ProblemScene";
+import { FeaturesScene, FEATURES_SCENE_DURATION } from "./scenes/FeaturesScene";
+import { DemoScene, DEMO_SCENE_DURATION } from "./scenes/DemoScene";
+import { OutroScene, OUTRO_SCENE_DURATION } from "./scenes/OutroScene";
+import { FullVideo, FULL_VIDEO_DURATION } from "./scenes/FullVideo";
+
+export const RemotionRoot: React.FC = () => {
+ return (
+ <>
+ {/* Full video composition */}
+
+
+ {/* Individual scene compositions for preview */}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/index.ts b/videos/laudos-launch/src/remotion/index.ts
new file mode 100644
index 0000000..a4e987f
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/index.ts
@@ -0,0 +1,5 @@
+import { registerRoot } from "remotion";
+import { RemotionRoot } from "./Root";
+import "../styles/global.css";
+
+registerRoot(RemotionRoot);
diff --git a/videos/laudos-launch/src/remotion/scenes/DemoScene.tsx b/videos/laudos-launch/src/remotion/scenes/DemoScene.tsx
new file mode 100644
index 0000000..6776691
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/DemoScene.tsx
@@ -0,0 +1,396 @@
+import React from "react";
+import {
+ AbsoluteFill,
+ useCurrentFrame,
+ interpolate,
+ Easing,
+ Sequence,
+ Audio,
+ staticFile,
+} from "remotion";
+import { loadFont, fontFamily } from "@remotion/google-fonts/Inter";
+
+loadFont("normal", {
+ subsets: ["latin"],
+ weights: ["400", "500", "600", "700"],
+});
+
+const COLORS = {
+ bg: "#f8f8f8",
+ bgWhite: "#FFFFFF",
+ text: "#171717",
+ textMuted: "#737373",
+ textLight: "#a3a3a3",
+ accent: "#171717",
+ border: "#e5e5e5",
+ success: "#10B981",
+ recording: "#ef4444",
+};
+
+// Timing - Fluxo correto de ditado
+const UI_APPEAR = 0;
+const CLICK_MIC = 30; // Clica no Ditar
+const RECORDING_START = 45; // Começa a gravar
+const VOICE_WAVE_1 = 60; // Ondas de voz
+const VOICE_WAVE_2 = 90;
+const VOICE_WAVE_3 = 120;
+const TRANSCRIPTION_START = 80; // IA começa a transcrever
+const TRANSCRIPTION_END = 200;
+const RECORDING_STOP = 210;
+const SAVE_HIGHLIGHT = 240;
+
+const RECENT_LAUDOS = [
+ { title: "Laudo TC Crânio - 24/01/2026", type: "TC de Crânio", category: "Radiologia", time: "há 2 horas" },
+ { title: "Laudo ANGIOTOMOGRAFIA...", type: "Angiotomografia", category: "Radiologia", time: "há 3 horas" },
+ { title: "Laudo por Voz - 24/01/2026", type: "", category: "Voz", time: "há 4 horas" },
+];
+
+// O que o médico FALA e a IA transcreve
+const SPOKEN_TEXT = `TÉCNICA:
+Tomografia computadorizada de tórax sem contraste.
+
+ACHADOS:
+Parênquima pulmonar com atenuação preservada.
+Ausência de nódulos, massas ou consolidações.
+Estruturas mediastinais sem alterações.
+Ausência de derrame pleural.
+
+IMPRESSÃO:
+Exame dentro dos limites da normalidade.`;
+
+export const DEMO_SCENE_DURATION = 270;
+
+export const DemoScene: React.FC = () => {
+ const frame = useCurrentFrame();
+
+ // UI appear
+ const uiOpacity = interpolate(frame, [UI_APPEAR, UI_APPEAR + 20], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ });
+
+ // Click animation on mic
+ const clickScale = frame >= CLICK_MIC && frame < CLICK_MIC + 10
+ ? interpolate(frame, [CLICK_MIC, CLICK_MIC + 5, CLICK_MIC + 10], [1, 0.9, 1])
+ : 1;
+
+ // Recording state
+ const isRecording = frame >= RECORDING_START && frame < RECORDING_STOP;
+ const recordingPulse = isRecording ? Math.sin(frame * 0.15) * 0.5 + 0.5 : 0;
+
+ // Voice wave animation
+ const getWaveHeight = (waveFrame: number, baseHeight: number) => {
+ if (frame < waveFrame || frame >= RECORDING_STOP) return baseHeight;
+ const t = (frame - waveFrame) * 0.2;
+ return baseHeight + Math.sin(t) * 15 + Math.random() * 5;
+ };
+
+ // Transcription progress
+ const transcriptionProgress = frame >= TRANSCRIPTION_START
+ ? interpolate(frame, [TRANSCRIPTION_START, TRANSCRIPTION_END], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+ const charsToShow = Math.floor(transcriptionProgress * SPOKEN_TEXT.length);
+
+ // Show transcribed text or empty state
+ const showTranscription = frame >= TRANSCRIPTION_START;
+
+ // Save button highlight
+ const saveHighlight = frame >= SAVE_HIGHLIGHT
+ ? interpolate(frame, [SAVE_HIGHLIGHT, SAVE_HIGHLIGHT + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+
+ // Cursor blink
+ const cursorVisible = frame % 16 < 8 && frame >= TRANSCRIPTION_START && frame < TRANSCRIPTION_END;
+
+ return (
+
+ {/* Main UI */}
+
+ {/* LEFT SIDEBAR */}
+
+ {/* Header */}
+
+
+ Sugerir melhoria
+
+
+ {/* Search */}
+
+
+
+
Buscar laudos...
+
+ ⌘
+ K
+
+
+
+
+ {/* Laudos Recentes */}
+
+ Laudos Recentes
+ Ver todos
+
+
+
+ {RECENT_LAUDOS.map((laudo, idx) => (
+
+
+
+
{laudo.title}
+ {laudo.type &&
{laudo.type}
}
+
+ {laudo.category}
+
+
{laudo.time}
+
+
+ ))}
+
+
+
+ {/* MAIN CONTENT */}
+
+ {/* TOP BAR */}
+
+
Novo Documento
+
+
+
+ {frame < SAVE_HIGHLIGHT ? "Editando..." : "Salvo"}
+
+
0 ? COLORS.success : COLORS.accent,
+ borderRadius: 8,
+ }}
+ >
+
+
Salvar
+
+
+
+
+ {/* TOOLBAR */}
+
+ {["B", "I", "U"].map((tool) => (
+
+ {tool}
+
+ ))}
+
+ {["H1", "H2"].map((tool) => (
+
{tool}
+ ))}
+
+
+ {/* SECONDARY TOOLBAR */}
+
+
+
+ Crit
+ Copiar
+ Atalhos
+
+
+
+ {/* EDITOR CONTENT */}
+
+
+ {!showTranscription ? (
+ /* ESTADO INICIAL - Botão Ditar */
+
+ {/* Mic Button - Clicável */}
+
+
+
+ {isRecording ? "Gravando..." : "Ditar laudo"}
+
+
+ {isRecording ? "Fale o exame e achados" : "Clique para iniciar"}
+
+
+ {/* Voice Waves - Aparecem durante gravação */}
+ {isRecording && (
+
+ {[0, 1, 2, 3, 4, 5, 6, 7].map((i) => (
+
+ ))}
+
+ )}
+
+ {/* Template selector */}
+
+
+
+
Selecionar modelo (opcional)
+
+
+
+
+ ) : (
+ /* ESTADO TRANSCREVENDO - Mostra o laudo sendo escrito pela IA */
+
+ {/* Recording indicator */}
+ {isRecording && (
+
+
+
Transcrevendo sua voz...
+ {/* Voice waves inline */}
+
+ {[0, 1, 2, 3, 4].map((i) => (
+
+ ))}
+
+
+ )}
+
+ {/* Transcribed text */}
+
+ {SPOKEN_TEXT.slice(0, charsToShow)}
+ {cursorVisible && (
+
+ )}
+
+
+ )}
+
+
+ {/* BOTTOM BAR */}
+
+
+ {[
+ { key: "Espaço", label: "Ditar" },
+ { key: "S", label: "Salvar" },
+ { key: "K", label: "Buscar" },
+ ].map((s) => (
+
+ {s.key}
+ {s.label}
+
+ ))}
+
+
+ Formato: Arial 12
+
+
+
+
+
+ {/* Audio */}
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/scenes/FeaturesScene.tsx b/videos/laudos-launch/src/remotion/scenes/FeaturesScene.tsx
new file mode 100644
index 0000000..d899e7d
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/FeaturesScene.tsx
@@ -0,0 +1,337 @@
+import React from "react";
+import {
+ AbsoluteFill,
+ useCurrentFrame,
+ interpolate,
+ Easing,
+ Sequence,
+ Audio,
+ staticFile,
+} from "remotion";
+import { loadFont, fontFamily } from "@remotion/google-fonts/Inter";
+
+loadFont("normal", {
+ subsets: ["latin"],
+ weights: ["400", "500", "600", "700", "800"],
+});
+
+// Clean white design system
+const COLORS = {
+ bg: "#FFFFFF",
+ bgSubtle: "#F8FAFC",
+ text: "#0F172A",
+ textMuted: "#64748B",
+ accent: "#0066FF",
+ accentLight: "#E0EDFF",
+ border: "#E2E8F0",
+};
+
+// Title timing
+const LOGO_APPEAR = 0;
+const TAGLINE_START = 25;
+
+// Feature cards data - showcasing unique LaudAI features
+const FEATURES = [
+ {
+ icon: "brain",
+ title: "LaudAI",
+ subtitle: "Nossa IA treinada",
+ description: "IA proprietária treinada especificamente para radiologia brasileira. Entende contexto clínico e gera laudos com precisão.",
+ color: "#0066FF",
+ },
+ {
+ icon: "agents",
+ title: "Agentic AI",
+ subtitle: "Multi-agent architecture",
+ description: "Múltiplos agentes de IA trabalhando juntos: validação, formatação, detecção de achados críticos em tempo real.",
+ color: "#8B5CF6",
+ },
+ {
+ icon: "voice",
+ title: "Ditado Inteligente",
+ subtitle: "Voz para laudo estruturado",
+ description: "Fale naturalmente em português. A IA transcreve, estrutura e aplica seu estilo pessoal automaticamente.",
+ color: "#10B981",
+ },
+ {
+ icon: "crit",
+ title: "Crit Enterprise",
+ subtitle: "Achados críticos",
+ description: "Detecção automática de achados urgentes. Alertas em tempo real para pneumotórax, AVC, embolia e mais.",
+ color: "#EF4444",
+ },
+];
+
+// Feature cards timing
+const CARDS_START = 60;
+const CARD_STAGGER = 15;
+
+// Feature icons
+const getIcon = (icon: string, color: string) => {
+ const iconSize = 32;
+ const icons: Record = {
+ brain: (
+
+ ),
+ agents: (
+
+ ),
+ voice: (
+
+ ),
+ crit: (
+
+ ),
+ };
+ return icons[icon];
+};
+
+export const FEATURES_SCENE_DURATION = 210; // 7 seconds
+
+export const FeaturesScene: React.FC = () => {
+ const frame = useCurrentFrame();
+
+ // Logo appear
+ const logoOpacity = interpolate(frame, [LOGO_APPEAR, LOGO_APPEAR + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ });
+ const logoScale = interpolate(frame, [LOGO_APPEAR, LOGO_APPEAR + 15], [0.9, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ });
+
+ // Tagline appear
+ const taglineOpacity = frame >= TAGLINE_START
+ ? interpolate(frame, [TAGLINE_START, TAGLINE_START + 12], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+
+ return (
+
+ {/* Logo and tagline */}
+
+ {/* Logo "laudos.ai" */}
+
+ laudos.ai
+
+
+ {/* Tagline */}
+
+ Assistente de laudos com IA agêntica
+
+
+
+ {/* Feature cards grid */}
+
+ {FEATURES.map((feature, idx) => {
+ const cardAppearFrame = CARDS_START + idx * CARD_STAGGER;
+ const isVisible = frame >= cardAppearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - cardAppearFrame;
+ const slideProgress = interpolate(
+ framesSinceAppear,
+ [0, 18],
+ [0, 1],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ const translateY = interpolate(slideProgress, [0, 1], [50, 0]);
+ const opacity = interpolate(framesSinceAppear, [0, 12], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+
+ return (
+
+ {/* Icon and title row */}
+
+ {/* Icon */}
+
+ {getIcon(feature.icon, feature.color)}
+
+
+ {/* Title and subtitle */}
+
+
+ {feature.title}
+
+
+ {feature.subtitle}
+
+
+
+
+ {/* Description */}
+
+ {feature.description}
+
+
+ );
+ })}
+
+
+ {/* Sound effects */}
+
+
+
+
+ {FEATURES.map((_, idx) => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/scenes/FullVideo.tsx b/videos/laudos-launch/src/remotion/scenes/FullVideo.tsx
new file mode 100644
index 0000000..2f54fca
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/FullVideo.tsx
@@ -0,0 +1,60 @@
+import React from "react";
+import { Sequence, Audio, staticFile } from "remotion";
+import { IntroScene, INTRO_SCENE_DURATION } from "./IntroScene";
+import { ProblemScene, PROBLEM_SCENE_DURATION } from "./ProblemScene";
+import { FeaturesScene, FEATURES_SCENE_DURATION } from "./FeaturesScene";
+import { DemoScene, DEMO_SCENE_DURATION } from "./DemoScene";
+import { OutroScene, OUTRO_SCENE_DURATION } from "./OutroScene";
+
+// Scene timing
+const INTRO_START = 0;
+const PROBLEM_START = INTRO_START + INTRO_SCENE_DURATION;
+const FEATURES_START = PROBLEM_START + PROBLEM_SCENE_DURATION;
+const DEMO_START = FEATURES_START + FEATURES_SCENE_DURATION;
+const OUTRO_START = DEMO_START + DEMO_SCENE_DURATION;
+
+// Total duration
+export const FULL_VIDEO_DURATION =
+ INTRO_SCENE_DURATION +
+ PROBLEM_SCENE_DURATION +
+ FEATURES_SCENE_DURATION +
+ DEMO_SCENE_DURATION +
+ OUTRO_SCENE_DURATION;
+
+export const FullVideo: React.FC = () => {
+ return (
+ <>
+ {/* Background music */}
+
+
+ {/* Scene 1: Intro - Problem statement */}
+
+
+
+
+ {/* Scene 2: Problem - Legacy software and lack of standardization */}
+
+
+
+
+ {/* Scene 3: Features - Key product features */}
+
+
+
+
+ {/* Scene 4: Demo - Product in action */}
+
+
+
+
+ {/* Scene 5: Outro - CTA */}
+
+
+
+ >
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/scenes/IntroScene.tsx b/videos/laudos-launch/src/remotion/scenes/IntroScene.tsx
new file mode 100644
index 0000000..dfd62ed
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/IntroScene.tsx
@@ -0,0 +1,262 @@
+import React from "react";
+import {
+ AbsoluteFill,
+ useCurrentFrame,
+ interpolate,
+ Easing,
+ Sequence,
+ Audio,
+ staticFile,
+} from "remotion";
+import { loadFont, fontFamily } from "@remotion/google-fonts/Inter";
+
+loadFont("normal", {
+ subsets: ["latin"],
+ weights: ["400", "500", "600", "700", "800"],
+});
+
+// Clean white design system
+const COLORS = {
+ bg: "#FFFFFF",
+ bgSubtle: "#F8FAFC",
+ text: "#0F172A",
+ textMuted: "#64748B",
+ accent: "#0066FF",
+ accentLight: "#E0EDFF",
+};
+
+// Word-by-word intro animation - PT-BR
+const LINE1_WORDS = [
+ { word: "Radiologistas", appearFrame: 15 },
+ { word: "gastam", appearFrame: 22 },
+ { word: "horas", appearFrame: 29 },
+ { word: "escrevendo", appearFrame: 36 },
+ { word: "laudos.", appearFrame: 43 },
+];
+
+const SLIDE_DURATION = 10;
+
+// Second line timing
+const LINE2_START = 75;
+const LINE2_WORDS = [
+ { word: "Todos.", appearFrame: LINE2_START },
+ { word: "Os.", appearFrame: LINE2_START + 12 },
+ { word: "Dias.", appearFrame: LINE2_START + 24 },
+];
+
+// Transition timing
+const FADE_OUT_START = 130;
+const FADE_OUT_DURATION = 20;
+
+// Third message timing
+const LINE3_START = 160;
+
+export const INTRO_SCENE_DURATION = 210; // 7 seconds at 30fps
+
+export const IntroScene: React.FC = () => {
+ const frame = useCurrentFrame();
+
+ // Fade out line 1 and 2
+ const fadeOutOpacity = frame >= FADE_OUT_START
+ ? interpolate(frame, [FADE_OUT_START, FADE_OUT_START + FADE_OUT_DURATION], [1, 0], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 1;
+
+ // Line 3 fade in
+ const line3Opacity = frame >= LINE3_START
+ ? interpolate(frame, [LINE3_START, LINE3_START + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+
+ const line3Scale = frame >= LINE3_START
+ ? interpolate(frame, [LINE3_START, LINE3_START + 15], [0.95, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ })
+ : 0.95;
+
+ return (
+
+ {/* Main content container */}
+
+ {/* Line 1: "Radiologistas gastam horas escrevendo laudos." */}
+
+ {LINE1_WORDS.map((wordData, idx) => {
+ const isVisible = frame >= wordData.appearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - wordData.appearFrame;
+ const slideProgress = interpolate(
+ framesSinceAppear,
+ [0, SLIDE_DURATION],
+ [0, 1],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ const translateY = interpolate(slideProgress, [0, 1], [40, 0]);
+ const opacity = interpolate(framesSinceAppear, [0, SLIDE_DURATION * 0.6], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+
+ // Highlight "horas" in accent color
+ const isHighlighted = wordData.word === "horas";
+
+ return (
+
+ {wordData.word}
+
+ );
+ })}
+
+
+ {/* Line 2: "Todos. Os. Dias." */}
+
+ {LINE2_WORDS.map((wordData, idx) => {
+ const isVisible = frame >= wordData.appearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - wordData.appearFrame;
+ const slideProgress = interpolate(
+ framesSinceAppear,
+ [0, SLIDE_DURATION],
+ [0, 1],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ const translateY = interpolate(slideProgress, [0, 1], [30, 0]);
+ const opacity = interpolate(framesSinceAppear, [0, SLIDE_DURATION * 0.5], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+
+ // Scale effect
+ const scale = interpolate(
+ framesSinceAppear,
+ [0, 8, 16],
+ [0.9, 1.05, 1],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ return (
+
+ {wordData.word}
+
+ );
+ })}
+
+
+ {/* Line 3: "E se a IA pudesse ajudar?" */}
+ {frame >= LINE3_START && (
+
+
+ E se a{" "}
+ IA
+ {" "}pudesse ajudar?
+
+
+ )}
+
+
+ {/* Sound effects */}
+ {LINE1_WORDS.map((wordData, idx) => (
+
+
+
+ ))}
+
+ {LINE2_WORDS.map((wordData, idx) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/scenes/OutroScene.tsx b/videos/laudos-launch/src/remotion/scenes/OutroScene.tsx
new file mode 100644
index 0000000..f57ef9c
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/OutroScene.tsx
@@ -0,0 +1,369 @@
+import React, { useMemo } from "react";
+import {
+ AbsoluteFill,
+ useCurrentFrame,
+ interpolate,
+ Easing,
+ Sequence,
+ Audio,
+ staticFile,
+} from "remotion";
+import { loadFont, fontFamily } from "@remotion/google-fonts/Inter";
+
+loadFont("normal", {
+ subsets: ["latin"],
+ weights: ["400", "500", "600", "700", "800"],
+});
+
+// Clean white design system
+const COLORS = {
+ bg: "#FFFFFF",
+ bgSubtle: "#F8FAFC",
+ text: "#0F172A",
+ textMuted: "#64748B",
+ accent: "#0066FF",
+ accentLight: "#E0EDFF",
+ border: "#E2E8F0",
+};
+
+// Scramble effect for logo reveal
+const SCRAMBLE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+const LOGO_TEXT = "laudos.ai";
+
+// Seeded random for consistent scramble
+const seededRandom = (seed: number) => {
+ const x = Math.sin(seed * 9999) * 10000;
+ return x - Math.floor(x);
+};
+
+// Timing
+const TAGLINE_START = 10;
+const TAGLINE_WORDS = [
+ { word: "Seu", appearFrame: TAGLINE_START },
+ { word: "assistente", appearFrame: TAGLINE_START + 6 },
+ { word: "de", appearFrame: TAGLINE_START + 12 },
+ { word: "laudos", appearFrame: TAGLINE_START + 18 },
+ { word: "com", appearFrame: TAGLINE_START + 24 },
+ { word: "IA", appearFrame: TAGLINE_START + 30 },
+ { word: "agêntica.", appearFrame: TAGLINE_START + 36 },
+];
+
+const LOGO_APPEAR_START = 65;
+const LOGO_SCRAMBLE_DURATION = 25;
+
+const CTA_START = LOGO_APPEAR_START + LOGO_SCRAMBLE_DURATION + 15;
+
+// Stats that appear at the bottom
+const STATS = [
+ { value: "100%", label: "Nuvem" },
+ { value: "LGPD", label: "Compliance" },
+ { value: "LaudAI", label: "Potência" },
+];
+const STATS_START = CTA_START + 20;
+const STATS_STAGGER = 10;
+
+export const OUTRO_SCENE_DURATION = 180; // 6 seconds
+
+export const OutroScene: React.FC = () => {
+ const frame = useCurrentFrame();
+
+ // Logo scramble effect
+ const decryptedLogoText = useMemo(() => {
+ if (frame < LOGO_APPEAR_START) return "";
+
+ const framesSinceReveal = frame - LOGO_APPEAR_START;
+
+ return LOGO_TEXT.split("").map((char, idx) => {
+ const charRevealFrame = idx * 2;
+ const framesSinceCharStart = framesSinceReveal - charRevealFrame;
+
+ if (framesSinceCharStart >= 10) {
+ return char;
+ } else if (framesSinceCharStart >= 0) {
+ const seed = idx * 100 + frame;
+ const randomIdx = Math.floor(seededRandom(seed) * SCRAMBLE_CHARS.length);
+ return SCRAMBLE_CHARS[randomIdx];
+ } else {
+ const seed = idx * 100 + frame;
+ const randomIdx = Math.floor(seededRandom(seed) * SCRAMBLE_CHARS.length);
+ return SCRAMBLE_CHARS[randomIdx];
+ }
+ }).join("");
+ }, [frame]);
+
+ // Logo appear progress
+ const logoAppearProgress = frame >= LOGO_APPEAR_START
+ ? interpolate(frame, [LOGO_APPEAR_START, LOGO_APPEAR_START + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ })
+ : 0;
+
+ const logoScale = interpolate(logoAppearProgress, [0, 1], [0.9, 1]);
+ const logoOpacity = interpolate(logoAppearProgress, [0, 1], [0, 1]);
+
+ // CTA button appear
+ const ctaOpacity = frame >= CTA_START
+ ? interpolate(frame, [CTA_START, CTA_START + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+ const ctaScale = frame >= CTA_START
+ ? interpolate(frame, [CTA_START, CTA_START + 15], [0.95, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ })
+ : 0.95;
+
+ return (
+
+ {/* Main content */}
+
+ {/* Tagline */}
+
+ {TAGLINE_WORDS.map((wordData, idx) => {
+ const isVisible = frame >= wordData.appearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - wordData.appearFrame;
+ const slideProgress = interpolate(
+ framesSinceAppear,
+ [0, 10],
+ [0, 1],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ const translateY = interpolate(slideProgress, [0, 1], [30, 0]);
+ const opacity = interpolate(framesSinceAppear, [0, 8], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+
+ const isHighlighted = wordData.word === "IA" || wordData.word === "agêntica.";
+
+ return (
+
+ {wordData.word}
+
+ );
+ })}
+
+
+ {/* Logo "laudos.ai" with scramble effect */}
+ {frame >= LOGO_APPEAR_START && (
+
+
+ {decryptedLogoText.replace(".ai", "")}
+ .ai
+
+
+ )}
+
+ {/* CTA Button */}
+ {frame >= CTA_START && (
+
+
+
+ Comece grátis
+
+
+
+
+ )}
+
+ {/* Stats row */}
+
+ {STATS.map((stat, idx) => {
+ const statAppearFrame = STATS_START + idx * STATS_STAGGER;
+ const isVisible = frame >= statAppearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - statAppearFrame;
+ const opacity = interpolate(framesSinceAppear, [0, 10], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+ const translateY = interpolate(
+ framesSinceAppear,
+ [0, 10],
+ [15, 0],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ return (
+
+
+ {stat.value}
+
+
+ {stat.label}
+
+
+ );
+ })}
+
+
+
+ {/* Bottom URL */}
+
+
+ www.laudos.ai
+
+
+
+ {/* Sound effects */}
+ {TAGLINE_WORDS.map((wordData, idx) => (
+
+
+
+ ))}
+
+ {/* Logo reveal sound */}
+
+
+
+
+ {/* CTA sound */}
+
+
+
+
+ {STATS.map((_, idx) => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/scenes/ProblemScene.tsx b/videos/laudos-launch/src/remotion/scenes/ProblemScene.tsx
new file mode 100644
index 0000000..80f9a68
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/scenes/ProblemScene.tsx
@@ -0,0 +1,398 @@
+import React from "react";
+import {
+ AbsoluteFill,
+ useCurrentFrame,
+ interpolate,
+ Easing,
+ Sequence,
+ Audio,
+ staticFile,
+} from "remotion";
+import { loadFont, fontFamily } from "@remotion/google-fonts/Inter";
+
+loadFont("normal", {
+ subsets: ["latin"],
+ weights: ["400", "500", "600", "700", "800"],
+});
+
+// Clean white design system
+const COLORS = {
+ bg: "#FFFFFF",
+ bgSubtle: "#F8FAFC",
+ text: "#0F172A",
+ textMuted: "#64748B",
+ accent: "#0066FF",
+ accentLight: "#E0EDFF",
+ error: "#EF4444",
+ errorLight: "#FEE2E2",
+ border: "#E2E8F0",
+};
+
+// Timing
+const TITLE_START = 10;
+const LEGACY_UI_START = 40;
+const PROBLEMS_START = 90;
+const PROBLEM_STAGGER = 25;
+const TRANSITION_START = 220;
+
+// Legacy software problems
+const PROBLEMS = [
+ {
+ icon: "old",
+ text: "Interfaces ultrapassadas dos anos 2000",
+ },
+ {
+ icon: "inconsistent",
+ text: "Laudos sem padronização",
+ },
+ {
+ icon: "manual",
+ text: "100% trabalho manual",
+ },
+ {
+ icon: "slow",
+ text: "Sem inteligência artificial",
+ },
+];
+
+export const PROBLEM_SCENE_DURATION = 270; // 9 seconds
+
+export const ProblemScene: React.FC = () => {
+ const frame = useCurrentFrame();
+
+ // Title animation
+ const titleOpacity = frame >= TITLE_START
+ ? interpolate(frame, [TITLE_START, TITLE_START + 15], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+
+ // Legacy UI fade in
+ const legacyOpacity = frame >= LEGACY_UI_START
+ ? interpolate(frame, [LEGACY_UI_START, LEGACY_UI_START + 20], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 0;
+
+ // Transition out
+ const transitionOpacity = frame >= TRANSITION_START
+ ? interpolate(frame, [TRANSITION_START, TRANSITION_START + 20], [1, 0], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ })
+ : 1;
+
+ return (
+
+ {/* Title */}
+
+
+ As soluções atuais são{" "}
+ ultrapassadas
+
+
+
+ {/* Main content - Legacy UI mockup */}
+
+ {/* Legacy software mockup */}
+
+ {/* Old-style title bar */}
+
+
+ Sistema de Laudos v2.1 - [Sem Licença]
+
+
+
+
+ {/* Old toolbar */}
+
+ {["Arquivo", "Editar", "Laudo", "Ajuda"].map((item) => (
+
+ {item}
+
+ ))}
+
+
+ {/* Content area with old form */}
+
+ {/* Form fields old style */}
+ {[
+ { label: "Paciente:", value: "JOÃO DA SILVA" },
+ { label: "Data:", value: "24/01/2026" },
+ { label: "Exame:", value: "TOMOGRAFIA" },
+ ].map((field, i) => (
+
+
+ {field.label}
+
+
+ {field.value}
+
+
+ ))}
+
+ {/* Text area */}
+
+
+ Laudo:
+
+
+ LAUDO RADIOLOGICO{"\n"}
+ {"\n"}
+ Exame: TC de torax s/ contraste{"\n"}
+ {"\n"}
+ TECNICA: cortes axiais...{"\n"}
+ {"\n"}
+ {">>>"} DIGITE SEU LAUDO AQUI {"<<<"}
+
+
+
+ {/* Old buttons */}
+
+ {["Salvar", "Imprimir", "Cancelar"].map((btn) => (
+
+ {btn}
+
+ ))}
+
+
+
+
+ {/* Problems list */}
+
+ {PROBLEMS.map((problem, idx) => {
+ const problemAppearFrame = PROBLEMS_START + idx * PROBLEM_STAGGER;
+ const isVisible = frame >= problemAppearFrame;
+ if (!isVisible) return null;
+
+ const framesSinceAppear = frame - problemAppearFrame;
+ const opacity = interpolate(framesSinceAppear, [0, 12], [0, 1], {
+ extrapolateRight: "clamp",
+ });
+ const translateX = interpolate(
+ framesSinceAppear,
+ [0, 12],
+ [30, 0],
+ {
+ extrapolateRight: "clamp",
+ easing: Easing.out(Easing.cubic),
+ }
+ );
+
+ return (
+
+ {/* X icon */}
+
+
+
+ {problem.text}
+
+
+ );
+ })}
+
+
+
+ {/* Sound effects */}
+
+
+
+
+
+
+
+
+ {PROBLEMS.map((_, idx) => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/videos/laudos-launch/src/remotion/webpack-override.mjs b/videos/laudos-launch/src/remotion/webpack-override.mjs
new file mode 100644
index 0000000..65121e1
--- /dev/null
+++ b/videos/laudos-launch/src/remotion/webpack-override.mjs
@@ -0,0 +1,5 @@
+import { enableTailwind } from "@remotion/tailwind-v4";
+
+export const webpackOverride = (config) => {
+ return enableTailwind(config);
+};
diff --git a/videos/laudos-launch/src/styles/global.css b/videos/laudos-launch/src/styles/global.css
new file mode 100644
index 0000000..1038643
--- /dev/null
+++ b/videos/laudos-launch/src/styles/global.css
@@ -0,0 +1,10 @@
+@import "tailwindcss";
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+}
diff --git a/videos/laudos-launch/tsconfig.json b/videos/laudos-launch/tsconfig.json
new file mode 100644
index 0000000..007e8f5
--- /dev/null
+++ b/videos/laudos-launch/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"],
+ "@launchpad/shared": ["../../packages/shared/src"],
+ "@launchpad/shared/*": ["../../packages/shared/src/*"],
+ "@launchpad/assets": ["../../packages/assets"],
+ "@launchpad/assets/*": ["../../packages/assets/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/videos/laudos-launch/types/constants.ts b/videos/laudos-launch/types/constants.ts
new file mode 100644
index 0000000..e0f036f
--- /dev/null
+++ b/videos/laudos-launch/types/constants.ts
@@ -0,0 +1,7 @@
+// Video configuration constants
+export const VIDEO_WIDTH = 1920;
+export const VIDEO_HEIGHT = 1080;
+export const VIDEO_FPS = 30;
+
+// Composition name (used for rendering)
+export const COMP_NAME = "LaudosLaunch";