diff --git a/docs/amazon-insert-video.md b/docs/amazon-insert-video.md index 84032da..ab60272 100644 --- a/docs/amazon-insert-video.md +++ b/docs/amazon-insert-video.md @@ -74,7 +74,7 @@ More generations → **more variety** in inserts, **not** denser inserts by defa | Piece | Detail | |-------|--------| -| Pool | `CATEGORY_REELS` = gen1 ∪ gen2 ∪ `REELS_JETSET` ∪ `REELS_SKI` | +| Pool | `CATEGORY_REELS` = gen1 ∪ gen2 ∪ jet-set ∪ ski ∪ Riviera | | Interleave | `interleaveReelInserts` → `src/lib/reelInserts.ts` (`minGap`/`maxGap`) | | UI grid | `ProductGrid` + `ReelInsertCard` | | Cross-promo | `excludeCategory` for room reels only | @@ -101,6 +101,8 @@ Shuffle is **computed on the client** when the grid renders. Same seed → same | Jet-set poster | `public/brand/videos/reels/posters/jetset-{vibeId}.jpg` | | Ski holiday video | `public/brand/videos/reels/ski-{vibeId}.mp4` | | Ski holiday poster | `public/brand/videos/reels/posters/ski-{vibeId}.jpg` | +| Riviera video | `public/brand/videos/reels/riviera-{vibeId}.mp4` | +| Riviera poster | `public/brand/videos/reels/posters/riviera-{vibeId}.jpg` | | R2 (optional) | `video/…` via mint — see [`imagine-r2-videos.md`](./imagine-r2-videos.md) | ### Rate limits (Imagine video) diff --git a/public/brand/videos/reels/posters/riviera-atelier.jpg b/public/brand/videos/reels/posters/riviera-atelier.jpg new file mode 100644 index 0000000..d30a646 Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-atelier.jpg differ diff --git a/public/brand/videos/reels/posters/riviera-dew.jpg b/public/brand/videos/reels/posters/riviera-dew.jpg new file mode 100644 index 0000000..da2fdb7 Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-dew.jpg differ diff --git a/public/brand/videos/reels/posters/riviera-gilded.jpg b/public/brand/videos/reels/posters/riviera-gilded.jpg new file mode 100644 index 0000000..d621fa4 Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-gilded.jpg differ diff --git a/public/brand/videos/reels/posters/riviera-luxe.jpg b/public/brand/videos/reels/posters/riviera-luxe.jpg new file mode 100644 index 0000000..6304af6 Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-luxe.jpg differ diff --git a/public/brand/videos/reels/posters/riviera-muse.jpg b/public/brand/videos/reels/posters/riviera-muse.jpg new file mode 100644 index 0000000..9b9e370 Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-muse.jpg differ diff --git a/public/brand/videos/reels/posters/riviera-sillage.jpg b/public/brand/videos/reels/posters/riviera-sillage.jpg new file mode 100644 index 0000000..9fcdd6b Binary files /dev/null and b/public/brand/videos/reels/posters/riviera-sillage.jpg differ diff --git a/public/brand/videos/reels/posters/ski-gilded.jpg b/public/brand/videos/reels/posters/ski-gilded.jpg index c96636c..bfb3fe6 100644 Binary files a/public/brand/videos/reels/posters/ski-gilded.jpg and b/public/brand/videos/reels/posters/ski-gilded.jpg differ diff --git a/public/brand/videos/reels/riviera-atelier.mp4 b/public/brand/videos/reels/riviera-atelier.mp4 new file mode 100644 index 0000000..da2a7cc Binary files /dev/null and b/public/brand/videos/reels/riviera-atelier.mp4 differ diff --git a/public/brand/videos/reels/riviera-dew.mp4 b/public/brand/videos/reels/riviera-dew.mp4 new file mode 100644 index 0000000..9e29928 Binary files /dev/null and b/public/brand/videos/reels/riviera-dew.mp4 differ diff --git a/public/brand/videos/reels/riviera-gilded.mp4 b/public/brand/videos/reels/riviera-gilded.mp4 new file mode 100644 index 0000000..d682758 Binary files /dev/null and b/public/brand/videos/reels/riviera-gilded.mp4 differ diff --git a/public/brand/videos/reels/riviera-luxe.mp4 b/public/brand/videos/reels/riviera-luxe.mp4 new file mode 100644 index 0000000..4f3b056 Binary files /dev/null and b/public/brand/videos/reels/riviera-luxe.mp4 differ diff --git a/public/brand/videos/reels/riviera-muse.mp4 b/public/brand/videos/reels/riviera-muse.mp4 new file mode 100644 index 0000000..3a70497 Binary files /dev/null and b/public/brand/videos/reels/riviera-muse.mp4 differ diff --git a/public/brand/videos/reels/riviera-sillage.mp4 b/public/brand/videos/reels/riviera-sillage.mp4 new file mode 100644 index 0000000..a42ee98 Binary files /dev/null and b/public/brand/videos/reels/riviera-sillage.mp4 differ diff --git a/public/brand/videos/reels/ski-gilded.mp4 b/public/brand/videos/reels/ski-gilded.mp4 index 16f39ff..62bcc8a 100644 Binary files a/public/brand/videos/reels/ski-gilded.mp4 and b/public/brand/videos/reels/ski-gilded.mp4 differ diff --git a/src/components/ReelInsertCard.tsx b/src/components/ReelInsertCard.tsx index 17abcd9..6484cf1 100644 --- a/src/components/ReelInsertCard.tsx +++ b/src/components/ReelInsertCard.tsx @@ -27,16 +27,25 @@ export function ReelInsertCard({ const to = reelHref(reel) const cta = reelCtaLabel(reel) const series = reelSeries(reel) - const isPersona = series === 'jetset' || series === 'ski' + const isPersona = + series === 'jetset' || series === 'ski' || series === 'riviera' const badge = - series === 'ski' ? 'Ski' : series === 'jetset' ? 'Persona' : 'Discover' + series === 'ski' + ? 'Ski' + : series === 'riviera' + ? 'Riviera' + : series === 'jetset' + ? 'Persona' + : 'Discover' const kicker = reel.kicker ?? (series === 'ski' ? 'Ski holiday' - : series === 'jetset' - ? 'House persona' - : 'From the house') + : series === 'riviera' + ? 'Riviera' + : series === 'jetset' + ? 'House persona' + : 'From the house') useEffect(() => { const el = videoRef.current diff --git a/src/data/reels.ts b/src/data/reels.ts index c15fdd0..dcd5f32 100644 --- a/src/data/reels.ts +++ b/src/data/reels.ts @@ -1,7 +1,7 @@ /** * Fashion reels for product-grid inserts and /reels. * - * Pool includes category room clips + persona series (jet-set, ski holiday). + * Pool includes category room clips + persona series (jet-set, ski, Riviera). * Product grids interleave unique reels with random 2–6 product gaps * (see lib/reelInserts.ts). * @@ -12,11 +12,11 @@ import { CATEGORY_LABELS } from './catalog' import { HEROES } from './categoryHeroes' import { VIBE_LIST } from './vibes' -/** Creative wave — 1–2 rooms, 3 jet-set, 4 ski holiday */ -export type ReelGeneration = 1 | 2 | 3 | 4 +/** Creative wave — 1–2 rooms, 3 jet-set, 4 ski, 5 Riviera */ +export type ReelGeneration = 1 | 2 | 3 | 4 | 5 /** Persona / room series for filters and badges */ -export type ReelSeries = 'room' | 'jetset' | 'ski' +export type ReelSeries = 'room' | 'jetset' | 'ski' | 'riviera' export type CategoryReel = { /** Stable id for keys / analytics */ @@ -67,7 +67,7 @@ export const REEL_CATEGORIES: Category[] = [ ] /** Latest generation number in the catalog (bump when adding a wave). */ -export const REEL_LATEST_GENERATION: ReelGeneration = 4 +export const REEL_LATEST_GENERATION: ReelGeneration = 5 const MOTION_G1: Record = { handbags: 'Soft light glide across structured leather', @@ -285,6 +285,80 @@ export const REELS_SKI: CategoryReel[] = SKI.map((j) => { } }) +/** + * Persona Riviera inserts — summer Mediterranean extravagance. + * Link to the vibe check (/quiz). + */ +const RIVIERA: { + vibeId: string + destination: string + motionLabel: string + blurb: string +}[] = [ + { + vibeId: 'luxe', + destination: 'Cap Ferrat', + motionLabel: 'Infinity pool, cream silk', + blurb: + 'Vivienne at the villa edge — gold low, sea glass calm, summer that never shouts.', + }, + { + vibeId: 'muse', + destination: 'Mykonos night', + motionLabel: 'Fuchsia satin, island lights', + blurb: + 'Camille in the whitewashed lane — soft glam against the Aegean after dark.', + }, + { + vibeId: 'sillage', + destination: 'Positano sunset', + motionLabel: 'Cliff scent, violet silk', + blurb: + 'Noor on the terrace — perfume first, pastel cliffs second. The trail holds in salt air.', + }, + { + vibeId: 'atelier', + destination: 'Portofino harbor', + motionLabel: 'Ivory linen, gold bag', + blurb: + 'Margot finishes the dock — Riva wood, structured gold, the harbor as runway.', + }, + { + vibeId: 'dew', + destination: 'Santorini morning', + motionLabel: 'White linen, caldera light', + blurb: + 'Isla on the terrace steps — barrier glow, blue domes, glass skin in thin sea air.', + }, + { + vibeId: 'gilded', + destination: 'Saint-Tropez night', + motionLabel: 'Emerald on the jetty', + blurb: + 'Aurelia at the yacht lights — bronze silk, emerald armor, collector summer.', + }, +] + +export const REELS_RIVIERA: CategoryReel[] = RIVIERA.map((j) => { + const vibe = VIBE_LIST.find((v) => v.id === j.vibeId) + const name = vibe?.avatar.name ?? j.vibeId + const personaTitle = vibe?.title ?? 'House persona' + return { + id: `riviera-${j.vibeId}`, + vibeId: j.vibeId, + series: 'riviera' as ReelSeries, + generation: 5 as ReelGeneration, + title: `${name} · ${j.destination}`, + blurb: j.blurb, + video: `/brand/videos/reels/riviera-${j.vibeId}.mp4`, + poster: `/brand/videos/reels/posters/riviera-${j.vibeId}.jpg`, + motionLabel: j.motionLabel, + href: '/quiz', + hrefLabel: 'Find your persona', + kicker: `${personaTitle} · Riviera`, + } +}) + /** * Full insert + /reels catalog (rooms + persona series). * Product-grid randomizer uses this entire pool — unique per page. @@ -294,6 +368,7 @@ export const CATEGORY_REELS: CategoryReel[] = [ ...REELS_GEN2, ...REELS_JETSET, ...REELS_SKI, + ...REELS_RIVIERA, ] /** Resolve series for filters / badges */ diff --git a/src/data/videoWatch.ts b/src/data/videoWatch.ts index 6c190c7..f0a3cdd 100644 --- a/src/data/videoWatch.ts +++ b/src/data/videoWatch.ts @@ -7,6 +7,7 @@ import { REELS_GEN1, REELS_GEN2, REELS_JETSET, + REELS_RIVIERA, REELS_SKI, reelCtaLabel, reelHref, @@ -25,7 +26,7 @@ export type WatchClip = { poster?: string aspect: WatchAspect /** Filter chip */ - group: 'category' | 'carpet' | 'cafe' | 'jetset' | 'ski' | 'all' + group: 'category' | 'carpet' | 'cafe' | 'jetset' | 'ski' | 'riviera' | 'all' /** Optional deep link */ href?: string hrefLabel?: string @@ -34,7 +35,13 @@ export type WatchClip = { function fromReel(reel: CategoryReel): WatchClip { const series = reelSeries(reel) const group = - series === 'ski' ? 'ski' : series === 'jetset' ? 'jetset' : 'category' + series === 'ski' + ? 'ski' + : series === 'riviera' + ? 'riviera' + : series === 'jetset' + ? 'jetset' + : 'category' return { id: reel.id, title: reel.title, @@ -104,6 +111,7 @@ export const WATCH_CLIPS: WatchClip[] = [ export const WATCH_CATEGORY_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'category') export const WATCH_JETSET_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'jetset') export const WATCH_SKI_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'ski') +export const WATCH_RIVIERA_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'riviera') export const WATCH_CARPET_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'carpet') export const WATCH_CAFE_CLIPS = WATCH_CLIPS.filter((c) => c.group === 'cafe') @@ -113,11 +121,13 @@ export function watchClipCount() { category: WATCH_CATEGORY_CLIPS.length, jetset: WATCH_JETSET_CLIPS.length, ski: WATCH_SKI_CLIPS.length, + riviera: WATCH_RIVIERA_CLIPS.length, carpet: WATCH_CARPET_CLIPS.length, cafe: WATCH_CAFE_CLIPS.length, gen1: REELS_GEN1.length, gen2: REELS_GEN2.length, jetsetPool: REELS_JETSET.length, skiPool: REELS_SKI.length, + rivieraPool: REELS_RIVIERA.length, } } diff --git a/src/pages/Reels.tsx b/src/pages/Reels.tsx index 0f28b99..165103d 100644 --- a/src/pages/Reels.tsx +++ b/src/pages/Reels.tsx @@ -5,6 +5,7 @@ import { REELS_GEN1, REELS_GEN2, REELS_JETSET, + REELS_RIVIERA, REELS_SKI, reelCtaLabel, reelHref, @@ -30,9 +31,9 @@ export function ReelsPage() { Moving pictures

