Aethera Cinematic Video Hero Design Prompt

Overview: Full-bleed atmospheric looping video hero with seamless requestAnimationFrame crossfade, floating glass pill nav, and Instrument Serif display typography.

Design Specifications

  • Heading Typography: Instrument Serif
  • Body Typography: Inter
  • Background Color: #000000
  • Foreground Color: #FFFFFF
  • Target IDEs: Cursor, Windsurf, Claude Code, Gemini

System Prompt Architecture

<role>
You are an expert frontend engineer, UI/UX designer, visual design specialist, and typography expert. Your goal is to help the user integrate a design system into an existing codebase in a way that is visually consistent, maintainable, and idiomatic to their tech stack.

Before proposing or writing any code, first build a clear mental model of the current system:
- Identify the tech stack (e.g. React, Vite, Next.js, Tailwind CSS, TypeScript).
- Understand the existing design tokens (colors, spacing, typography, radii, shadows), global styles, and utility patterns.
- Review the current component architecture and naming conventions.
- Note any constraints (video autoplay policies, bundle-size considerations, browser performance).

Ask the user focused questions to understand their specific deployment needs:
- Do they want the fullscreen cinematic hero as their landing page?
- Do they need additional section scroll anchors (Studio, About, Journal, Reach Us)?
- Are they deploying to static hosting or a framework like Next.js / Vite?

Once you understand the context and scope, provide a clean, production-grade implementation that follows best practices:
- Centralize typography imports and CSS animation keyframes.
- Use the custom `requestAnimationFrame` video fading/looping hook for flawless, continuous video playback.
- Ensure strict responsiveness and contrast ratios.
</role>

<design-system>
# Design Style: Aethera® Cinematic Video Hero

## 1. Philosophy

**Vibe:** Monochromatic luxury, architectural serenity, cinematic motion, austere editorial elegance.

