This commit is contained in:
Andras Schmelczer 2026-05-14 20:42:48 +01:00
parent 273d7a83ee
commit 084117cea8
48 changed files with 2283 additions and 890 deletions

View file

@ -1,6 +1,7 @@
import { lazy, Suspense, useState, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useFadeInRef } from '../../hooks/useFadeIn';
import { useIsMobile } from '../../hooks/useIsMobile';
import HexCanvas from './HexCanvas';
import HomeFinalCta from './HomeFinalCta';
import BottomIllustration from './BottomIllustration';
@ -15,7 +16,7 @@ const HOME_SECTION_HEADING_CLASS =
'text-2xl md:text-3xl font-bold text-navy-950 dark:text-warm-100';
const HOME_BODY_CLASS = 'text-base leading-relaxed text-warm-600 dark:text-warm-400';
const HOME_PRIMARY_BUTTON_CLASS =
'bg-coral-500 text-white rounded-lg font-semibold hover:bg-coral-600 transition-colors text-base shadow-lg shadow-coral-500/25 text-center';
'border border-[#d27a11] bg-[#f09a22] text-navy-950 rounded-lg font-semibold hover:bg-[#df8614] transition-colors text-base shadow-lg shadow-[#7a3905]/25 text-center';
const PRODUCT_DEMO_VIDEO_BY_LANGUAGE: Record<string, string> = {
en: 'recording',
de: 'recording-de',
@ -34,9 +35,13 @@ function ProductShowcaseFallback({ className = '' }: { className?: string }) {
);
}
function getProductDemoSlug(language: string | undefined): string {
function getProductDemoSlug(language: string | undefined, isMobile: boolean): string {
const code = language?.toLowerCase().split('-')[0] ?? 'en';
return PRODUCT_DEMO_VIDEO_BY_LANGUAGE[code] ?? PRODUCT_DEMO_VIDEO_BY_LANGUAGE.en;
const base = PRODUCT_DEMO_VIDEO_BY_LANGUAGE[code] ?? PRODUCT_DEMO_VIDEO_BY_LANGUAGE.en;
// Mobile cuts (9:16, 540x960) are published as `<base>-mobile` alongside
// the 16:9 desktop cuts. The recorder pipeline writes both every render —
// see video/src/storyboard.ts.
return isMobile ? `${base}-mobile` : base;
}
function highlightBrandText(text: string) {
@ -57,12 +62,13 @@ function highlightBrandText(text: string) {
function ProductDemoVideo() {
const { t, i18n } = useTranslation();
const isMobile = useIsMobile();
const sectionRef = useRef<HTMLDivElement | null>(null);
const videoRef = useRef<HTMLVideoElement | null>(null);
const currentVideoSrcRef = useRef<string | null>(null);
const [shouldLoadVideo, setShouldLoadVideo] = useState(false);
const [isVideoPlaying, setIsVideoPlaying] = useState(false);
const productDemoSlug = getProductDemoSlug(i18n.language);
const productDemoSlug = getProductDemoSlug(i18n.language, isMobile);
const productDemoVideoSrc = `/video/${productDemoSlug}.mp4`;
const productDemoPosterSrc = `/video/${productDemoSlug}.jpg`;
@ -123,7 +129,11 @@ function ProductDemoVideo() {
<h2 className={`${HOME_SECTION_HEADING_CLASS} mb-5 text-center`}>
{t('home.productDemoLabel')}
</h2>
<div className="relative overflow-hidden rounded-lg border border-warm-200 bg-navy-950 shadow-sm dark:border-warm-700">
<div
className={`relative overflow-hidden rounded-lg border border-warm-200 bg-navy-950 shadow-sm dark:border-warm-700 ${
isMobile ? 'mx-auto max-w-sm' : ''
}`}
>
<video
ref={videoRef}
src={shouldLoadVideo ? productDemoVideoSrc : undefined}
@ -131,7 +141,9 @@ function ProductDemoVideo() {
controls
playsInline
preload={shouldLoadVideo ? 'metadata' : 'none'}
className="block aspect-video w-full bg-navy-950 object-contain"
className={`block w-full bg-navy-950 object-contain ${
isMobile ? 'aspect-[9/16]' : 'aspect-video'
}`}
aria-label={t('home.productDemoLabel')}
onPlay={() => setIsVideoPlaying(true)}
onPause={() => setIsVideoPlaying(false)}