@remotion/promo-pages 4.0.260 → 5.0.0-canary.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/Homepage.js +2 -4
  2. package/package.json +52 -48
  3. package/.turbo/turbo-lint.log +0 -5
  4. package/.turbo/turbo-make.log +0 -8
  5. package/LICENSE.md +0 -49
  6. package/bundle.ts +0 -46
  7. package/eslint.config.mjs +0 -7
  8. package/index.html +0 -13
  9. package/public/fire.mp3 +0 -0
  10. package/public/img/cluster.png +0 -0
  11. package/public/img/code-sample-new.png +0 -0
  12. package/public/img/freelancers/alex.jpeg +0 -0
  13. package/public/img/freelancers/antoine.jpeg +0 -0
  14. package/public/img/freelancers/ayush.png +0 -0
  15. package/public/img/freelancers/benjamin.jpeg +0 -0
  16. package/public/img/freelancers/default.png +0 -0
  17. package/public/img/freelancers/florent.jpeg +0 -0
  18. package/public/img/freelancers/karel.jpeg +0 -0
  19. package/public/img/freelancers/lorenzo.jpeg +0 -0
  20. package/public/img/freelancers/mickael.jpeg +0 -0
  21. package/public/img/freelancers/mohit.jpeg +0 -0
  22. package/public/img/freelancers/pramod.jpg +0 -0
  23. package/public/img/freelancers/pranav.jpg +0 -0
  24. package/public/img/freelancers/rahul.png +0 -0
  25. package/public/img/freelancers/ray.jpeg +0 -0
  26. package/public/img/freelancers/stephen.png +0 -0
  27. package/public/img/freelancers/umungo.png +0 -0
  28. package/public/img/freelancers/yehor.jpeg +0 -0
  29. package/public/img/gt-planar-black.woff2 +0 -0
  30. package/public/img/gt-planar-bold.woff2 +0 -0
  31. package/public/img/gt-planar-medium.woff2 +0 -0
  32. package/public/img/gt-planar-regular.woff2 +0 -0
  33. package/public/img/logo-small.png +0 -0
  34. package/public/img/mp4.png +0 -0
  35. package/public/img/player-demo.mp4 +0 -0
  36. package/public/img/player-example-dark.png +0 -0
  37. package/public/img/player-example.png +0 -0
  38. package/public/img/writeinreact.png +0 -0
  39. package/public/partyhorn.mp3 +0 -0
  40. package/public/sad.mp3 +0 -0
  41. package/src/cn.ts +0 -6
  42. package/src/components/Homepage.tsx +0 -88
  43. package/src/components/homepage/BackgroundAnimation.tsx +0 -108
  44. package/src/components/homepage/ChooseTemplate.tsx +0 -57
  45. package/src/components/homepage/CodeExample.tsx +0 -89
  46. package/src/components/homepage/CommunityStats.tsx +0 -54
  47. package/src/components/homepage/CommunityStatsItems.tsx +0 -304
  48. package/src/components/homepage/Counter.tsx +0 -110
  49. package/src/components/homepage/Demo/Card.tsx +0 -273
  50. package/src/components/homepage/Demo/Cards.tsx +0 -129
  51. package/src/components/homepage/Demo/Comp.tsx +0 -157
  52. package/src/components/homepage/Demo/CurrentCountry.tsx +0 -97
  53. package/src/components/homepage/Demo/DemoError.tsx +0 -18
  54. package/src/components/homepage/Demo/DemoErrorIcon.tsx +0 -32
  55. package/src/components/homepage/Demo/DemoRender.tsx +0 -166
  56. package/src/components/homepage/Demo/DigitWheel.tsx +0 -179
  57. package/src/components/homepage/Demo/DisplayedEmoji.tsx +0 -81
  58. package/src/components/homepage/Demo/DoneCheckmark.tsx +0 -39
  59. package/src/components/homepage/Demo/DownloadNudge.tsx +0 -62
  60. package/src/components/homepage/Demo/DragAndDropNudge.tsx +0 -57
  61. package/src/components/homepage/Demo/EmojiCard.tsx +0 -198
  62. package/src/components/homepage/Demo/Minus.tsx +0 -21
  63. package/src/components/homepage/Demo/PlayPauseButton.tsx +0 -66
  64. package/src/components/homepage/Demo/PlayerControls.tsx +0 -48
  65. package/src/components/homepage/Demo/PlayerSeekBar.tsx +0 -325
  66. package/src/components/homepage/Demo/PlayerVolume.tsx +0 -83
  67. package/src/components/homepage/Demo/Progress.tsx +0 -38
  68. package/src/components/homepage/Demo/Spinner.tsx +0 -60
  69. package/src/components/homepage/Demo/Switcher.tsx +0 -54
  70. package/src/components/homepage/Demo/Temperature.tsx +0 -44
  71. package/src/components/homepage/Demo/TemperatureNumber.tsx +0 -68
  72. package/src/components/homepage/Demo/ThemeNudge.tsx +0 -72
  73. package/src/components/homepage/Demo/TimeDisplay.tsx +0 -43
  74. package/src/components/homepage/Demo/TrendingRepos.tsx +0 -106
  75. package/src/components/homepage/Demo/icons.tsx +0 -114
  76. package/src/components/homepage/Demo/index.tsx +0 -158
  77. package/src/components/homepage/Demo/math.ts +0 -43
  78. package/src/components/homepage/Demo/types.ts +0 -6
  79. package/src/components/homepage/Editor.tsx +0 -67
  80. package/src/components/homepage/EvaluateRemotion.tsx +0 -92
  81. package/src/components/homepage/FreePricing.tsx +0 -295
  82. package/src/components/homepage/GetStartedStrip.tsx +0 -77
  83. package/src/components/homepage/GitHubButton.tsx +0 -23
  84. package/src/components/homepage/IconForTemplate.tsx +0 -154
  85. package/src/components/homepage/IfYouKnowReact.tsx +0 -29
  86. package/src/components/homepage/InfoTooltip.tsx +0 -25
  87. package/src/components/homepage/MoreTemplatesButton.tsx +0 -29
  88. package/src/components/homepage/MuxVideo.tsx +0 -68
  89. package/src/components/homepage/NewsletterButton.tsx +0 -88
  90. package/src/components/homepage/Pricing.tsx +0 -49
  91. package/src/components/homepage/PricingBulletPoint.tsx +0 -50
  92. package/src/components/homepage/RealMp4Videos.tsx +0 -50
  93. package/src/components/homepage/Spacer.tsx +0 -5
  94. package/src/components/homepage/TemplateIcon.tsx +0 -36
  95. package/src/components/homepage/TextInput.tsx +0 -62
  96. package/src/components/homepage/TrustedByBanner.tsx +0 -194
  97. package/src/components/homepage/VideoApps.tsx +0 -231
  98. package/src/components/homepage/VideoAppsShowcase.tsx +0 -276
  99. package/src/components/homepage/VideoAppsTitle.tsx +0 -24
  100. package/src/components/homepage/VideoPlayerWithControls.tsx +0 -188
  101. package/src/components/homepage/WriteInReact.tsx +0 -34
  102. package/src/components/homepage/YouAreHere.tsx +0 -30
  103. package/src/components/homepage/custom.css +0 -57
  104. package/src/components/homepage/layout/Button.tsx +0 -93
  105. package/src/components/homepage/layout/colors.ts +0 -17
  106. package/src/components/homepage/layout/use-color-mode.tsx +0 -44
  107. package/src/components/homepage/layout/use-el-size.ts +0 -51
  108. package/src/components/homepage/layout/use-mobile-layout.ts +0 -8
  109. package/src/components/homepage/video-player.css +0 -24
  110. package/src/components/icons/blank.tsx +0 -13
  111. package/src/components/icons/clone.tsx +0 -10
  112. package/src/components/icons/code-hike.tsx +0 -15
  113. package/src/components/icons/cubes.tsx +0 -13
  114. package/src/components/icons/js.tsx +0 -17
  115. package/src/components/icons/next.tsx +0 -64
  116. package/src/components/icons/overlay.tsx +0 -24
  117. package/src/components/icons/remix.tsx +0 -24
  118. package/src/components/icons/skia.tsx +0 -13
  119. package/src/components/icons/stargazer.tsx +0 -13
  120. package/src/components/icons/still.tsx +0 -13
  121. package/src/components/icons/tailwind.tsx +0 -22
  122. package/src/components/icons/tiktok.tsx +0 -13
  123. package/src/components/icons/ts.tsx +0 -18
  124. package/src/components/icons/tts.tsx +0 -13
  125. package/src/components/icons/undo.tsx +0 -11
  126. package/src/components/icons/waveform.tsx +0 -13
  127. package/src/fonts.css +0 -30
  128. package/src/index.css +0 -74
  129. package/src/main.tsx +0 -12
  130. package/tsconfig.json +0 -15
  131. package/vite.config.ts +0 -9
