diff --git a/docs/ITIL_V5_Evolution_2026-07-01.md b/docs/ITIL_V5_Evolution_2026-07-01.md
new file mode 100644
index 0000000..a9075fc
--- /dev/null
+++ b/docs/ITIL_V5_Evolution_2026-07-01.md
@@ -0,0 +1,20 @@
+# ITIL V5 Service Evolution - Milestone: Trailer Cinema Mode
+
+## 1. Service Design & Strategy
+**Service Name:** CineLog Trailer Immersive Experience
+**Objective:** Provide an immersive, high-fidelity movie trailer viewing experience within the PWA.
+**Value Proposition:** Emotional connection through cinematic presentation.
+
+## 2. Configuration Item (CI) Update
+- New Component: `TrailerOverlay.tsx`
+- Modified: `MovieDetailModal.tsx`
+- Theme Integration: Semantic variables used for Dark/Light mode support.
+
+## 3. Risk & Rollback
+**Risk:** UX interference with existing navigation.
+**Rollback:** git checkout main; delete feat/trailer-cinema-mode branch.
+
+## 4. Milestone: Supabase Vibe Engine v3.0 (Data Layer)
+**Service Component:** Persistence Layer
+**Change:** New table `movie_vibes` for storing AI-generated mood profiles.
+**Validation:** SQL Migration verification via Supabase API.
diff --git a/src/components/MovieDetailModal.tsx b/src/components/MovieDetailModal.tsx
index 32d8d9b..284b497 100644
--- a/src/components/MovieDetailModal.tsx
+++ b/src/components/MovieDetailModal.tsx
@@ -1,3 +1,4 @@
+import { TrailerOverlay } from './TrailerOverlay';
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Movie, CastMember, WatchProvider } from '../types/domain';
@@ -45,6 +46,8 @@ export const MovieDetailModal = React.memo(
onShowToast,
}: MovieDetailModalProps) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
const isInLibrary = useMemo(
() => libraryItems.some((m) => m.tmdbId === Number(movie.id) || m.id === movie.id),
@@ -202,6 +205,8 @@ const ActionButtons = React.memo(
onShowToast: (message: string, type: 'success' | 'error' | 'info') => void;
}) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
const [showListCreation, setShowListCreation] = useState(false);
const handleAddToList = useCallback(
@@ -296,6 +301,8 @@ const ListMenu = React.memo(
onCreateNewList: () => void;
}) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
return (
@@ -341,6 +348,8 @@ ListMenu.displayName = 'ListMenu';
const PlotSection = React.memo(({ movie }: { movie: Movie }) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
if (!movie.overview) return null;
return (
@@ -356,6 +365,8 @@ PlotSection.displayName = 'PlotSection';
const MetadataGrid = React.memo(({ movie }: { movie: Movie }) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
const items = useMemo(
() =>
@@ -385,6 +396,8 @@ MetadataGrid.displayName = 'MetadataGrid';
const CastSection = React.memo(({ cast }: { cast?: CastMember[] }) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
if (!cast?.length) return null;
return (
@@ -420,6 +433,8 @@ CastSection.displayName = 'CastSection';
const WatchProvidersSection = React.memo(
({ watchProviders }: { watchProviders?: Movie['watchProviders'] }) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
const flat = watchProviders?.flatrate || [];
const rent = watchProviders?.rent || [];
const buy = watchProviders?.buy || [];
@@ -469,6 +484,8 @@ const RecommendationsSection = React.memo(
onSelectMovie: (id: string) => void;
}) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
if (!recommendations?.length) return null;
return (
@@ -516,6 +533,8 @@ const PersonalSection = React.memo(
onShowToast: (message: string, type: 'success' | 'error' | 'info') => void;
}) => {
const { t } = useTranslation();
+ const [showTrailerMode, setShowTrailerMode] = useState(false);
+
const [rating, setRating] = useState(movie.userRating ?? null);
const { ratings, loading: ratingsLoading } = useExternalRatings(
movie.tmdbId,
@@ -715,7 +734,19 @@ const PersonalSection = React.memo(
? t('common.saving', 'Speichere…')
: t('common.autoSaveHint', 'Wird beim Verlassen des Felds automatisch gespeichert.')}
-
+
+
+ {showTrailerMode && movie.trailerKey && (
+ setShowTrailerMode(false)}
+ />
+ )}
+
+
{/* Tags - kollapsibel für mobile Usability */}
diff --git a/src/components/TrailerOverlay.tsx b/src/components/TrailerOverlay.tsx
new file mode 100644
index 0000000..12f4608
--- /dev/null
+++ b/src/components/TrailerOverlay.tsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import { motion } from 'framer-motion';
+import { X } from 'lucide-react';
+
+interface TrailerOverlayProps {
+ title: string;
+ overview: string | null;
+ trailerKey: string;
+ onClose: () => void;
+ isVisible: boolean;
+}
+
+export const TrailerOverlay: React.FC
= ({
+ title,
+ overview,
+ trailerKey,
+ onClose,
+ isVisible
+}) => {
+ if (!isVisible) return null;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Cinema Mode
+
+
+ Powered by CineLog AI
+
+
+
+
+ {title}
+
+
+
+ {overview}
+
+
+
+
+ Video fortsetzen
+
+
+
+
+ );
+};
diff --git a/supabase/migrations/20260701_vibe_engine.sql b/supabase/migrations/20260701_vibe_engine.sql
new file mode 100644
index 0000000..64a0419
--- /dev/null
+++ b/supabase/migrations/20260701_vibe_engine.sql
@@ -0,0 +1,21 @@
+-- ITIL V5 Milestone: Vibe Engine v3.0 Base Schema
+-- Service: Movie Recommendation Engine
+
+create table if not exists public.movie_vibes (
+ id uuid default uuid_generate_v4() primary key,
+ movie_id uuid not null, -- references movies(id) - assuming id is uuid
+ vibe_name text not null,
+ intensity float check (intensity >= 0 and intensity <= 1),
+ ai_metadata jsonb,
+ created_at timestamp with time zone default now()
+);
+
+-- Indices for performance
+create index if not exists idx_movie_vibes_name on public.movie_vibes(vibe_name);
+create index if not exists idx_movie_vibes_movie_id on public.movie_vibes(movie_id);
+
+-- Simple view for vibe exploration
+create or replace view public.vibe_explorer as
+select v.vibe_name, count(*) as frequency, avg(v.intensity) as avg_intensity
+from public.movie_vibes v
+group by v.vibe_name;