← back to IWasCute

src/app/v2/page.tsx

507 lines

'use client'

import Link from 'next/link'
import { motion } from 'framer-motion'
import { Camera, Shield, DollarSign, ArrowRight, Sparkles } from 'lucide-react'
import clsx from 'clsx'

// ---------------------------------------------------------------------------
// AnimationStyles — injects CSS keyframes for blob motion + glass utilities.
// Content is a static compile-time string with NO user-supplied data, so
// rendering it via innerHTML carries zero XSS risk (same as any <style> tag
// in a CSS file or styled-component). This is a standard Next.js / React
// pattern for scoped global keyframes in a Client Component.
// ---------------------------------------------------------------------------
function AnimationStyles() {
  const css = [
    '@keyframes blob-float-a{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(40px,-60px) scale(1.08)}}',
    '@keyframes blob-float-b{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(-50px,40px) scale(1.05)}}',
    '@keyframes blob-float-c{0%{transform:translate(0px,0px) scale(1.02)}100%{transform:translate(30px,50px) scale(0.96)}}',
    '@keyframes blob-float-d{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(-30px,-40px) scale(1.06)}}',
    '@keyframes blob-float-e{0%{transform:translate(0px,0px) scale(0.98)}100%{transform:translate(20px,30px) scale(1.04)}}',
    '@keyframes shimmer{0%{background-position:-200% center}100%{background-position:200% center}}',
    '.blob-a{animation:blob-float-a 18s ease-in-out infinite alternate}',
    '.blob-b{animation:blob-float-b 22s ease-in-out infinite alternate}',
    '.blob-c{animation:blob-float-c 16s ease-in-out infinite alternate}',
    '.blob-d{animation:blob-float-d 20s ease-in-out infinite alternate}',
    '.blob-e{animation:blob-float-e 14s ease-in-out infinite alternate}',
    '.glass{background:rgba(255,255,255,0.38);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,0.32)}',
    '.glass-dark{background:rgba(255,255,255,0.22);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(255,255,255,0.24)}',
    '.shimmer-text{background:linear-gradient(90deg,#3D405B 0%,#F4846F 30%,#8BBCAA 55%,#3D405B 80%);background-size:200% auto;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:shimmer 5s linear infinite}',
    '.btn-primary{background:linear-gradient(135deg,#FFB5A7 0%,#F4846F 100%);transition:transform 0.2s ease,box-shadow 0.2s ease,filter 0.2s ease}',
    '.btn-primary:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 12px 40px rgba(244,132,111,0.45);filter:brightness(1.05)}',
    '.btn-secondary{transition:transform 0.2s ease,box-shadow 0.2s ease,background 0.2s ease}',
    '.btn-secondary:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 12px 32px rgba(139,188,170,0.35);background:rgba(255,255,255,0.55)}',
    '.divider-v{width:1px;background:rgba(61,64,91,0.12);align-self:stretch}',
  ].join('\n')

  // React renders <style> tags safely — this is static content, not user data.
  return <style>{css}</style>
}

// ---------------------------------------------------------------------------
// Animated gradient mesh — five large blobs using pure CSS keyframes.
// Fixed so it covers the entire viewport regardless of page scroll length.
// ---------------------------------------------------------------------------
function GradientMeshBackground() {
  return (
    <div
      aria-hidden="true"
      className="pointer-events-none fixed inset-0 overflow-hidden"
      style={{ zIndex: 0 }}
    >
      {/* Blob A — top-left, peach */}
      <div
        className="blob-a absolute -top-24 -left-24 w-[520px] h-[520px] rounded-full blur-3xl"
        style={{ background: '#FFB5A7', opacity: 0.42 }}
      />
      {/* Blob B — top-right, sage */}
      <div
        className="blob-b absolute -top-12 right-0 w-[480px] h-[480px] rounded-full blur-3xl"
        style={{ background: '#B5D5C5', opacity: 0.38 }}
      />
      {/* Blob C — center, cream-dark */}
      <div
        className="blob-c absolute top-1/3 left-1/4 w-[400px] h-[400px] rounded-full blur-3xl"
        style={{ background: '#FFE0CB', opacity: 0.50 }}
      />
      {/* Blob D — bottom-left, peach-dark */}
      <div
        className="blob-d absolute bottom-0 -left-12 w-[440px] h-[440px] rounded-full blur-3xl"
        style={{ background: '#F4846F', opacity: 0.28 }}
      />
      {/* Blob E — bottom-right, sage-dark */}
      <div
        className="blob-e absolute -bottom-16 right-8 w-[460px] h-[460px] rounded-full blur-3xl"
        style={{ background: '#8BBCAA', opacity: 0.35 }}
      />
    </div>
  )
}

