Innovations
heroes /

AI Summit Tilted (Diagonal Marquee · Deep Teal)

Teal-palette variant of the AI Summit hero with the dual speaker marquees rotated 45° as a unit. Two columns of uniform 3:4 portraits scroll in opposite directions along the diagonal, creating ribbons of speakers crossing the right half of the section. A subtle horizontal vignette keeps the headline readable over the photos.

Preview

Source

tsx
import { Cpu } from "lucide-react";

interface NavItem {
  label: string;
  href: string;
}

interface PortraitTile {
  src: string;
  alt: string;
}

export interface AiSummitTiltedProps {
  brand?: string;
  navItems?: NavItem[];
  registerCta?: { label: string; href: string };
  headlinePart1?: string;
  headlinePart2?: string;
  body?: string;
  primaryCta?: { label: string; href: string };
  /** Speaker portraits. Uniform 3:4 tiles, split evenly across the two tilted marquee columns. */
  portraits?: PortraitTile[];
  /** Deep base color. */
  bgColor?: string;
  /** Bright accent for glow + CTA gradient. */
  accentColor?: string;
  /** Seconds for one full marquee loop. */
  marqueeDurationSec?: number;
  /** Tilt of the column pair, in degrees. Default 22. */
  tiltDeg?: number;
  /** Path to the background video (mp4). Pass null to disable. */
  videoSrc?: string | null;
  /** Optional poster image shown while the video buffers. */
  videoPoster?: string;
}

const DEFAULT_PORTRAITS: PortraitTile[] = [
  {
    src: "https://images.unsplash.com/photo-1531123897727-8f129e1688ce?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait with dramatic red and blue lighting on orange backdrop",
  },
  {
    src: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=900&h=1200&fit=crop&crop=faces",
    alt: "Smiling speaker portrait in pink turtleneck against teal backdrop",
  },
  {
    src: "https://images.unsplash.com/photo-1525134479668-1bee5c7c6845?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait wearing a black cap against orange backdrop",
  },
  {
    src: "https://images.unsplash.com/photo-1488426862026-3ee34a7d66df?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait in red knit sweater",
  },
  {
    src: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait on teal backdrop",
  },
  {
    src: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait — woman with curly hair",
  },
  {
    src: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait — bearded man",
  },
  {
    src: "https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?w=900&h=1200&fit=crop&crop=faces",
    alt: "Speaker portrait — man in glasses",
  },
];