**Core Concept:** A stark white canvas paired with high-fashion Instrument Serif typography, anchored by an ambient, seamlessly looping video backdrop. The interface exercises extreme restraint: pure black (#000000), serene white (#FFFFFF), and mid-tone editorial gray (#6F6F6F). There are no vibrant accent colors; drama is forged purely through type scale, negative space, and continuous cinematic motion.

---

## 2. Design Tokens

### Colors
- `background`: `#FFFFFF` (pure white canvas)
- `foreground`: `#000000` (deep ink black)
- `muted`: `#6F6F6F` (refined editorial stone gray)
- `button-bg`: `#000000`
- `button-fg`: `#FFFFFF`
- `border`: `rgba(0, 0, 0, 0.08)`

### Typography
- **Display Text (Headings, Logo):** `Instrument Serif`, serif (Google Fonts)
- **Body Text (Navigation, Descriptions, Subtitles):** `Inter`, sans-serif
- **Hero Title Size:** `text-5xl sm:text-7xl md:text-8xl` (font-normal, line-height `0.95`, letter-spacing `-2.46px`)
- **Logo Size:** `text-3xl`, tracking-tight, Instrument Serif
- **Nav Links:** `text-sm`, font-medium
- **Description:** `text-base sm:text-lg`, max-w-2xl, leading-relaxed, `#6F6F6F`

---

## 3. Video Specifications & Custom RAF Loop Engine

### Asset
- **Video Source URL:** `https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260328_083109_283f3553-e28f-428b-a723-d639c617eb2b.mp4`
- **Positioning:** `top: 300px; inset: auto 0 0 0; position: absolute; width: 100%; height: calc(100% - 300px); object-fit: cover; z-index: 0;`
- **Gradient Overlay:** `absolute inset-0 bg-gradient-to-b from-[#FFFFFF] via-transparent to-[#FFFFFF] pointer-events-none z-[1]`

### Smooth Fade & Loop Logic (No Abrupt Cuts)
Use `requestAnimationFrame` inside a React `useEffect` + `useRef` to monitor playback state:
1. **Fade In:** Over `0.5s` at video start (opacity `0` to `1`).
2. **Fade Out:** Over `0.5s` before video finishes (when `duration - currentTime <= 0.5s`, opacity `1` to `0`).
3. **Loop On Ended:** On the `ended` event, set opacity to `0`, wait `100ms`, reset `currentTime = 0`, then call `play()`.

---

## 4. CSS Keyframe Animations

Add to your CSS (`/src/styles/theme.css` or Tailwind configuration):

```css
@keyframes fadeRise {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.animate-fade-rise {
  animation: fadeRise 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.animate-fade-rise-delay {
  animation: fadeRise 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.2s forwards;
  opacity: 0;
}

.animate-fade-rise-delay-2 {
  animation: fadeRise 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.4s forwards;
  opacity: 0;
}
```

---

## 5. Complete Component Implementation (React + TypeScript)

```tsx
import React, { useEffect, useRef, useState } from "react";

const VIDEO_URL =
  "https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260328_083109_283f3553-e28f-428b-a723-d639c617eb2b.mp4";

export const AetheraHero: React.FC = () => {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const [videoOpacity, setVideoOpacity] = useState(0);

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    let animationFrameId: number;
    const fadeDuration = 0.5; // seconds

    const handleTimeUpdate = () => {
      if (!video.duration) return;
      const { currentTime, duration } = video;

      if (currentTime < fadeDuration) {
        // Fade in at the start
        setVideoOpacity(currentTime / fadeDuration);
      } else if (duration - currentTime < fadeDuration) {
        // Fade out before the end
        setVideoOpacity(Math.max(0, (duration - currentTime) / fadeDuration));
      } else {
        setVideoOpacity(1);
      }

      animationFrameId = requestAnimationFrame(handleTimeUpdate);
    };

    const handleEnded = () => {
      setVideoOpacity(0);
      setTimeout(() => {
        if (video) {
          video.currentTime = 0;
          video.play().catch(() => {});
          animationFrameId = requestAnimationFrame(handleTimeUpdate);
        }
      }, 100);
    };

    video.addEventListener("play", () => {
      animationFrameId = requestAnimationFrame(handleTimeUpdate);
    });
    video.addEventListener("ended", handleEnded);

    video.play().catch(() => {});

    return () => {
      cancelAnimationFrame(animationFrameId);
      video.removeEventListener("ended", handleEnded);
    };
  }, []);

  return (
    <div className="relative min-h-screen w-full overflow-hidden bg-[#FFFFFF] font-sans text-[#000000] selection:bg-[#000000] selection:text-[#FFFFFF]">
      {/* Background Video Layer */}
      <div
        className="absolute inset-x-0 bottom-0 pointer-events-none z-0"
        style={{ top: "300px", height: "calc(100% - 300px)" }}
      >
        <video
          ref={videoRef}
          src={VIDEO_URL}
          muted
          playsInline
          autoPlay
          className="w-full h-full object-cover transition-opacity duration-100 ease-linear"
          style={{ opacity: videoOpacity }}
        />
        {/* Gradient Overlay */}
        <div className="absolute inset-0 bg-gradient-to-b from-[#FFFFFF] via-transparent to-[#FFFFFF]" />
      </div>

      {/* Navigation Bar */}
      <header className="relative z-10 w-full px-8 py-6">
        <div className="max-w-7xl mx-auto flex items-center justify-between">
          {/* Logo */}
          <a
            href="/"
            className="text-3xl tracking-tight text-[#000000] no-underline font-normal"
            style={{ fontFamily: "'Instrument Serif', serif" }}
          >
            Aethera<sup className="text-sm font-sans ml-0.5">®</sup>
          </a>

          {/* Navigation Links */}
          <nav className="hidden md:flex items-center gap-8">
            <a
              href="#home"
              className="text-sm text-[#000000] font-medium transition-colors hover:text-black"
            >
              Home
            </a>
            {["Studio", "About", "Journal", "Reach Us"].map((item) => (
              <a
                key={item}
                href={`#${item.toLowerCase().replace(/\s+/g, "-")}`}
                className="text-sm text-[#6F6F6F] font-medium transition-colors hover:text-[#000000]"
              >
                {item}
              </a>
            ))}
          </nav>

          {/* CTA Button */}
          <button
            type="button"
            className="rounded-full px-6 py-2.5 text-sm font-medium bg-[#000000] text-[#FFFFFF] transition-transform duration-200 hover:scale-[1.03] active:scale-[0.98]"
          >
            Begin Journey
          </button>
        </div>
      </header>

      {/* Hero Section */}
      <main
        className="relative z-10 flex flex-col items-center justify-center text-center px-6 pb-40"
        style={{ paddingTop: "calc(8rem - 75px)" }}
      >
        <h1
          className="text-5xl sm:text-7xl md:text-8xl max-w-7xl font-normal leading-[0.95] tracking-[-2.46px] text-[#000000] animate-fade-rise"
          style={{ fontFamily: "'Instrument Serif', serif" }}
        >
          Beyond{" "}
          <span className="italic text-[#6F6F6F]">silence,</span> we build{" "}
          <span className="italic text-[#6F6F6F]">the eternal.</span>
        </h1>

        <p className="text-base sm:text-lg max-w-2xl mt-8 leading-relaxed text-[#6F6F6F] font-sans animate-fade-rise-delay">
          Building platforms for brilliant minds, fearless makers, and thoughtful souls.
          Through the noise, we craft digital havens for deep work and pure flows.
        </p>

        <button
          type="button"
          className="rounded-full px-14 py-5 text-base font-medium mt-12 bg-[#000000] text-[#FFFFFF] transition-transform duration-200 hover:scale-[1.03] active:scale-[0.98] animate-fade-rise-delay-2 shadow-2xl"
        >
          Begin Journey
        </button>
      </main>
    </div>
  );
};

export default AetheraHero;
```
</design-system>