// ---------------------------------------------------------------------------
// NavBar
// ---------------------------------------------------------------------------
function NavBar() {
  return (
    <nav
      className="glass relative z-20 flex items-center justify-between px-6 py-4 md:px-12 mx-4 mt-4 rounded-2xl"
      style={{ color: 'var(--color-brown)' }}
    >
      <div className="flex items-center gap-2">
        <Sparkles
          size={18}
          strokeWidth={1.8}
          style={{ color: 'var(--color-peach-dark)' }}
        />
        <span
          className="text-xl font-black tracking-tighter"
          style={{ color: 'var(--color-brown)' }}
        >
          i was cute
        </span>
      </div>

      <div
        className="hidden sm:flex items-center gap-6 text-sm font-medium"
        style={{ color: 'var(--color-warm-gray)' }}
      >
        <Link href="/browse" className="hover:opacity-70 transition-opacity">
          Browse
        </Link>
        <Link href="/licensor" className="hover:opacity-70 transition-opacity">
          Upload
        </Link>
        <Link href="/login" className="hover:opacity-70 transition-opacity">
          Login
        </Link>
      </div>

      <Link
        href="/licensor"
        className="btn-primary inline-flex items-center gap-1.5 px-5 py-2.5 rounded-xl text-sm font-semibold"
        style={{ color: 'white' }}
      >
        Get Started
        <ArrowRight size={14} strokeWidth={2.5} />
      </Link>
    </nav>
  )
}

// ---------------------------------------------------------------------------
// Hero Card — glassmorphism panel, centered on the page
// ---------------------------------------------------------------------------
function HeroCard() {
  return (
    <section className="relative z-10 flex flex-col items-center justify-center px-4 pt-16 pb-8 md:pt-24">
      <motion.div
        whileHover={{ scale: 1.005 }}
        transition={{ type: 'spring', stiffness: 300, damping: 30 }}
        className="glass w-full max-w-3xl rounded-3xl px-8 py-14 md:px-16 md:py-20 flex flex-col items-center text-center gap-7"
        style={{
          boxShadow:
            '0 32px 80px rgba(61,64,91,0.10), 0 0 0 1px rgba(255,255,255,0.30)',
        }}
      >
        {/* Badge */}
        <div
          className="glass-dark inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-xs font-semibold tracking-widest uppercase"
          style={{ color: 'var(--color-warm-gray)' }}
        >
          <span
            className="w-1.5 h-1.5 rounded-full"
            style={{ background: 'var(--color-peach-dark)' }}
          />
          Childhood Photo Marketplace
        </div>

        {/* Headline */}
        <h1 className="text-6xl md:text-8xl font-black tracking-tighter leading-[0.9] select-none">
          <span className="shimmer-text">i was</span>
          <br />
          <span style={{ color: 'var(--color-peach-dark)' }}>cute</span>
        </h1>

        {/* Tagline */}
        <p
          className="text-xl md:text-2xl font-light tracking-wide max-w-md leading-relaxed"
          style={{ color: 'var(--color-warm-gray)' }}
        >
          Your childhood photos, legally licensed and earning for you.
        </p>

        {/* Description */}
        <p
          className="text-base max-w-lg leading-relaxed"
          style={{ color: 'var(--color-warm-gray)', opacity: 0.85 }}
        >
          Turn nostalgic memories into royalties. Upload, verify rights, and
          let advertisers, filmmakers, and designers celebrate the real you —
          at your most adorable.
        </p>

        {/* CTA buttons */}
        <div className="flex flex-col sm:flex-row gap-3 mt-2 w-full sm:w-auto">
          <Link
            href="/licensor"
            className="btn-primary inline-flex items-center justify-center gap-2 px-8 py-4 rounded-2xl text-base font-bold tracking-wide"
            style={{ color: 'white' }}
          >
            <Camera size={18} strokeWidth={2} />
            Upload Your Photos
          </Link>
          <Link
            href="/browse"
            className="btn-secondary glass-dark inline-flex items-center justify-center gap-2 px-8 py-4 rounded-2xl text-base font-semibold tracking-wide"
            style={{ color: 'var(--color-brown)' }}
          >
            Browse Library
            <ArrowRight size={16} strokeWidth={2} />
          </Link>
        </div>

        {/* Trust micro-copy */}
        <p
          className="text-xs font-medium tracking-wide"
          style={{ color: 'var(--color-warm-gray)', opacity: 0.6 }}
        >
          No credit card required&nbsp;&middot;&nbsp;Free to upload&nbsp;&middot;&nbsp;You
          keep all rights
        </p>
      </motion.div>
    </section>
  )
}