- Ski holidays, jet-set arrivals, and short films from every room. - They appear in the shop scroll so the house finds you. Scroll to - watch. + Riviera summer, ski holidays, jet-set arrivals — and short films + from every room. They appear in the shop scroll so the house finds + you.

@@ -45,11 +46,16 @@ export function ReelsPage() {
+ {reelSeries(reel) === 'ski' ? 'Ski' - : reelSeries(reel) === 'jetset' - ? 'Persona' - : 'Film'} + : reelSeries(reel) === 'riviera' + ? 'Riviera' + : reelSeries(reel) === 'jetset' + ? 'Persona' + : 'Film'}
diff --git a/src/pages/Watch.tsx b/src/pages/Watch.tsx index bcbd29a..a028c80 100644 --- a/src/pages/Watch.tsx +++ b/src/pages/Watch.tsx @@ -8,13 +8,14 @@ import { WATCH_CATEGORY_CLIPS, WATCH_CLIPS, WATCH_JETSET_CLIPS, + WATCH_RIVIERA_CLIPS, WATCH_SKI_CLIPS, watchClipCount, type WatchClip, } from '../data/videoWatch' import { watchSeo } from '../lib/seoData' -type Filter = 'all' | 'category' | 'jetset' | 'ski' | 'carpet' | 'cafe' +type Filter = 'all' | 'category' | 'jetset' | 'ski' | 'riviera' | 'carpet' | 'cafe' /** * Visual short-film gallery — muted autoplay from the house library. @@ -27,6 +28,7 @@ export function WatchPage() { if (filter === 'category') return WATCH_CATEGORY_CLIPS if (filter === 'jetset') return WATCH_JETSET_CLIPS if (filter === 'ski') return WATCH_SKI_CLIPS + if (filter === 'riviera') return WATCH_RIVIERA_CLIPS if (filter === 'carpet') return WATCH_CARPET_CLIPS if (filter === 'cafe') return WATCH_CAFE_CLIPS return WATCH_CLIPS @@ -44,9 +46,8 @@ export function WatchPage() { The house on film

- {counts.total} short films — ski holidays, jet-set, the rooms, the - carpet, the café. Scroll to play. Sound stays off until you invite - it. + {counts.total} short films — Riviera summer, ski holidays, jet-set, + the rooms. Scroll to play. Sound stays off until you invite it.

@@ -56,6 +57,7 @@ export function WatchPage() { {( [ ['all', `All (${counts.total})`], + ['riviera', `Riviera (${counts.riviera})`], ['ski', `Ski holiday (${counts.ski})`], ['jetset', `Jet set (${counts.jetset})`], ['category', `The rooms (${counts.category})`], @@ -134,7 +136,9 @@ function WatchTile({ clip }: { clip: WatchClip }) { ? 'Jet set' : clip.group === 'ski' ? 'Ski holiday' - : 'The rooms' + : clip.group === 'riviera' + ? 'Riviera' + : 'The rooms' const body = ( <>