export default function AiSummitTilted({
  brand = "AI SUMMIT",
  navItems = [
    { label: "Sessions", href: "#sessions" },
    { label: "Speakers", href: "#speakers" },
    { label: "Ticket Options", href: "#tickets" },
    { label: "FAQ", href: "#faq" },
  ],
  registerCta = { label: "Register Your Ticket", href: "#register" },
  headlinePart1 = "AI Horizons:",
  headlinePart2 = "Shaping the Future of Intelligence",
  body = "Join us at AI Tech Summit, the premier global event where industry leaders, innovators, and visionaries converge to explore the transformative power of artificial intelligence.",
  primaryCta = { label: "Learn More", href: "#learn-more" },
  portraits = DEFAULT_PORTRAITS,
  bgColor = "#0A3D44",
  accentColor = "#22E5D0",
  marqueeDurationSec = 55,
  tiltDeg = 22,
  videoSrc = "/heroes/ai-summit/hero-video.mp4",
  videoPoster,
}: AiSummitTiltedProps) {
  const col1 = portraits.filter((_, i) => i % 2 === 0);
  const col2 = portraits.filter((_, i) => i % 2 === 1);
  // Triple the list so the strip is always much taller than the visible column.
  // 3x duplication keeps the loop seamless (any integer >=2 works; 3 gives us
  // generous overhang above and below the visible area at every keyframe).
  const col1Loop = [...col1, ...col1, ...col1];
  const col2Loop = [...col2, ...col2, ...col2];

  return (
    <section
      className="ai-summit-tilted relative isolate h-screen min-h-[760px] overflow-hidden bg-black font-sans text-white"
    >
      {/* VIDEO BACKGROUND */}
      {videoSrc && (
        <video
          autoPlay
          loop
          muted
          playsInline
          preload="auto"
          poster={videoPoster}
          className="absolute inset-0 z-0 h-full w-full object-cover"
        >
          <source src={videoSrc} type="video/mp4" />
        </video>
      )}

      {/* GRADIENT OVERLAY — strong dark on the left for headline contrast,
          fading lighter toward the right where the photo marquee sits. */}
      <div
        aria-hidden
        className="pointer-events-none absolute inset-0 z-[1]"
        style={{
          background: `linear-gradient(95deg, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.78) 28%, rgba(0,0,0,0.55) 52%, rgba(0,0,0,0.35) 78%, rgba(0,0,0,0.45) 100%)`,
        }}
      />

      {/* Color flavor — subtle accent glows sit above the gradient overlay */}
      <div
        aria-hidden
        className="pointer-events-none absolute -left-32 bottom-0 z-[2] h-[420px] w-[420px] rounded-full opacity-40 blur-3xl"
        style={{ background: accentColor }}
      />
      <div
        aria-hidden
        className="pointer-events-none absolute right-1/3 -top-24 z-[2] h-[320px] w-[320px] rounded-full opacity-30 blur-3xl"
        style={{ background: "#FFB347" }}
      />

      {/* TILTED MARQUEES — wrapper rotated as a unit; section's overflow-hidden clips the corners */}
      <div className="pointer-events-none absolute inset-0 z-10 hidden overflow-hidden lg:block">
        <div
          className="absolute flex items-center justify-center gap-5 will-change-transform"
          style={{
            width: "120vh",
            height: "260vh",
            top: "50%",
            right: "-15vh",
            transform: `translateY(-50%) rotate(${tiltDeg}deg)`,
            transformOrigin: "center center",
          }}
        >
          {/* Column 1 — scrolling DOWN (visually toward lower-right after tilt) */}
          <div className="relative h-full w-[24vh] overflow-hidden">
            <div
              className="flex flex-col gap-5 will-change-transform"
              style={{
                animation: `aiSummitTiltedDown ${marqueeDurationSec}s linear infinite`,
              }}
            >
              {col1Loop.map((p, i) => (
                <PortraitFrame key={`c1-${i}`} img={p} />
              ))}
            </div>
          </div>
          {/* Column 2 — scrolling UP (visually toward upper-left after tilt) */}
          <div className="relative h-full w-[24vh] overflow-hidden">
            <div
              className="flex flex-col gap-5 will-change-transform"
              style={{
                animation: `aiSummitTiltedUp ${marqueeDurationSec}s linear infinite`,
              }}
            >
              {col2Loop.map((p, i) => (
                <PortraitFrame key={`c2-${i}`} img={p} />
              ))}
            </div>
          </div>
        </div>
      </div>

      {/* Extra darkening behind the headline so it stays readable
          on top of both the video and the tilted photos. */}
      <div
        aria-hidden
        className="pointer-events-none absolute inset-0 z-[15] hidden lg:block"
        style={{
          background: `linear-gradient(95deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.5) 22%, rgba(0,0,0,0.2) 38%, transparent 50%)`,
        }}
      />

      {/* FOREGROUND: nav + copy */}
      <div className="relative z-20 mx-auto flex h-full max-w-7xl flex-col px-4 sm:px-6 lg:px-8">
        {/* GLASS PILL NAV */}
        <nav className="mt-6 flex items-center justify-between gap-4 rounded-full border border-white/15 bg-white/10 px-4 py-2 backdrop-blur-md sm:px-5 sm:py-3">
          <a href="/" className="flex items-center gap-2 pl-1">
            <Cpu className="h-6 w-6" strokeWidth={1.5} />
            <span className="text-sm font-semibold tracking-[0.18em] sm:text-base">
              {brand}
            </span>
          </a>
          <div className="hidden items-center gap-8 md:flex">
            {navItems.map((item) => (
              <a
                key={item.label}
                href={item.href}
                className="text-sm font-medium text-white/85 transition-colors hover:text-white"
              >
                {item.label}
              </a>
            ))}
          </div>
          <a
            href={registerCta.href}
            className="rounded-full px-4 py-2 text-xs font-semibold transition-all hover:-translate-y-0.5 sm:px-6 sm:py-2.5 sm:text-sm"
            style={{
              background: `linear-gradient(135deg, ${accentColor} 0%, #0E8C7E 100%)`,
              color: "#062326",
              boxShadow: `0 8px 24px ${accentColor}55`,
            }}
          >
            {registerCta.label}
          </a>
        </nav>

        {/* COPY BLOCK */}
        <div className="flex flex-1 items-center py-10 lg:py-16">
          <div className="max-w-xl lg:max-w-[46%]">
            <h1 className="text-[44px] font-bold leading-[1.05] tracking-tight sm:text-5xl lg:text-6xl">
              {headlinePart1}
              <br />
              {headlinePart2}
            </h1>
            <p className="mt-7 max-w-md text-base leading-relaxed text-white/75">
              {body}
            </p>
            <a
              href={primaryCta.href}
              className="mt-10 inline-flex items-center rounded-full px-9 py-3.5 text-sm font-semibold transition-all hover:-translate-y-0.5"
              style={{
                background: `linear-gradient(135deg, ${accentColor} 0%, #0E8C7E 100%)`,
                color: "#062326",
                boxShadow: `0 12px 32px ${accentColor}66`,
              }}
            >
              {primaryCta.label}
            </a>
          </div>
        </div>
      </div>

      <style>{`
        /* Strips are 3 sublists tall (tripled). Animating between -33.333% and
           -66.666% scrolls by one sublist (seamless loop) while keeping the
           visible window centered in the strip — so there's always ~1 sublist
           of photos extending off the top and off the bottom of the column. */
        @keyframes aiSummitTiltedUp {
          from { transform: translateY(-33.333%); }
          to { transform: translateY(-66.666%); }
        }
        @keyframes aiSummitTiltedDown {
          from { transform: translateY(-66.666%); }
          to { transform: translateY(-33.333%); }
        }
        @media (prefers-reduced-motion: reduce) {
          .ai-summit-tilted [style*="aiSummitTiltedUp"],
          .ai-summit-tilted [style*="aiSummitTiltedDown"] {
            animation: none !important;
          }
        }
      `}</style>
    </section>
  );
}