// ---------------------------------------------------------------------------
// Stats Bar — glassmorphism strip with 3 metrics
// ---------------------------------------------------------------------------
const STATS = [
  { value: '10K+', label: 'Photos Licensed' },
  { value: '500+', label: 'Verified Licensors' },
  { value: '100%', label: 'Rights Verified' },
] as const

function StatsBar() {
  return (
    <section className="relative z-10 px-4 py-4">
      <div
        className="glass mx-auto max-w-2xl rounded-2xl px-6 py-5 flex items-center justify-around gap-4"
        style={{ boxShadow: '0 8px 32px rgba(61,64,91,0.08)' }}
      >
        {STATS.map((stat, i) => (
          <div key={stat.label} className="flex items-center gap-4">
            <div className="flex flex-col items-center gap-0.5">
              <span
                className="text-2xl md:text-3xl font-black tracking-tight"
                style={{ color: 'var(--color-peach-dark)' }}
              >
                {stat.value}
              </span>
              <span
                className="text-xs font-semibold tracking-widest uppercase"
                style={{ color: 'var(--color-warm-gray)', opacity: 0.75 }}
              >
                {stat.label}
              </span>
            </div>
            {i < STATS.length - 1 && (
              <div className="divider-v hidden sm:block" />
            )}
          </div>
        ))}
      </div>
    </section>
  )
}

// ---------------------------------------------------------------------------
// Feature Grid — three glassmorphism cards
// ---------------------------------------------------------------------------
interface Feature {
  icon: React.ReactNode
  title: string
  description: string
  accent: string
  step: string
}

const FEATURES: Feature[] = [
  {
    icon: <Camera size={28} strokeWidth={1.6} />,
    title: 'Upload Photos',
    description:
      'Drag and drop your childhood photos in any format. We handle compression, metadata extraction, and secure storage.',
    accent: '#FFB5A7',
    step: '01',
  },
  {
    icon: <Shield size={28} strokeWidth={1.6} />,
    title: 'Verify Rights',
    description:
      'Our guided rights-clearance wizard walks you through copyright and likeness release in under five minutes.',
    accent: '#B5D5C5',
    step: '02',
  },
  {
    icon: <DollarSign size={28} strokeWidth={1.6} />,
    title: 'Earn Royalties',
    description:
      'When your photo is licensed by a brand or filmmaker, royalties land directly in your account. Automatically.',
    accent: '#FFE0CB',
    step: '03',
  },
]