@@ -1,158 +0,0 @@
1
- import type {PlayerRef} from '@remotion/player';
2
- import {Player} from '@remotion/player';
3
- import React, {
4
- useCallback,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- type CSSProperties,
10
- } from 'react';
11
-
12
- import {PALETTE} from '../layout/colors';
13
- import {useColorMode} from '../layout/use-color-mode';
14
- import {SectionTitle} from '../VideoAppsTitle';
15
- import {
16
- getDataAndProps,
17
- HomepageVideoComp,
18
- type DemoPlayerProps,
19
- type LocationAndTrending,
20
- type RemoteData,
21
- } from './Comp';
22
- import {DemoError} from './DemoError';
23
- import {RenderButton} from './DemoRender';
24
- import {DownloadNudge} from './DownloadNudge';
25
- import {DragAndDropNudge} from './DragAndDropNudge';
26
- import {PlayerControls} from './PlayerControls';
27
- import {ThemeNudge} from './ThemeNudge';
28
- import type {Location} from './types';
29
-
30
- const style: React.CSSProperties = {
31
- width: '100%',
32
- aspectRatio: '640 / 360',
33
- borderBottom: `2px solid ${PALETTE.BOX_STROKE}`,
34
- touchAction: 'none', // prevent page from scrolling when dragging children on mobile
35
- };
36
-
37
- const playerWrapper: CSSProperties = {
38
- border: `2px solid ${PALETTE.BOX_STROKE}`,
39
- borderBottom: `4px solid ${PALETTE.BOX_STROKE}`,
40
- borderRadius: 8,
41
- width: '100%',
42
- overflow: 'hidden',
43
- };
44
-
45
- export const Demo: React.FC = () => {
46
- const {colorMode} = useColorMode();
47
- const [data, setData] = useState<LocationAndTrending | null>(null);
48
- const ref = useRef<PlayerRef>(null);
49
-
50
- const [isFullscreen, setIsFullscreen] = useState(false);
51
- const [cardOrder, setCardOrder] = useState([0, 1, 2, 3]);
52
- const [emojiIndex, setEmojiIndex] = useState<number>(0);
53
- const [error, setError] = useState(false);
54
-
55
- useEffect(() => {
56
- getDataAndProps()
57
- .then((d) => {
58
- setData(d);
59
- })
60
- .catch((err) => {
61
- // eslint-disable-next-line no-console
62
- console.log(err);
63
- setError(true);
64
- });
65
- }, []);
66
-
67
- useEffect(() => {
68
- const {current: playerRef} = ref;
69
- if (!playerRef || !data) {
70
- return;
71
- }
72
-
73
- const onFullscreenChange = () => {
74
- setIsFullscreen(playerRef.isFullscreen());
75
- };
76
-
77
- playerRef.addEventListener('fullscreenchange', onFullscreenChange);
78
-
79
- return () => {
80
- playerRef.removeEventListener('fullscreenchange', onFullscreenChange);
81
- };
82
- }, [data]);
83
-
84
- const updateCardOrder = useCallback((newCardOrder: number[]) => {
85
- setCardOrder(newCardOrder);
86
- }, []);
87
-
88
- const props: DemoPlayerProps = useMemo(() => {
89
- return {
90
- theme: colorMode,
91
- onToggle: () => {
92
- ref.current?.toggle();
93
- },
94
- cardOrder,
95
- updateCardOrder,
96
- location: data?.location ?? null,
97
- trending: data?.trending ?? null,
98
- onClickLeft: () => {
99
- setEmojiIndex((e) => e - 1);
100
- },
101
- onClickRight: () => {
102
- setEmojiIndex((e) => e + 1);
103
- },
104
- emojiIndex,
105
- };
106
- }, [cardOrder, emojiIndex, colorMode, data, updateCardOrder]);
107
-
108
- const onError = useCallback(() => {
109
- setError(true);
110
- }, []);
111
-
112
- return (
113
- <div>
114
- <br />
115
- <br />
116
- <SectionTitle>Demo</SectionTitle>
117
- <div style={{height: 140, position: 'relative'}}>
118
- <DragAndDropNudge />
119
- <ThemeNudge />
120
- </div>
121
- <div style={playerWrapper}>
122
- <Player
123
- ref={ref}
124
- component={HomepageVideoComp}
125
- compositionWidth={640}
126
- compositionHeight={360}
127
- durationInFrames={120}
128
- fps={30}
129
- autoPlay
130
- controls={isFullscreen}
131
- clickToPlay={false}
132
- style={style}
133
- initiallyMuted
134
- inputProps={props}
135
- loop
136
- />
137
- <PlayerControls playerRef={ref} durationInFrames={120} fps={30}>
138
- <RenderButton
139
- onError={onError}
140
- renderData={
141
- data
142
- ? {
143
- cardOrder,
144
- emojiIndex,
145
- location: data.location as Location,
146
- theme: colorMode,
147
- trending: data.trending as RemoteData,
148
- }
149
- : null
150
- }
151
- />
152
- </PlayerControls>
153
- </div>
154
- {error ? <DemoError /> : null}
155
- <DownloadNudge />
156
- </div>
157
- );
158
- };
@@ -1,43 +0,0 @@
1
- export type Position = {
2
- x: number;
3
- y: number;
4
- };
5
-
6
- export const paddingAndMargin = 20;
7
- const height = 360;
8
- const width = 640;
9
- export const cardHeight = (height - paddingAndMargin * 3) / 2;
10
- export const cardWidth = (width - paddingAndMargin * 3) / 2;
11
-
12
- export const getPositionForIndex = (index: number): Position => {
13
- const x =
14
- index % 2 === 0 ? paddingAndMargin : cardWidth + paddingAndMargin * 2;
15
- const y = index < 2 ? paddingAndMargin : cardHeight + paddingAndMargin * 2;
16
-
17
- return {x, y};
18
- };
19
-
20
- export const getInitialPositions = () => {
21
- return new Array(4).fill(true).map((_, i) => getPositionForIndex(i));
22
- };
23
-
24
- export const getIndexFromPosition = (clientX: number, clientY: number) => {
25
- const left = clientX < cardWidth / 2 + paddingAndMargin;
26
- const top = clientY < cardHeight / 2 + paddingAndMargin;
27
-
28
- if (left && top) {
29
- return 0;
30
- }
31
-
32
- if (!left && top) {
33
- return 1;
34
- }
35
-
36
- if (left && !top) {
37
- return 2;
38
- }
39
-
40
- if (!left && !top) {
41
- return 3;
42
- }
43
- };
@@ -1,6 +0,0 @@
1
- export type Location = {
2
- country: string;
3
- city: string;
4
- latitude: number | string;
5
- longitude: number | string;
6
- };
@@ -1,67 +0,0 @@
1
- import React, {useCallback, useEffect, useRef, useState} from 'react';
2
-
3
- export const LightningFastEditor: React.FC = () => {
4
- const ref = useRef<HTMLDivElement>(null);
5
- const videoRef = useRef<HTMLVideoElement>(null);
6
- const [isIntersecting, setIsIntersecting] = useState(false);
7
-
8
- const callback: IntersectionObserverCallback = useCallback((data) => {
9
- const {isIntersecting: intersecting} = data[0];
10
- setIsIntersecting(intersecting);
11
- if (intersecting) {
12
- videoRef.current?.play();
13
- } else {
14
- videoRef.current?.pause();
15
- }
16
- }, []);
17
-
18
- useEffect(() => {
19
- const {current} = ref;
20
- if (!current) {
21
- return;
22
- }
23
-
24
- const observer = new IntersectionObserver(callback, {
25
- root: null,
26
- threshold: 0.2,
27
- });
28
- observer.observe(current);
29
-
30
- return () => observer.unobserve(current);
31
- }, [callback]);
32
- return (
33
- <div
34
- ref={ref}
35
- className={
36
- 'flex flex-col-reverse lg:flex-row justify-start lg:justify-end lg:text-right items-start lg:items-center'
37
- }
38
- >
39
- <div>
40
- <video
41
- src="/img/player-demo.mp4"
42
- autoPlay
43
- muted
44
- playsInline
45
- loop
46
- style={{
47
- animationPlayState: isIntersecting ? 'running' : 'paused',
48
- width: 500,
49
- maxWidth: '100%',
50
- borderRadius: 7,
51
- overflow: 'hidden',
52
- }}
53
- />
54
- </div>
55
- <div style={{flex: 1}}>
56
- <h2 className={'fontbrand text-4xl'}>
57
- Fast and <br /> <span className="text-brand">delightful</span> editing
58
- </h2>
59
- <p className="leading-relaxed">
60
- Preview your video in the browser. <br />
61
- Fast refresh while the video is playing. <br />
62
- Scrub through the timeline to get every frame perfect.
63
- </p>
64
- </div>
65
- </div>
66
- );
67
- };
@@ -1,92 +0,0 @@
1
- // Adjustments to the EvaluateRemotionSection.tsx to separate the flex box into two boxes
2
- // with specified alignment and positioning requirements.
3
-
4
- import React, {useEffect, useState} from 'react';
5
- import {BlueButton} from './layout/Button';
6
-
7
- const EvaluateRemotionSection: React.FC = () => {
8
- const [dailyAvatars, setDailyAvatars] = useState<string[]>([]);
9
-
10
- useEffect(() => {
11
- const avatars = [
12
- '/img/freelancers/alex.jpeg',
13
- '/img/freelancers/antoine.jpeg',
14
- '/img/freelancers/ayush.png',
15
- '/img/freelancers/benjamin.jpeg',
16
- '/img/freelancers/florent.jpeg',
17
- '/img/freelancers/karel.jpeg',
18
- '/img/freelancers/lorenzo.jpeg',
19
- '/img/freelancers/mickael.jpeg',
20
- '/img/freelancers/mohit.jpeg',
21
- '/img/freelancers/pramod.jpg',
22
- '/img/freelancers/pranav.jpg',
23
- '/img/freelancers/rahul.png',
24
- '/img/freelancers/ray.jpeg',
25
- '/img/freelancers/stephen.png',
26
- '/img/freelancers/umungo.png',
27
- '/img/freelancers/yehor.jpeg',
28
- // Add more avatar paths here
29
- ];
30
-
31
- const selectedAvatars: string[] = [];
32
- for (let i = 0; i < 3; i++) {
33
- const index = Math.floor(Math.random() * avatars.length);
34
- selectedAvatars.push(avatars[index]);
35
- avatars.splice(index, 1); // Remove selected avatar to avoid duplicates
36
- }
37
-
38
- setDailyAvatars(selectedAvatars);
39
- }, []);
40
-
41
- return (
42
- <div className="flex flex-col lg:flex-row gap-2">
43
- <div className={'card flex-1 flex flex-col'}>
44
- <div className={'fontbrand text-2xl font-bold'}>
45
- Evaluate Remotion for your company
46
- </div>
47
- <p className={'text-muted fontbrand leading-snug'}>
48
- Book a 20 minute call with us to get all your questions answered.
49
- </p>
50
- <div className="flex-1" />
51
- <a
52
- target="_blank"
53
- href="https://cal.com/remotion/evaluate"
54
- style={{textDecoration: 'none'}}
55
- >
56
- <BlueButton size="sm" loading={false}>
57
- Schedule a call
58
- </BlueButton>
59
- </a>
60
- </div>
61
- <div className={'card flex-1 flex flex-col'}>
62
- <div className={'fontbrand text-2xl font-bold'}>
63
- Get help for your projects
64
- </div>
65
- <p className={'text-muted fontbrand leading-snug'}>
66
- Contact our experts for help and work.
67
- </p>
68
- <div className="flex-1" />
69
- <div className={'flex flex-row justify-between'}>
70
- <a href="/experts" style={{textDecoration: 'none'}}>
71
- <BlueButton size="sm" loading={false}>
72
- Remotion Experts
73
- </BlueButton>
74
- </a>
75
- <div className={'flex justify-end items-end gap-3'}>
76
- {dailyAvatars.map((avatar) => (
77
- <div
78
- key={avatar}
79
- className={
80
- 'w-12 h-12 rounded-full bg-muted bg-cover bg-center -ml-5 border-2 border-black'
81
- }
82
- style={{backgroundImage: `url(${avatar})`}}
83
- />
84
- ))}
85
- </div>
86
- </div>
87
- </div>
88
- </div>
89
- );
90
- };
91
-
92
- export default EvaluateRemotionSection;
@@ -1,295 +0,0 @@
1
- import React, {useCallback, useMemo} from 'react';
2
- import {cn} from '../../cn';
3
- import {Counter} from './Counter';
4
- import {InfoTooltip} from './InfoTooltip';
5
- import {PricingBulletPoint} from './PricingBulletPoint';
6
-
7
- const Container: React.FC<{readonly children: React.ReactNode}> = ({
8
- children,
9
- }) => {
10
- return (
11
- <div className={'flex flex-col border-effect rounded-xl p-5 bg-pane'}>
12
- {children}
13
- </div>
14
- );
15
- };
16
-
17
- const Title: React.FC<{
18
- readonly children: React.ReactNode;
19
- }> = ({children}) => {
20
- return (
21
- <div className="text-4xl font-bold leading-none fontbrand mt-2 mb-5">
22
- {children}
23
- </div>
24
- );
25
- };
26
-
27
- const Audience: React.FC<{
28
- readonly children: React.ReactNode;
29
- }> = ({children}) => {
30
- return <div className={'fontbrand text-lg leading-none'}>{children}</div>;
31
- };
32
-
33
- const BottomInfo: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
34
- children,
35
- className,
36
- ...props
37
- }) => {
38
- return (
39
- <div
40
- className={cn(className, 'text-[var(--subtitle)] fontbrand text-sm')}
41
- {...props}
42
- >
43
- {children}
44
- </div>
45
- );
46
- };
47
-
48
- const PriceTag: React.FC<{
49
- readonly children: React.ReactNode;
50
- }> = ({children}) => {
51
- return (
52
- <div
53
- className={
54
- 'fontbrand text-2xl font-bold min-w-[80px] w-auto text-right shrink-0 ml-4'
55
- }
56
- >
57
- {children}
58
- </div>
59
- );
60
- };
61
-
62
- const SmallPriceTag: React.FC<{
63
- readonly children: React.ReactNode;
64
- }> = ({children}) => {
65
- return (
66
- <div
67
- className={
68
- 'fontbrand text-2xl font-medium w-auto min-w-[80px] text-right shrink-0 ml-4'
69
- }
70
- >
71
- {children}
72
- </div>
73
- );
74
- };
75
-
76
- export const FreePricing: React.FC = () => {
77
- return (
78
- <Container>
79
- <Audience>For individuals and companies of up to 3 people</Audience>
80
- <Title>Free License</Title>
81
- <PricingBulletPoint text="Unlimited videos" checked />
82
- <PricingBulletPoint text="Commercial use allowed" checked />
83
- <PricingBulletPoint text="Self-hosted cloud rendering allowed" checked />
84
- <PricingBulletPoint
85
- text="Must upgrade when your team grows"
86
- checked={false}
87
- />
88
- </Container>
89
- );
90
- };
91
-
92
- const textUnitWrapper: React.CSSProperties = {
93
- display: 'flex',
94
- flexDirection: 'column',
95
- };
96
-
97
- export const EnterpriseLicense: React.FC = () => {
98
- return (
99
- <Container>
100
- <Audience>For advanced needs</Audience>
101
- <Title>Enterprise License</Title>
102
- <PricingBulletPoint text="Everything in Company License" checked />
103
- <PricingBulletPoint text="Custom terms, billing and pricing" checked />
104
- <PricingBulletPoint text="Compliance forms" checked />
105
- <PricingBulletPoint text="Prioritized feature requests" checked />
106
- <PricingBulletPoint text="Private support channel" checked />
107
- <PricingBulletPoint text="Monthly consulting session" checked />
108
- <div style={{height: 30}} />
109
- <div className={'flex flex-row justify-end'}>
110
- <div
111
- style={{
112
- ...textUnitWrapper,
113
- alignItems: 'flex-end',
114
- }}
115
- >
116
- <PriceTag>
117
- <a
118
- className={
119
- 'cursor-pointer no-underline text-inherit hover:text-brand'
120
- }
121
- target={'_blank'}
122
- href="https://www.remotion.pro/contact"
123
- >
124
- Contact us
125
- </a>
126
- </PriceTag>
127
- <div className={'text-[var(--subtitle)] fontbrand text-sm'}>
128
- Starting at $500 per month
129
- </div>
130
- </div>
131
- </div>
132
- </Container>
133
- );
134
- };
135
-
136
- const SEAT_PRICE = 25;
137
- const RENDER_UNIT_PRICE = 10;
138
- const WEBCODECS_UNIT_PRICE = 10;
139
-
140
- export const CompanyPricing: React.FC = () => {
141
- const [devSeatCount, setDevSeatCount] = React.useState(1);
142
- const [cloudUnitCount, setCloudUnitCount] = React.useState(1);
143
- const [webcodecsUnits, setWebcodecsUnits] = React.useState(1);
144
-
145
- const formatPrice = useCallback((price: number) => {
146
- const formatter = new Intl.NumberFormat('en-US', {
147
- style: 'currency',
148
- currency: 'USD',
149
- maximumFractionDigits: 0,
150
- });
151
- return formatter.format(price);
152
- }, []);
153
-
154
- const totalPrice = useMemo(() => {
155
- return Math.max(
156
- 100,
157
- devSeatCount * SEAT_PRICE +
158
- cloudUnitCount * RENDER_UNIT_PRICE +
159
- webcodecsUnits * WEBCODECS_UNIT_PRICE,
160
- );
161
- }, [cloudUnitCount, devSeatCount, webcodecsUnits]);
162
-
163
- const totalPriceString = useMemo(() => {
164
- return formatPrice(totalPrice);
165
- }, [formatPrice, totalPrice]);
166
-
167
- const rendersPerMonth = useMemo(() => {
168
- const formatter = new Intl.NumberFormat('en-US', {
169
- maximumFractionDigits: 0,
170
- });
171
- return formatter.format(cloudUnitCount * 1000);
172
- }, [cloudUnitCount]);
173
-
174
- const conversionsPerMonth = useMemo(() => {
175
- const formatter = new Intl.NumberFormat('en-US', {
176
- maximumFractionDigits: 0,
177
- });
178
- return formatter.format(webcodecsUnits * 1000);
179
- }, [webcodecsUnits]);
180
-
181
- return (
182
- <Container>
183
- <Audience>For collaborations and companies of 4+ people</Audience>
184
- <Title>Company License</Title>
185
- <PricingBulletPoint text="Commercial use allowed" checked />
186
- <PricingBulletPoint text="Self-hosted cloud rendering allowed" checked />
187
- <PricingBulletPoint text="Prioritized Support" checked />
188
- <PricingBulletPoint
189
- text={
190
- <span>
191
- <a
192
- href="https://remotion.dev/recorder"
193
- className="underline underline-offset-4 text-inherit"
194
- >
195
- Remotion Recorder
196
- </a>{' '}
197
- included
198
- </span>
199
- }
200
- checked
201
- />
202
- <PricingBulletPoint text="$250 Mux credits" checked>
203
- <InfoTooltip text="Credits for Mux.com. Applies only to new Mux customers." />
204
- </PricingBulletPoint>
205
- <div style={{height: 30}} />
206
- <div className={'flex flex-row items-center'}>
207
- <div style={textUnitWrapper}>
208
- <div className={'fontbrand font-bold text-lg'}>Developer Seats</div>
209
- <div className={'text-muted fontbrand text-sm'}>
210
- Number of developers working with Remotion
211
- </div>
212
- </div>
213
- <div style={{flex: 3}} />
214
- <Counter count={devSeatCount} setCount={setDevSeatCount} minCount={1} />
215
- <SmallPriceTag>
216
- $
217
- {new Intl.NumberFormat('en-US', {
218
- maximumFractionDigits: 0,
219
- }).format(SEAT_PRICE * devSeatCount)}
220
- </SmallPriceTag>
221
- </div>
222
- <div style={{height: 14}} />
223
- <div className={'flex flex-row items-center'}>
224
- <div style={textUnitWrapper}>
225
- <div className={'fontbrand font-bold text-lg'}>
226
- Cloud Rendering Units
227
- </div>
228
- <div className={'text-muted fontbrand text-sm'}>
229
- Allows for {rendersPerMonth}{' '}
230
- <a
231
- href="https://www.remotion.dev/docs/compare-ssr"
232
- className="underline underline-offset-4 text-inherit"
233
- >
234
- self-hosted renders per month
235
- </a>{' '}
236
- each
237
- </div>
238
- </div>
239
- <div style={{flex: 3}} />
240
- <Counter
241
- count={cloudUnitCount}
242
- setCount={setCloudUnitCount}
243
- minCount={0}
244
- />
245
- <SmallPriceTag>
246
- $
247
- {new Intl.NumberFormat('en-US', {
248
- maximumFractionDigits: 0,
249
- }).format(RENDER_UNIT_PRICE * cloudUnitCount)}
250
- </SmallPriceTag>
251
- </div>
252
- <div style={{height: 14}} />
253
- <div className={'flex flex-row items-center'}>
254
- <div style={textUnitWrapper}>
255
- <div className={'fontbrand font-bold text-lg'}>
256
- WebCodecs Conversion Units
257
- </div>
258
- <div className={'text-muted fontbrand text-sm'}>
259
- Allows for{' '}
260
- <a
261
- className="underline underline-offset-4 text-inherit"
262
- href="https://remotion.dev/webcodecs"
263
- >
264
- {conversionsPerMonth} client-side video conversions{' '}
265
- </a>
266
- </div>
267
- </div>
268
- <div style={{flex: 3}} />
269
- <Counter
270
- count={webcodecsUnits}
271
- setCount={setWebcodecsUnits}
272
- minCount={0}
273
- />
274
- <SmallPriceTag>
275
- $
276
- {new Intl.NumberFormat('en-US', {
277
- maximumFractionDigits: 0,
278
- }).format(RENDER_UNIT_PRICE * webcodecsUnits)}
279
- </SmallPriceTag>
280
- </div>
281
- <div style={{height: 20}} />
282
- <div className={'flex flex-row justify-end'}>
283
- <div style={{...textUnitWrapper, alignItems: 'flex-end'}}>
284
- <PriceTag>{totalPriceString}/mo</PriceTag>
285
- <BottomInfo
286
- data-visible={totalPrice <= 100}
287
- className="opacity-0 data-[visible=true]:opacity-100 transition-opacity"
288
- >
289
- The minimum is $100 per month
290
- </BottomInfo>
291
- </div>
292
- </div>
293
- </Container>
294
- );
295
- };