function PortraitFrame({ img }: { img: PortraitTile }) {
  return (
    <div className="relative aspect-[3/4] w-full shrink-0 overflow-hidden rounded-2xl bg-white/5 shadow-lg ring-1 ring-white/10">
      <img
        src={img.src}
        alt={img.alt}
        loading="eager"
        decoding="async"
        className="absolute inset-0 h-full w-full object-cover"
      />
    </div>
  );
}
Claude Code Instructions

CLI Install

npx innovations add ai-summit-tilted

Where to use it

Use for AI conferences, tech summits, speaker-led events where you want more motion than the standard AI Summit hero. Pairs well with bold/futuristic brand systems. In Astro: --- import AiSummitTilted from '../components/innovations/heroes/ai-summit-tilted'; --- <AiSummitTilted /> In Next.js (app/page.tsx): import AiSummitTilted from '@/components/innovations/heroes/ai-summit-tilted'; No client:* required — server-renderable, no hooks. CUSTOMIZATION: <AiSummitTilted brand="AI SUMMIT" navItems={[ { label: "Sessions", href: "#sessions" }, ... ]} registerCta={{ label: "Register Your Ticket", href: "#register" }} headlinePart1="AI Horizons:" headlinePart2="Shaping the Future of Intelligence" body="..." primaryCta={{ label: "Learn More", href: "#learn-more" }} portraits={[ { src: "...", alt: "..." }, ... ]} bgColor="#0A3D44" // deep teal base accentColor="#22E5D0" // bright cyan-mint accent marqueeDurationSec={55} // lower = faster tiltDeg={45} // rotation of the column pair /> ROTATION: the column pair is wrapped in a fixed 150vh × 180vh element that's translated to the right edge and rotated by tiltDeg. The section's overflow-hidden clips the diagonal corners. For a less aggressive tilt try 20–30 deg; for vertical (no tilt) use the sibling ai-summit-hero instead. VIGNETTE: a left-to-right gradient overlay sits between the marquees (z-10) and the text (z-20) so the headline stays readable on top of the photo flow. If you swap bgColor, the vignette auto-adapts (it uses the same bg color with stepped opacity). ACCESSIBILITY: animations respect prefers-reduced-motion: reduce and stop entirely for those users. MOBILE: the tilted marquee is hidden below lg. Below lg the hero collapses to the text block on its own.