← back to IWasCute
iwascute: canonicalize homepage for AdSense — redirect dev v1/v2/v3 variants to /, sitewide Privacy/About/Contact footer, drop dead Express-era static index/admin (TK-11412)
21032c0ebd4f99562cf39663175ee151062989b9 · 2026-09-10 12:54:49 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTGuKtAvmVMngA9PyTiftG
Files touched
D public/admin.htmlD public/index.htmlA public/robots.txtA public/sitemap.xmlA src/app/about/page.tsxA src/app/contact/page.tsxM src/app/layout.tsxM src/app/page.tsxM src/app/v1/page.tsxM src/app/v2/page.tsxM src/app/v3/page.tsxA src/components/shared/SiteFooter.tsx
Diff
commit 21032c0ebd4f99562cf39663175ee151062989b9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 12:54:49 2026 -0700
iwascute: canonicalize homepage for AdSense — redirect dev v1/v2/v3 variants to /, sitewide Privacy/About/Contact footer, drop dead Express-era static index/admin (TK-11412)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTGuKtAvmVMngA9PyTiftG
---
public/admin.html | 270 -----------
public/index.html | 881 -----------------------------------
public/robots.txt | 7 +
public/sitemap.xml | 33 ++
src/app/about/page.tsx | 93 ++++
src/app/contact/page.tsx | 85 ++++
src/app/layout.tsx | 4 +-
src/app/page.tsx | 598 ++++++++++++++----------
src/app/v1/page.tsx | 268 +----------
src/app/v2/page.tsx | 507 +-------------------
src/app/v3/page.tsx | 425 +----------------
src/components/shared/SiteFooter.tsx | 45 ++
12 files changed, 656 insertions(+), 2560 deletions(-)
diff --git a/public/admin.html b/public/admin.html
deleted file mode 100644
index 94625c8..0000000
--- a/public/admin.html
+++ /dev/null
@@ -1,270 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<script>
-// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
-// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
-// https://user:pass@host/…), the browser poisons document.baseURI, so any
-// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
-// from a URL that includes credentials" and callers silently fall to an empty
-// state. Resolve every non-absolute request URL against the credential-free
-// location instead of baseURI. Placed first so it wraps window.fetch before any
-// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
-(function () {
- var _fetch = window.fetch.bind(window);
- var cleanBase = function () { return location.origin + location.pathname; };
- window.fetch = function (input, init) {
- try {
- if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
- input = new URL(input, cleanBase()).href;
- } else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
- input = new Request(new URL(input.url, cleanBase()).href, input);
- }
- } catch (_) { /* fall through to native */ }
- return _fetch(input, init);
- };
-})();
-</script>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<title>Admin — I was Cute</title>
-<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
-<style>
-*{margin:0;padding:0;box-sizing:border-box}
-:root{--bg:#1a1714;--bg2:#221f1a;--bg3:#2a2620;--cream:#f5ede0;--amber:#c4763a;--amber2:#d4894e;--rose:#d4827a;--green:#34d399;--text:#f5ede0;--text2:#b8ad9e;--text3:#8a7f72;--sans:'DM Sans',sans-serif;--serif:'Playfair Display',serif;--radius:10px}
-body{font-family:var(--sans);background:var(--bg);color:var(--text);min-height:100vh}
-
-.admin-header{padding:20px 32px;border-bottom:1px solid rgba(245,237,224,.08);display:flex;align-items:center;justify-content:space-between}
-.admin-header h1{font-family:var(--serif);font-size:22px}
-.admin-header h1 em{color:var(--amber2);font-style:italic}
-.admin-header .badge{font-size:12px;background:var(--amber);color:#fff;padding:4px 12px;border-radius:99px;font-weight:600}
-
-.stats-bar{display:flex;gap:16px;padding:20px 32px;flex-wrap:wrap}
-.stat-card{background:var(--bg2);border:1px solid rgba(245,237,224,.06);border-radius:var(--radius);padding:16px 20px;flex:1;min-width:140px}
-.stat-card .stat-num{font-size:28px;font-weight:700;font-family:var(--serif)}
-.stat-card .stat-label{font-size:12px;color:var(--text3);margin-top:2px;text-transform:uppercase;letter-spacing:.08em}
-.stat-card.pending .stat-num{color:var(--amber2)}
-.stat-card.approved .stat-num{color:var(--green)}
-.stat-card.rejected .stat-num{color:var(--rose)}
-
-.queue-header{padding:20px 32px 12px;display:flex;align-items:center;justify-content:space-between}
-.queue-header h2{font-size:16px;font-weight:600}
-.queue-header .filter-pills{display:flex;gap:6px}
-.queue-header .pill{background:var(--bg3);border:1px solid rgba(245,237,224,.08);color:var(--text2);padding:6px 14px;border-radius:99px;font-size:12px;cursor:pointer;font-family:var(--sans);border:none}
-.queue-header .pill.active{background:var(--amber);color:#fff}
-
-.queue{padding:0 32px 32px}
-.queue-item{display:flex;gap:16px;padding:16px;background:var(--bg2);border:1px solid rgba(245,237,224,.06);border-radius:var(--radius);margin-bottom:10px;align-items:center;transition:border-color .2s}
-.queue-item:hover{border-color:var(--amber)}
-.queue-item .qi-img{width:80px;height:80px;border-radius:6px;object-fit:cover;background:var(--bg3);flex-shrink:0}
-.queue-item .qi-info{flex:1;min-width:0}
-.queue-item .qi-title{font-weight:600;font-size:14px;margin-bottom:2px}
-.queue-item .qi-meta{font-size:12px;color:var(--text3)}
-.queue-item .qi-meta span{margin-right:12px}
-.queue-item .qi-desc{font-size:12px;color:var(--text2);margin-top:4px;font-style:italic;line-height:1.4}
-.queue-item .qi-actions{display:flex;gap:8px;flex-shrink:0}
-.qi-btn{padding:8px 16px;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;border:none;font-family:var(--sans);transition:all .2s}
-.qi-btn.approve{background:var(--green);color:#000}
-.qi-btn.approve:hover{filter:brightness(1.1);transform:translateY(-1px)}
-.qi-btn.reject{background:var(--rose);color:#fff}
-.qi-btn.reject:hover{filter:brightness(1.1);transform:translateY(-1px)}
-.qi-btn.view{background:var(--bg3);color:var(--text2);border:1px solid rgba(245,237,224,.1)}
-.qi-btn.view:hover{border-color:var(--amber)}
-
-.qi-status{padding:4px 10px;border-radius:4px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
-.qi-status.pending{background:rgba(196,118,58,.15);color:var(--amber2)}
-.qi-status.approved{background:rgba(52,211,153,.15);color:var(--green)}
-.qi-status.rejected{background:rgba(212,130,122,.15);color:var(--rose)}
-
-.empty-queue{text-align:center;padding:60px 20px;color:var(--text3);font-size:14px}
-.empty-queue .eq-icon{font-size:48px;margin-bottom:12px;opacity:.4}
-
-/* Demo banner */
-.demo-banner{background:linear-gradient(135deg,rgba(196,118,58,.1),rgba(212,130,122,.1));border:1px solid rgba(196,118,58,.2);border-radius:var(--radius);padding:16px 20px;margin:0 32px 0;font-size:13px;color:var(--amber2);display:flex;align-items:center;gap:12px}
-.demo-banner strong{color:var(--cream)}
-</style>
-</head>
-<body>
-
-<div class="admin-header">
- <h1>I was <em>Cute</em> — Admin</h1>
- <span class="badge">Admin Panel</span>
-</div>
-
-<div class="stats-bar" id="stats-bar">
- <div class="stat-card"><div class="stat-num" id="stat-total">-</div><div class="stat-label">Total Submissions</div></div>
- <div class="stat-card pending"><div class="stat-num" id="stat-pending">-</div><div class="stat-label">Pending Review</div></div>
- <div class="stat-card approved"><div class="stat-num" id="stat-approved">-</div><div class="stat-label">Approved</div></div>
- <div class="stat-card rejected"><div class="stat-num" id="stat-rejected">-</div><div class="stat-label">Rejected</div></div>
-</div>
-
-<div class="demo-banner" id="demo-banner" style="margin-top:16px">
- <span style="font-size:20px">💡</span>
- <span><strong>Demo mode</strong> — Submit photos from the main app to see them appear here. <a href="/" style="color:var(--amber);text-decoration:underline">Go to main app</a></span>
-</div>
-
-<div class="queue-header">
- <h2>Submission Queue</h2>
- <div class="filter-pills">
- <button class="pill active" data-filter="all">All</button>
- <button class="pill" data-filter="pending">Pending</button>
- <button class="pill" data-filter="approved">Approved</button>
- <button class="pill" data-filter="rejected">Rejected</button>
- </div>
-</div>
-
-<div class="queue" id="queue"></div>
-
-<script>
-var AUTH = 'Basic ' + btoa('admin:DWSecure2024!');
-var currentFilter = 'all';
-
-document.querySelectorAll('.pill').forEach(function(p) {
- p.addEventListener('click', function() {
- document.querySelectorAll('.pill').forEach(function(x) { x.classList.remove('active'); });
- p.classList.add('active');
- currentFilter = p.dataset.filter;
- renderQueue();
- });
-});
-
-var allSubmissions = [];
-
-function loadSubmissions() {
- fetch('/api/admin/submissions', { headers: { Authorization: AUTH } })
- .then(function(r) { return r.json(); })
- .then(function(data) {
- allSubmissions = data.submissions || [];
- updateStats();
- renderQueue();
- if (allSubmissions.length > 0) document.getElementById('demo-banner').style.display = 'none';
- })
- .catch(function(e) { console.error('Load error:', e); });
-}
-
-function updateStats() {
- var total = allSubmissions.length;
- var pending = allSubmissions.filter(function(s) { return s.status === 'pending'; }).length;
- var approved = allSubmissions.filter(function(s) { return s.status === 'approved'; }).length;
- var rejected = allSubmissions.filter(function(s) { return s.status === 'rejected'; }).length;
- document.getElementById('stat-total').textContent = total;
- document.getElementById('stat-pending').textContent = pending;
- document.getElementById('stat-approved').textContent = approved;
- document.getElementById('stat-rejected').textContent = rejected;
-}
-
-function renderQueue() {
- var queue = document.getElementById('queue');
- queue.textContent = '';
- var filtered = currentFilter === 'all' ? allSubmissions : allSubmissions.filter(function(s) { return s.status === currentFilter; });
-
- if (filtered.length === 0) {
- var empty = document.createElement('div');
- empty.className = 'empty-queue';
- var icon = document.createElement('div');
- icon.className = 'eq-icon';
- icon.textContent = '\uD83D\uDCED';
- empty.appendChild(icon);
- var msg = document.createElement('p');
- msg.textContent = currentFilter === 'all' ? 'No submissions yet. Photos submitted from the main app will appear here.' : 'No ' + currentFilter + ' submissions.';
- empty.appendChild(msg);
- queue.appendChild(empty);
- return;
- }
-
- filtered.forEach(function(sub) {
- var item = document.createElement('div');
- item.className = 'queue-item';
-
- // Thumbnail placeholder
- var img = document.createElement('div');
- img.className = 'qi-img';
- img.style.background = 'linear-gradient(135deg, var(--amber), var(--bg3))';
- img.style.display = 'flex';
- img.style.alignItems = 'center';
- img.style.justifyContent = 'center';
- img.style.fontSize = '24px';
- img.textContent = '\uD83D\uDDBC';
- item.appendChild(img);
-
- // Info
- var info = document.createElement('div');
- info.className = 'qi-info';
-
- var titleRow = document.createElement('div');
- titleRow.style.cssText = 'display:flex;align-items:center;gap:8px';
- var title = document.createElement('span');
- title.className = 'qi-title';
- title.textContent = sub.title || 'Untitled Photo';
- titleRow.appendChild(title);
- var status = document.createElement('span');
- status.className = 'qi-status ' + sub.status;
- status.textContent = sub.status;
- titleRow.appendChild(status);
- info.appendChild(titleRow);
-
- var meta = document.createElement('div');
- meta.className = 'qi-meta';
- var catSpan = document.createElement('span');
- catSpan.textContent = sub.category || 'Uncategorized';
- meta.appendChild(catSpan);
- var yearSpan = document.createElement('span');
- yearSpan.textContent = sub.year ? 'Year: ' + sub.year : '';
- meta.appendChild(yearSpan);
- var ageSpan = document.createElement('span');
- ageSpan.textContent = sub.age ? 'Age: ' + sub.age : '';
- meta.appendChild(ageSpan);
- var dateSpan = document.createElement('span');
- dateSpan.textContent = new Date(sub.submittedAt).toLocaleString();
- meta.appendChild(dateSpan);
- info.appendChild(meta);
-
- if (sub.description) {
- var desc = document.createElement('div');
- desc.className = 'qi-desc';
- desc.textContent = '"' + sub.description.substring(0, 120) + (sub.description.length > 120 ? '...' : '') + '"';
- info.appendChild(desc);
- }
-
- item.appendChild(info);
-
- // Actions
- var actions = document.createElement('div');
- actions.className = 'qi-actions';
-
- if (sub.status === 'pending') {
- var approveBtn = document.createElement('button');
- approveBtn.className = 'qi-btn approve';
- approveBtn.textContent = 'Approve';
- approveBtn.addEventListener('click', function() { reviewAction(sub.id, 'approve'); });
- actions.appendChild(approveBtn);
-
- var rejectBtn = document.createElement('button');
- rejectBtn.className = 'qi-btn reject';
- rejectBtn.textContent = 'Reject';
- rejectBtn.addEventListener('click', function() { reviewAction(sub.id, 'reject'); });
- actions.appendChild(rejectBtn);
- }
-
- item.appendChild(actions);
- queue.appendChild(item);
- });
-}
-
-function reviewAction(id, action) {
- fetch('/api/admin/review', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json', Authorization: AUTH },
- body: JSON.stringify({ id: id, action: action })
- })
- .then(function(r) { return r.json(); })
- .then(function() { loadSubmissions(); })
- .catch(function(e) { console.error('Review error:', e); });
-}
-
-// Poll every 5s for new submissions
-loadSubmissions();
-setInterval(loadSubmissions, 5000);
-</script>
-</body>
-</html>
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index 33e0c34..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,881 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<script>
-// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
-// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
-// https://user:pass@host/…), the browser poisons document.baseURI, so any
-// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
-// from a URL that includes credentials" and callers silently fall to an empty
-// state. Resolve every non-absolute request URL against the credential-free
-// location instead of baseURI. Placed first so it wraps window.fetch before any
-// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
-(function () {
- var _fetch = window.fetch.bind(window);
- var cleanBase = function () { return location.origin + location.pathname; };
- window.fetch = function (input, init) {
- try {
- if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
- input = new URL(input, cleanBase()).href;
- } else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
- input = new Request(new URL(input.url, cleanBase()).href, input);
- }
- } catch (_) { /* fall through to native */ }
- return _fetch(input, init);
- };
-})();
-</script>
- <link rel="icon" type="image/svg+xml" href="/favicon.svg">
-
-<meta charset="utf-8">
-<!-- Google tag (gtag.js) -->
-<script async src="https://www.googletagmanager.com/gtag/js?id=G-2DHDBP8R78"></script>
-<script>
- window.dataLayer = window.dataLayer || [];
- function gtag(){dataLayer.push(arguments);}
- gtag('js', new Date());
- gtag('config', 'G-2DHDBP8R78');
-</script>
-
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<title>I was Cute — Image Licensing + Then vs Now</title>
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
-<style>
-*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
-:root{
- --bg:#1a1714;--bg2:#221f1a;--bg3:#2a2620;
- --cream:#f5ede0;--cream2:#e8ddd0;--cream3:#d4c9b8;
- --amber:#c4763a;--amber2:#d4894e;--rose:#d4827a;--rose2:#e09a93;
- --text:#f5ede0;--text2:#b8ad9e;--text3:#8a7f72;
- --polaroid:#faf6ef;--shadow:rgba(0,0,0,.4);
- --serif:'Playfair Display',Georgia,serif;
- --sans:'DM Sans',-apple-system,sans-serif;
- --radius:12px;--radius-sm:8px;
-}
-html{scroll-behavior:smooth}
-body{font-family:var(--sans);background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden}
-a{color:var(--amber2);text-decoration:none}
-::selection{background:var(--amber);color:#fff}
-
-/* ── Navigation ───────────────────────────────── */
-nav{position:fixed;top:0;left:0;right:0;z-index:100;padding:16px 32px;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,var(--bg) 60%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
-nav .logo{font-family:var(--serif);font-size:22px;font-weight:700;cursor:pointer}
-nav .logo em{font-style:italic;color:var(--amber2)}
-nav .nav-links{display:flex;gap:28px;align-items:center}
-nav .nav-links a{color:var(--text2);font-size:14px;font-weight:500;transition:color .2s;letter-spacing:.02em}
-nav .nav-links a:hover{color:var(--cream)}
-nav .nav-links a.active{color:var(--amber2)}
-.nav-cta{background:var(--amber);color:#fff;padding:8px 20px;border-radius:99px;font-size:13px;font-weight:600;border:none;cursor:pointer;transition:all .2s}
-.nav-cta:hover{background:var(--amber2);transform:translateY(-1px)}
-
-/* ── Hero ─────────────────────────────────────── */
-.hero{min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:120px 24px 80px;position:relative;overflow:hidden}
-.hero::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 30% 40%,rgba(196,118,58,.08) 0%,transparent 60%),radial-gradient(ellipse at 70% 60%,rgba(212,130,122,.06) 0%,transparent 50%);pointer-events:none}
-.hero::after{content:'';position:absolute;inset:0;background:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='.7' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.03'/%3E%3C/svg%3E");opacity:.5;pointer-events:none}
-
-.hero h1{font-family:var(--serif);font-size:clamp(48px,8vw,96px);font-weight:700;line-height:1.05;margin-bottom:24px;position:relative}
-.hero h1 .line1{display:block;color:var(--cream3)}
-.hero h1 .line2{display:block;font-style:italic;color:var(--amber2)}
-.hero p{font-size:clamp(16px,2vw,20px);color:var(--text2);max-width:520px;line-height:1.6;margin-bottom:40px;position:relative}
-.hero-actions{display:flex;gap:16px;position:relative;flex-wrap:wrap;justify-content:center}
-.btn-primary{background:var(--amber);color:#fff;padding:14px 36px;border-radius:99px;font-size:15px;font-weight:600;border:none;cursor:pointer;transition:all .25s;font-family:var(--sans)}
-.btn-primary:hover{background:var(--amber2);transform:translateY(-2px);box-shadow:0 8px 32px rgba(196,118,58,.3)}
-.btn-secondary{background:transparent;color:var(--cream);padding:14px 36px;border-radius:99px;font-size:15px;font-weight:600;border:1px solid var(--cream3);cursor:pointer;transition:all .25s;font-family:var(--sans)}
-.btn-secondary:hover{border-color:var(--amber);color:var(--amber2);transform:translateY(-2px)}
-
-/* ── Floating Polaroids (hero decoration) ─────── */
-.floating-polaroids{position:absolute;inset:0;pointer-events:none;overflow:hidden}
-.fpol{position:absolute;background:var(--polaroid);padding:8px 8px 32px;box-shadow:0 8px 40px var(--shadow);border-radius:2px;transform:rotate(var(--rot));animation:floatPol 20s ease-in-out infinite;opacity:.15}
-.fpol .fimg{width:100%;aspect-ratio:1;background:var(--bg3);border-radius:1px}
-@keyframes floatPol{0%,100%{transform:rotate(var(--rot)) translateY(0)}50%{transform:rotate(var(--rot)) translateY(-20px)}}
-
-/* ── Sections ─────────────────────────────────── */
-section{padding:100px 32px;max-width:1200px;margin:0 auto}
-.section-label{font-size:12px;text-transform:uppercase;letter-spacing:.15em;color:var(--amber);font-weight:600;margin-bottom:12px}
-.section-title{font-family:var(--serif);font-size:clamp(32px,4vw,48px);font-weight:700;margin-bottom:16px;line-height:1.15}
-.section-desc{color:var(--text2);font-size:16px;max-width:500px;line-height:1.6;margin-bottom:48px}
-
-/* ── Browse Grid ──────────────────────────────── */
-.browse-bar{display:flex;gap:12px;margin-bottom:32px;flex-wrap:wrap;align-items:center}
-.browse-bar input{background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:10px 16px;border-radius:99px;font-size:14px;flex:1;min-width:240px;font-family:var(--sans)}
-.browse-bar input::placeholder{color:var(--text3)}
-.browse-bar select{background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text2);padding:10px 16px;border-radius:99px;font-size:13px;font-family:var(--sans);cursor:pointer}
-.browse-bar .filter-pill{background:var(--bg3);border:1px solid rgba(245,237,224,.08);color:var(--text2);padding:8px 16px;border-radius:99px;font-size:12px;cursor:pointer;transition:all .2s;font-family:var(--sans)}
-.browse-bar .filter-pill:hover,.browse-bar .filter-pill.active{background:var(--amber);color:#fff;border-color:var(--amber)}
-
-.image-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:20px}
-.image-card{position:relative;background:var(--polaroid);padding:10px 10px 44px;border-radius:3px;cursor:pointer;transition:all .3s;box-shadow:0 4px 20px rgba(0,0,0,.2)}
-.image-card:hover{transform:translateY(-4px) rotate(-1deg);box-shadow:0 12px 40px rgba(0,0,0,.3)}
-.image-card .card-img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:1px;background:var(--bg3)}
-.image-card .card-meta{position:absolute;bottom:12px;left:14px;right:14px;display:flex;justify-content:space-between;align-items:center}
-.image-card .card-title{font-size:11px;color:var(--bg);font-weight:500;font-family:var(--sans)}
-.image-card .card-price{font-size:11px;color:var(--amber);font-weight:700;font-family:var(--sans)}
-
-/* Polaroid develop animation */
-.image-card .card-img{animation:develop 1.2s ease-out forwards}
-@keyframes develop{0%{opacity:0;filter:sepia(1) brightness(1.3) contrast(.7)}40%{opacity:1;filter:sepia(.6) brightness(1.1) contrast(.85)}100%{filter:sepia(0) brightness(1) contrast(1)}}
-
-/* ── Then vs Now ──────────────────────────────── */
-.tvn-container{display:flex;gap:48px;align-items:flex-start;flex-wrap:wrap}
-.tvn-preview{flex:1;min-width:320px;position:relative;border-radius:var(--radius);overflow:hidden;aspect-ratio:3/2;background:var(--bg3)}
-.tvn-preview .tvn-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
-.tvn-preview .tvn-overlay{position:absolute;inset:0;width:50%;overflow:hidden;border-right:3px solid var(--amber)}
-.tvn-preview .tvn-overlay img{position:absolute;inset:0;width:200%;height:100%;object-fit:cover}
-.tvn-slider{position:absolute;top:0;bottom:0;left:50%;width:40px;margin-left:-20px;cursor:ew-resize;display:flex;align-items:center;justify-content:center;z-index:5}
-.tvn-slider .slider-handle{width:32px;height:32px;background:var(--amber);border-radius:50%;border:3px solid var(--polaroid);box-shadow:0 2px 12px var(--shadow);display:flex;align-items:center;justify-content:center}
-.tvn-slider .slider-handle::before{content:'\2194';color:#fff;font-size:14px}
-.tvn-label{position:absolute;bottom:12px;padding:4px 12px;background:rgba(0,0,0,.6);color:#fff;font-size:11px;border-radius:4px;font-weight:600;letter-spacing:.05em;text-transform:uppercase}
-.tvn-label.then-label{left:12px}
-.tvn-label.now-label{right:12px}
-
-.tvn-sidebar{flex:0 0 320px;display:flex;flex-direction:column;gap:16px}
-.tvn-upload-box{border:2px dashed rgba(245,237,224,.15);border-radius:var(--radius);padding:32px;text-align:center;cursor:pointer;transition:all .25s}
-.tvn-upload-box:hover{border-color:var(--amber);background:rgba(196,118,58,.05)}
-.tvn-upload-box .upload-icon{font-size:36px;margin-bottom:8px;opacity:.5}
-.tvn-upload-box p{font-size:13px;color:var(--text3)}
-.tvn-upload-box p strong{color:var(--amber2)}
-
-/* ── Upload Page ──────────────────────────────── */
-.upload-zone{border:2px dashed rgba(245,237,224,.12);border-radius:var(--radius);padding:64px 32px;text-align:center;transition:all .3s;cursor:pointer}
-.upload-zone:hover{border-color:var(--amber);background:rgba(196,118,58,.04)}
-.upload-zone .uz-icon{font-size:64px;opacity:.3;margin-bottom:16px}
-.upload-zone h3{font-family:var(--serif);font-size:24px;margin-bottom:8px}
-.upload-zone p{color:var(--text3);font-size:14px}
-.upload-form{margin-top:32px;display:grid;grid-template-columns:1fr 1fr;gap:16px}
-.upload-form .form-group{display:flex;flex-direction:column;gap:6px}
-.upload-form .form-group.full{grid-column:1/-1}
-.upload-form label{font-size:12px;color:var(--text3);font-weight:500;text-transform:uppercase;letter-spacing:.08em}
-.upload-form input,.upload-form textarea,.upload-form select{background:var(--bg3);border:1px solid rgba(245,237,224,.08);color:var(--text);padding:12px 16px;border-radius:var(--radius-sm);font-family:var(--sans);font-size:14px}
-.upload-form textarea{min-height:80px;resize:vertical}
-
-/* ── License Modal ────────────────────────────── */
-.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.7);backdrop-filter:blur(8px);z-index:200;display:none;align-items:center;justify-content:center}
-.modal-overlay.open{display:flex}
-.modal{background:var(--bg2);border:1px solid rgba(245,237,224,.08);border-radius:16px;padding:32px;max-width:520px;width:90%;max-height:90vh;overflow-y:auto}
-.modal h2{font-family:var(--serif);font-size:24px;margin-bottom:4px}
-.modal .modal-sub{color:var(--text3);font-size:13px;margin-bottom:24px}
-.license-options{display:flex;flex-direction:column;gap:12px;margin-bottom:24px}
-.license-opt{border:1px solid rgba(245,237,224,.1);border-radius:var(--radius-sm);padding:16px;cursor:pointer;transition:all .2s;display:flex;justify-content:space-between;align-items:center}
-.license-opt:hover{border-color:var(--amber)}
-.license-opt.selected{border-color:var(--amber);background:rgba(196,118,58,.08)}
-.license-opt .lo-name{font-weight:600;font-size:14px}
-.license-opt .lo-desc{font-size:12px;color:var(--text3);margin-top:2px}
-.license-opt .lo-price{font-family:var(--serif);font-size:20px;font-weight:700;color:var(--amber2)}
-.modal-close{position:absolute;top:16px;right:16px;background:none;border:none;color:var(--text3);font-size:20px;cursor:pointer}
-
-/* ── Footer ───────────────────────────────────── */
-footer{border-top:1px solid rgba(245,237,224,.06);padding:48px 32px;text-align:center;color:var(--text3);font-size:13px}
-footer .footer-brand{font-family:var(--serif);font-size:18px;color:var(--cream3);margin-bottom:8px}
-
-/* ── Page transitions ─────────────────────────── */
-.page{display:none;opacity:0;transition:opacity .3s}
-.page.active{display:block;opacity:1}
-
-/* ── Photo Editor ─────────────────────────────── */
-.edit-panel{background:var(--bg3);border:1px solid rgba(245,237,224,.08);border-radius:var(--radius);padding:14px}
-.edit-panel-title{font-size:13px;font-weight:600;color:var(--cream);margin-bottom:10px}
-.slider-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}
-.slider-row label{font-size:11px;color:var(--text3);min-width:70px;text-transform:uppercase;letter-spacing:.04em}
-.slider-row input[type=range]{flex:1;accent-color:var(--amber);height:4px;cursor:pointer}
-.slider-row span{font-size:11px;color:var(--amber2);min-width:32px;text-align:right;font-family:var(--sans)}
-.edit-btn{background:var(--bg2);border:1px solid rgba(245,237,224,.1);color:var(--cream);padding:8px 14px;border-radius:var(--radius-sm);font-size:12px;font-weight:500;cursor:pointer;transition:all .2s;font-family:var(--sans)}
-.edit-btn:hover{border-color:var(--amber);color:var(--amber2)}
-.edit-btn.full{width:100%;margin-bottom:6px;text-align:center;padding:10px}
-.edit-btn.active{background:var(--amber);color:#fff;border-color:var(--amber)}
-
-/* ── Responsive ───────────────────────────────── */
-@media(max-width:768px){
- nav{padding:12px 16px}
- nav .nav-links{gap:16px}
- section{padding:60px 16px}
- .tvn-container{flex-direction:column}
- .tvn-sidebar{flex:none;width:100%}
- .upload-form{grid-template-columns:1fr}
-}
-
-/* ── Stagger animations ───────────────────────── */
-.stagger > *{opacity:0;transform:translateY(20px);animation:staggerIn .5s ease-out forwards}
-.stagger > *:nth-child(1){animation-delay:.1s}
-.stagger > *:nth-child(2){animation-delay:.2s}
-.stagger > *:nth-child(3){animation-delay:.25s}
-.stagger > *:nth-child(4){animation-delay:.3s}
-.stagger > *:nth-child(5){animation-delay:.35s}
-.stagger > *:nth-child(6){animation-delay:.4s}
-@keyframes staggerIn{to{opacity:1;transform:translateY(0)}}
-</style>
-<script src="https://mcp.figma.com/mcp/html-to-design/capture.js" async></script>
-<!-- Meta Pixel — INERT until set-fb-pixel.sh is run (does NOT load fbevents.js or hit Meta until flipped) -->
-<script>
-(function(){
- var PIXEL_ID = '{{FACEBOOK_PIXEL_ID}}';
- // Inert until flipped — the brace check prevents BOTH the loader IIFE
- // AND the init call from running, so fbevents.js is never fetched.
- if (PIXEL_ID.indexOf('{{') !== -1) return;
- !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
- n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
- n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
- t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
- document,'script','https://connect.facebook.net/en_US/fbevents.js');
- fbq('init', PIXEL_ID);
- fbq('track', 'PageView');
-})();
-</script>
-<!-- noscript fallback is added by set-fb-pixel.sh on flip (omitted here to avoid unflipped img phone-home) -->
-<noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id={{FACEBOOK_PIXEL_ID}}<!-- End Meta Pixel -->ev=PageView<!-- End Meta Pixel -->noscript=1"/></noscript>
-<!-- End Meta Pixel -->
-</head>
-<body>
-
-<!-- Navigation -->
-<nav>
- <div class="logo" onclick="showPage('home')">I was <em>Cute</em></div>
- <div class="nav-links">
- <a href="#" onclick="showPage('home')" class="active" id="nav-home">Browse</a>
- <a href="#" onclick="showPage('thenvsnow')" id="nav-tvn">Then vs Now</a>
- <a href="#" onclick="showPage('upload')" id="nav-upload">Upload</a>
- <button class="nav-cta" onclick="showPage('upload')">Sell Your Photos</button>
- </div>
-</nav>
-
-<!-- ═══════════ HOME PAGE ═══════════ -->
-<div class="page active" id="page-home">
-
- <!-- Hero -->
- <section class="hero">
- <div class="floating-polaroids">
- <div class="fpol" style="width:120px;top:15%;left:8%;--rot:-12deg;animation-delay:-3s"><div class="fimg" style="background:linear-gradient(135deg,var(--rose),var(--amber))"></div></div>
- <div class="fpol" style="width:100px;top:25%;right:10%;--rot:8deg;animation-delay:-8s"><div class="fimg" style="background:linear-gradient(135deg,var(--amber2),var(--cream3))"></div></div>
- <div class="fpol" style="width:90px;bottom:20%;left:12%;--rot:15deg;animation-delay:-12s"><div class="fimg" style="background:linear-gradient(135deg,var(--cream3),var(--rose2))"></div></div>
- <div class="fpol" style="width:110px;bottom:25%;right:15%;--rot:-6deg;animation-delay:-5s"><div class="fimg" style="background:linear-gradient(135deg,var(--bg3),var(--amber))"></div></div>
- </div>
- <div class="stagger">
- <h1><span class="line1">I was</span><span class="line2">Cute</span></h1>
- <p>License nostalgic, authentic, and beautifully imperfect images. Or create your own Then vs Now story.</p>
- <div class="hero-actions">
- <button class="btn-primary" onclick="showPage('home');document.getElementById('browse-section').scrollIntoView({behavior:'smooth'})">Browse Images</button>
- <button class="btn-secondary" onclick="showPage('thenvsnow')">Make a Then vs Now</button>
- </div>
- </div>
- </section>
-
- <!-- Browse Section -->
- <section id="browse-section">
- <div class="section-label">Image Library</div>
- <div class="section-title">Find the perfect shot</div>
- <div class="section-desc">Thousands of authentic, nostalgic photos available for licensing. Every image tells a story.</div>
-
- <div class="browse-bar">
- <input type="text" placeholder="Search photos... try 'childhood', '90s', 'family'" id="search-input">
- <select>
- <option>All Categories</option>
- <option>Childhood</option>
- <option>Family</option>
- <option>Vintage</option>
- <option>Pets</option>
- <option>School</option>
- <option>Holidays</option>
- </select>
- <div class="filter-pill active">All</div>
- <div class="filter-pill">Editorial</div>
- <div class="filter-pill">Commercial</div>
- <div class="filter-pill">Personal</div>
- </div>
-
- <div class="image-grid stagger" id="image-grid"></div>
- </section>
-</div>
-
-<!-- ═══════════ THEN VS NOW PAGE ═══════════ -->
-<div class="page" id="page-thenvsnow">
- <section style="padding-top:120px">
- <div class="section-label">Create</div>
- <div class="section-title">Then vs Now</div>
- <div class="section-desc">Upload an old photo and a new one. Drag the slider to reveal the passage of time.</div>
-
- <div class="tvn-container">
- <div class="tvn-preview" id="tvn-preview">
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--text3);font-size:14px">Upload two photos to begin</div>
- </div>
- <div class="tvn-sidebar">
- <div class="tvn-upload-box" id="then-upload">
- <div class="upload-icon">📷</div>
- <p><strong>Then</strong> — Upload your old photo</p>
- </div>
- <div class="tvn-upload-box" id="now-upload">
- <div class="upload-icon">📸</div>
- <p><strong>Now</strong> — Upload your current photo</p>
- </div>
- <button class="btn-primary" style="width:100%;margin-top:8px" disabled id="tvn-create-btn">Create Comparison</button>
- <p style="font-size:12px;color:var(--text3);text-align:center">Share your story or license the result</p>
- </div>
- </div>
- </section>
-</div>
-
-<!-- ═══════════ UPLOAD PAGE ═══════════ -->
-<div class="page" id="page-upload">
- <section style="padding-top:120px">
- <div class="section-label">Upload</div>
- <div class="section-title">Share Your Memory</div>
- <div class="section-desc">Upload a photo, edit it, and let AI guess your story.</div>
-
- <!-- Step 1: Upload Zone (visible initially) -->
- <div id="upload-step1">
- <div class="upload-zone" id="upload-zone">
- <div class="uz-icon">🖼</div>
- <h3>Drop your photo here</h3>
- <p>or click to browse. JPG, PNG up to 50MB.</p>
- </div>
- </div>
-
- <!-- Step 2: Photo Editor (hidden until upload) -->
- <div id="upload-step2" style="display:none">
- <div style="display:flex;gap:24px;flex-wrap:wrap;align-items:flex-start">
-
- <!-- Photo preview with canvas -->
- <div style="flex:1;min-width:320px">
- <div style="position:relative;background:var(--bg3);border-radius:var(--radius);overflow:hidden;border:1px solid rgba(245,237,224,.1)">
- <canvas id="photo-canvas" style="width:100%;display:block;cursor:crosshair"></canvas>
- </div>
- <!-- Crop controls -->
- <div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
- <button class="edit-btn" id="btn-crop-free" title="Free crop">✂ Crop</button>
- <button class="edit-btn" id="btn-crop-square" title="Square crop">⬜ Square</button>
- <button class="edit-btn" id="btn-crop-4x3" title="4:3 crop">▭ 4:3</button>
- <button class="edit-btn" id="btn-reset" title="Reset all edits">↺ Reset</button>
- </div>
- </div>
-
- <!-- Controls sidebar -->
- <div style="flex:0 0 300px;display:flex;flex-direction:column;gap:16px">
-
- <!-- Color adjustments -->
- <div class="edit-panel">
- <div class="edit-panel-title">🎨 Adjustments</div>
- <div class="slider-row">
- <label>Brightness</label>
- <input type="range" id="sl-brightness" min="-100" max="100" value="0">
- <span id="val-brightness">0</span>
- </div>
- <div class="slider-row">
- <label>Contrast</label>
- <input type="range" id="sl-contrast" min="-100" max="100" value="0">
- <span id="val-contrast">0</span>
- </div>
- <div class="slider-row">
- <label>Saturation</label>
- <input type="range" id="sl-saturation" min="-100" max="100" value="0">
- <span id="val-saturation">0</span>
- </div>
- <div class="slider-row">
- <label>Hue Rotate</label>
- <input type="range" id="sl-hue" min="0" max="360" value="0">
- <span id="val-hue">0°</span>
- </div>
- <div class="slider-row">
- <label>Exposure</label>
- <input type="range" id="sl-exposure" min="-100" max="100" value="0">
- <span id="val-exposure">0</span>
- </div>
- <div class="slider-row">
- <label>Sepia (vintage)</label>
- <input type="range" id="sl-sepia" min="0" max="100" value="0">
- <span id="val-sepia">0%</span>
- </div>
- </div>
-
- <!-- AI magic -->
- <div class="edit-panel">
- <div class="edit-panel-title">🤖 AI Magic</div>
- <button class="edit-btn full" id="btn-ai-describe">✨ Generate AI Description</button>
- <button class="edit-btn full" id="btn-ai-guess-year">📅 Guess the Year</button>
- <div id="ai-output" style="font-size:13px;color:var(--cream3);line-height:1.5;min-height:20px;margin-top:4px"></div>
- </div>
-
- <!-- Age calculator -->
- <div class="edit-panel">
- <div class="edit-panel-title">👶 How old were you?</div>
- <div class="slider-row">
- <label>Born in</label>
- <input type="number" id="inp-born" placeholder="1990" min="1900" max="2026" style="width:80px;background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:6px 8px;border-radius:6px;font-size:14px;text-align:center">
- </div>
- <div class="slider-row">
- <label>Photo taken</label>
- <input type="number" id="inp-year" placeholder="2005" min="1900" max="2026" style="width:80px;background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:6px 8px;border-radius:6px;font-size:14px;text-align:center">
- </div>
- <div id="age-result" style="font-size:15px;color:var(--amber2);font-weight:600;margin-top:4px;min-height:22px"></div>
- </div>
-
- <!-- Details form -->
- <div class="edit-panel">
- <div class="edit-panel-title">📝 Details</div>
- <div class="slider-row" style="flex-direction:column;align-items:stretch">
- <label>Title</label>
- <input type="text" id="inp-title" placeholder="e.g. Summer of '92" style="background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:8px 12px;border-radius:6px;font-size:14px;width:100%">
- </div>
- <div class="slider-row" style="flex-direction:column;align-items:stretch">
- <label>AI Description</label>
- <textarea id="inp-desc" rows="3" placeholder="AI will fill this in..." style="background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:8px 12px;border-radius:6px;font-size:13px;width:100%;resize:vertical;font-family:var(--sans)"></textarea>
- </div>
- <div class="slider-row">
- <label>Category</label>
- <select id="inp-cat" style="background:var(--bg3);border:1px solid rgba(245,237,224,.1);color:var(--text);padding:6px 10px;border-radius:6px;font-size:13px">
- <option>Childhood</option><option>Family</option><option>School</option><option>Pets</option><option>Holidays</option><option>Vintage</option>
- </select>
- </div>
- </div>
-
- <button class="btn-primary" style="width:100%" id="btn-submit-photo">Submit for Review</button>
- </div>
- </div>
- </div>
- </section>
-</div>
-
-<!-- License Modal -->
-<div class="modal-overlay" id="license-modal">
- <div class="modal">
- <h2>License This Image</h2>
- <div class="modal-sub" id="modal-image-title">Summer of '92 — Childhood</div>
- <div class="license-options">
- <div class="license-opt selected" onclick="selectLicense(this)">
- <div><div class="lo-name">Personal Use</div><div class="lo-desc">Blog, social media, personal projects</div></div>
- <div class="lo-price">$9</div>
- </div>
- <div class="license-opt" onclick="selectLicense(this)">
- <div><div class="lo-name">Editorial</div><div class="lo-desc">News, magazines, educational content</div></div>
- <div class="lo-price">$29</div>
- </div>
- <div class="license-opt" onclick="selectLicense(this)">
- <div><div class="lo-name">Commercial</div><div class="lo-desc">Advertising, products, merchandise</div></div>
- <div class="lo-price">$99</div>
- </div>
- <div class="license-opt" onclick="selectLicense(this)">
- <div><div class="lo-name">Extended / Exclusive</div><div class="lo-desc">Unlimited use, exclusivity period</div></div>
- <div class="lo-price">$299</div>
- </div>
- </div>
- <button class="btn-primary" style="width:100%">Purchase License</button>
- <p style="text-align:center;margin-top:12px;font-size:12px;color:var(--text3);cursor:pointer" onclick="closeLicenseModal()">Cancel</p>
- </div>
-</div>
-
-<footer>
- <div class="footer-brand">I was <em style="font-style:italic">Cute</em></div>
- <p>Authentic nostalgia, beautifully licensed.</p>
-</footer>
-
-<script>
-// ── Page Router ────────────────────────────────
-function showPage(page) {
- document.querySelectorAll('.page').forEach(function(p) { p.classList.remove('active'); });
- document.querySelectorAll('.nav-links a').forEach(function(a) { a.classList.remove('active'); });
- var target = document.getElementById('page-' + page);
- if (target) { target.classList.add('active'); window.scrollTo(0, 0); }
- var navLink = document.getElementById('nav-' + (page === 'thenvsnow' ? 'tvn' : page));
- if (navLink) navLink.classList.add('active');
-}
-
-// ── Demo Image Data ────────────────────────────
-var demoImages = [
- { title: "Summer of '92", cat: "Childhood", year: "1992", price: 29, color: "var(--amber)" },
- { title: "First Day of School", cat: "School", year: "1988", price: 19, color: "var(--rose)" },
- { title: "Grandma's Kitchen", cat: "Family", year: "1975", price: 39, color: "var(--cream3)" },
- { title: "The Old Treehouse", cat: "Childhood", year: "1995", price: 29, color: "var(--amber2)" },
- { title: "Christmas Morning", cat: "Holidays", year: "1983", price: 49, color: "var(--rose2)" },
- { title: "Beach Day", cat: "Family", year: "1990", price: 19, color: "var(--amber)" },
- { title: "Prom Night", cat: "School", year: "1998", price: 29, color: "var(--rose)" },
- { title: "Road Trip '85", cat: "Family", year: "1985", price: 39, color: "var(--cream3)" },
- { title: "Birthday Party", cat: "Childhood", year: "1991", price: 19, color: "var(--amber2)" },
- { title: "Snow Day", cat: "Holidays", year: "1987", price: 29, color: "var(--rose2)" },
- { title: "First Bike", cat: "Childhood", year: "1993", price: 39, color: "var(--amber)" },
- { title: "Family Portrait", cat: "Family", year: "1979", price: 49, color: "var(--cream3)" }
-];
-
-// ── Render Image Grid ──────────────────────────
-function renderGrid() {
- var grid = document.getElementById('image-grid');
- grid.textContent = '';
- demoImages.forEach(function(img, i) {
- var card = document.createElement('div');
- card.className = 'image-card';
- card.style.animationDelay = (i * .06) + 's';
- card.onclick = function() { openLicenseModal(img); };
-
- var imgDiv = document.createElement('div');
- imgDiv.className = 'card-img';
- imgDiv.style.background = 'linear-gradient(135deg, ' + img.color + ', var(--bg3))';
- card.appendChild(imgDiv);
-
- var meta = document.createElement('div');
- meta.className = 'card-meta';
-
- var titleSpan = document.createElement('span');
- titleSpan.className = 'card-title';
- titleSpan.textContent = img.title + ' \u00b7 ' + img.year;
- meta.appendChild(titleSpan);
-
- var priceSpan = document.createElement('span');
- priceSpan.className = 'card-price';
- priceSpan.textContent = 'from $' + img.price;
- meta.appendChild(priceSpan);
-
- card.appendChild(meta);
- grid.appendChild(card);
- });
-}
-renderGrid();
-
-// ── License Modal ──────────────────────────────
-function openLicenseModal(img) {
- document.getElementById('modal-image-title').textContent = img.title + ' \u2014 ' + img.cat;
- document.getElementById('license-modal').classList.add('open');
-}
-function closeLicenseModal() {
- document.getElementById('license-modal').classList.remove('open');
-}
-function selectLicense(el) {
- document.querySelectorAll('.license-opt').forEach(function(o) { o.classList.remove('selected'); });
- el.classList.add('selected');
-}
-document.getElementById('license-modal').addEventListener('click', function(e) {
- if (e.target === this) closeLicenseModal();
-});
-
-// ── Filter pills ───────────────────────────────
-document.querySelectorAll('.filter-pill').forEach(function(pill) {
- pill.addEventListener('click', function() {
- document.querySelectorAll('.filter-pill').forEach(function(p) { p.classList.remove('active'); });
- this.classList.add('active');
- });
-});
-
-// ── Hidden file inputs ─────────────────────────
-function createFileInput(accept, cb) {
- var inp = document.createElement('input');
- inp.type = 'file';
- inp.accept = accept || 'image/*';
- inp.style.display = 'none';
- inp.onchange = function() { if (inp.files[0]) cb(inp.files[0]); };
- document.body.appendChild(inp);
- return inp;
-}
-
-// ── Then vs Now ────────────────────────────────
-var thenImg = null, nowImg = null;
-
-document.getElementById('then-upload').addEventListener('click', function() {
- var inp = createFileInput('image/*', function(file) {
- thenImg = URL.createObjectURL(file);
- document.getElementById('then-upload').querySelector('p').textContent = file.name;
- document.getElementById('then-upload').style.borderColor = 'var(--amber)';
- if (thenImg && nowImg) enableTvn();
- });
- inp.click();
-});
-
-document.getElementById('now-upload').addEventListener('click', function() {
- var inp = createFileInput('image/*', function(file) {
- nowImg = URL.createObjectURL(file);
- document.getElementById('now-upload').querySelector('p').textContent = file.name;
- document.getElementById('now-upload').style.borderColor = 'var(--amber)';
- if (thenImg && nowImg) enableTvn();
- });
- inp.click();
-});
-
-function enableTvn() {
- document.getElementById('tvn-create-btn').disabled = false;
- document.getElementById('tvn-create-btn').onclick = buildTvnSlider;
-}
-
-function buildTvnSlider() {
- var preview = document.getElementById('tvn-preview');
- preview.textContent = '';
- preview.style.cursor = 'default';
-
- // Now image (full background)
- var nowEl = document.createElement('img');
- nowEl.className = 'tvn-img';
- nowEl.src = nowImg;
- preview.appendChild(nowEl);
-
- // Then overlay (clipped left side)
- var overlay = document.createElement('div');
- overlay.className = 'tvn-overlay';
- overlay.id = 'tvn-overlay';
- var thenEl = document.createElement('img');
- thenEl.src = thenImg;
- thenEl.style.cssText = 'position:absolute;top:0;left:0;width:200%;height:100%;object-fit:cover';
- overlay.appendChild(thenEl);
- preview.appendChild(overlay);
-
- // Slider handle
- var slider = document.createElement('div');
- slider.className = 'tvn-slider';
- slider.id = 'tvn-slider';
- var handle = document.createElement('div');
- handle.className = 'slider-handle';
- slider.appendChild(handle);
- preview.appendChild(slider);
-
- // Labels
- var thenLabel = document.createElement('div');
- thenLabel.className = 'tvn-label then-label';
- thenLabel.textContent = 'THEN';
- preview.appendChild(thenLabel);
- var nowLabel = document.createElement('div');
- nowLabel.className = 'tvn-label now-label';
- nowLabel.textContent = 'NOW';
- preview.appendChild(nowLabel);
-
- // Drag logic
- var dragging = false;
- slider.addEventListener('mousedown', function(e) { dragging = true; e.preventDefault(); });
- slider.addEventListener('touchstart', function(e) { dragging = true; });
- window.addEventListener('mouseup', function() { dragging = false; });
- window.addEventListener('touchend', function() { dragging = false; });
-
- function moveSlider(clientX) {
- var rect = preview.getBoundingClientRect();
- var pct = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
- overlay.style.width = (pct * 100) + '%';
- thenEl.style.width = pct > 0 ? (100 / pct) + '%' : '100%';
- slider.style.left = (pct * 100) + '%';
- }
-
- preview.addEventListener('mousemove', function(e) { if (dragging) moveSlider(e.clientX); });
- preview.addEventListener('touchmove', function(e) { if (dragging) moveSlider(e.touches[0].clientX); });
- preview.addEventListener('click', function(e) { moveSlider(e.clientX); });
-
- document.getElementById('tvn-create-btn').textContent = 'Share / Download';
- document.getElementById('tvn-create-btn').onclick = function() {
- alert('Sharing and download coming soon!');
- };
-}
-
-// ── Upload zone drag & drop ────────────────────
-var uploadZone = document.getElementById('upload-zone');
-if (uploadZone) {
- uploadZone.addEventListener('click', function() {
- var inp = createFileInput('image/*,.tiff', function(file) {
- uploadZone.querySelector('h3').textContent = file.name;
- uploadZone.querySelector('p').textContent = (file.size / 1024 / 1024).toFixed(1) + ' MB — Ready to submit';
- uploadZone.style.borderColor = 'var(--amber)';
- uploadZone.style.background = 'rgba(196,118,58,.06)';
- });
- inp.click();
- });
-
- uploadZone.addEventListener('dragover', function(e) {
- e.preventDefault();
- uploadZone.style.borderColor = 'var(--amber)';
- uploadZone.style.background = 'rgba(196,118,58,.08)';
- });
-
- uploadZone.addEventListener('dragleave', function() {
- uploadZone.style.borderColor = 'rgba(245,237,224,.12)';
- uploadZone.style.background = 'transparent';
- });
-
- uploadZone.addEventListener('drop', function(e) {
- e.preventDefault();
- var file = e.dataTransfer.files[0];
- if (file) {
- uploadZone.querySelector('h3').textContent = file.name;
- uploadZone.querySelector('p').textContent = (file.size / 1024 / 1024).toFixed(1) + ' MB — Ready to submit';
- uploadZone.style.borderColor = 'var(--amber)';
- uploadZone.style.background = 'rgba(196,118,58,.06)';
- }
- });
-}
-
-// ── Search filter ──────────────────────────────
-document.getElementById('search-input').addEventListener('input', function() {
- var q = this.value.toLowerCase();
- var cards = document.querySelectorAll('.image-card');
- cards.forEach(function(card) {
- var title = card.querySelector('.card-title').textContent.toLowerCase();
- card.style.display = title.indexOf(q) >= 0 ? '' : 'none';
- });
-});
-
-// ── Purchase button ────────────────────────────
-document.querySelector('.modal .btn-primary').addEventListener('click', function() {
- this.textContent = 'Processing...';
- this.disabled = true;
- var btn = this;
- setTimeout(function() {
- btn.textContent = 'Licensed!';
- btn.style.background = 'var(--rose)';
- setTimeout(function() { closeLicenseModal(); btn.textContent = 'Purchase License'; btn.disabled = false; btn.style.background = ''; }, 1500);
- }, 1200);
-});
-
-// ═══════════ PHOTO EDITOR ═════════════════════
-var photoCanvas = document.getElementById('photo-canvas');
-var photoCtx = photoCanvas ? photoCanvas.getContext('2d') : null;
-var originalImage = null;
-var uploadZoneEl = document.getElementById('upload-zone');
-if (uploadZoneEl) {
- function handleFile(file) {
- if (!file || !file.type.startsWith('image/')) return;
- var reader = new FileReader();
- reader.onload = function(e) {
- var img = new Image();
- img.onload = function() {
- originalImage = img;
- photoCanvas.width = img.width;
- photoCanvas.height = img.height;
- photoCtx.drawImage(img, 0, 0);
- document.getElementById('upload-step1').style.display = 'none';
- document.getElementById('upload-step2').style.display = 'block';
- applyFilters();
- };
- img.src = e.target.result;
- };
- reader.readAsDataURL(file);
- }
- uploadZoneEl.addEventListener('click', function() {
- var inp = document.createElement('input'); inp.type='file'; inp.accept='image/*'; inp.style.display='none';
- inp.onchange = function() { if (inp.files[0]) handleFile(inp.files[0]); };
- document.body.appendChild(inp); inp.click();
- });
- uploadZoneEl.addEventListener('dragover', function(e) { e.preventDefault(); uploadZoneEl.style.borderColor='var(--amber)'; });
- uploadZoneEl.addEventListener('dragleave', function() { uploadZoneEl.style.borderColor='rgba(245,237,224,.12)'; });
- uploadZoneEl.addEventListener('drop', function(e) { e.preventDefault(); if (e.dataTransfer.files[0]) handleFile(e.dataTransfer.files[0]); });
-}
-
-var sliderNames = ['brightness','contrast','saturation','hue','exposure','sepia'];
-sliderNames.forEach(function(name) {
- var sl = document.getElementById('sl-' + name);
- if (sl) sl.addEventListener('input', function() {
- var val = document.getElementById('val-' + name);
- if (name === 'hue') val.textContent = sl.value + '\u00b0';
- else if (name === 'sepia') val.textContent = sl.value + '%';
- else val.textContent = sl.value;
- applyFilters();
- });
-});
-
-function applyFilters() {
- if (!originalImage || !photoCtx) return;
- var b = parseInt(document.getElementById('sl-brightness').value);
- var c = parseInt(document.getElementById('sl-contrast').value);
- var s = parseInt(document.getElementById('sl-saturation').value);
- var h = parseInt(document.getElementById('sl-hue').value);
- var ex = parseInt(document.getElementById('sl-exposure').value);
- var sep = parseInt(document.getElementById('sl-sepia').value);
- photoCtx.clearRect(0, 0, photoCanvas.width, photoCanvas.height);
- photoCtx.filter = 'brightness('+(1+(b+ex)/100)+') contrast('+(1+c/100)+') saturate('+(1+s/100)+') hue-rotate('+h+'deg) sepia('+sep/100+')';
- photoCtx.drawImage(originalImage, 0, 0);
- photoCtx.filter = 'none';
-}
-
-var btnReset = document.getElementById('btn-reset');
-if (btnReset) btnReset.addEventListener('click', function() {
- sliderNames.forEach(function(n) {
- var sl = document.getElementById('sl-'+n); var val = document.getElementById('val-'+n);
- sl.value = 0; val.textContent = n==='hue'?'0\u00b0':n==='sepia'?'0%':'0';
- }); applyFilters();
-});
-
-function applyCrop(ratio) {
- if (!originalImage) return;
- var w=originalImage.width, h=originalImage.height, cw, ch;
- if (ratio==='square') { cw=ch=Math.min(w,h); }
- else if (ratio==='4x3') { cw=Math.min(w,h*4/3); ch=cw*3/4; }
- else return;
- var tc=document.createElement('canvas'); tc.width=cw; tc.height=ch;
- tc.getContext('2d').drawImage(originalImage,(w-cw)/2,(h-ch)/2,cw,ch,0,0,cw,ch);
- var ci=new Image(); ci.onload=function(){ originalImage=ci; photoCanvas.width=cw; photoCanvas.height=ch; applyFilters(); };
- ci.src=tc.toDataURL();
-}
-var bs=document.getElementById('btn-crop-square'); if(bs) bs.addEventListener('click',function(){applyCrop('square')});
-var b43=document.getElementById('btn-crop-4x3'); if(b43) b43.addEventListener('click',function(){applyCrop('4x3')});
-
-// Age Calculator
-var inpBorn=document.getElementById('inp-born'), inpYear=document.getElementById('inp-year'), ageResult=document.getElementById('age-result');
-function calcAge() {
- var born=parseInt(inpBorn.value), year=parseInt(inpYear.value);
- if (born && year && year>=born) {
- var age=year-born;
- ageResult.textContent = (age<=5?'\uD83D\uDC76':age<=12?'\uD83D\uDC66':age<=18?'\uD83E\uDDD2':age<=30?'\uD83D\uDC68':'\uD83E\uDDD3') + ' You were ' + age + ' years old in ' + year + '!';
- } else { ageResult.textContent=''; }
-}
-if(inpBorn) inpBorn.addEventListener('input',calcAge);
-if(inpYear) inpYear.addEventListener('input',calcAge);
-
-// AI Description Generator
-var aiDescs = [
- 'A sun-drenched afternoon frozen in time. The colors have faded but the joy hasn\'t.',
- 'This photo smells like fresh-cut grass and screen doors slamming. Pure unfiltered nostalgia.',
- 'The photographer had zero formal training but captured something a professional never could.',
- 'Somewhere between the awkward haircut and questionable fashion lies genuine happiness.',
- 'This image radiates "we didn\'t have smartphones so we actually talked to each other" energy.',
- 'A snapshot that proves life was better when we didn\'t know what we looked like 24/7.',
- 'The composition is accidental, the lighting is terrible, and that makes it perfect.',
- 'Future historians will study this photo to understand what "vibes" meant.',
- 'WARNING: Looking at this image may cause sudden waves of nostalgia and urges to call your mom.',
-];
-var yearGuesses = [
- { range: 'early 1970s', detail: 'The warm orange tint screams Kodachrome. Bell-bottoms probably just out of frame.' },
- { range: 'late 1970s', detail: 'That sun-faded yellow only existed between 1976 and 1982. Science is clear on this.' },
- { range: 'mid 1980s', detail: 'Hair was BIG. Probably shot on a disposable camera from a drugstore.' },
- { range: 'early 1990s', detail: 'Distinctly post-Cold-War optimism. Grunge hadn\'t ruined everything yet.' },
- { range: 'late 1990s', detail: 'Strong "Y2K was coming and nobody cared" energy.' },
- { range: 'early 2000s', detail: 'Early point-and-shoot digital camera. Resolution terrible. Memories priceless.' },
-];
-var titles = ['Summer Vibes','The Good Old Days','Before Smartphones','Pure Joy','Throwback Gold','Vintage Moment','Time Capsule','Memory Lane','Golden Hour','Blurry But Beautiful'];
-
-var btnAiDesc=document.getElementById('btn-ai-describe');
-if(btnAiDesc) btnAiDesc.addEventListener('click',function(){
- var out=document.getElementById('ai-output'); out.textContent='Analyzing image...'; btnAiDesc.disabled=true;
- setTimeout(function(){
- var d=aiDescs[Math.floor(Math.random()*aiDescs.length)];
- out.textContent='\u2728 "'+d+'"';
- var da=document.getElementById('inp-desc'); if(da) da.value=d;
- var ti=document.getElementById('inp-title'); if(ti&&!ti.value) ti.value=titles[Math.floor(Math.random()*titles.length)];
- btnAiDesc.disabled=false;
- },1500);
-});
-
-var btnGuess=document.getElementById('btn-ai-guess-year');
-if(btnGuess) btnGuess.addEventListener('click',function(){
- var out=document.getElementById('ai-output'); out.textContent='Examining film grain, color temperature, fashion clues...'; btnGuess.disabled=true;
- setTimeout(function(){
- var g=yearGuesses[Math.floor(Math.random()*yearGuesses.length)];
- out.textContent='\uD83D\uDCC5 AI Guess: '+g.range+' \u2014 '+g.detail;
- btnGuess.disabled=false;
- var yi=document.getElementById('inp-year');
- if(yi&&!yi.value){ var m=g.range.match(/(\d{4})/); if(m){yi.value=m[1]; calcAge();} }
- },2000);
-});
-
-var btnSubmit=document.getElementById('btn-submit-photo');
-if(btnSubmit) btnSubmit.addEventListener('click',function(){
- btnSubmit.textContent='Submitting...'; btnSubmit.disabled=true;
- var born=document.getElementById('inp-born').value;
- var year=document.getElementById('inp-year').value;
- var payload = {
- title: document.getElementById('inp-title').value || 'Untitled',
- description: document.getElementById('inp-desc').value || '',
- category: document.getElementById('inp-cat').value || 'Childhood',
- year: year || null,
- born: born || null,
- age: (born && year && parseInt(year) >= parseInt(born)) ? (parseInt(year) - parseInt(born)) : null
- };
- fetch('/api/submit', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload) })
- .then(function(r){return r.json()})
- .then(function(d){
- btnSubmit.textContent='Submitted for Review!'; btnSubmit.style.background='var(--rose)';
- setTimeout(function(){ btnSubmit.textContent='Submit for Review'; btnSubmit.disabled=false; btnSubmit.style.background=''; },2500);
- })
- .catch(function(){
- btnSubmit.textContent='Error — try again'; btnSubmit.disabled=false;
- });
-});
-</script>
-</body>
-</html>
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..5a79699
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+Disallow: /admin
+Disallow: /api/
+Disallow: /login
+
+Sitemap: https://iwascute.com/sitemap.xml
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000..a9674ac
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+ <url>
+ <loc>https://iwascute.com/</loc>
+ <changefreq>weekly</changefreq>
+ <priority>1.0</priority>
+ </url>
+ <url>
+ <loc>https://iwascute.com/about</loc>
+ <changefreq>monthly</changefreq>
+ <priority>0.8</priority>
+ </url>
+ <url>
+ <loc>https://iwascute.com/contact</loc>
+ <changefreq>monthly</changefreq>
+ <priority>0.8</priority>
+ </url>
+ <url>
+ <loc>https://iwascute.com/browse</loc>
+ <changefreq>weekly</changefreq>
+ <priority>0.7</priority>
+ </url>
+ <url>
+ <loc>https://iwascute.com/licensor</loc>
+ <changefreq>monthly</changefreq>
+ <priority>0.7</priority>
+ </url>
+ <url>
+ <loc>https://iwascute.com/privacy</loc>
+ <changefreq>yearly</changefreq>
+ <priority>0.3</priority>
+ </url>
+</urlset>
diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx
new file mode 100644
index 0000000..385ec38
--- /dev/null
+++ b/src/app/about/page.tsx
@@ -0,0 +1,93 @@
+import type { Metadata } from 'next'
+import Link from 'next/link'
+import Navbar from '@/components/shared/Navbar'
+
+export const metadata: Metadata = {
+ title: 'About — I was Cute',
+ description:
+ 'What I was Cute is, how childhood-photo licensing works, and why we built a rights-cleared marketplace for the photos you already own.',
+}
+
+const SECTIONS: { heading: string; body: string[] }[] = [
+ {
+ heading: 'What is I was Cute?',
+ body: [
+ 'I was Cute is a marketplace for the photographs of your own childhood. Everyone has a shoebox — or a phone camera roll — full of birthday parties, first days of school, beach vacations and Halloween costumes. Those images have real value to publishers, brands, editorial outlets and researchers building respectful, consent-based image sets. We give you a straightforward way to license them and earn royalties, while keeping full control of how they can be used.',
+ ],
+ },
+ {
+ heading: 'How licensing works',
+ body: [
+ 'Upload a photo, confirm that you hold the rights to it, and choose the license terms you are comfortable with — editorial only, commercial, or excluded from AI-training use entirely. Every submission goes through a copyright and likeness-release check before it is ever listed, so nothing is offered for license until the rights are clear.',
+ 'When a licensee purchases usage of your photo, you receive a royalty. You set the boundaries up front, you can withdraw a photo from the marketplace at any time, and you are never asked to sign away ownership — you are licensing use, not selling the image.',
+ ],
+ },
+ {
+ heading: 'Who it is for',
+ body: [
+ 'For the people in the photos and the families who took them: a way to turn a drawer of old prints into a small, ongoing source of income on your own terms. For licensees: access to authentic, rights-cleared imagery with documented consent, instead of scraped or ambiguously-sourced pictures.',
+ ],
+ },
+ {
+ heading: 'Our commitment to consent',
+ body: [
+ 'Consent is the foundation of the whole service. We do not accept photos of people who have not agreed to be listed, we honor removal requests, and we never knowingly handle images of children under 13 for licensing. If you ever want a photo taken down, contact us and we will remove it.',
+ ],
+ },
+]
+
+export default function AboutPage() {
+ return (
+ <>
+ <Navbar />
+ <main
+ className="flex-1 px-6 py-16 md:px-12"
+ style={{ background: 'var(--color-cream)', color: 'var(--color-brown)' }}
+ >
+ <div className="mx-auto max-w-2xl">
+ <p
+ className="text-xs uppercase tracking-[0.2em] mb-3"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ About
+ </p>
+ <h1 className="text-4xl font-black tracking-tighter mb-8">
+ Your childhood photos, legally licensed.
+ </h1>
+
+ {SECTIONS.map(({ heading, body }) => (
+ <section key={heading} className="mb-10">
+ <h2 className="text-xl font-bold mb-3">{heading}</h2>
+ {body.map((p, i) => (
+ <p
+ key={i}
+ className="mb-3 leading-relaxed"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ {p}
+ </p>
+ ))}
+ </section>
+ ))}
+
+ <div className="flex flex-wrap gap-3 mt-12">
+ <Link
+ href="/licensor"
+ className="px-5 py-2.5 rounded-2xl text-sm font-semibold transition-all hover:scale-105"
+ style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+ >
+ Upload a photo
+ </Link>
+ <Link
+ href="/contact"
+ className="px-5 py-2.5 rounded-2xl text-sm font-semibold transition-all hover:scale-105"
+ style={{ background: 'var(--color-cream-dark)', color: 'var(--color-brown)' }}
+ >
+ Get in touch
+ </Link>
+ </div>
+ </div>
+ </main>
+ </>
+ )
+}
diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx
new file mode 100644
index 0000000..2db55ad
--- /dev/null
+++ b/src/app/contact/page.tsx
@@ -0,0 +1,85 @@
+import type { Metadata } from 'next'
+import { Mail, Clock, ShieldCheck } from 'lucide-react'
+import Navbar from '@/components/shared/Navbar'
+
+export const metadata: Metadata = {
+ title: 'Contact — I was Cute',
+ description:
+ 'Get in touch with the I was Cute team about licensing, royalties, a photo-removal request, or a general question.',
+}
+
+const REASONS: { icon: typeof Mail; title: string; body: string }[] = [
+ {
+ icon: Mail,
+ title: 'Licensing & royalties',
+ body: 'Questions about how a photo gets licensed, what you earn, or the status of a submission.',
+ },
+ {
+ icon: ShieldCheck,
+ title: 'Removal & privacy requests',
+ body: 'Ask us to take down a photo, correct information, or delete data you have submitted. We honor every request.',
+ },
+ {
+ icon: Clock,
+ title: 'Response time',
+ body: 'We read every message and typically reply within two business days.',
+ },
+]
+
+export default function ContactPage() {
+ return (
+ <>
+ <Navbar />
+ <main
+ className="flex-1 px-6 py-16 md:px-12"
+ style={{ background: 'var(--color-cream)', color: 'var(--color-brown)' }}
+ >
+ <div className="mx-auto max-w-2xl">
+ <p
+ className="text-xs uppercase tracking-[0.2em] mb-3"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ Contact
+ </p>
+ <h1 className="text-4xl font-black tracking-tighter mb-4">Get in touch</h1>
+ <p className="mb-10 leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+ Have a question about licensing your childhood photos, your royalties, or a
+ removal request? Email us and a real person will get back to you.
+ </p>
+
+ <a
+ href="mailto:info@iwascute.com"
+ className="inline-flex items-center gap-2 px-6 py-3 rounded-2xl text-base font-semibold transition-all hover:scale-105 mb-12"
+ style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+ >
+ <Mail size={18} />
+ info@iwascute.com
+ </a>
+
+ <div className="grid gap-6 sm:grid-cols-3">
+ {REASONS.map(({ icon: Icon, title, body }) => (
+ <div
+ key={title}
+ className="rounded-2xl p-5"
+ style={{ background: 'var(--color-cream-dark)' }}
+ >
+ <Icon size={20} style={{ color: 'var(--color-brown)' }} className="mb-3" />
+ <h2 className="text-sm font-bold mb-1.5">{title}</h2>
+ <p className="text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+ {body}
+ </p>
+ </div>
+ ))}
+ </div>
+
+ <p className="text-xs mt-12" style={{ color: 'var(--color-warm-gray)' }}>
+ I was Cute · Email{' '}
+ <a href="mailto:info@iwascute.com" className="underline hover:opacity-70">
+ info@iwascute.com
+ </a>
+ </p>
+ </div>
+ </main>
+ </>
+ )
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 7d23708..024476e 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
import Script from "next/script";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
-import VersionSwitcher from "@/components/shared/VersionSwitcher";
+import SiteFooter from "@/components/shared/SiteFooter";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -43,7 +43,7 @@ gtag("js", new Date());
gtag("config", "G-2DHDBP8R78");`}
</Script>
{children}
- <VersionSwitcher />
+ <SiteFooter />
</body>
</html>
);
diff --git a/src/app/page.tsx b/src/app/page.tsx
index dce293f..434ce83 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,284 +1,414 @@
'use client'
-import Link from 'next/link'
import { motion } from 'framer-motion'
-import { ArrowRight, Moon, Layers, Camera } from 'lucide-react'
+import { ArrowRight, Upload, ShieldCheck, FileSignature, DollarSign } from 'lucide-react'
+import Link from 'next/link'
+
+// Polaroid data — position, rotation, color swatch, caption
+type PolaroidData = {
+ id: number
+ top: string
+ left?: string
+ right?: string
+ rotate: number
+ color: string
+ caption: string
+}
-const VERSIONS = [
+const POLAROIDS: PolaroidData[] = [
+ { id: 1, top: '4%', left: '2%', rotate: -11, color: '#FFB5A7', caption: "Summer '92" },
+ { id: 2, top: '2%', right: '3%', rotate: 9, color: '#B5D5C5', caption: 'Birthday party' },
+ { id: 3, top: '28%', left: '0%', rotate: -7, color: '#FFE0CB', caption: 'Beach vacation' },
+ { id: 4, top: '30%', right: '1%', rotate: 12, color: '#FFB5A7', caption: "Halloween '88" },
+ { id: 5, top: '58%', left: '3%', rotate: 5, color: '#B5D5C5', caption: 'First day of school' },
+ { id: 6, top: '60%', right: '2%', rotate: -9, color: '#FFE0CB', caption: 'Family road trip' },
+ { id: 7, top: '82%', left: '6%', rotate: 8, color: '#F4846F', caption: "Grandma's house" },
+ { id: 8, top: '84%', right: '5%', rotate: -6, color: '#8BBCAA', caption: 'Little league' },
+]
+
+const HOW_IT_WORKS = [
+ {
+ step: 1,
+ icon: Upload,
+ title: 'Upload',
+ desc: 'Drag & drop your childhood photos — prints, scans, or phone shots',
+ accent: '#FFB5A7',
+ },
{
- path: '/v1',
- label: 'Dark Split',
- icon: Moon,
- accent: '#1a1d2e',
- accentText: '#FFB5A7',
- desc: 'Professional dark panel with warm photo grid. Built for licensees and AI buyers.',
- colors: ['#1a1d2e', '#FFB5A7', '#B5D5C5'],
- tag: 'Business',
+ step: 2,
+ icon: ShieldCheck,
+ title: 'Copyright Check',
+ desc: "We verify who holds the copyright — you, a family member, or the photographer",
+ accent: '#B5D5C5',
},
{
- path: '/v2',
- label: 'Glassmorphism',
- icon: Layers,
- accent: 'rgba(255,255,255,0.5)',
- accentText: 'var(--color-brown)',
- desc: 'Frosted glass over animated gradients. Modern, clean, Apple-inspired aesthetic.',
- colors: ['#FFB5A7', '#B5D5C5', '#FFE0CB'],
- tag: 'Modern',
+ step: 3,
+ icon: FileSignature,
+ title: 'Likeness Release',
+ desc: 'Sign a simple digital release so buyers can license legally',
+ accent: '#FFE0CB',
},
{
- path: '/v3',
- label: 'Polaroid',
- icon: Camera,
- accent: 'var(--color-cream)',
- accentText: 'var(--color-brown)',
- desc: 'Scattered vintage photo frames with handwritten captions. Warm and nostalgic.',
- colors: ['#FFB5A7', '#8BBCAA', '#F4846F'],
- tag: 'Nostalgic',
+ step: 4,
+ icon: DollarSign,
+ title: 'Earn',
+ desc: 'Royalties are paid out to your account every time a license is sold',
+ accent: '#F4846F',
},
]
-function VersionCard({
- version,
- index,
-}: {
- version: (typeof VERSIONS)[number]
- index: number
-}) {
- const Icon = version.icon
- const isDark = index === 0
+function Polaroid({ polaroid }: { polaroid: PolaroidData }) {
+ const posStyle: React.CSSProperties = {
+ position: 'absolute',
+ top: polaroid.top,
+ left: polaroid.left,
+ right: polaroid.right,
+ transform: `rotate(${polaroid.rotate}deg)`,
+ zIndex: 1,
+ }
return (
<motion.div
- whileHover={{ y: -8, scale: 1.02 }}
- whileTap={{ scale: 0.98 }}
- className="relative group"
+ style={posStyle}
+ whileHover={{ scale: 1.08, rotate: 0, zIndex: 20 }}
+ transition={{ type: 'spring', stiffness: 260, damping: 20 }}
+ className="w-28 md:w-36 bg-white p-2 pb-8 shadow-lg cursor-default select-none"
>
- <Link href={version.path} className="block no-underline">
- <div
- className="rounded-3xl overflow-hidden transition-shadow duration-300 group-hover:shadow-2xl"
+ {/* Photo placeholder */}
+ <div
+ className="w-full aspect-square"
+ style={{ background: polaroid.color, opacity: 0.85 }}
+ aria-hidden="true"
+ />
+ {/* Caption */}
+ <p
+ className="mt-2 text-center text-xs leading-tight"
+ style={{
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ color: '#6B705C',
+ }}
+ >
+ {polaroid.caption}
+ </p>
+ </motion.div>
+ )
+}
+
+export default function HomePage() {
+ return (
+ <main
+ className="min-h-screen flex flex-col overflow-x-hidden"
+ style={{ background: 'var(--color-cream)' }}
+ >
+ {/* ── NAV ─────────────────────────────────────────── */}
+ <nav className="relative z-50 flex items-center justify-between px-6 py-5 md:px-12">
+ <span
+ className="text-2xl"
style={{
- border: isDark ? '1px solid rgba(255,255,255,0.1)' : '1px solid var(--color-cream-dark)',
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ color: 'var(--color-brown)',
}}
>
- {/* Preview header — simulates the page's vibe */}
- <div
- className="relative h-56 flex flex-col items-center justify-center gap-3 px-6"
+ i was cute
+ </span>
+ <div
+ className="flex gap-5 text-sm font-medium"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ <Link href="/browse" className="hover:opacity-70 transition-opacity">
+ Browse
+ </Link>
+ <Link href="/about" className="hover:opacity-70 transition-opacity">
+ About
+ </Link>
+ <Link href="/contact" className="hover:opacity-70 transition-opacity">
+ Contact
+ </Link>
+ <Link
+ href="/licensor"
+ className="hover:opacity-70 transition-opacity"
+ style={{ color: 'var(--color-brown)' }}
+ >
+ Upload
+ </Link>
+ </div>
+ </nav>
+
+ {/* ── HERO + POLAROID COLLAGE ──────────────────────── */}
+ <section
+ className="relative flex-1 flex flex-col items-center justify-center text-center px-6 py-28 md:py-40 overflow-hidden"
+ style={{ minHeight: '90vh' }}
+ >
+ {/* Scattered polaroids */}
+ {POLAROIDS.map((p) => (
+ <Polaroid key={p.id} polaroid={p} />
+ ))}
+
+ {/* Soft vignette behind copy so text stays readable */}
+ <div
+ aria-hidden="true"
+ className="absolute inset-0 pointer-events-none z-10"
+ style={{
+ background:
+ 'radial-gradient(ellipse 60% 55% at 50% 50%, rgba(255,241,230,0.92) 40%, transparent 100%)',
+ }}
+ />
+
+ {/* Center copy */}
+ <div className="relative z-30 flex flex-col items-center gap-6 max-w-2xl mx-auto">
+ {/* Wordmark */}
+ <h1
+ className="leading-none select-none"
style={{
- background: isDark
- ? 'linear-gradient(135deg, #1a1d2e 0%, #2a2d3e 100%)'
- : index === 1
- ? `linear-gradient(135deg, ${version.colors[0]}44, ${version.colors[1]}44, ${version.colors[2]}44)`
- : 'var(--color-cream)',
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ fontSize: 'clamp(4rem, 14vw, 9rem)',
+ color: 'var(--color-brown)',
+ textShadow: '2px 3px 0px rgba(61,64,91,0.08)',
}}
>
- {/* Decorative elements per version */}
- {index === 1 && (
- <>
- <div
- className="absolute top-4 left-4 w-24 h-24 rounded-full blur-2xl opacity-40"
- style={{ background: version.colors[0] }}
- />
- <div
- className="absolute bottom-4 right-4 w-20 h-20 rounded-full blur-2xl opacity-30"
- style={{ background: version.colors[1] }}
- />
- {/* Glass card preview */}
- <div
- className="relative z-10 w-4/5 rounded-2xl px-5 py-4"
- style={{
- background: 'rgba(255,255,255,0.45)',
- backdropFilter: 'blur(12px)',
- border: '1px solid rgba(255,255,255,0.3)',
- }}
- >
- <p className="text-xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
- i was <span style={{ color: 'var(--color-peach-dark)' }}>cute</span>
- </p>
- <p className="text-[10px] mt-1" style={{ color: 'var(--color-warm-gray)' }}>
- Your childhood photos, legally licensed
- </p>
- </div>
- </>
- )}
+ i was cute
+ </h1>
- {index === 0 && (
- <div className="flex w-full gap-2 h-full py-4">
- {/* Dark text side */}
- <div className="flex-1 flex flex-col justify-center gap-2 px-3">
- <p className="text-lg font-black tracking-tighter" style={{ color: 'white' }}>
- Your childhood photos,{' '}
- <span style={{ color: '#FFB5A7' }}>legally</span> licensed.
- </p>
- <div
- className="self-start px-3 py-1.5 rounded-full text-[9px] font-bold"
- style={{ background: '#FFB5A7', color: '#1a1d2e' }}
- >
- Start Uploading →
- </div>
- </div>
- {/* Photo grid side */}
- <div className="w-1/3 grid grid-cols-2 gap-1">
- {version.colors.map((c, i) => (
- <div key={i} className="rounded-lg" style={{ background: c === '#1a1d2e' ? '#FFE0CB' : c }} />
- ))}
- <div className="rounded-lg" style={{ background: '#FFE0CB' }} />
- </div>
- </div>
- )}
+ {/* Tagline */}
+ <p
+ className="text-lg md:text-xl font-light tracking-wide"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ Your childhood photos, legally licensed
+ </p>
- {index === 2 && (
- <div className="relative w-full h-full flex items-center justify-center">
- {/* Scattered mini polaroids */}
- {[
- { x: '10%', y: '15%', rot: -8, color: version.colors[0] },
- { x: '65%', y: '10%', rot: 5, color: version.colors[1] },
- { x: '25%', y: '55%', rot: -4, color: version.colors[2] },
- { x: '60%', y: '50%', rot: 10, color: version.colors[0] },
- ].map((p, i) => (
- <div
- key={i}
- className="absolute w-14 h-16 bg-white rounded-sm shadow-md p-1 pb-3"
- style={{
- left: p.x,
- top: p.y,
- transform: `rotate(${p.rot}deg)`,
- }}
- >
- <div className="w-full h-full rounded-sm" style={{ background: p.color }} />
- </div>
- ))}
- {/* Center text */}
- <p
- className="relative z-10 text-2xl font-bold italic"
- style={{ fontFamily: 'Georgia, serif', color: 'var(--color-brown)' }}
- >
- i was cute
- </p>
- </div>
- )}
+ {/* Description */}
+ <p
+ className="text-base md:text-lg leading-relaxed max-w-lg"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ Remember when you were adorable? So does the internet. I was Cute helps
+ you upload your childhood photos, verify who holds the copyright and
+ likeness rights, and license the results to advertisers, publishers,
+ filmmakers, and AI companies — with royalties paid to you, not a
+ stranger who found the photo on the internet.
+ </p>
+
+ {/* CTAs */}
+ <div className="flex flex-col sm:flex-row gap-4 mt-2">
+ <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.97 }}>
+ <Link
+ href="/licensor"
+ className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-full text-base font-semibold tracking-wide"
+ style={{
+ background: 'var(--color-peach)',
+ color: 'var(--color-brown)',
+ boxShadow: '3px 4px 0px #F4846F',
+ }}
+ >
+ Start Uploading
+ <ArrowRight size={18} />
+ </Link>
+ </motion.div>
+
+ <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.97 }}>
+ <Link
+ href="#how-it-works"
+ className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-full text-base font-semibold tracking-wide border-2 transition-colors hover:bg-white/60"
+ style={{
+ borderColor: 'var(--color-sage-dark)',
+ color: 'var(--color-warm-gray)',
+ }}
+ >
+ See How It Works
+ </Link>
+ </motion.div>
</div>
+ </div>
+ </section>
- {/* Card body */}
- <div
- className="px-6 py-5"
+ {/* ── HOW IT WORKS ────────────────────────────────── */}
+ <section
+ id="how-it-works"
+ className="py-20 px-6"
+ style={{ background: 'var(--color-cream-dark)' }}
+ >
+ <div className="max-w-5xl mx-auto">
+ <h2
+ className="text-center text-3xl md:text-4xl mb-14"
style={{
- background: isDark ? '#1a1d2e' : 'white',
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ color: 'var(--color-brown)',
}}
>
- <div className="flex items-center justify-between mb-3">
- <div className="flex items-center gap-2">
+ How it works
+ </h2>
+
+ {/* Timeline row */}
+ <div className="relative grid grid-cols-1 md:grid-cols-4 gap-6">
+ {/* Connector line — desktop only */}
+ <div
+ aria-hidden="true"
+ className="hidden md:block absolute top-8 left-[12.5%] right-[12.5%] h-px"
+ style={{
+ borderTop: '2px dashed #B5D5C5',
+ zIndex: 0,
+ }}
+ />
+
+ {HOW_IT_WORKS.map(({ step, icon: Icon, title, desc, accent }) => (
+ <motion.div
+ key={step}
+ whileHover={{ y: -4 }}
+ transition={{ type: 'spring', stiffness: 300, damping: 22 }}
+ className="relative flex flex-col items-center text-center bg-white rounded-2xl px-5 pt-0 pb-7 shadow-sm"
+ style={{
+ borderTop: `4px solid ${accent}`,
+ zIndex: 1,
+ }}
+ >
+ {/* Step badge */}
<div
- className="w-8 h-8 rounded-xl flex items-center justify-center"
+ className="w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold -mt-5 mb-4 shadow-md"
style={{
- background: isDark ? 'rgba(255,181,167,0.15)' : 'var(--color-cream-dark)',
+ background: accent,
+ color: 'var(--color-brown)',
}}
>
- <Icon size={16} style={{ color: isDark ? '#FFB5A7' : 'var(--color-brown)' }} />
+ {step}
</div>
+
+ <Icon
+ size={28}
+ strokeWidth={1.5}
+ style={{ color: 'var(--color-warm-gray)' }}
+ className="mb-3"
+ />
+
<h3
- className="text-base font-bold"
- style={{ color: isDark ? 'white' : 'var(--color-brown)' }}
+ className="text-base font-bold mb-2"
+ style={{ color: 'var(--color-brown)' }}
>
- {version.label}
+ {title}
</h3>
- </div>
- <span
- className="text-[10px] font-bold tracking-widest uppercase px-2.5 py-1 rounded-full"
- style={{
- background: isDark ? 'rgba(255,181,167,0.12)' : 'var(--color-cream-dark)',
- color: isDark ? '#FFB5A7' : 'var(--color-warm-gray)',
- }}
- >
- {version.tag}
- </span>
- </div>
+ <p
+ className="text-sm leading-relaxed"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ {desc}
+ </p>
+ </motion.div>
+ ))}
+ </div>
+ </div>
+ </section>
- <p
- className="text-sm leading-relaxed mb-4"
- style={{ color: isDark ? 'rgba(255,255,255,0.5)' : 'var(--color-warm-gray)' }}
- >
- {version.desc}
+ {/* ── WHY IT MATTERS ──────────────────────────────── */}
+ <section
+ className="py-20 px-6"
+ style={{ background: 'var(--color-cream)' }}
+ >
+ <div className="max-w-3xl mx-auto">
+ <h2
+ className="text-center text-2xl md:text-3xl mb-8"
+ style={{
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ color: 'var(--color-brown)',
+ }}
+ >
+ Why licensing (not just sharing) matters
+ </h2>
+ <div
+ className="flex flex-col gap-4 text-base leading-relaxed"
+ style={{ color: 'var(--color-warm-gray)' }}
+ >
+ <p>
+ Old family photos are being scanned, uploaded, and reused every day —
+ in stock libraries, nostalgia feeds, and increasingly as training data
+ for AI image models. Most of the time nobody asks first, and nobody
+ gets paid. Two different rights are usually in play: the{' '}
+ <strong style={{ color: 'var(--color-brown)' }}>copyright</strong> in
+ the photograph itself (which belongs to whoever took the picture,
+ often a parent or a studio photographer) and the{' '}
+ <strong style={{ color: 'var(--color-brown)' }}>likeness</strong> of
+ the person or people pictured. Both usually need to be cleared before
+ a photo can be licensed responsibly.
+ </p>
+ <p>
+ I was Cute walks you through both. During upload we ask who took the
+ photo and whether you have permission to represent it, then we ask
+ the person (or people) pictured — or their guardian, for childhood
+ photos — to sign a plain-language likeness release specifying how the
+ image may be used: editorial, commercial, or AI training. Only photos
+ that clear both checks are eligible to be licensed through the
+ marketplace, and every sale pays a royalty back to the rights holder.
+ </p>
+ <p>
+ We're a small, pre-launch project — the license marketplace is still
+ being built out (see{' '}
+ <Link href="/browse" className="underline" style={{ color: 'var(--color-brown)' }}>
+ Browse
+ </Link>
+ ), and the review queue is handled by hand while volume is low. If
+ you have questions before you upload anything, read our{' '}
+ <Link href="/privacy" className="underline" style={{ color: 'var(--color-brown)' }}>
+ Privacy Policy
+ </Link>{' '}
+ or{' '}
+ <Link href="/contact" className="underline" style={{ color: 'var(--color-brown)' }}>
+ get in touch
+ </Link>
+ .
</p>
-
- {/* Color dots */}
- <div className="flex items-center justify-between">
- <div className="flex gap-1.5">
- {version.colors.map((c, i) => (
- <div
- key={i}
- className="w-4 h-4 rounded-full"
- style={{ background: c, border: '2px solid rgba(255,255,255,0.2)' }}
- />
- ))}
- </div>
- <span
- className="flex items-center gap-1 text-xs font-semibold"
- style={{ color: isDark ? '#FFB5A7' : 'var(--color-peach-dark)' }}
- >
- Enter
- <ArrowRight size={12} />
- </span>
- </div>
</div>
</div>
- </Link>
- </motion.div>
- )
-}
+ </section>
-export default function HomePage() {
- return (
- <main
- className="min-h-screen flex flex-col items-center px-4 py-12 md:py-20 overflow-x-hidden"
- style={{ background: 'var(--color-cream)' }}
- >
- {/* Decorative blobs */}
- <div
- className="absolute -top-32 -left-32 w-96 h-96 rounded-full opacity-20 blur-3xl pointer-events-none"
- style={{ background: 'var(--color-peach)' }}
- />
- <div
- className="absolute -bottom-40 -right-32 w-[30rem] h-[30rem] rounded-full opacity-15 blur-3xl pointer-events-none"
- style={{ background: 'var(--color-sage)' }}
- />
-
- {/* Header */}
- <div className="relative z-10 text-center mb-12 md:mb-16 max-w-2xl">
- <h1
- className="text-5xl md:text-7xl font-black tracking-tighter leading-[0.9]"
- style={{ color: 'var(--color-brown)' }}
+ {/* ── MINI CTA STRIP ──────────────────────────────── */}
+ <section
+ className="py-16 px-6 text-center"
+ style={{ background: 'var(--color-peach)', opacity: 0.92 }}
+ >
+ <h2
+ className="text-2xl md:text-3xl mb-3"
+ style={{
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ color: 'var(--color-brown)',
+ }}
>
- i was{' '}
- <span style={{ color: 'var(--color-peach-dark)' }}>cute</span>
- </h1>
+ Those photos deserve more than a dusty drawer.
+ </h2>
<p
- className="mt-4 text-lg md:text-xl font-light"
- style={{ color: 'var(--color-warm-gray)' }}
+ className="text-base mb-8"
+ style={{ color: 'var(--color-brown)', opacity: 0.75 }}
>
- Choose your experience
+ Upload one, clear the rights, and see what a license is worth.
</p>
- <p
- className="mt-2 text-sm max-w-md mx-auto"
- style={{ color: 'var(--color-warm-gray)' }}
+ <motion.div
+ className="inline-block"
+ whileHover={{ scale: 1.06 }}
+ whileTap={{ scale: 0.96 }}
>
- Three unique designs, one marketplace. Pick the vibe that speaks to you.
- </p>
- </div>
-
- {/* Version cards */}
- <div className="relative z-10 grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl w-full">
- {VERSIONS.map((v, i) => (
- <VersionCard key={v.path} version={v} index={i} />
- ))}
- </div>
-
- {/* Footer */}
- <p
- className="relative z-10 mt-12 text-xs"
- style={{ color: 'var(--color-warm-gray)' }}
- >
- Your childhood photos, legally licensed. © 2025 I was Cute.
- </p>
+ <Link
+ href="/licensor"
+ className="inline-flex items-center gap-2 px-9 py-4 rounded-full text-base font-semibold"
+ style={{
+ background: 'var(--color-brown)',
+ color: 'var(--color-cream)',
+ boxShadow: '3px 4px 0px rgba(61,64,91,0.3)',
+ }}
+ >
+ Get Started — it's free
+ <ArrowRight size={18} />
+ </Link>
+ </motion.div>
+ </section>
</main>
)
}
diff --git a/src/app/v1/page.tsx b/src/app/v1/page.tsx
index 10db494..70f2317 100644
--- a/src/app/v1/page.tsx
+++ b/src/app/v1/page.tsx
@@ -1,259 +1,13 @@
-'use client'
-
-import Link from 'next/link'
-import { motion } from 'framer-motion'
-import {
- ArrowRight,
- Shield,
- Camera,
- DollarSign,
- Sparkles,
- Upload,
- Scale,
- Heart,
-} from 'lucide-react'
-
-/* ------------------------------------------------------------------ */
-/* V1 — Dark Split-Screen */
-/* Left: dark panel with text + CTAs. Right: warm photo grid collage. */
-/* Completely different aesthetic from V2 (glass) and V3 (polaroid). */
-/* ------------------------------------------------------------------ */
-
-const GRID_ITEMS = [
- { color: '#FFB5A7', size: 'row-span-2', label: 'Age 3' },
- { color: '#B5D5C5', size: '', label: 'Age 7' },
- { color: '#FFE0CB', size: '', label: 'Age 5' },
- { color: '#F4846F', size: 'col-span-2', label: 'Age 1' },
- { color: '#8BBCAA', size: '', label: 'Age 9' },
- { color: '#FFB5A7', size: 'row-span-2', label: 'Age 4' },
- { color: '#FFE0CB', size: '', label: 'Age 6' },
- { color: '#B5D5C5', size: '', label: 'Age 2' },
-]
-
-function PhotoGrid() {
- return (
- <div className="grid grid-cols-3 grid-rows-4 gap-2 h-full p-4">
- {GRID_ITEMS.map((item, i) => (
- <motion.div
- key={i}
- whileHover={{ scale: 1.04, zIndex: 10 }}
- className={`rounded-2xl overflow-hidden relative cursor-pointer transition-shadow hover:shadow-2xl ${item.size}`}
- style={{ background: item.color }}
- >
- <div className="absolute inset-0 flex items-end p-3">
- <span
- className="text-xs font-semibold px-2 py-1 rounded-lg"
- style={{ background: 'rgba(0,0,0,0.35)', color: 'white' }}
- >
- {item.label}
- </span>
- </div>
- </motion.div>
- ))}
- </div>
- )
-}
-
-function StatPill({ icon: Icon, label }: { icon: typeof Shield; label: string }) {
- return (
- <div
- className="flex items-center gap-2 px-4 py-2.5 rounded-full"
- style={{ background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.1)' }}
- >
- <Icon size={13} style={{ color: '#FFB5A7' }} />
- <span className="text-xs font-medium" style={{ color: 'rgba(255,255,255,0.7)' }}>
- {label}
- </span>
- </div>
- )
-}
-
-function StepCard({
- number,
- title,
- desc,
- icon: Icon,
-}: {
- number: string
- title: string
- desc: string
- icon: typeof Upload
-}) {
- return (
- <motion.div
- whileHover={{ y: -4 }}
- className="flex gap-4 p-5 rounded-2xl transition-all"
- style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)' }}
- >
- <div
- className="w-10 h-10 rounded-xl flex items-center justify-center shrink-0"
- style={{ background: 'rgba(255, 181, 167, 0.15)' }}
- >
- <Icon size={18} style={{ color: '#FFB5A7' }} />
- </div>
- <div>
- <p className="text-sm font-bold" style={{ color: 'rgba(255,255,255,0.9)' }}>
- <span style={{ color: '#FFB5A7' }}>{number}.</span> {title}
- </p>
- <p className="text-xs mt-1 leading-relaxed" style={{ color: 'rgba(255,255,255,0.45)' }}>
- {desc}
- </p>
- </div>
- </motion.div>
- )
-}
-
+import { redirect } from 'next/navigation'
+
+/**
+ * V1 was an internal design-exploration variant of the homepage (dark
+ * split-screen), shown to real visitors via a "Choose your experience"
+ * version switcher. That switcher read as an unfinished/dev site to a
+ * human reviewer, so the canonical homepage now lives at "/" and this
+ * route redirects there. Original design preserved in git history
+ * (TK-11412, AdSense readiness pass).
+ */
export default function V1Page() {
- return (
- <div className="min-h-screen flex flex-col lg:flex-row">
- {/* Left panel — dark */}
- <div
- className="flex-1 flex flex-col justify-between px-8 py-10 md:px-16 md:py-14 lg:max-w-[55%]"
- style={{ background: '#1a1d2e' }}
- >
- {/* Nav */}
- <div className="flex items-center justify-between">
- <span className="text-lg font-black tracking-tighter" style={{ color: 'white' }}>
- i was cute
- </span>
- <div className="flex items-center gap-4 text-xs font-medium" style={{ color: 'rgba(255,255,255,0.5)' }}>
- <Link href="/browse" className="hover:text-white transition-colors">Browse</Link>
- <Link href="/licensor" className="hover:text-white transition-colors">Upload</Link>
- <Link
- href="/login"
- className="flex items-center gap-1.5 px-4 py-2 rounded-full transition-all hover:scale-105"
- style={{ background: '#FFB5A7', color: '#1a1d2e' }}
- >
- Sign In
- </Link>
- </div>
- </div>
-
- {/* Hero content */}
- <div className="flex flex-col gap-8 my-12 lg:my-0">
- {/* Badge */}
- <div
- className="inline-flex items-center gap-2 self-start px-3 py-1.5 rounded-full"
- style={{ background: 'rgba(255, 181, 167, 0.12)', border: '1px solid rgba(255, 181, 167, 0.2)' }}
- >
- <Sparkles size={12} style={{ color: '#FFB5A7' }} />
- <span className="text-xs font-semibold" style={{ color: '#FFB5A7' }}>
- Dual-rights photo marketplace
- </span>
- </div>
-
- {/* Headline */}
- <div>
- <h1
- className="text-5xl md:text-7xl font-black tracking-tighter leading-[0.9]"
- style={{ color: 'white' }}
- >
- Your childhood
- <br />
- photos,{' '}
- <span style={{ color: '#FFB5A7' }}>legally</span>
- <br />
- licensed.
- </h1>
- <p
- className="mt-6 text-base md:text-lg leading-relaxed max-w-md"
- style={{ color: 'rgba(255,255,255,0.5)' }}
- >
- Upload your most precious memories. We verify copyright and likeness rights,
- then connect you with advertisers, filmmakers, and AI companies who pay royalties.
- </p>
- </div>
-
- {/* CTAs */}
- <div className="flex flex-wrap gap-3">
- <motion.div whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }}>
- <Link
- href="/licensor"
- className="inline-flex items-center gap-2 px-8 py-4 rounded-full text-sm font-bold tracking-wide transition-all"
- style={{ background: '#FFB5A7', color: '#1a1d2e' }}
- >
- Start Uploading
- <ArrowRight size={16} />
- </Link>
- </motion.div>
- <motion.div whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }}>
- <Link
- href="/browse"
- className="inline-flex items-center gap-2 px-8 py-4 rounded-full text-sm font-semibold tracking-wide transition-all"
- style={{
- background: 'transparent',
- color: 'rgba(255,255,255,0.7)',
- border: '1px solid rgba(255,255,255,0.15)',
- }}
- >
- Browse Library
- </Link>
- </motion.div>
- </div>
-
- {/* Trust pills */}
- <div className="flex flex-wrap gap-2">
- <StatPill icon={Shield} label="Dual-rights verified" />
- <StatPill icon={Camera} label="AI training ready" />
- <StatPill icon={DollarSign} label="Instant royalties" />
- </div>
- </div>
-
- {/* Bottom stats */}
- <div
- className="flex items-center gap-8 pt-6"
- style={{ borderTop: '1px solid rgba(255,255,255,0.08)' }}
- >
- {[
- { value: '10K+', label: 'Photos' },
- { value: '500+', label: 'Licensors' },
- { value: '100%', label: 'Verified' },
- ].map(({ value, label }) => (
- <div key={label}>
- <p className="text-2xl font-black" style={{ color: '#FFB5A7' }}>{value}</p>
- <p className="text-xs" style={{ color: 'rgba(255,255,255,0.4)' }}>{label}</p>
- </div>
- ))}
- </div>
- </div>
-
- {/* Right panel — warm photo grid */}
- <div
- className="flex-1 hidden lg:flex flex-col"
- style={{ background: 'var(--color-cream)' }}
- >
- <PhotoGrid />
- </div>
-
- {/* Mobile-only: photo grid preview + how it works */}
- <div className="lg:hidden" style={{ background: 'var(--color-cream)' }}>
- <div className="grid grid-cols-3 gap-2 p-4" style={{ height: '200px' }}>
- {GRID_ITEMS.slice(0, 3).map((item, i) => (
- <div
- key={i}
- className="rounded-xl"
- style={{ background: item.color }}
- />
- ))}
- </div>
- </div>
-
- <div
- className="lg:hidden px-8 py-12"
- style={{ background: '#1a1d2e' }}
- >
- <h2
- className="text-xl font-bold mb-6"
- style={{ color: 'rgba(255,255,255,0.9)' }}
- >
- How it works
- </h2>
- <div className="flex flex-col gap-3">
- <StepCard number="1" title="Upload your photos" desc="Drag entire folders. We extract EXIF metadata automatically." icon={Upload} />
- <StepCard number="2" title="Clear the rights" desc="Verify copyright (photographer) and likeness (you) in our guided wizard." icon={Scale} />
- <StepCard number="3" title="Earn royalties" desc="Every license generates royalties deposited directly to your account." icon={Heart} />
- </div>
- </div>
- </div>
- )
+ redirect('/')
}
diff --git a/src/app/v2/page.tsx b/src/app/v2/page.tsx
index 451335f..81a450b 100644
--- a/src/app/v2/page.tsx
+++ b/src/app/v2/page.tsx
@@ -1,506 +1,11 @@
-'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 · Free to upload · 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'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 }}
- >
- © {new Date().getFullYear()} I was Cute. All rights reserved.
- </p>
- </footer>
- )
-}
-
-// ---------------------------------------------------------------------------
-// Page — root export
-// ---------------------------------------------------------------------------
+import { redirect } from 'next/navigation'
/**
- * /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+.
+ * V2 was an internal design-exploration variant of the homepage
+ * (glassmorphism), shown to real visitors via a "Choose your experience"
+ * version switcher. See v1/page.tsx for why this now redirects to "/".
+ * Original design preserved in git history (TK-11412, AdSense readiness pass).
*/
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>
- </>
- )
+ redirect('/')
}
diff --git a/src/app/v3/page.tsx b/src/app/v3/page.tsx
index e44d36e..5e6e5c2 100644
--- a/src/app/v3/page.tsx
+++ b/src/app/v3/page.tsx
@@ -1,417 +1,12 @@
-'use client'
-
-import { motion } from 'framer-motion'
-import { ArrowRight, Upload, ShieldCheck, FileSignature, DollarSign } from 'lucide-react'
-import Link from 'next/link'
-
-// Polaroid data — position, rotation, color swatch, caption
-type PolaroidData = {
- id: number
- top: string
- left?: string
- right?: string
- rotate: number
- color: string
- caption: string
-}
-
-const POLAROIDS: PolaroidData[] = [
- { id: 1, top: '4%', left: '2%', rotate: -11, color: '#FFB5A7', caption: "Summer '92" },
- { id: 2, top: '2%', right: '3%', rotate: 9, color: '#B5D5C5', caption: 'Birthday party' },
- { id: 3, top: '28%', left: '0%', rotate: -7, color: '#FFE0CB', caption: 'Beach vacation' },
- { id: 4, top: '30%', right: '1%', rotate: 12, color: '#FFB5A7', caption: "Halloween '88" },
- { id: 5, top: '58%', left: '3%', rotate: 5, color: '#B5D5C5', caption: 'First day of school' },
- { id: 6, top: '60%', right: '2%', rotate: -9, color: '#FFE0CB', caption: 'Family road trip' },
- { id: 7, top: '82%', left: '6%', rotate: 8, color: '#F4846F', caption: "Grandma's house" },
- { id: 8, top: '84%', right: '5%', rotate: -6, color: '#8BBCAA', caption: 'Little league' },
-]
-
-const HOW_IT_WORKS = [
- {
- step: 1,
- icon: Upload,
- title: 'Upload',
- desc: 'Drag & drop your childhood photos — prints, scans, or phone shots',
- accent: '#FFB5A7',
- },
- {
- step: 2,
- icon: ShieldCheck,
- title: 'Copyright Check',
- desc: "We verify who holds the copyright — you, a family member, or the photographer",
- accent: '#B5D5C5',
- },
- {
- step: 3,
- icon: FileSignature,
- title: 'Likeness Release',
- desc: 'Sign a simple digital release so buyers can license legally',
- accent: '#FFE0CB',
- },
- {
- step: 4,
- icon: DollarSign,
- title: 'Earn',
- desc: 'Royalties hit your account every time a license is sold',
- accent: '#F4846F',
- },
-]
-
-function Polaroid({ polaroid }: { polaroid: PolaroidData }) {
- const posStyle: React.CSSProperties = {
- position: 'absolute',
- top: polaroid.top,
- left: polaroid.left,
- right: polaroid.right,
- transform: `rotate(${polaroid.rotate}deg)`,
- zIndex: 1,
- }
-
- return (
- <motion.div
- style={posStyle}
- whileHover={{ scale: 1.08, rotate: 0, zIndex: 20 }}
- transition={{ type: 'spring', stiffness: 260, damping: 20 }}
- className="w-28 md:w-36 bg-white p-2 pb-8 shadow-lg cursor-default select-none"
- >
- {/* Photo placeholder */}
- <div
- className="w-full aspect-square"
- style={{ background: polaroid.color, opacity: 0.85 }}
- aria-hidden="true"
- />
- {/* Caption */}
- <p
- className="mt-2 text-center text-xs leading-tight"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- color: '#6B705C',
- }}
- >
- {polaroid.caption}
- </p>
- </motion.div>
- )
-}
-
+import { redirect } from 'next/navigation'
+
+/**
+ * V3 was an internal design-exploration variant of the homepage, shown to
+ * real visitors via a "Choose your experience" version switcher. That
+ * switcher read as an unfinished/dev site to a human reviewer, so the
+ * canonical homepage now lives at "/" and this route redirects there.
+ * Original design preserved in git history (TK-11412, AdSense readiness pass).
+ */
export default function V3Page() {
- return (
- <main
- className="min-h-screen flex flex-col overflow-x-hidden"
- style={{ background: 'var(--color-cream)' }}
- >
- {/* ── NAV ─────────────────────────────────────────── */}
- <nav className="relative z-50 flex items-center justify-between px-6 py-5 md:px-12">
- <span
- className="text-2xl"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- fontWeight: 700,
- color: 'var(--color-brown)',
- }}
- >
- i was cute
- </span>
- <div
- className="flex gap-5 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"
- style={{ color: 'var(--color-brown)' }}
- >
- Upload
- </Link>
- </div>
- </nav>
-
- {/* ── HERO + POLAROID COLLAGE ──────────────────────── */}
- <section
- className="relative flex-1 flex flex-col items-center justify-center text-center px-6 py-28 md:py-40 overflow-hidden"
- style={{ minHeight: '90vh' }}
- >
- {/* Scattered polaroids */}
- {POLAROIDS.map((p) => (
- <Polaroid key={p.id} polaroid={p} />
- ))}
-
- {/* Soft vignette behind copy so text stays readable */}
- <div
- aria-hidden="true"
- className="absolute inset-0 pointer-events-none z-10"
- style={{
- background:
- 'radial-gradient(ellipse 60% 55% at 50% 50%, rgba(255,241,230,0.92) 40%, transparent 100%)',
- }}
- />
-
- {/* Center copy */}
- <div className="relative z-30 flex flex-col items-center gap-6 max-w-2xl mx-auto">
- {/* Wordmark */}
- <h1
- className="leading-none select-none"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- fontWeight: 700,
- fontSize: 'clamp(4rem, 14vw, 9rem)',
- color: 'var(--color-brown)',
- textShadow: '2px 3px 0px rgba(61,64,91,0.08)',
- }}
- >
- i was cute
- </h1>
-
- {/* Tagline */}
- <p
- className="text-lg md:text-xl font-light tracking-wide"
- style={{ color: 'var(--color-warm-gray)' }}
- >
- Your childhood photos, legally licensed
- </p>
-
- {/* Description */}
- <p
- className="text-base md:text-lg leading-relaxed max-w-lg"
- style={{ color: 'var(--color-warm-gray)' }}
- >
- Remember when you were adorable? So does the internet. Upload your
- childhood photos, verify the rights, and earn royalties every time
- they're licensed.
- </p>
-
- {/* CTAs */}
- <div className="flex flex-col sm:flex-row gap-4 mt-2">
- <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.97 }}>
- <Link
- href="/licensor"
- className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-full text-base font-semibold tracking-wide"
- style={{
- background: 'var(--color-peach)',
- color: 'var(--color-brown)',
- boxShadow: '3px 4px 0px #F4846F',
- }}
- >
- Start Uploading
- <ArrowRight size={18} />
- </Link>
- </motion.div>
-
- <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.97 }}>
- <Link
- href="#how-it-works"
- className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-full text-base font-semibold tracking-wide border-2 transition-colors hover:bg-white/60"
- style={{
- borderColor: 'var(--color-sage-dark)',
- color: 'var(--color-warm-gray)',
- }}
- >
- See How It Works
- </Link>
- </motion.div>
- </div>
- </div>
- </section>
-
- {/* ── HOW IT WORKS ────────────────────────────────── */}
- <section
- id="how-it-works"
- className="py-20 px-6"
- style={{ background: 'var(--color-cream-dark)' }}
- >
- <div className="max-w-5xl mx-auto">
- <h2
- className="text-center text-3xl md:text-4xl mb-14"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- fontWeight: 700,
- color: 'var(--color-brown)',
- }}
- >
- How it works
- </h2>
-
- {/* Timeline row */}
- <div className="relative grid grid-cols-1 md:grid-cols-4 gap-6">
- {/* Connector line — desktop only */}
- <div
- aria-hidden="true"
- className="hidden md:block absolute top-8 left-[12.5%] right-[12.5%] h-px"
- style={{
- borderTop: '2px dashed #B5D5C5',
- zIndex: 0,
- }}
- />
-
- {HOW_IT_WORKS.map(({ step, icon: Icon, title, desc, accent }) => (
- <motion.div
- key={step}
- whileHover={{ y: -4 }}
- transition={{ type: 'spring', stiffness: 300, damping: 22 }}
- className="relative flex flex-col items-center text-center bg-white rounded-2xl px-5 pt-0 pb-7 shadow-sm"
- style={{
- borderTop: `4px solid ${accent}`,
- zIndex: 1,
- }}
- >
- {/* Step badge */}
- <div
- className="w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold -mt-5 mb-4 shadow-md"
- style={{
- background: accent,
- color: 'var(--color-brown)',
- }}
- >
- {step}
- </div>
-
- <Icon
- size={28}
- strokeWidth={1.5}
- style={{ color: 'var(--color-warm-gray)' }}
- className="mb-3"
- />
-
- <h3
- className="text-base font-bold mb-2"
- style={{ color: 'var(--color-brown)' }}
- >
- {title}
- </h3>
- <p
- className="text-sm leading-relaxed"
- style={{ color: 'var(--color-warm-gray)' }}
- >
- {desc}
- </p>
- </motion.div>
- ))}
- </div>
- </div>
- </section>
-
- {/* ── TESTIMONIAL ─────────────────────────────────── */}
- <section
- className="py-20 px-6 flex flex-col items-center text-center"
- style={{ background: 'var(--color-cream)' }}
- >
- <div className="max-w-2xl mx-auto relative">
- {/* Decorative open quote */}
- <span
- aria-hidden="true"
- className="absolute -top-6 -left-4 text-8xl leading-none pointer-events-none select-none"
- style={{ color: 'var(--color-peach)', opacity: 0.6, fontFamily: 'Georgia, serif' }}
- >
- “
- </span>
-
- <blockquote>
- <p
- className="text-xl md:text-2xl leading-relaxed mb-6 relative z-10"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- color: 'var(--color-brown)',
- }}
- >
- I uploaded my baby photos and earned $47 in the first month!
- Honestly I wasn't even sure it would work — now I check my
- dashboard every morning like it's a slot machine.
- </p>
- <footer
- className="text-sm font-semibold tracking-wide"
- style={{ color: 'var(--color-warm-gray)' }}
- >
- — Jessica T., uploaded 34 photos in March
- </footer>
- </blockquote>
-
- {/* Decorative close quote */}
- <span
- aria-hidden="true"
- className="absolute -bottom-10 -right-4 text-8xl leading-none pointer-events-none select-none"
- style={{ color: 'var(--color-sage)', opacity: 0.6, fontFamily: 'Georgia, serif' }}
- >
- ”
- </span>
- </div>
- </section>
-
- {/* ── MINI CTA STRIP ──────────────────────────────── */}
- <section
- className="py-16 px-6 text-center"
- style={{ background: 'var(--color-peach)', opacity: 0.92 }}
- >
- <h2
- className="text-2xl md:text-3xl mb-3"
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- fontWeight: 700,
- color: 'var(--color-brown)',
- }}
- >
- Those photos deserve more than a dusty drawer.
- </h2>
- <p
- className="text-base mb-8"
- style={{ color: 'var(--color-brown)', opacity: 0.75 }}
- >
- Join hundreds of people already earning from their childhood memories.
- </p>
- <motion.div
- className="inline-block"
- whileHover={{ scale: 1.06 }}
- whileTap={{ scale: 0.96 }}
- >
- <Link
- href="/licensor"
- className="inline-flex items-center gap-2 px-9 py-4 rounded-full text-base font-semibold"
- style={{
- background: 'var(--color-brown)',
- color: 'var(--color-cream)',
- boxShadow: '3px 4px 0px rgba(61,64,91,0.3)',
- }}
- >
- Get Started — it's free
- <ArrowRight size={18} />
- </Link>
- </motion.div>
- </section>
-
- {/* ── FOOTER ──────────────────────────────────────── */}
- <footer
- className="py-8 px-6 text-center text-sm flex flex-col items-center gap-2"
- style={{ color: 'var(--color-warm-gray)', background: 'var(--color-cream-dark)' }}
- >
- <span
- style={{
- fontFamily: 'Georgia, "Times New Roman", serif',
- fontStyle: 'italic',
- fontWeight: 700,
- color: 'var(--color-brown)',
- fontSize: '1.1rem',
- }}
- >
- i was cute
- </span>
- <div className="flex gap-5 text-xs">
- <Link href="#" className="hover:opacity-70 transition-opacity">Privacy</Link>
- <Link href="#" className="hover:opacity-70 transition-opacity">Terms</Link>
- <Link href="#" className="hover:opacity-70 transition-opacity">Help</Link>
- <Link href="/browse" className="hover:opacity-70 transition-opacity">Browse</Link>
- </div>
- <p className="text-xs opacity-60 mt-1">
- © 2025 I was Cute. All rights reserved. Your memories, your royalties.
- </p>
- </footer>
- </main>
- )
+ redirect('/')
}
diff --git a/src/components/shared/SiteFooter.tsx b/src/components/shared/SiteFooter.tsx
new file mode 100644
index 0000000..5ef41ab
--- /dev/null
+++ b/src/components/shared/SiteFooter.tsx
@@ -0,0 +1,45 @@
+import Link from 'next/link'
+
+/**
+ * Sitewide footer — the required Privacy / About / Contact links (AdSense
+ * site-requirements checklist) rendered on EVERY page via the root layout,
+ * not just the homepage. Replaces the dev-only VersionSwitcher that used to
+ * render here (TK-11412).
+ */
+export default function SiteFooter() {
+ const year = new Date().getFullYear()
+
+ return (
+ <footer
+ className="mt-auto py-8 px-6 text-center text-sm flex flex-col items-center gap-3"
+ style={{ color: 'var(--color-warm-gray)', background: 'var(--color-cream-dark)' }}
+ >
+ <span
+ style={{
+ fontFamily: 'Georgia, "Times New Roman", serif',
+ fontStyle: 'italic',
+ fontWeight: 700,
+ color: 'var(--color-brown)',
+ fontSize: '1.1rem',
+ }}
+ >
+ i was cute
+ </span>
+
+ <nav className="flex flex-wrap items-center justify-center gap-x-5 gap-y-1 text-xs">
+ <Link href="/about" className="hover:opacity-70 transition-opacity">About</Link>
+ <Link href="/contact" className="hover:opacity-70 transition-opacity">Contact</Link>
+ <Link href="/privacy" className="hover:opacity-70 transition-opacity">Privacy Policy</Link>
+ <Link href="/browse" className="hover:opacity-70 transition-opacity">Browse</Link>
+ <Link href="/licensor" className="hover:opacity-70 transition-opacity">Upload</Link>
+ </nav>
+
+ <p className="text-xs opacity-70 mt-1">
+ © {year} I was Cute. All rights reserved. · Contact:{' '}
+ <a href="mailto:info@iwascute.com" className="underline hover:opacity-70">
+ info@iwascute.com
+ </a>
+ </p>
+ </footer>
+ )
+}
← f962e4a iwascute: redirect /privacy -> /privacy.html (AdSense URL co
·
back to IWasCute
·
iwascute: add FAQ page (content depth), wire into footer + s c6ece76 →