function FeatureGrid() {
  return (
    <section className="relative z-10 px-4 py-10 md:py-16">
      <div className="max-w-4xl mx-auto">
        {/* Section header */}
        <div className="text-center mb-10">
          <h2
            className="text-3xl md:text-4xl font-black tracking-tight mb-3"
            style={{ color: 'var(--color-brown)' }}
          >
            How it works
          </h2>
          <p
            className="text-base max-w-md mx-auto leading-relaxed"
            style={{ color: 'var(--color-warm-gray)', opacity: 0.8 }}
          >
            Three steps from photo to paycheck.
          </p>
        </div>

        {/* Cards */}
        <div className="grid grid-cols-1 md:grid-cols-3 gap-5">
          {FEATURES.map((feature) => (
            <motion.div
              key={feature.title}
              whileHover={{ y: -6, scale: 1.01 }}
              transition={{ type: 'spring', stiffness: 400, damping: 28 }}
              className={clsx(
                'glass rounded-3xl p-8 flex flex-col gap-5 cursor-default',
              )}
              style={{ boxShadow: '0 8px 32px rgba(61,64,91,0.07)' }}
            >
              {/* Icon bubble */}
              <div
                className="w-14 h-14 rounded-2xl flex items-center justify-center flex-shrink-0"
                style={{ background: feature.accent, color: 'var(--color-brown)' }}
              >
                {feature.icon}
              </div>

              {/* Step label */}
              <span
                className="text-xs font-bold tracking-widest uppercase"
                style={{ color: 'var(--color-warm-gray)', opacity: 0.45 }}
              >
                Step {feature.step}
              </span>

              <div className="flex flex-col gap-2">
                <h3
                  className="text-lg font-bold tracking-tight"
                  style={{ color: 'var(--color-brown)' }}
                >
                  {feature.title}
                </h3>
                <p
                  className="text-sm leading-relaxed"
                  style={{ color: 'var(--color-warm-gray)', opacity: 0.85 }}
                >
                  {feature.description}
                </p>
              </div>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  )
}

// ---------------------------------------------------------------------------
// CTA Banner — second conversion touchpoint
// ---------------------------------------------------------------------------
function CallToActionBanner() {
  return (
    <section className="relative z-10 px-4 py-8 md:py-12">
      <motion.div
        whileHover={{ scale: 1.008 }}
        transition={{ type: 'spring', stiffness: 300, damping: 30 }}
        className="glass max-w-3xl mx-auto rounded-3xl px-8 py-12 md:px-16 flex flex-col items-center text-center gap-6"
        style={{
          boxShadow:
            '0 20px 60px rgba(244,132,111,0.14), 0 0 0 1px rgba(255,255,255,0.28)',
          background: 'rgba(255,241,230,0.45)',
        }}
      >
        <h2
          className="text-3xl md:text-4xl font-black tracking-tight"
          style={{ color: 'var(--color-brown)' }}
        >
          Ready to be remembered?
        </h2>
        <p
          className="text-base max-w-md leading-relaxed"
          style={{ color: 'var(--color-warm-gray)' }}
        >
          Join hundreds of people who have already turned their most charming
          memories into a passive income stream.
        </p>
        <Link
          href="/licensor"
          className="btn-primary inline-flex items-center gap-2 px-10 py-4 rounded-2xl text-base font-bold"
          style={{ color: 'white' }}
        >
          Start Uploading — It&apos;s Free
          <ArrowRight size={16} strokeWidth={2.5} />
        </Link>
      </motion.div>
    </section>
  )
}

// ---------------------------------------------------------------------------
// Footer
// ---------------------------------------------------------------------------
const FOOTER_LINKS = [
  { href: '/browse', label: 'Browse' },
  { href: '/licensor', label: 'Upload' },
  { href: '/login', label: 'Login' },
  { href: '#', label: 'Privacy' },
  { href: '#', label: 'Terms' },
] as const

function Footer() {
  return (
    <footer className="relative z-10 px-6 py-10 flex flex-col items-center gap-4">
      <div className="flex items-center gap-2 mb-1">
        <Sparkles
          size={14}
          strokeWidth={1.8}
          style={{ color: 'var(--color-peach-dark)' }}
        />
        <span
          className="text-sm font-black tracking-tighter"
          style={{ color: 'var(--color-brown)' }}
        >
          i was cute
        </span>
      </div>

      <nav className="flex flex-wrap items-center justify-center gap-5">
        {FOOTER_LINKS.map((link) => (
          <Link
            key={link.label}
            href={link.href}
            className="text-xs font-medium hover:opacity-70 transition-opacity"
            style={{ color: 'var(--color-warm-gray)' }}
          >
            {link.label}
          </Link>
        ))}
      </nav>

      <p
        className="text-xs"
        style={{ color: 'var(--color-warm-gray)', opacity: 0.55 }}
      >
        &copy; {new Date().getFullYear()} I was Cute. All rights reserved.
      </p>
    </footer>
  )
}

// ---------------------------------------------------------------------------
// Page — root export
// ---------------------------------------------------------------------------

/**
 * /v2 — Glassmorphism + Animated Gradient Mesh landing page.
 *
 * Usage: navigate to /v2 in the running Next.js dev server.
 *
 * Architecture notes:
 * - 'use client' is required for framer-motion whileHover.
 * - Blob animations are pure CSS (@keyframes), zero JS runtime cost.
 * - Glass utility classes are injected via <style>{css}</style> — static
 *   string, no XSS surface — using the same mechanism as styled-jsx.
 * - framer-motion is used ONLY for whileHover on interactive cards.
 *   No initial/animate props to avoid SSR hydration mismatches.
 * - Fixed-position mesh background sits at z-index 0; page content at z-10+.
 */
export default function V2Page() {
  return (
    <>
      <AnimationStyles />

      {/*
        Root wrapper: `position: relative` establishes a stacking context so
        the fixed blobs render behind page content. overflow-x-hidden prevents
        the translated blobs from creating a horizontal scrollbar.
      */}
      <div
        className="relative min-h-screen flex flex-col overflow-x-hidden"
        style={{ background: 'var(--color-cream)' }}
      >
        <GradientMeshBackground />

        {/* Page content — stacks above the fixed mesh */}
        <div className="relative z-10 flex flex-col flex-1">
          <NavBar />
          <HeroCard />
          <StatsBar />
          <FeatureGrid />
          <CallToActionBanner />
          <Footer />
        </div>
      </div>
    </>
  )
}