Add Summertide VFX reel: hero loop, Play reel lightbox, project page
- 15s muted hero loop (1280w, 2.3 MB) cut from finished shots - Full reel re-encoded for web (letterbox cropped, CRF 28, faststart, 45 MB) - PlayReel lightbox component; Summertide project with breakdown stills Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -1,5 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import Fox from "./Fox";
|
||||
import PlayReel from "./PlayReel";
|
||||
import Timecode from "./Timecode";
|
||||
import { site } from "@/content/site";
|
||||
|
||||
@@ -9,7 +10,7 @@ export default function Hero() {
|
||||
{/* Plate: showreel if configured, otherwise a moving light field */}
|
||||
{site.reel ? (
|
||||
<video
|
||||
className="absolute inset-0 h-full w-full object-cover opacity-60"
|
||||
className="absolute inset-0 h-full w-full object-cover opacity-55"
|
||||
src={site.reel}
|
||||
poster={site.reelPoster ?? undefined}
|
||||
autoPlay
|
||||
@@ -57,7 +58,12 @@ export default function Hero() {
|
||||
<p className="max-w-md text-base leading-relaxed text-muted md:text-lg">
|
||||
Compositing, CG and finishing for series, film and commercials, built on a pipeline we wrote ourselves.
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{site.fullReel && (
|
||||
<span className="mr-3">
|
||||
<PlayReel src={site.fullReel} poster={site.reelPoster ?? undefined} />
|
||||
</span>
|
||||
)}
|
||||
<Link
|
||||
href="/work/"
|
||||
className="bg-fg px-5 py-3.5 whitespace-nowrap md:px-6 font-mono text-xs tracking-[0.2em] text-ink uppercase transition-colors hover:bg-accent"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
// "Play reel" button that opens the full reel in a fullscreen lightbox.
|
||||
export default function PlayReel({ src, poster, label = "Play reel" }: { src: string; poster?: string; label?: string }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
|
||||
document.addEventListener("keydown", onKey);
|
||||
document.body.style.overflow = "hidden";
|
||||
videoRef.current?.play().catch(() => {});
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKey);
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
className="group flex items-center gap-3 font-mono text-xs tracking-[0.2em] whitespace-nowrap uppercase"
|
||||
>
|
||||
<span className="flex h-11 w-11 items-center justify-center rounded-full border border-white/40 transition-colors group-hover:border-accent group-hover:bg-accent">
|
||||
<svg viewBox="0 0 10 12" className="ml-0.5 h-3 w-3 fill-current" aria-hidden="true">
|
||||
<path d="M0 0l10 6-10 6z" />
|
||||
</svg>
|
||||
</span>
|
||||
{label}
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Showreel"
|
||||
className="fixed inset-0 z-[70] flex items-center justify-center bg-black/95 p-4 md:p-10"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(false)}
|
||||
className="absolute top-4 right-5 font-mono text-xs tracking-[0.2em] text-muted uppercase hover:text-fg"
|
||||
>
|
||||
Close ✕
|
||||
</button>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={src}
|
||||
poster={poster}
|
||||
controls
|
||||
playsInline
|
||||
className="max-h-full w-full max-w-7xl bg-black"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user