[object Object]

← back to IWasCute

initial scaffold (gitify-all 2026-05-06)

5f5efec617868324ff2afb87d38322afa272c60e · 2026-05-06 10:25:05 -0700 · Steve Abrams

Files touched

Diff

commit 5f5efec617868324ff2afb87d38322afa272c60e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed May 6 10:25:05 2026 -0700

    initial scaffold (gitify-all 2026-05-06)
---
 .gitignore                                  |   50 +
 AGENTS.md                                   |    5 +
 CLAUDE.md                                   |    1 +
 README.md                                   |   36 +
 db/001_schema.sql                           |  256 +
 ecosystem.config.js                         |   14 +
 eslint.config.mjs                           |   18 +
 next.config.ts                              |    7 +
 package-lock.json                           | 6828 +++++++++++++++++++++++++++
 package.json                                |   33 +
 postcss.config.mjs                          |    7 +
 public/admin.html                           |  246 +
 public/favicon.svg                          |    4 +
 public/file.svg                             |    1 +
 public/globe.svg                            |    1 +
 public/index.html                           |  827 ++++
 public/next.svg                             |    1 +
 public/vercel.svg                           |    1 +
 public/window.svg                           |    1 +
 server.js                                   |   57 +
 src/app/admin/page.tsx                      | 1371 ++++++
 src/app/api/admin/route.ts                  |  283 ++
 src/app/api/auth/route.ts                   |   72 +
 src/app/browse/page.tsx                     |  281 ++
 src/app/dashboard/page.tsx                  |  320 ++
 src/app/favicon.ico                         |  Bin 0 -> 25931 bytes
 src/app/globals.css                         |   79 +
 src/app/layout.tsx                          |   37 +
 src/app/licensor/layout.tsx                 |   76 +
 src/app/licensor/page.tsx                   |   95 +
 src/app/login/page.tsx                      |  242 +
 src/app/page.tsx                            |  284 ++
 src/app/v1/page.tsx                         |  259 +
 src/app/v2/page.tsx                         |  506 ++
 src/app/v3/page.tsx                         |  417 ++
 src/components/licensor/CopyrightInfo.tsx   |  534 +++
 src/components/licensor/LikenessRelease.tsx |  364 ++
 src/components/licensor/PhotoUpload.tsx     |  797 ++++
 src/components/licensor/ReviewSubmit.tsx    |  359 ++
 src/components/licensor/Stepper.tsx         |  139 +
 src/components/shared/Navbar.tsx            |   86 +
 src/components/shared/VersionSwitcher.tsx   |   53 +
 src/lib/auth.ts                             |   23 +
 src/lib/db.ts                               |   11 +
 tsconfig.json                               |   34 +
 45 files changed, 15116 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f522159
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,50 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+node_modules/
+.env.local
+.env.*.local
+.env.*
+tmp/
+*.log
+dist/
+build/
+*.bak
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..8bd0e39
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,5 @@
+<!-- BEGIN:nextjs-agent-rules -->
+# This is NOT the Next.js you know
+
+This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
+<!-- END:nextjs-agent-rules -->
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..43c994c
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+@AGENTS.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e215bc4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/db/001_schema.sql b/db/001_schema.sql
new file mode 100644
index 0000000..e336b29
--- /dev/null
+++ b/db/001_schema.sql
@@ -0,0 +1,256 @@
+-- Iwascute.com — Core Schema
+-- Dual-rights clearance marketplace for childhood photos
+
+-- Extensions
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+CREATE EXTENSION IF NOT EXISTS "pgcrypto";
+
+-- ============================================================
+-- USERS
+-- ============================================================
+CREATE TABLE users (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  email TEXT UNIQUE NOT NULL,
+  password_hash TEXT, -- NULL for OAuth-only users
+  display_name TEXT NOT NULL,
+  role TEXT NOT NULL DEFAULT 'licensor' CHECK (role IN ('licensor', 'licensee', 'admin')),
+  is_verified BOOLEAN DEFAULT FALSE,
+  is_adult_confirmed BOOLEAN DEFAULT FALSE,
+  avatar_url TEXT,
+  created_at TIMESTAMPTZ DEFAULT NOW(),
+  updated_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- Admin user
+INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+VALUES (
+  'admin@iwascute.com',
+  crypt('DWSecure2024!', gen_salt('bf')),
+  'Admin',
+  'admin',
+  TRUE,
+  TRUE
+);
+
+-- ============================================================
+-- UPLOADS (a submission = batch of photos + rights info)
+-- ============================================================
+CREATE TABLE uploads (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+  status TEXT NOT NULL DEFAULT 'pending'
+    CHECK (status IN ('pending', 'in_review', 'approved', 'rejected', 'flagged', 'dmca_removed')),
+
+  -- Copyright info (step 2)
+  copyright_relationship TEXT CHECK (copyright_relationship IN ('parent', 'family', 'professional', 'unknown')),
+  copyright_photographer TEXT,
+  copyright_year INTEGER,
+  copyright_permission TEXT CHECK (copyright_permission IN ('yes', 'no', 'deceased')),
+  copyright_estate_rights BOOLEAN DEFAULT FALSE,
+
+  -- Likeness release (step 3)
+  likeness_is_subject BOOLEAN DEFAULT FALSE,
+  likeness_is_adult BOOLEAN DEFAULT FALSE,
+  likeness_editorial BOOLEAN DEFAULT FALSE,
+  likeness_commercial BOOLEAN DEFAULT FALSE,
+  likeness_ai_training BOOLEAN DEFAULT FALSE,
+  likeness_signature TEXT,
+  likeness_signed_at TIMESTAMPTZ,
+
+  -- Review
+  reviewed_by UUID REFERENCES users(id),
+  reviewed_at TIMESTAMPTZ,
+  review_notes TEXT,
+  rejection_reason TEXT,
+
+  created_at TIMESTAMPTZ DEFAULT NOW(),
+  updated_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- ============================================================
+-- PHOTOS (individual images within an upload)
+-- ============================================================
+CREATE TABLE photos (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  upload_id UUID NOT NULL REFERENCES uploads(id) ON DELETE CASCADE,
+  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+
+  -- File info
+  filename TEXT NOT NULL,
+  file_size INTEGER,
+  mime_type TEXT,
+  storage_path TEXT, -- future: S3/R2 path
+  thumbnail_path TEXT,
+
+  -- EXIF metadata
+  exif_date_taken TIMESTAMPTZ,
+  exif_year INTEGER,
+  exif_latitude DOUBLE PRECISION,
+  exif_longitude DOUBLE PRECISION,
+  exif_camera_make TEXT,
+  exif_camera_model TEXT,
+  exif_width INTEGER,
+  exif_height INTEGER,
+
+  -- Clearance status (inherits from upload, can be overridden per-photo)
+  clearance_level TEXT DEFAULT 'none'
+    CHECK (clearance_level IN ('none', 'editorial', 'commercial', 'ai_training')),
+
+  -- Licensing
+  view_count INTEGER DEFAULT 0,
+  license_count INTEGER DEFAULT 0,
+  total_earnings NUMERIC(10,2) DEFAULT 0,
+
+  -- Directory grouping
+  directory_name TEXT,
+
+  created_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- ============================================================
+-- DMCA CLAIMS
+-- ============================================================
+CREATE TABLE dmca_claims (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  upload_id UUID REFERENCES uploads(id),
+  photo_id UUID REFERENCES photos(id),
+
+  claimant_name TEXT NOT NULL,
+  claimant_email TEXT,
+  reason TEXT NOT NULL,
+  evidence_url TEXT,
+
+  status TEXT NOT NULL DEFAULT 'open'
+    CHECK (status IN ('open', 'investigating', 'taken_down', 'counter_noticed', 'resolved', 'dismissed')),
+
+  -- Resolution
+  resolved_by UUID REFERENCES users(id),
+  resolved_at TIMESTAMPTZ,
+  resolution_notes TEXT,
+
+  filed_at TIMESTAMPTZ DEFAULT NOW(),
+  updated_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- ============================================================
+-- AUDIT LOG
+-- ============================================================
+CREATE TABLE audit_log (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  user_id UUID REFERENCES users(id),
+  action TEXT NOT NULL, -- 'upload.approve', 'upload.reject', 'dmca.takedown', etc
+  target_type TEXT, -- 'upload', 'photo', 'dmca_claim', 'user'
+  target_id UUID,
+  details JSONB,
+  ip_address INET,
+  created_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- ============================================================
+-- SESSIONS (simple cookie auth)
+-- ============================================================
+CREATE TABLE sessions (
+  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+  token TEXT UNIQUE NOT NULL DEFAULT encode(gen_random_bytes(32), 'hex'),
+  expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '7 days',
+  created_at TIMESTAMPTZ DEFAULT NOW()
+);
+
+-- ============================================================
+-- INDEXES
+-- ============================================================
+CREATE INDEX idx_uploads_user ON uploads(user_id);
+CREATE INDEX idx_uploads_status ON uploads(status);
+CREATE INDEX idx_photos_upload ON photos(upload_id);
+CREATE INDEX idx_photos_user ON photos(user_id);
+CREATE INDEX idx_dmca_status ON dmca_claims(status);
+CREATE INDEX idx_sessions_token ON sessions(token);
+CREATE INDEX idx_sessions_expires ON sessions(expires_at);
+CREATE INDEX idx_audit_user ON audit_log(user_id);
+CREATE INDEX idx_audit_action ON audit_log(action);
+
+-- ============================================================
+-- SEED DATA (demo uploads for admin testing)
+-- ============================================================
+DO $$
+DECLARE
+  u1 UUID; u2 UUID; u3 UUID; u4 UUID; u5 UUID; u6 UUID;
+  up1 UUID; up2 UUID; up3 UUID; up4 UUID; up5 UUID; up6 UUID;
+BEGIN
+  -- Create demo users
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('sarah.m@email.com', crypt('demo123', gen_salt('bf')), 'Sarah Martinez', 'licensor', TRUE, TRUE) RETURNING id INTO u1;
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('james.k@email.com', crypt('demo123', gen_salt('bf')), 'James Kim', 'licensor', TRUE, TRUE) RETURNING id INTO u2;
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('maria.l@email.com', crypt('demo123', gen_salt('bf')), 'Maria Lopez', 'licensor', TRUE, TRUE) RETURNING id INTO u3;
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('david.r@email.com', crypt('demo123', gen_salt('bf')), 'David Reynolds', 'licensor', TRUE, TRUE) RETURNING id INTO u4;
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('emma.t@email.com', crypt('demo123', gen_salt('bf')), 'Emma Thompson', 'licensor', TRUE, TRUE) RETURNING id INTO u5;
+  INSERT INTO users (email, password_hash, display_name, role, is_verified, is_adult_confirmed)
+  VALUES ('alex.w@email.com', crypt('demo123', gen_salt('bf')), 'Alex Wilson', 'licensor', TRUE, TRUE) RETURNING id INTO u6;
+
+  -- Create uploads (pending review)
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_commercial, likeness_signature, likeness_signed_at)
+  VALUES (u1, 'pending', 'parent', 'yes', TRUE, TRUE, TRUE, TRUE, 'Sarah Martinez', NOW() - INTERVAL '2 hours') RETURNING id INTO up1;
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_photographer, copyright_permission, copyright_estate_rights, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_signature, likeness_signed_at)
+  VALUES (u2, 'pending', 'professional', 'Jane Doe Photography', 'deceased', TRUE, TRUE, TRUE, TRUE, 'James Kim', NOW() - INTERVAL '5 hours') RETURNING id INTO up2;
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_commercial, likeness_ai_training, likeness_signature, likeness_signed_at)
+  VALUES (u3, 'pending', 'family', 'yes', TRUE, TRUE, TRUE, TRUE, TRUE, 'Maria Lopez', NOW() - INTERVAL '8 hours') RETURNING id INTO up3;
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_signature, likeness_signed_at)
+  VALUES (u4, 'flagged', 'unknown', 'no', TRUE, TRUE, TRUE, 'David Reynolds', NOW() - INTERVAL '12 hours') RETURNING id INTO up4;
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_commercial, likeness_signature, likeness_signed_at)
+  VALUES (u5, 'pending', 'parent', 'yes', TRUE, TRUE, TRUE, TRUE, 'Emma Thompson', NOW() - INTERVAL '1 day') RETURNING id INTO up5;
+  INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, copyright_estate_rights, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_commercial, likeness_ai_training, likeness_signature, likeness_signed_at)
+  VALUES (u6, 'pending', 'parent', 'deceased', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 'Alex Wilson', NOW() - INTERVAL '1 day') RETURNING id INTO up6;
+
+  -- Add photos to uploads
+  INSERT INTO photos (upload_id, user_id, filename, file_size, mime_type, exif_year) VALUES
+    (up1, u1, 'summer_1992.jpg', 2400000, 'image/jpeg', 1992),
+    (up1, u1, 'birthday_party.jpg', 1800000, 'image/jpeg', 1993),
+    (up1, u1, 'first_steps.jpg', 3100000, 'image/jpeg', 1991),
+    (up1, u1, 'beach_day.jpg', 2200000, 'image/jpeg', 1994),
+    (up2, u2, 'school_portrait.jpg', 4500000, 'image/jpeg', 1988),
+    (up3, u3, 'christmas_1995.jpg', 1900000, 'image/jpeg', 1995),
+    (up3, u3, 'halloween.jpg', 2100000, 'image/jpeg', 1996),
+    (up3, u3, 'easter_egg_hunt.jpg', 1700000, 'image/jpeg', 1995),
+    (up3, u3, 'playground.jpg', 2800000, 'image/jpeg', 1997),
+    (up3, u3, 'first_bike.jpg', 3200000, 'image/jpeg', 1996),
+    (up3, u3, 'family_vacation.jpg', 2500000, 'image/jpeg', 1998),
+    (up3, u3, 'grandma_house.jpg', 1600000, 'image/jpeg', 1994),
+    (up3, u3, 'snow_day.jpg', 2900000, 'image/jpeg', 1997),
+    (up4, u4, 'old_photo_1.jpg', 1200000, 'image/jpeg', NULL),
+    (up4, u4, 'old_photo_2.jpg', 1500000, 'image/jpeg', NULL),
+    (up5, u5, 'toddler_pic.jpg', 2600000, 'image/jpeg', 2000),
+    (up5, u5, 'kindergarten.jpg', 2100000, 'image/jpeg', 2002),
+    (up5, u5, 'dance_recital.jpg', 3400000, 'image/jpeg', 2003),
+    (up6, u6, 'baby_photo_1.jpg', 1800000, 'image/jpeg', 1985),
+    (up6, u6, 'baby_photo_2.jpg', 2200000, 'image/jpeg', 1985),
+    (up6, u6, 'baby_photo_3.jpg', 1900000, 'image/jpeg', 1986),
+    (up6, u6, 'toddler_1.jpg', 2700000, 'image/jpeg', 1987),
+    (up6, u6, 'toddler_2.jpg', 2400000, 'image/jpeg', 1987),
+    (up6, u6, 'preschool.jpg', 3000000, 'image/jpeg', 1988);
+
+  -- DMCA claims
+  INSERT INTO dmca_claims (upload_id, claimant_name, claimant_email, reason, status, filed_at) VALUES
+    (up1, 'John Smith Photography', 'john@smithphoto.com', 'Copyright infringement — claims to be the original photographer of these images', 'open', NOW() - INTERVAL '3 days'),
+    (up3, 'Getty Images Legal', 'legal@gettyimages.com', 'Stock photo mistakenly uploaded as personal childhood photo', 'resolved', NOW() - INTERVAL '7 days'),
+    (up5, 'Anonymous', NULL, 'Subject requests removal — disputes likeness consent was given', 'open', NOW() - INTERVAL '2 days');
+
+  -- Some approved uploads (historical)
+  FOR i IN 1..20 LOOP
+    INSERT INTO uploads (user_id, status, copyright_relationship, copyright_permission, likeness_is_subject, likeness_is_adult, likeness_editorial, likeness_commercial, likeness_signature, likeness_signed_at, reviewed_at)
+    VALUES (
+      (ARRAY[u1, u2, u3, u5, u6])[1 + (i % 5)],
+      'approved',
+      (ARRAY['parent', 'family', 'professional'])[1 + (i % 3)],
+      'yes', TRUE, TRUE, TRUE, TRUE,
+      'Demo User ' || i,
+      NOW() - (i || ' days')::INTERVAL,
+      NOW() - ((i - 1) || ' days')::INTERVAL
+    );
+  END LOOP;
+
+END $$;
diff --git a/ecosystem.config.js b/ecosystem.config.js
new file mode 100644
index 0000000..41f6e79
--- /dev/null
+++ b/ecosystem.config.js
@@ -0,0 +1,14 @@
+module.exports = {
+  apps: [
+    {
+      name: 'iwascute',
+      cwd: '/root/Projects/iwascute',
+      script: 'node_modules/.bin/next',
+      args: 'start -p 7450',
+      env: {
+        NODE_ENV: 'production',
+        PORT: 7450,
+      },
+    },
+  ],
+}
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..05e726d
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,18 @@
+import { defineConfig, globalIgnores } from "eslint/config";
+import nextVitals from "eslint-config-next/core-web-vitals";
+import nextTs from "eslint-config-next/typescript";
+
+const eslintConfig = defineConfig([
+  ...nextVitals,
+  ...nextTs,
+  // Override default ignores of eslint-config-next.
+  globalIgnores([
+    // Default ignores of eslint-config-next:
+    ".next/**",
+    "out/**",
+    "build/**",
+    "next-env.d.ts",
+  ]),
+]);
+
+export default eslintConfig;
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 0000000..e9ffa30
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+  /* config options here */
+};
+
+export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e21fa3b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6828 @@
+{
+  "name": "iwascute",
+  "version": "0.1.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "iwascute",
+      "version": "0.1.0",
+      "dependencies": {
+        "@tabler/icons-react": "^3.41.1",
+        "clsx": "^2.1.1",
+        "exifr": "^7.1.3",
+        "framer-motion": "^12.38.0",
+        "lucide-react": "^1.8.0",
+        "next": "16.2.3",
+        "pg": "^8.20.0",
+        "react": "19.2.4",
+        "react-dom": "19.2.4"
+      },
+      "devDependencies": {
+        "@tailwindcss/postcss": "^4",
+        "@types/node": "^20",
+        "@types/pg": "^8.20.0",
+        "@types/react": "^19",
+        "@types/react-dom": "^19",
+        "eslint": "^9",
+        "eslint-config-next": "16.2.3",
+        "tailwindcss": "^4",
+        "typescript": "^5"
+      }
+    },
+    "node_modules/@alloc/quick-lru": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+      "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+      "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.28.5",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+      "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+      "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.0",
+        "@babel/generator": "^7.29.0",
+        "@babel/helper-compilation-targets": "^7.28.6",
+        "@babel/helper-module-transforms": "^7.28.6",
+        "@babel/helpers": "^7.28.6",
+        "@babel/parser": "^7.29.0",
+        "@babel/template": "^7.28.6",
+        "@babel/traverse": "^7.29.0",
+        "@babel/types": "^7.29.0",
+        "@jridgewell/remapping": "^2.3.5",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.29.1",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+      "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.29.0",
+        "@babel/types": "^7.29.0",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+      "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.28.6",
+        "@babel/helper-validator-option": "^7.27.1",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+      "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+      "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.28.6",
+        "@babel/types": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+      "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.28.6",
+        "@babel/helper-validator-identifier": "^7.28.5",
+        "@babel/traverse": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+      "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.29.2",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+      "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.28.6",
+        "@babel/types": "^7.29.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.2",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+      "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.0"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+      "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.28.6",
+        "@babel/parser": "^7.28.6",
+        "@babel/types": "^7.28.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+      "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.0",
+        "@babel/generator": "^7.29.0",
+        "@babel/helper-globals": "^7.28.0",
+        "@babel/parser": "^7.29.0",
+        "@babel/template": "^7.28.6",
+        "@babel/types": "^7.29.0",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+      "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@emnapi/core": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
+      "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/wasi-threads": "1.2.1",
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
+      "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/wasi-threads": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+      "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+      "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.21.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.7",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.5"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/config-helpers": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
+      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.14.0",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.1",
+        "minimatch": "^3.1.5",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.17.0",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.7",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+      "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@img/colour": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+      "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@img/sharp-darwin-arm64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+      "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-arm64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-darwin-x64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+      "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-x64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-arm64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+      "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-x64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+      "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+      "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+      "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-ppc64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+      "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-riscv64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+      "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-s390x": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+      "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-x64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+      "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+      "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+      "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-linux-arm": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+      "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-arm64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+      "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-ppc64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+      "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-ppc64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-riscv64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+      "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-riscv64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-s390x": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+      "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-s390x": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-x64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+      "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-x64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-arm64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+      "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-x64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+      "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+      }
+    },
+    "node_modules/@img/sharp-wasm32": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+      "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+      "cpu": [
+        "wasm32"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/runtime": "^1.7.0"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-win32-arm64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+      "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-win32-ia32": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+      "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-win32-x64": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+      "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@napi-rs/wasm-runtime": {
+      "version": "0.2.12",
+      "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+      "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/core": "^1.4.3",
+        "@emnapi/runtime": "^1.4.3",
+        "@tybys/wasm-util": "^0.10.0"
+      }
+    },
+    "node_modules/@next/env": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.3.tgz",
+      "integrity": "sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==",
+      "license": "MIT"
+    },
+    "node_modules/@next/eslint-plugin-next": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.3.tgz",
+      "integrity": "sha512-nE/b9mht28XJxjTwKs/yk7w4XTaU3t40UHVAky6cjiijdP/SEy3hGsnQMPxmXPTpC7W4/97okm6fngKnvCqVaA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-glob": "3.3.1"
+      }
+    },
+    "node_modules/@next/swc-darwin-arm64": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.3.tgz",
+      "integrity": "sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-darwin-x64": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.3.tgz",
+      "integrity": "sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-linux-arm64-gnu": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.3.tgz",
+      "integrity": "sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-linux-arm64-musl": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.3.tgz",
+      "integrity": "sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-linux-x64-gnu": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.3.tgz",
+      "integrity": "sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-linux-x64-musl": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.3.tgz",
+      "integrity": "sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-win32-arm64-msvc": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.3.tgz",
+      "integrity": "sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@next/swc-win32-x64-msvc": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.3.tgz",
+      "integrity": "sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nolyfill/is-core-module": {
+      "version": "1.0.39",
+      "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+      "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.4.0"
+      }
+    },
+    "node_modules/@rtsao/scc": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+      "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.5.15",
+      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+      "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@tabler/icons": {
+      "version": "3.41.1",
+      "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.41.1.tgz",
+      "integrity": "sha512-OaRnVbRmH2nHtFeg+RmMJ/7m2oBIF9XCJAUD5gQnMrpK9f05ydj8MZrAf3NZQqOXyxGN1UBL0D5IKLLEUfr74Q==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/codecalm"
+      }
+    },
+    "node_modules/@tabler/icons-react": {
+      "version": "3.41.1",
+      "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.41.1.tgz",
+      "integrity": "sha512-kUgweE+DJtAlMZVIns1FTDdcbpRVnkK7ZpUOXmoxy3JAF0rSHj0TcP4VHF14+gMJGnF+psH2Zt26BLT6owetBA==",
+      "license": "MIT",
+      "dependencies": {
+        "@tabler/icons": "3.41.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/codecalm"
+      },
+      "peerDependencies": {
+        "react": ">= 16"
+      }
+    },
+    "node_modules/@tailwindcss/node": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz",
+      "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/remapping": "^2.3.5",
+        "enhanced-resolve": "^5.19.0",
+        "jiti": "^2.6.1",
+        "lightningcss": "1.32.0",
+        "magic-string": "^0.30.21",
+        "source-map-js": "^1.2.1",
+        "tailwindcss": "4.2.2"
+      }
+    },
+    "node_modules/@tailwindcss/oxide": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz",
+      "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 20"
+      },
+      "optionalDependencies": {
+        "@tailwindcss/oxide-android-arm64": "4.2.2",
+        "@tailwindcss/oxide-darwin-arm64": "4.2.2",
+        "@tailwindcss/oxide-darwin-x64": "4.2.2",
+        "@tailwindcss/oxide-freebsd-x64": "4.2.2",
+        "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2",
+        "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2",
+        "@tailwindcss/oxide-linux-arm64-musl": "4.2.2",
+        "@tailwindcss/oxide-linux-x64-gnu": "4.2.2",
+        "@tailwindcss/oxide-linux-x64-musl": "4.2.2",
+        "@tailwindcss/oxide-wasm32-wasi": "4.2.2",
+        "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2",
+        "@tailwindcss/oxide-win32-x64-msvc": "4.2.2"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-android-arm64": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz",
+      "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-darwin-arm64": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz",
+      "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-darwin-x64": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz",
+      "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-freebsd-x64": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz",
+      "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz",
+      "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz",
+      "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz",
+      "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz",
+      "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz",
+      "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz",
+      "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==",
+      "bundleDependencies": [
+        "@napi-rs/wasm-runtime",
+        "@emnapi/core",
+        "@emnapi/runtime",
+        "@tybys/wasm-util",
+        "@emnapi/wasi-threads",
+        "tslib"
+      ],
+      "cpu": [
+        "wasm32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/core": "^1.8.1",
+        "@emnapi/runtime": "^1.8.1",
+        "@emnapi/wasi-threads": "^1.1.0",
+        "@napi-rs/wasm-runtime": "^1.1.1",
+        "@tybys/wasm-util": "^0.10.1",
+        "tslib": "^2.8.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz",
+      "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz",
+      "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@tailwindcss/postcss": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz",
+      "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@alloc/quick-lru": "^5.2.0",
+        "@tailwindcss/node": "4.2.2",
+        "@tailwindcss/oxide": "4.2.2",
+        "postcss": "^8.5.6",
+        "tailwindcss": "4.2.2"
+      }
+    },
+    "node_modules/@tybys/wasm-util": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+      "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "20.19.39",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
+      "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "node_modules/@types/pg": {
+      "version": "8.20.0",
+      "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
+      "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "pg-protocol": "*",
+        "pg-types": "^2.2.0"
+      }
+    },
+    "node_modules/@types/react": {
+      "version": "19.2.14",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+      "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "csstype": "^3.2.2"
+      }
+    },
+    "node_modules/@types/react-dom": {
+      "version": "19.2.3",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+      "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "^19.2.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz",
+      "integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.12.2",
+        "@typescript-eslint/scope-manager": "8.58.1",
+        "@typescript-eslint/type-utils": "8.58.1",
+        "@typescript-eslint/utils": "8.58.1",
+        "@typescript-eslint/visitor-keys": "8.58.1",
+        "ignore": "^7.0.5",
+        "natural-compare": "^1.4.0",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^8.58.1",
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz",
+      "integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "8.58.1",
+        "@typescript-eslint/types": "8.58.1",
+        "@typescript-eslint/typescript-estree": "8.58.1",
+        "@typescript-eslint/visitor-keys": "8.58.1",
+        "debug": "^4.4.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz",
+      "integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.58.1",
+        "@typescript-eslint/types": "^8.58.1",
+        "debug": "^4.4.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz",
+      "integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.58.1",
+        "@typescript-eslint/visitor-keys": "8.58.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/tsconfig-utils": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz",
+      "integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz",
+      "integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.58.1",
+        "@typescript-eslint/typescript-estree": "8.58.1",
+        "@typescript-eslint/utils": "8.58.1",
+        "debug": "^4.4.3",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
+      "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz",
+      "integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/project-service": "8.58.1",
+        "@typescript-eslint/tsconfig-utils": "8.58.1",
+        "@typescript-eslint/types": "8.58.1",
+        "@typescript-eslint/visitor-keys": "8.58.1",
+        "debug": "^4.4.3",
+        "minimatch": "^10.2.2",
+        "semver": "^7.7.3",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+      "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "brace-expansion": "^5.0.5"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz",
+      "integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.9.1",
+        "@typescript-eslint/scope-manager": "8.58.1",
+        "@typescript-eslint/types": "8.58.1",
+        "@typescript-eslint/typescript-estree": "8.58.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz",
+      "integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.58.1",
+        "eslint-visitor-keys": "^5.0.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+      "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-android-arm64": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+      "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-darwin-arm64": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+      "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-darwin-x64": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+      "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-freebsd-x64": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+      "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+      "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+      "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+      "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+      "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+      "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+      "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+      "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+      "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+      "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+      "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+      "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+      "cpu": [
+        "wasm32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@napi-rs/wasm-runtime": "^0.2.11"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+      "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+      "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+      "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/acorn": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+      "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+      "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/aria-query": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/array-buffer-byte-length": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+      "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "is-array-buffer": "^3.0.5"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array-includes": {
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+      "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.24.0",
+        "es-object-atoms": "^1.1.1",
+        "get-intrinsic": "^1.3.0",
+        "is-string": "^1.1.1",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.findlast": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+      "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
+        "es-shim-unscopables": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.findlastindex": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+      "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.9",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "es-shim-unscopables": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flat": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+      "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.5",
+        "es-shim-unscopables": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flatmap": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+      "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.5",
+        "es-shim-unscopables": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.tosorted": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+      "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.3",
+        "es-errors": "^1.3.0",
+        "es-shim-unscopables": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/arraybuffer.prototype.slice": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+      "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-buffer-byte-length": "^1.0.1",
+        "call-bind": "^1.0.8",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.5",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "is-array-buffer": "^3.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ast-types-flow": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+      "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/async-function": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+      "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+      "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "possible-typed-array-names": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/axe-core": {
+      "version": "4.11.2",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz",
+      "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==",
+      "dev": true,
+      "license": "MPL-2.0",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/axobject-query": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+      "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.10.18",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz",
+      "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==",
+      "license": "Apache-2.0",
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+      "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.2",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+      "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "baseline-browser-mapping": "^2.10.12",
+        "caniuse-lite": "^1.0.30001782",
+        "electron-to-chromium": "^1.5.328",
+        "node-releases": "^2.0.36",
+        "update-browserslist-db": "^1.2.3"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
+      "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "get-intrinsic": "^1.3.0",
+        "set-function-length": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001787",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz",
+      "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "CC-BY-4.0"
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/client-only": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+      "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+      "license": "MIT"
+    },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/damerau-levenshtein": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+      "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/data-view-buffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+      "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/data-view-byte-length": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+      "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/inspect-js"
+      }
+    },
+    "node_modules/data-view-byte-offset": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+      "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.0.1",
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.335",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.335.tgz",
+      "integrity": "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.20.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
+      "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/es-abstract": {
+      "version": "1.24.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
+      "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-buffer-byte-length": "^1.0.2",
+        "arraybuffer.prototype.slice": "^1.0.4",
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "data-view-buffer": "^1.0.2",
+        "data-view-byte-length": "^1.0.2",
+        "data-view-byte-offset": "^1.0.1",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "es-set-tostringtag": "^2.1.0",
+        "es-to-primitive": "^1.3.0",
+        "function.prototype.name": "^1.1.8",
+        "get-intrinsic": "^1.3.0",
+        "get-proto": "^1.0.1",
+        "get-symbol-description": "^1.1.0",
+        "globalthis": "^1.0.4",
+        "gopd": "^1.2.0",
+        "has-property-descriptors": "^1.0.2",
+        "has-proto": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "internal-slot": "^1.1.0",
+        "is-array-buffer": "^3.0.5",
+        "is-callable": "^1.2.7",
+        "is-data-view": "^1.0.2",
+        "is-negative-zero": "^2.0.3",
+        "is-regex": "^1.2.1",
+        "is-set": "^2.0.3",
+        "is-shared-array-buffer": "^1.0.4",
+        "is-string": "^1.1.1",
+        "is-typed-array": "^1.1.15",
+        "is-weakref": "^1.1.1",
+        "math-intrinsics": "^1.1.0",
+        "object-inspect": "^1.13.4",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.7",
+        "own-keys": "^1.0.1",
+        "regexp.prototype.flags": "^1.5.4",
+        "safe-array-concat": "^1.1.3",
+        "safe-push-apply": "^1.0.0",
+        "safe-regex-test": "^1.1.0",
+        "set-proto": "^1.0.0",
+        "stop-iteration-iterator": "^1.1.0",
+        "string.prototype.trim": "^1.2.10",
+        "string.prototype.trimend": "^1.0.9",
+        "string.prototype.trimstart": "^1.0.8",
+        "typed-array-buffer": "^1.0.3",
+        "typed-array-byte-length": "^1.0.3",
+        "typed-array-byte-offset": "^1.0.4",
+        "typed-array-length": "^1.0.7",
+        "unbox-primitive": "^1.1.0",
+        "which-typed-array": "^1.1.19"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-iterator-helpers": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz",
+      "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.24.2",
+        "es-errors": "^1.3.0",
+        "es-set-tostringtag": "^2.1.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.3.0",
+        "globalthis": "^1.0.4",
+        "gopd": "^1.2.0",
+        "has-property-descriptors": "^1.0.2",
+        "has-proto": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "internal-slot": "^1.1.0",
+        "iterator.prototype": "^1.1.5",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-shim-unscopables": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+      "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-to-primitive": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+      "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-callable": "^1.2.7",
+        "is-date-object": "^1.0.5",
+        "is-symbol": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
+      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.2",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.5",
+        "@eslint/js": "9.39.4",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.14.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.5",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-config-next": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.3.tgz",
+      "integrity": "sha512-Dnkrylzjof/Az7iNoIQJqD18zTxQZcngir19KJaiRsMnnjpQSVoa6aEg/1Q4hQC+cW90uTlgQYadwL1CYNwFWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@next/eslint-plugin-next": "16.2.3",
+        "eslint-import-resolver-node": "^0.3.6",
+        "eslint-import-resolver-typescript": "^3.5.2",
+        "eslint-plugin-import": "^2.32.0",
+        "eslint-plugin-jsx-a11y": "^6.10.0",
+        "eslint-plugin-react": "^7.37.0",
+        "eslint-plugin-react-hooks": "^7.0.0",
+        "globals": "16.4.0",
+        "typescript-eslint": "^8.46.0"
+      },
+      "peerDependencies": {
+        "eslint": ">=9.0.0",
+        "typescript": ">=3.3.1"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-config-next/node_modules/globals": {
+      "version": "16.4.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+      "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-import-resolver-node": {
+      "version": "0.3.10",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz",
+      "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^3.2.7",
+        "is-core-module": "^2.16.1",
+        "resolve": "^2.0.0-next.6"
+      }
+    },
+    "node_modules/eslint-import-resolver-node/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-import-resolver-typescript": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+      "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@nolyfill/is-core-module": "1.0.39",
+        "debug": "^4.4.0",
+        "get-tsconfig": "^4.10.0",
+        "is-bun-module": "^2.0.0",
+        "stable-hash": "^0.0.5",
+        "tinyglobby": "^0.2.13",
+        "unrs-resolver": "^1.6.2"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-import-resolver-typescript"
+      },
+      "peerDependencies": {
+        "eslint": "*",
+        "eslint-plugin-import": "*",
+        "eslint-plugin-import-x": "*"
+      },
+      "peerDependenciesMeta": {
+        "eslint-plugin-import": {
+          "optional": true
+        },
+        "eslint-plugin-import-x": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-module-utils": {
+      "version": "2.12.1",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+      "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^3.2.7"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-module-utils/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-plugin-import": {
+      "version": "2.32.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+      "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@rtsao/scc": "^1.1.0",
+        "array-includes": "^3.1.9",
+        "array.prototype.findlastindex": "^1.2.6",
+        "array.prototype.flat": "^1.3.3",
+        "array.prototype.flatmap": "^1.3.3",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.9",
+        "eslint-module-utils": "^2.12.1",
+        "hasown": "^2.0.2",
+        "is-core-module": "^2.16.1",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.fromentries": "^2.0.8",
+        "object.groupby": "^1.0.3",
+        "object.values": "^1.2.1",
+        "semver": "^6.3.1",
+        "string.prototype.trimend": "^1.0.9",
+        "tsconfig-paths": "^3.15.0"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-plugin-jsx-a11y": {
+      "version": "6.10.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+      "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "aria-query": "^5.3.2",
+        "array-includes": "^3.1.8",
+        "array.prototype.flatmap": "^1.3.2",
+        "ast-types-flow": "^0.0.8",
+        "axe-core": "^4.10.0",
+        "axobject-query": "^4.1.0",
+        "damerau-levenshtein": "^1.0.8",
+        "emoji-regex": "^9.2.2",
+        "hasown": "^2.0.2",
+        "jsx-ast-utils": "^3.3.5",
+        "language-tags": "^1.0.9",
+        "minimatch": "^3.1.2",
+        "object.fromentries": "^2.0.8",
+        "safe-regex-test": "^1.0.3",
+        "string.prototype.includes": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependencies": {
+        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+      }
+    },
+    "node_modules/eslint-plugin-react": {
+      "version": "7.37.5",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+      "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-includes": "^3.1.8",
+        "array.prototype.findlast": "^1.2.5",
+        "array.prototype.flatmap": "^1.3.3",
+        "array.prototype.tosorted": "^1.1.4",
+        "doctrine": "^2.1.0",
+        "es-iterator-helpers": "^1.2.1",
+        "estraverse": "^5.3.0",
+        "hasown": "^2.0.2",
+        "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.9",
+        "object.fromentries": "^2.0.8",
+        "object.values": "^1.2.1",
+        "prop-types": "^15.8.1",
+        "resolve": "^2.0.0-next.5",
+        "semver": "^6.3.1",
+        "string.prototype.matchall": "^4.0.12",
+        "string.prototype.repeat": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+      }
+    },
+    "node_modules/eslint-plugin-react-hooks": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+      "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.24.4",
+        "@babel/parser": "^7.24.4",
+        "hermes-parser": "^0.25.1",
+        "zod": "^3.25.0 || ^4.0.0",
+        "zod-validation-error": "^3.5.0 || ^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+      "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/exifr": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz",
+      "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==",
+      "license": "MIT"
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+      "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/for-each": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+      "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-callable": "^1.2.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/framer-motion": {
+      "version": "12.38.0",
+      "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
+      "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
+      "license": "MIT",
+      "dependencies": {
+        "motion-dom": "^12.38.0",
+        "motion-utils": "^12.36.0",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "@emotion/is-prop-valid": "*",
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/is-prop-valid": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/function.prototype.name": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+      "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "functions-have-names": "^1.2.3",
+        "hasown": "^2.0.2",
+        "is-callable": "^1.2.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/generator-function": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+      "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/get-symbol-description": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+      "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.13.7",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz",
+      "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globalthis": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/has-bigints": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+      "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-proto": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+      "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hermes-estree": {
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+      "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/hermes-parser": {
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+      "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hermes-estree": "0.25.1"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/internal-slot": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+      "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "hasown": "^2.0.2",
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/is-array-buffer": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+      "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "get-intrinsic": "^1.2.6"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-async-function": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+      "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "async-function": "^1.0.0",
+        "call-bound": "^1.0.3",
+        "get-proto": "^1.0.1",
+        "has-tostringtag": "^1.0.2",
+        "safe-regex-test": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-bigint": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+      "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-bigints": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-boolean-object": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+      "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-bun-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+      "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.7.1"
+      }
+    },
+    "node_modules/is-bun-module/node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.16.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-data-view": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+      "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "get-intrinsic": "^1.2.6",
+        "is-typed-array": "^1.1.13"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-date-object": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+      "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-finalizationregistry": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+      "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-generator-function": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+      "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.4",
+        "generator-function": "^2.0.0",
+        "get-proto": "^1.0.1",
+        "has-tostringtag": "^1.0.2",
+        "safe-regex-test": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+      "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-negative-zero": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+      "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-number-object": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+      "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-regex": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+      "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "gopd": "^1.2.0",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-set": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+      "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-shared-array-buffer": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+      "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-string": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+      "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-symbol": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+      "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "has-symbols": "^1.1.0",
+        "safe-regex-test": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-typed-array": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+      "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "which-typed-array": "^1.1.16"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakmap": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+      "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakref": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+      "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakset": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+      "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "get-intrinsic": "^1.2.6"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/iterator.prototype": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+      "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.1.4",
+        "es-object-atoms": "^1.0.0",
+        "get-intrinsic": "^1.2.6",
+        "get-proto": "^1.0.0",
+        "has-symbols": "^1.1.0",
+        "set-function-name": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/jiti": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+      "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jiti": "lib/jiti-cli.mjs"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsx-ast-utils": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+      "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flat": "^1.3.1",
+        "object.assign": "^4.1.4",
+        "object.values": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/language-subtag-registry": {
+      "version": "0.3.23",
+      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+      "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/language-tags": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+      "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "language-subtag-registry": "^0.3.20"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/lightningcss": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+      "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+      "dev": true,
+      "license": "MPL-2.0",
+      "dependencies": {
+        "detect-libc": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "lightningcss-android-arm64": "1.32.0",
+        "lightningcss-darwin-arm64": "1.32.0",
+        "lightningcss-darwin-x64": "1.32.0",
+        "lightningcss-freebsd-x64": "1.32.0",
+        "lightningcss-linux-arm-gnueabihf": "1.32.0",
+        "lightningcss-linux-arm64-gnu": "1.32.0",
+        "lightningcss-linux-arm64-musl": "1.32.0",
+        "lightningcss-linux-x64-gnu": "1.32.0",
+        "lightningcss-linux-x64-musl": "1.32.0",
+        "lightningcss-win32-arm64-msvc": "1.32.0",
+        "lightningcss-win32-x64-msvc": "1.32.0"
+      }
+    },
+    "node_modules/lightningcss-android-arm64": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+      "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-darwin-arm64": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+      "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-darwin-x64": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+      "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-freebsd-x64": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+      "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm-gnueabihf": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+      "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-gnu": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+      "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-musl": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+      "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-gnu": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+      "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-musl": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+      "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-win32-arm64-msvc": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+      "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-win32-x64-msvc": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+      "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MPL-2.0",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/lucide-react": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.8.0.tgz",
+      "integrity": "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==",
+      "license": "ISC",
+      "peerDependencies": {
+        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/motion-dom": {
+      "version": "12.38.0",
+      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
+      "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
+      "license": "MIT",
+      "dependencies": {
+        "motion-utils": "^12.36.0"
+      }
+    },
+    "node_modules/motion-utils": {
+      "version": "12.36.0",
+      "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
+      "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/napi-postinstall": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+      "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "napi-postinstall": "lib/cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/napi-postinstall"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/next": {
+      "version": "16.2.3",
+      "resolved": "https://registry.npmjs.org/next/-/next-16.2.3.tgz",
+      "integrity": "sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==",
+      "license": "MIT",
+      "dependencies": {
+        "@next/env": "16.2.3",
+        "@swc/helpers": "0.5.15",
+        "baseline-browser-mapping": "^2.9.19",
+        "caniuse-lite": "^1.0.30001579",
+        "postcss": "8.4.31",
+        "styled-jsx": "5.1.6"
+      },
+      "bin": {
+        "next": "dist/bin/next"
+      },
+      "engines": {
+        "node": ">=20.9.0"
+      },
+      "optionalDependencies": {
+        "@next/swc-darwin-arm64": "16.2.3",
+        "@next/swc-darwin-x64": "16.2.3",
+        "@next/swc-linux-arm64-gnu": "16.2.3",
+        "@next/swc-linux-arm64-musl": "16.2.3",
+        "@next/swc-linux-x64-gnu": "16.2.3",
+        "@next/swc-linux-x64-musl": "16.2.3",
+        "@next/swc-win32-arm64-msvc": "16.2.3",
+        "@next/swc-win32-x64-msvc": "16.2.3",
+        "sharp": "^0.34.5"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.1.0",
+        "@playwright/test": "^1.51.1",
+        "babel-plugin-react-compiler": "*",
+        "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+        "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+        "sass": "^1.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@opentelemetry/api": {
+          "optional": true
+        },
+        "@playwright/test": {
+          "optional": true
+        },
+        "babel-plugin-react-compiler": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/next/node_modules/postcss": {
+      "version": "8.4.31",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.6",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/node-exports-info": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz",
+      "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array.prototype.flatmap": "^1.3.3",
+        "es-errors": "^1.3.0",
+        "object.entries": "^1.1.9",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.37",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz",
+      "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.assign": {
+      "version": "4.1.7",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+      "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0",
+        "has-symbols": "^1.1.0",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.entries": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+      "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.fromentries": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.groupby": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.values": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+      "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/own-keys": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+      "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "get-intrinsic": "^1.2.6",
+        "object-keys": "^1.1.1",
+        "safe-push-apply": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pg": {
+      "version": "8.20.0",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
+      "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-connection-string": "^2.12.0",
+        "pg-pool": "^3.13.0",
+        "pg-protocol": "^1.13.0",
+        "pg-types": "2.2.0",
+        "pgpass": "1.0.5"
+      },
+      "engines": {
+        "node": ">= 16.0.0"
+      },
+      "optionalDependencies": {
+        "pg-cloudflare": "^1.3.0"
+      },
+      "peerDependencies": {
+        "pg-native": ">=3.0.1"
+      },
+      "peerDependenciesMeta": {
+        "pg-native": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pg-cloudflare": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
+      "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/pg-connection-string": {
+      "version": "2.12.0",
+      "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
+      "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
+      "license": "MIT"
+    },
+    "node_modules/pg-int8": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+      "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/pg-pool": {
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
+      "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "pg": ">=8.0"
+      }
+    },
+    "node_modules/pg-protocol": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
+      "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
+      "license": "MIT"
+    },
+    "node_modules/pg-types": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+      "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-int8": "1.0.1",
+        "postgres-array": "~2.0.0",
+        "postgres-bytea": "~1.0.0",
+        "postgres-date": "~1.0.4",
+        "postgres-interval": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/pgpass": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+      "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+      "license": "MIT",
+      "dependencies": {
+        "split2": "^4.1.0"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/possible-typed-array-names": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+      "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.9",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
+      "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postgres-array": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+      "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postgres-bytea": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+      "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-date": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+      "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-interval": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+      "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+      "license": "MIT",
+      "dependencies": {
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/react": {
+      "version": "19.2.4",
+      "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
+      "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "19.2.4",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
+      "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+      "license": "MIT",
+      "dependencies": {
+        "scheduler": "^0.27.0"
+      },
+      "peerDependencies": {
+        "react": "^19.2.4"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/reflect.getprototypeof": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+      "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.9",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
+        "get-intrinsic": "^1.2.7",
+        "get-proto": "^1.0.1",
+        "which-builtin-type": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/regexp.prototype.flags": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+      "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "define-properties": "^1.2.1",
+        "es-errors": "^1.3.0",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "set-function-name": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "2.0.0-next.6",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
+      "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.1",
+        "node-exports-info": "^1.6.0",
+        "object-keys": "^1.1.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-array-concat": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+      "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.2",
+        "get-intrinsic": "^1.2.6",
+        "has-symbols": "^1.1.0",
+        "isarray": "^2.0.5"
+      },
+      "engines": {
+        "node": ">=0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/safe-push-apply": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+      "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "isarray": "^2.0.5"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/safe-regex-test": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+      "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "is-regex": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/scheduler": {
+      "version": "0.27.0",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+      "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/set-function-length": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
+        "gopd": "^1.0.1",
+        "has-property-descriptors": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/set-function-name": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+      "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
+        "functions-have-names": "^1.2.3",
+        "has-property-descriptors": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/set-proto": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+      "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/sharp": {
+      "version": "0.34.5",
+      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+      "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@img/colour": "^1.0.0",
+        "detect-libc": "^2.1.2",
+        "semver": "^7.7.3"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-darwin-arm64": "0.34.5",
+        "@img/sharp-darwin-x64": "0.34.5",
+        "@img/sharp-libvips-darwin-arm64": "1.2.4",
+        "@img/sharp-libvips-darwin-x64": "1.2.4",
+        "@img/sharp-libvips-linux-arm": "1.2.4",
+        "@img/sharp-libvips-linux-arm64": "1.2.4",
+        "@img/sharp-libvips-linux-ppc64": "1.2.4",
+        "@img/sharp-libvips-linux-riscv64": "1.2.4",
+        "@img/sharp-libvips-linux-s390x": "1.2.4",
+        "@img/sharp-libvips-linux-x64": "1.2.4",
+        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+        "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+        "@img/sharp-linux-arm": "0.34.5",
+        "@img/sharp-linux-arm64": "0.34.5",
+        "@img/sharp-linux-ppc64": "0.34.5",
+        "@img/sharp-linux-riscv64": "0.34.5",
+        "@img/sharp-linux-s390x": "0.34.5",
+        "@img/sharp-linux-x64": "0.34.5",
+        "@img/sharp-linuxmusl-arm64": "0.34.5",
+        "@img/sharp-linuxmusl-x64": "0.34.5",
+        "@img/sharp-wasm32": "0.34.5",
+        "@img/sharp-win32-arm64": "0.34.5",
+        "@img/sharp-win32-ia32": "0.34.5",
+        "@img/sharp-win32-x64": "0.34.5"
+      }
+    },
+    "node_modules/sharp/node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "license": "ISC",
+      "optional": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/stable-hash": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+      "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/stop-iteration-iterator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+      "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "internal-slot": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/string.prototype.includes": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+      "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/string.prototype.matchall": {
+      "version": "4.0.12",
+      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+      "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.6",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
+        "get-intrinsic": "^1.2.6",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "internal-slot": "^1.1.0",
+        "regexp.prototype.flags": "^1.5.3",
+        "set-function-name": "^2.0.2",
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.repeat": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+      "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "node_modules/string.prototype.trim": {
+      "version": "1.2.10",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+      "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.2",
+        "define-data-property": "^1.1.4",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.5",
+        "es-object-atoms": "^1.0.0",
+        "has-property-descriptors": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trimend": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+      "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.2",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trimstart": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+      "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/styled-jsx": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+      "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+      "license": "MIT",
+      "dependencies": {
+        "client-only": "0.0.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "peerDependencies": {
+        "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "babel-plugin-macros": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/tailwindcss": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz",
+      "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tapable": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz",
+      "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.16",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+      "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tinyglobby/node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tinyglobby/node_modules/picomatch": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+      "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.12"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4"
+      }
+    },
+    "node_modules/tsconfig-paths": {
+      "version": "3.15.0",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+      "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "node_modules/tsconfig-paths/node_modules/json5": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.0"
+      },
+      "bin": {
+        "json5": "lib/cli.js"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/typed-array-buffer": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+      "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.14"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/typed-array-byte-length": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+      "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.8",
+        "for-each": "^0.3.3",
+        "gopd": "^1.2.0",
+        "has-proto": "^1.2.0",
+        "is-typed-array": "^1.1.14"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/typed-array-byte-offset": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+      "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.8",
+        "for-each": "^0.3.3",
+        "gopd": "^1.2.0",
+        "has-proto": "^1.2.0",
+        "is-typed-array": "^1.1.15",
+        "reflect.getprototypeof": "^1.0.9"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/typed-array-length": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+      "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "is-typed-array": "^1.1.13",
+        "possible-typed-array-names": "^1.0.0",
+        "reflect.getprototypeof": "^1.0.6"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typescript-eslint": {
+      "version": "8.58.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.1.tgz",
+      "integrity": "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.58.1",
+        "@typescript-eslint/parser": "8.58.1",
+        "@typescript-eslint/typescript-estree": "8.58.1",
+        "@typescript-eslint/utils": "8.58.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/unbox-primitive": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+      "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "has-bigints": "^1.0.2",
+        "has-symbols": "^1.1.0",
+        "which-boxed-primitive": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unrs-resolver": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+      "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "napi-postinstall": "^0.3.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unrs-resolver"
+      },
+      "optionalDependencies": {
+        "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+        "@unrs/resolver-binding-android-arm64": "1.11.1",
+        "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+        "@unrs/resolver-binding-darwin-x64": "1.11.1",
+        "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+        "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+        "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+        "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+        "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+        "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+        "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+        "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+        "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+        "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/which-boxed-primitive": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+      "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-bigint": "^1.1.0",
+        "is-boolean-object": "^1.2.1",
+        "is-number-object": "^1.1.1",
+        "is-string": "^1.1.1",
+        "is-symbol": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-builtin-type": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+      "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "function.prototype.name": "^1.1.6",
+        "has-tostringtag": "^1.0.2",
+        "is-async-function": "^2.0.0",
+        "is-date-object": "^1.1.0",
+        "is-finalizationregistry": "^1.1.0",
+        "is-generator-function": "^1.0.10",
+        "is-regex": "^1.2.1",
+        "is-weakref": "^1.0.2",
+        "isarray": "^2.0.5",
+        "which-boxed-primitive": "^1.1.0",
+        "which-collection": "^1.0.2",
+        "which-typed-array": "^1.1.16"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-collection": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+      "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-map": "^2.0.3",
+        "is-set": "^2.0.3",
+        "is-weakmap": "^2.0.2",
+        "is-weakset": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-typed-array": {
+      "version": "1.1.20",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+      "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "for-each": "^0.3.5",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zod": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+      "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    },
+    "node_modules/zod-validation-error": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+      "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "peerDependencies": {
+        "zod": "^3.25.0 || ^4.0.0"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6e282df
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "iwascute",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "dev": "next dev",
+    "build": "next build",
+    "start": "next start",
+    "lint": "eslint"
+  },
+  "dependencies": {
+    "@tabler/icons-react": "^3.41.1",
+    "clsx": "^2.1.1",
+    "exifr": "^7.1.3",
+    "framer-motion": "^12.38.0",
+    "lucide-react": "^1.8.0",
+    "next": "16.2.3",
+    "pg": "^8.20.0",
+    "react": "19.2.4",
+    "react-dom": "19.2.4"
+  },
+  "devDependencies": {
+    "@tailwindcss/postcss": "^4",
+    "@types/node": "^20",
+    "@types/pg": "^8.20.0",
+    "@types/react": "^19",
+    "@types/react-dom": "^19",
+    "eslint": "^9",
+    "eslint-config-next": "16.2.3",
+    "tailwindcss": "^4",
+    "typescript": "^5"
+  }
+}
diff --git a/postcss.config.mjs b/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+  plugins: {
+    "@tailwindcss/postcss": {},
+  },
+};
+
+export default config;
diff --git a/public/admin.html b/public/admin.html
new file mode 100644
index 0000000..74f8992
--- /dev/null
+++ b/public/admin.html
@@ -0,0 +1,246 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<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> &mdash; 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">&#128161;</span>
+  <span><strong>Demo mode</strong> &mdash; 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/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..24b9870
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+<rect width="32" height="32" rx="6" fill="#10b981"/>
+<text x="50%" y="55%" text-anchor="middle" dominant-baseline="middle" font-size="20" font-family="Apple Color Emoji, Segoe UI Emoji, sans-serif" fill="white">I</text>
+</svg>
\ No newline at end of file
diff --git a/public/file.svg b/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/public/file.svg
@@ -0,0 +1 @@
+<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/public/globe.svg b/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/public/globe.svg
@@ -0,0 +1 @@
+<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..21cab56
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,827 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<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>
+</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">&#128247;</div>
+          <p><strong>Then</strong> — Upload your old photo</p>
+        </div>
+        <div class="tvn-upload-box" id="now-upload">
+          <div class="upload-icon">&#128248;</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">&#128444;</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">&#9986; Crop</button>
+            <button class="edit-btn" id="btn-crop-square" title="Square crop">&#11036; Square</button>
+            <button class="edit-btn" id="btn-crop-4x3" title="4:3 crop">&#9645; 4:3</button>
+            <button class="edit-btn" id="btn-reset" title="Reset all edits">&#8634; 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">&#127912; 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&deg;</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">&#129302; AI Magic</div>
+            <button class="edit-btn full" id="btn-ai-describe">&#10024; Generate AI Description</button>
+            <button class="edit-btn full" id="btn-ai-guess-year">&#128197; 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">&#128118; 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">&#128221; 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 = (100 / pct) + '%';
+    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/next.svg b/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/public/next.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
\ No newline at end of file
diff --git a/public/vercel.svg b/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/public/vercel.svg
@@ -0,0 +1 @@
+<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
\ No newline at end of file
diff --git a/public/window.svg b/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/public/window.svg
@@ -0,0 +1 @@
+<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..6d02890
--- /dev/null
+++ b/server.js
@@ -0,0 +1,57 @@
+const express = require('express');
+const path = require('path');
+const app = express();
+const PORT = 7350;
+
+// Basic auth for admin
+function adminAuth(req, res, next) {
+  const auth = req.headers.authorization;
+  if (!auth || auth !== 'Basic ' + Buffer.from('admin:DWSecure2024!').toString('base64')) {
+    res.set('WWW-Authenticate', 'Basic realm="IWasCute Admin"');
+    return res.status(401).send('Auth required');
+  }
+  next();
+}
+
+app.use(express.json());
+
+// In-memory submissions store (would be DB in production)
+const submissions = [];
+
+// API: submit a photo
+app.post('/api/submit', (req, res) => {
+  const sub = { id: Date.now(), ...req.body, status: 'pending', submittedAt: new Date().toISOString() };
+  submissions.push(sub);
+  res.json({ ok: true, id: sub.id });
+});
+
+// API: list submissions (admin)
+app.get('/api/admin/submissions', adminAuth, (req, res) => {
+  res.json({ submissions: submissions.slice().reverse() });
+});
+
+// API: approve/reject (admin)
+app.post('/api/admin/review', adminAuth, (req, res) => {
+  const { id, action } = req.body;
+  const sub = submissions.find(s => s.id === id);
+  if (!sub) return res.status(404).json({ error: 'Not found' });
+  sub.status = action === 'approve' ? 'approved' : 'rejected';
+  sub.reviewedAt = new Date().toISOString();
+  res.json({ ok: true, status: sub.status });
+});
+
+// Admin dashboard
+app.get('/admin', adminAuth, (req, res) => {
+  res.sendFile(path.join(__dirname, 'public', 'admin.html'));
+});
+
+app.use(express.static(path.join(__dirname, 'public')));
+
+// SPA fallback
+app.use((req, res) => {
+  res.sendFile(path.join(__dirname, 'public', 'index.html'));
+});
+
+app.listen(PORT, '0.0.0.0', () => {
+  console.log(`[IWasCute] Running on http://45.61.58.125:${PORT}`);
+});
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
new file mode 100644
index 0000000..7a746d8
--- /dev/null
+++ b/src/app/admin/page.tsx
@@ -0,0 +1,1371 @@
+'use client'
+
+import { useState, useEffect, useCallback } from 'react'
+import { motion } from 'framer-motion'
+import {
+  Shield,
+  CheckCircle2,
+  XCircle,
+  Clock,
+  AlertTriangle,
+  Users,
+  ImageIcon,
+  DollarSign,
+  Search,
+  Eye,
+  Scale,
+  FileWarning,
+  Ban,
+  BarChart3,
+  Settings,
+  LogOut,
+  Loader2,
+  Flag,
+  RefreshCw,
+} from 'lucide-react'
+import Link from 'next/link'
+
+/* ------------------------------------------------------------------ */
+/*  Types                                                              */
+/* ------------------------------------------------------------------ */
+
+type AdminTab = 'overview' | 'queue' | 'dmca' | 'users'
+
+interface AuthUser {
+  id: string
+  email: string
+  display_name: string
+  role: string
+}
+
+interface Stats {
+  pending: number
+  approved: number
+  rejected: number
+  users: number
+  revenue: number
+  dmca_open: number
+}
+
+interface QueueItem {
+  id: string
+  status: string
+  created_at: string
+  copyright_relationship: string
+  copyright_permission: string
+  display_name: string
+  email: string
+  photo_count: number
+  photos: { id: string; filename: string; exif_year: number | null }[] | null
+}
+
+interface DMCAClaim {
+  id: string
+  claimant_name: string
+  claimant_email: string
+  reason: string
+  status: string
+  filed_at: string
+  upload_uuid: string | null
+  upload_user: string | null
+}
+
+interface UserRow {
+  id: string
+  email: string
+  display_name: string
+  role: string
+  is_verified: boolean
+  created_at: string
+  upload_count: number
+  photo_count: number
+  total_earnings: number
+  approved_uploads: number
+}
+
+interface OverviewData {
+  stats: Stats
+  recent_queue: QueueItem[]
+  recent_dmca: DMCAClaim[]
+}
+
+/* ------------------------------------------------------------------ */
+/*  Helpers                                                            */
+/* ------------------------------------------------------------------ */
+
+function timeAgo(dateStr: string): string {
+  const now = new Date()
+  const date = new Date(dateStr)
+  const diffMs = now.getTime() - date.getTime()
+  const diffMin = Math.floor(diffMs / 60000)
+  if (diffMin < 1) return 'just now'
+  if (diffMin < 60) return `${diffMin}m ago`
+  const diffHours = Math.floor(diffMin / 60)
+  if (diffHours < 24) return `${diffHours}h ago`
+  const diffDays = Math.floor(diffHours / 24)
+  if (diffDays < 30) return `${diffDays}d ago`
+  const diffMonths = Math.floor(diffDays / 30)
+  return `${diffMonths}mo ago`
+}
+
+function formatDate(dateStr: string): string {
+  return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
+}
+
+function formatCurrency(amount: number): string {
+  return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount)
+}
+
+/* ------------------------------------------------------------------ */
+/*  Alert Banner                                                       */
+/* ------------------------------------------------------------------ */
+
+interface AlertState {
+  type: 'success' | 'error'
+  message: string
+}
+
+function AlertBanner({ alert, onDismiss }: { alert: AlertState; onDismiss: () => void }) {
+  useEffect(() => {
+    const timer = setTimeout(onDismiss, 4000)
+    return () => clearTimeout(timer)
+  }, [onDismiss])
+
+  return (
+    <div
+      className="fixed top-4 right-4 z-50 px-5 py-3 rounded-xl shadow-lg flex items-center gap-3 text-sm font-semibold"
+      style={{
+        background: alert.type === 'success' ? '#8BBCAA' : '#F4846F',
+        color: 'white',
+      }}
+    >
+      {alert.type === 'success' ? <CheckCircle2 size={16} /> : <XCircle size={16} />}
+      {alert.message}
+      <button onClick={onDismiss} className="ml-2 opacity-70 hover:opacity-100">
+        <XCircle size={14} />
+      </button>
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  StatusBadge                                                        */
+/* ------------------------------------------------------------------ */
+
+function StatusBadge({ status }: { status: string }) {
+  const config: Record<string, { bg: string; color: string }> = {
+    pending: { bg: 'rgba(255, 181, 167, 0.2)', color: '#F4846F' },
+    flagged: { bg: 'rgba(244, 132, 111, 0.25)', color: '#F4846F' },
+    in_review: { bg: 'rgba(255, 224, 203, 0.4)', color: '#6B705C' },
+    approved: { bg: 'rgba(139, 188, 170, 0.25)', color: '#8BBCAA' },
+    rejected: { bg: 'rgba(107, 112, 92, 0.2)', color: '#6B705C' },
+    open: { bg: 'rgba(244, 132, 111, 0.2)', color: '#F4846F' },
+    investigating: { bg: 'rgba(255, 224, 203, 0.4)', color: '#6B705C' },
+    taken_down: { bg: 'rgba(107, 112, 92, 0.2)', color: '#6B705C' },
+    counter_noticed: { bg: 'rgba(139, 188, 170, 0.25)', color: '#8BBCAA' },
+    dismissed: { bg: 'rgba(139, 188, 170, 0.25)', color: '#8BBCAA' },
+    resolved: { bg: 'rgba(139, 188, 170, 0.25)', color: '#8BBCAA' },
+    active: { bg: 'rgba(139, 188, 170, 0.25)', color: '#8BBCAA' },
+    suspended: { bg: 'rgba(244, 132, 111, 0.2)', color: '#F4846F' },
+    dmca_removed: { bg: 'rgba(107, 112, 92, 0.2)', color: '#6B705C' },
+  }
+  const c = config[status] ?? config.pending
+  return (
+    <span
+      className="inline-flex items-center px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider whitespace-nowrap"
+      style={{ background: c.bg, color: c.color }}
+    >
+      {status.replace(/_/g, ' ')}
+    </span>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Loading Spinner                                                    */
+/* ------------------------------------------------------------------ */
+
+function LoadingPane({ text }: { text?: string }) {
+  return (
+    <div className="flex flex-col items-center justify-center py-20 gap-3">
+      <Loader2 size={28} className="animate-spin" style={{ color: '#F4846F' }} />
+      <p className="text-xs font-semibold" style={{ color: '#6B705C' }}>{text || 'Loading...'}</p>
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Empty State                                                        */
+/* ------------------------------------------------------------------ */
+
+function EmptyState({ icon: Icon, message }: { icon: typeof Shield; message: string }) {
+  return (
+    <div className="flex flex-col items-center justify-center py-16 gap-3">
+      <Icon size={32} style={{ color: '#B5D5C5' }} />
+      <p className="text-sm font-semibold" style={{ color: '#6B705C' }}>{message}</p>
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Login Form                                                         */
+/* ------------------------------------------------------------------ */
+
+function LoginForm({ onLogin }: { onLogin: (user: AuthUser) => void }) {
+  const [email, setEmail] = useState('')
+  const [password, setPassword] = useState('')
+  const [error, setError] = useState('')
+  const [loading, setLoading] = useState(false)
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault()
+    setError('')
+    setLoading(true)
+
+    try {
+      const res = await fetch('/api/auth', {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({ action: 'login', email, password }),
+      })
+      const data = await res.json()
+
+      if (!res.ok) {
+        setError(data.error || 'Login failed')
+        setLoading(false)
+        return
+      }
+
+      if (data.user?.role !== 'admin') {
+        setError('Admin access required')
+        setLoading(false)
+        return
+      }
+
+      onLogin(data.user)
+    } catch {
+      setError('Network error. Please try again.')
+      setLoading(false)
+    }
+  }
+
+  return (
+    <div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--color-cream)' }}>
+      <div
+        className="w-full max-w-sm rounded-3xl p-8 shadow-xl"
+        style={{ background: 'rgba(255,255,255,0.7)', border: '1px solid rgba(255,224,203,0.5)' }}
+      >
+        <div className="flex items-center gap-2 mb-6 justify-center">
+          <Shield size={22} style={{ color: '#F4846F' }} />
+          <span className="text-lg font-black tracking-tighter" style={{ color: '#3D405B' }}>
+            i was cute
+          </span>
+          <span className="text-[9px] font-bold px-1.5 py-0.5 rounded" style={{ background: 'rgba(255,181,167,0.2)', color: '#FFB5A7' }}>
+            ADMIN
+          </span>
+        </div>
+
+        <form onSubmit={handleSubmit} className="flex flex-col gap-4">
+          <div>
+            <label className="block text-[10px] font-bold uppercase tracking-wider mb-1.5" style={{ color: '#6B705C' }}>
+              Email
+            </label>
+            <input
+              type="email"
+              value={email}
+              onChange={(e) => setEmail(e.target.value)}
+              className="w-full px-4 py-3 rounded-xl text-sm border outline-none"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+              placeholder="admin@iwascute.com"
+              required
+            />
+          </div>
+          <div>
+            <label className="block text-[10px] font-bold uppercase tracking-wider mb-1.5" style={{ color: '#6B705C' }}>
+              Password
+            </label>
+            <input
+              type="password"
+              value={password}
+              onChange={(e) => setPassword(e.target.value)}
+              className="w-full px-4 py-3 rounded-xl text-sm border outline-none"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+              placeholder="Enter password"
+              required
+            />
+          </div>
+
+          {error && (
+            <p className="text-xs font-semibold px-3 py-2 rounded-lg" style={{ background: 'rgba(244,132,111,0.15)', color: '#F4846F' }}>
+              {error}
+            </p>
+          )}
+
+          <motion.button
+            whileHover={{ scale: 1.02 }}
+            whileTap={{ scale: 0.98 }}
+            type="submit"
+            disabled={loading}
+            className="w-full py-3 rounded-xl text-sm font-bold flex items-center justify-center gap-2"
+            style={{ background: '#F4846F', color: 'white', opacity: loading ? 0.7 : 1 }}
+          >
+            {loading ? <Loader2 size={16} className="animate-spin" /> : <Shield size={16} />}
+            {loading ? 'Signing in...' : 'Sign In'}
+          </motion.button>
+        </form>
+      </div>
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Overview Tab                                                       */
+/* ------------------------------------------------------------------ */
+
+function OverviewTab({
+  data,
+  loading,
+  onSwitchTab,
+}: {
+  data: OverviewData | null
+  loading: boolean
+  onSwitchTab: (tab: AdminTab) => void
+}) {
+  if (loading || !data) return <LoadingPane text="Loading overview..." />
+
+  const { stats, recent_queue, recent_dmca } = data
+
+  const statCards = [
+    { label: 'Pending Review', value: String(stats.pending), icon: Clock, color: '#F4846F' },
+    { label: 'Approved', value: String(stats.approved), icon: CheckCircle2, color: '#8BBCAA' },
+    { label: 'Rejected', value: String(stats.rejected), icon: XCircle, color: '#6B705C' },
+    { label: 'Total Users', value: String(stats.users), icon: Users, color: '#FFB5A7' },
+    { label: 'Revenue', value: formatCurrency(stats.revenue), icon: DollarSign, color: '#F4846F' },
+    { label: 'DMCA Claims', value: String(stats.dmca_open), icon: AlertTriangle, color: '#FFB5A7' },
+  ]
+
+  return (
+    <div className="flex flex-col gap-6">
+      {/* Stats */}
+      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
+        {statCards.map((s) => {
+          const Icon = s.icon
+          return (
+            <div
+              key={s.label}
+              className="p-4 rounded-2xl"
+              style={{ background: 'rgba(255,255,255,0.5)', border: '1px solid rgba(255,224,203,0.5)' }}
+            >
+              <div className="flex items-center gap-2 mb-2">
+                <Icon size={14} style={{ color: s.color }} />
+                <span className="text-[10px] font-semibold uppercase tracking-wider" style={{ color: '#6B705C' }}>
+                  {s.label}
+                </span>
+              </div>
+              <p className="text-2xl font-black" style={{ color: '#3D405B' }}>{s.value}</p>
+            </div>
+          )
+        })}
+      </div>
+
+      {/* Quick actions */}
+      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+        {/* Recent queue */}
+        <div
+          className="rounded-2xl p-5"
+          style={{ background: 'rgba(255,255,255,0.5)', border: '1px solid rgba(255,224,203,0.5)' }}
+        >
+          <div className="flex items-center justify-between mb-4">
+            <h3 className="text-sm font-bold" style={{ color: '#3D405B' }}>Review Queue</h3>
+            <motion.button
+              whileHover={{ scale: 1.05 }}
+              whileTap={{ scale: 0.95 }}
+              onClick={() => onSwitchTab('queue')}
+              className="text-xs font-semibold px-2 py-1 rounded-full cursor-pointer"
+              style={{ background: 'rgba(244,132,111,0.15)', color: '#F4846F' }}
+            >
+              {stats.pending} pending
+            </motion.button>
+          </div>
+          {recent_queue.length === 0 ? (
+            <p className="text-xs py-4 text-center" style={{ color: '#6B705C' }}>No pending uploads</p>
+          ) : (
+            recent_queue.slice(0, 5).map((item) => (
+              <div
+                key={item.id}
+                className="flex items-center justify-between py-3 border-b last:border-b-0"
+                style={{ borderColor: 'rgba(255,224,203,0.4)' }}
+              >
+                <div className="flex items-center gap-3">
+                  <div className="w-8 h-8 rounded-lg flex items-center justify-center" style={{ background: 'rgba(255,181,167,0.2)' }}>
+                    <ImageIcon size={14} style={{ color: '#F4846F' }} />
+                  </div>
+                  <div>
+                    <p className="text-xs font-semibold" style={{ color: '#3D405B' }}>{item.display_name || item.email}</p>
+                    <p className="text-[10px]" style={{ color: '#6B705C' }}>
+                      {item.photo_count} photo{Number(item.photo_count) !== 1 ? 's' : ''} &middot; {timeAgo(item.created_at)}
+                    </p>
+                  </div>
+                </div>
+                <StatusBadge status={item.status} />
+              </div>
+            ))
+          )}
+        </div>
+
+        {/* DMCA alerts */}
+        <div
+          className="rounded-2xl p-5"
+          style={{ background: 'rgba(255,255,255,0.5)', border: '1px solid rgba(255,224,203,0.5)' }}
+        >
+          <div className="flex items-center justify-between mb-4">
+            <h3 className="text-sm font-bold" style={{ color: '#3D405B' }}>DMCA Claims</h3>
+            <motion.button
+              whileHover={{ scale: 1.05 }}
+              whileTap={{ scale: 0.95 }}
+              onClick={() => onSwitchTab('dmca')}
+              className="text-xs font-semibold px-2 py-1 rounded-full cursor-pointer"
+              style={{ background: 'rgba(244,132,111,0.15)', color: '#F4846F' }}
+            >
+              {stats.dmca_open} open
+            </motion.button>
+          </div>
+          {recent_dmca.length === 0 ? (
+            <p className="text-xs py-4 text-center" style={{ color: '#6B705C' }}>No DMCA claims</p>
+          ) : (
+            recent_dmca.slice(0, 5).map((claim) => (
+              <div
+                key={claim.id}
+                className="flex items-center justify-between py-3 border-b last:border-b-0"
+                style={{ borderColor: 'rgba(255,224,203,0.4)' }}
+              >
+                <div className="flex items-center gap-3">
+                  <div
+                    className="w-8 h-8 rounded-lg flex items-center justify-center"
+                    style={{ background: claim.status === 'open' ? 'rgba(244,132,111,0.2)' : 'rgba(139,188,170,0.2)' }}
+                  >
+                    <FileWarning size={14} style={{ color: claim.status === 'open' ? '#F4846F' : '#8BBCAA' }} />
+                  </div>
+                  <div>
+                    <p className="text-xs font-semibold" style={{ color: '#3D405B' }}>{claim.claimant_name}</p>
+                    <p className="text-[10px]" style={{ color: '#6B705C' }}>{timeAgo(claim.filed_at)}</p>
+                  </div>
+                </div>
+                <StatusBadge status={claim.status} />
+              </div>
+            ))
+          )}
+        </div>
+      </div>
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Queue Tab                                                          */
+/* ------------------------------------------------------------------ */
+
+function QueueTab({
+  onAction,
+  actionLoading,
+}: {
+  onAction: (action: string, payload: Record<string, string>) => Promise<void>
+  actionLoading: string | null
+}) {
+  const [queue, setQueue] = useState<QueueItem[]>([])
+  const [loading, setLoading] = useState(true)
+  const [search, setSearch] = useState('')
+  const [statusFilter, setStatusFilter] = useState('all')
+  const [rejectId, setRejectId] = useState<string | null>(null)
+  const [rejectReason, setRejectReason] = useState('')
+  const [flagId, setFlagId] = useState<string | null>(null)
+  const [flagNotes, setFlagNotes] = useState('')
+
+  const fetchQueue = useCallback(async () => {
+    setLoading(true)
+    try {
+      const params = new URLSearchParams({ view: 'queue' })
+      if (statusFilter !== 'all') params.set('status', statusFilter)
+      if (search) params.set('search', search)
+      const res = await fetch(`/api/admin?${params}`)
+      if (res.ok) {
+        const data = await res.json()
+        setQueue(data.queue || [])
+      }
+    } catch {
+      // silently fail, user can retry
+    } finally {
+      setLoading(false)
+    }
+  }, [statusFilter, search])
+
+  useEffect(() => {
+    fetchQueue()
+  }, [fetchQueue])
+
+  const handleApprove = async (uploadId: string) => {
+    await onAction('approve_upload', { upload_id: uploadId })
+    fetchQueue()
+  }
+
+  const handleReject = async () => {
+    if (!rejectId) return
+    await onAction('reject_upload', { upload_id: rejectId, reason: rejectReason || 'Rights verification failed' })
+    setRejectId(null)
+    setRejectReason('')
+    fetchQueue()
+  }
+
+  const handleFlag = async () => {
+    if (!flagId) return
+    await onAction('flag_upload', { upload_id: flagId, notes: flagNotes })
+    setFlagId(null)
+    setFlagNotes('')
+    fetchQueue()
+  }
+
+  return (
+    <div className="flex flex-col gap-4">
+      {/* Search / Filter */}
+      <div className="flex items-center gap-3">
+        <div className="relative flex-1">
+          <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: '#6B705C' }} />
+          <input
+            type="text"
+            placeholder="Search by user or submission ID..."
+            value={search}
+            onChange={(e) => setSearch(e.target.value)}
+            className="w-full pl-9 pr-4 py-2.5 rounded-xl text-sm border outline-none"
+            style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+          />
+        </div>
+        <select
+          value={statusFilter}
+          onChange={(e) => setStatusFilter(e.target.value)}
+          className="px-3 py-2.5 rounded-xl text-xs font-semibold border"
+          style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+        >
+          <option value="all">All status</option>
+          <option value="pending">Pending</option>
+          <option value="flagged">Flagged</option>
+          <option value="in_review">In Review</option>
+        </select>
+        <motion.button
+          whileHover={{ scale: 1.05 }}
+          whileTap={{ scale: 0.95 }}
+          onClick={fetchQueue}
+          className="p-2.5 rounded-xl border"
+          style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#6B705C' }}
+        >
+          <RefreshCw size={14} />
+        </motion.button>
+      </div>
+
+      {loading ? (
+        <LoadingPane text="Loading review queue..." />
+      ) : queue.length === 0 ? (
+        <EmptyState icon={CheckCircle2} message="No uploads matching this filter" />
+      ) : (
+        queue.map((item) => {
+          const photoCount = Number(item.photo_count)
+          const isActing = actionLoading === item.id
+
+          return (
+            <div
+              key={item.id}
+              className="rounded-2xl p-5 flex flex-col md:flex-row md:items-center gap-4"
+              style={{
+                background: 'rgba(255,255,255,0.5)',
+                border: `1px solid ${item.status === 'flagged' ? 'rgba(244,132,111,0.4)' : 'rgba(255,224,203,0.5)'}`,
+                opacity: isActing ? 0.6 : 1,
+              }}
+            >
+              {/* Thumbnails placeholder */}
+              <div className="flex gap-1 shrink-0">
+                {Array.from({ length: Math.min(photoCount, 3) }).map((_, i) => (
+                  <div
+                    key={i}
+                    className="w-12 h-12 rounded-lg"
+                    style={{ background: ['#FFB5A7', '#B5D5C5', '#FFE0CB'][i % 3] }}
+                  />
+                ))}
+                {photoCount > 3 && (
+                  <div
+                    className="w-12 h-12 rounded-lg flex items-center justify-center text-xs font-bold"
+                    style={{ background: 'rgba(255,224,203,0.5)', color: '#6B705C' }}
+                  >
+                    +{photoCount - 3}
+                  </div>
+                )}
+                {photoCount === 0 && (
+                  <div
+                    className="w-12 h-12 rounded-lg flex items-center justify-center"
+                    style={{ background: 'rgba(255,224,203,0.3)' }}
+                  >
+                    <ImageIcon size={14} style={{ color: '#6B705C' }} />
+                  </div>
+                )}
+              </div>
+
+              {/* Info */}
+              <div className="flex-1 min-w-0">
+                <div className="flex items-center gap-2 flex-wrap">
+                  <p className="text-sm font-bold" style={{ color: '#3D405B' }}>
+                    {item.display_name || 'Anonymous'}
+                  </p>
+                  <StatusBadge status={item.status} />
+                  {item.status === 'flagged' && <AlertTriangle size={12} style={{ color: '#F4846F' }} />}
+                </div>
+                <p className="text-xs mt-1" style={{ color: '#6B705C' }}>
+                  {item.id.slice(0, 8)}... &middot; {photoCount} photo{photoCount !== 1 ? 's' : ''} &middot;
+                  Copyright: {item.copyright_relationship || 'n/a'} &middot;
+                  Permission: {item.copyright_permission || 'n/a'} &middot; {timeAgo(item.created_at)}
+                </p>
+                {item.email && (
+                  <p className="text-[10px] mt-0.5" style={{ color: '#6B705C' }}>{item.email}</p>
+                )}
+              </div>
+
+              {/* Actions */}
+              <div className="flex items-center gap-2 shrink-0 flex-wrap">
+                <motion.button
+                  whileHover={{ scale: 1.05 }}
+                  whileTap={{ scale: 0.95 }}
+                  onClick={() => handleApprove(item.id)}
+                  disabled={isActing}
+                  className="flex items-center gap-1 px-3 py-2 rounded-xl text-xs font-semibold"
+                  style={{ background: 'rgba(139,188,170,0.25)', color: '#8BBCAA' }}
+                >
+                  {isActing ? <Loader2 size={12} className="animate-spin" /> : <CheckCircle2 size={12} />} Approve
+                </motion.button>
+                <motion.button
+                  whileHover={{ scale: 1.05 }}
+                  whileTap={{ scale: 0.95 }}
+                  onClick={() => setRejectId(item.id)}
+                  disabled={isActing}
+                  className="flex items-center gap-1 px-3 py-2 rounded-xl text-xs font-semibold"
+                  style={{ background: 'rgba(244,132,111,0.15)', color: '#F4846F' }}
+                >
+                  <XCircle size={12} /> Reject
+                </motion.button>
+                <motion.button
+                  whileHover={{ scale: 1.05 }}
+                  whileTap={{ scale: 0.95 }}
+                  onClick={() => setFlagId(item.id)}
+                  disabled={isActing}
+                  className="flex items-center gap-1 px-3 py-2 rounded-xl text-xs font-semibold"
+                  style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C' }}
+                >
+                  <Flag size={12} /> Flag
+                </motion.button>
+              </div>
+            </div>
+          )
+        })
+      )}
+
+      {/* Reject Modal */}
+      {rejectId && (
+        <div className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4" onClick={() => setRejectId(null)}>
+          <div
+            className="w-full max-w-md rounded-2xl p-6 shadow-xl"
+            style={{ background: 'var(--color-cream)', border: '1px solid rgba(255,224,203,0.5)' }}
+            onClick={(e) => e.stopPropagation()}
+          >
+            <h3 className="text-sm font-bold mb-3" style={{ color: '#3D405B' }}>Reject Upload</h3>
+            <p className="text-xs mb-3" style={{ color: '#6B705C' }}>Upload: {rejectId.slice(0, 8)}...</p>
+            <textarea
+              value={rejectReason}
+              onChange={(e) => setRejectReason(e.target.value)}
+              placeholder="Reason for rejection (optional)..."
+              rows={3}
+              className="w-full px-3 py-2 rounded-xl text-sm border outline-none resize-none mb-4"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+            />
+            <div className="flex gap-2 justify-end">
+              <motion.button
+                whileHover={{ scale: 1.03 }}
+                whileTap={{ scale: 0.97 }}
+                onClick={() => setRejectId(null)}
+                className="px-4 py-2 rounded-xl text-xs font-semibold"
+                style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C' }}
+              >
+                Cancel
+              </motion.button>
+              <motion.button
+                whileHover={{ scale: 1.03 }}
+                whileTap={{ scale: 0.97 }}
+                onClick={handleReject}
+                className="px-4 py-2 rounded-xl text-xs font-semibold"
+                style={{ background: '#F4846F', color: 'white' }}
+              >
+                Confirm Reject
+              </motion.button>
+            </div>
+          </div>
+        </div>
+      )}
+
+      {/* Flag Modal */}
+      {flagId && (
+        <div className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4" onClick={() => setFlagId(null)}>
+          <div
+            className="w-full max-w-md rounded-2xl p-6 shadow-xl"
+            style={{ background: 'var(--color-cream)', border: '1px solid rgba(255,224,203,0.5)' }}
+            onClick={(e) => e.stopPropagation()}
+          >
+            <h3 className="text-sm font-bold mb-3" style={{ color: '#3D405B' }}>Flag Upload for Review</h3>
+            <p className="text-xs mb-3" style={{ color: '#6B705C' }}>Upload: {flagId.slice(0, 8)}...</p>
+            <textarea
+              value={flagNotes}
+              onChange={(e) => setFlagNotes(e.target.value)}
+              placeholder="Notes about why this is flagged..."
+              rows={3}
+              className="w-full px-3 py-2 rounded-xl text-sm border outline-none resize-none mb-4"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+            />
+            <div className="flex gap-2 justify-end">
+              <motion.button
+                whileHover={{ scale: 1.03 }}
+                whileTap={{ scale: 0.97 }}
+                onClick={() => setFlagId(null)}
+                className="px-4 py-2 rounded-xl text-xs font-semibold"
+                style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C' }}
+              >
+                Cancel
+              </motion.button>
+              <motion.button
+                whileHover={{ scale: 1.03 }}
+                whileTap={{ scale: 0.97 }}
+                onClick={handleFlag}
+                className="px-4 py-2 rounded-xl text-xs font-semibold"
+                style={{ background: '#F4846F', color: 'white' }}
+              >
+                Confirm Flag
+              </motion.button>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  DMCA Tab                                                           */
+/* ------------------------------------------------------------------ */
+
+function DMCATab({
+  onAction,
+  actionLoading,
+}: {
+  onAction: (action: string, payload: Record<string, string>) => Promise<void>
+  actionLoading: string | null
+}) {
+  const [claims, setClaims] = useState<DMCAClaim[]>([])
+  const [loading, setLoading] = useState(true)
+  const [statusFilter, setStatusFilter] = useState('all')
+  const [counterModal, setCounterModal] = useState<string | null>(null)
+  const [counterNotes, setCounterNotes] = useState('')
+  const [dismissModal, setDismissModal] = useState<string | null>(null)
+  const [dismissNotes, setDismissNotes] = useState('')
+
+  const fetchClaims = useCallback(async () => {
+    setLoading(true)
+    try {
+      const params = new URLSearchParams({ view: 'dmca' })
+      if (statusFilter !== 'all') params.set('status', statusFilter)
+      const res = await fetch(`/api/admin?${params}`)
+      if (res.ok) {
+        const data = await res.json()
+        setClaims(data.claims || [])
+      }
+    } catch {
+      // silently fail
+    } finally {
+      setLoading(false)
+    }
+  }, [statusFilter])
+
+  useEffect(() => {
+    fetchClaims()
+  }, [fetchClaims])
+
+  const handleTakedown = async (claimId: string) => {
+    await onAction('dmca_takedown', { claim_id: claimId })
+    fetchClaims()
+  }
+
+  const handleCounter = async () => {
+    if (!counterModal) return
+    await onAction('dmca_counter', { claim_id: counterModal, notes: counterNotes })
+    setCounterModal(null)
+    setCounterNotes('')
+    fetchClaims()
+  }
+
+  const handleDismiss = async () => {
+    if (!dismissModal) return
+    await onAction('dmca_dismiss', { claim_id: dismissModal, notes: dismissNotes })
+    setDismissModal(null)
+    setDismissNotes('')
+    fetchClaims()
+  }
+
+  return (
+    <div className="flex flex-col gap-4">
+      {/* Filter */}
+      <div className="flex items-center gap-3">
+        <select
+          value={statusFilter}
+          onChange={(e) => setStatusFilter(e.target.value)}
+          className="px-3 py-2.5 rounded-xl text-xs font-semibold border"
+          style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+        >
+          <option value="all">All status</option>
+          <option value="open">Open</option>
+          <option value="investigating">Investigating</option>
+          <option value="taken_down">Taken Down</option>
+          <option value="counter_noticed">Counter Noticed</option>
+          <option value="dismissed">Dismissed</option>
+        </select>
+        <motion.button
+          whileHover={{ scale: 1.05 }}
+          whileTap={{ scale: 0.95 }}
+          onClick={fetchClaims}
+          className="p-2.5 rounded-xl border"
+          style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#6B705C' }}
+        >
+          <RefreshCw size={14} />
+        </motion.button>
+      </div>
+
+      {loading ? (
+        <LoadingPane text="Loading DMCA claims..." />
+      ) : claims.length === 0 ? (
+        <EmptyState icon={FileWarning} message="No DMCA claims matching this filter" />
+      ) : (
+        claims.map((claim) => {
+          const isOpen = claim.status === 'open' || claim.status === 'investigating'
+          const isActing = actionLoading === claim.id
+
+          return (
+            <div
+              key={claim.id}
+              className="rounded-2xl p-6"
+              style={{
+                background: 'rgba(255,255,255,0.5)',
+                border: `1px solid ${isOpen ? 'rgba(244,132,111,0.3)' : 'rgba(255,224,203,0.5)'}`,
+                opacity: isActing ? 0.6 : 1,
+              }}
+            >
+              <div className="flex items-center justify-between mb-3">
+                <div className="flex items-center gap-3">
+                  <div
+                    className="w-10 h-10 rounded-xl flex items-center justify-center"
+                    style={{ background: isOpen ? 'rgba(244,132,111,0.15)' : 'rgba(139,188,170,0.15)' }}
+                  >
+                    <FileWarning size={18} style={{ color: isOpen ? '#F4846F' : '#8BBCAA' }} />
+                  </div>
+                  <div>
+                    <p className="text-sm font-bold" style={{ color: '#3D405B' }}>DMCA-{claim.id.slice(0, 8)}</p>
+                    <p className="text-xs" style={{ color: '#6B705C' }}>
+                      Filed {timeAgo(claim.filed_at)}
+                      {claim.upload_uuid && <> &middot; Upload: {claim.upload_uuid.slice(0, 8)}...</>}
+                    </p>
+                  </div>
+                </div>
+                <StatusBadge status={claim.status} />
+              </div>
+              <p className="text-sm mb-2" style={{ color: '#6B705C' }}>
+                <strong style={{ color: '#3D405B' }}>Claimant:</strong> {claim.claimant_name}
+                {claim.claimant_email && <span className="ml-2 text-xs">({claim.claimant_email})</span>}
+              </p>
+              {claim.upload_user && (
+                <p className="text-sm mb-2" style={{ color: '#6B705C' }}>
+                  <strong style={{ color: '#3D405B' }}>Upload by:</strong> {claim.upload_user}
+                </p>
+              )}
+              <p className="text-sm mb-4" style={{ color: '#6B705C' }}>
+                <strong style={{ color: '#3D405B' }}>Reason:</strong> {claim.reason}
+              </p>
+              {isOpen && (
+                <div className="flex gap-2 flex-wrap">
+                  <motion.button
+                    whileHover={{ scale: 1.03 }}
+                    whileTap={{ scale: 0.97 }}
+                    onClick={() => handleTakedown(claim.id)}
+                    disabled={isActing}
+                    className="px-4 py-2 rounded-xl text-xs font-semibold flex items-center gap-1"
+                    style={{ background: 'rgba(244,132,111,0.15)', color: '#F4846F' }}
+                  >
+                    {isActing ? <Loader2 size={12} className="animate-spin" /> : <Ban size={12} />} Take Down
+                  </motion.button>
+                  <motion.button
+                    whileHover={{ scale: 1.03 }}
+                    whileTap={{ scale: 0.97 }}
+                    onClick={() => setCounterModal(claim.id)}
+                    disabled={isActing}
+                    className="px-4 py-2 rounded-xl text-xs font-semibold flex items-center gap-1"
+                    style={{ background: 'rgba(139,188,170,0.2)', color: '#8BBCAA' }}
+                  >
+                    <Scale size={12} /> Counter-notice
+                  </motion.button>
+                  <motion.button
+                    whileHover={{ scale: 1.03 }}
+                    whileTap={{ scale: 0.97 }}
+                    onClick={() => setDismissModal(claim.id)}
+                    disabled={isActing}
+                    className="px-4 py-2 rounded-xl text-xs font-semibold flex items-center gap-1"
+                    style={{ background: 'rgba(255,255,255,0.5)', color: '#6B705C' }}
+                  >
+                    <Eye size={12} /> Dismiss
+                  </motion.button>
+                </div>
+              )}
+            </div>
+          )
+        })
+      )}
+
+      {/* Counter-notice Modal */}
+      {counterModal && (
+        <div className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4" onClick={() => setCounterModal(null)}>
+          <div
+            className="w-full max-w-md rounded-2xl p-6 shadow-xl"
+            style={{ background: 'var(--color-cream)', border: '1px solid rgba(255,224,203,0.5)' }}
+            onClick={(e) => e.stopPropagation()}
+          >
+            <h3 className="text-sm font-bold mb-3" style={{ color: '#3D405B' }}>File Counter-Notice</h3>
+            <textarea
+              value={counterNotes}
+              onChange={(e) => setCounterNotes(e.target.value)}
+              placeholder="Counter-notice details..."
+              rows={4}
+              className="w-full px-3 py-2 rounded-xl text-sm border outline-none resize-none mb-4"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+            />
+            <div className="flex gap-2 justify-end">
+              <motion.button whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }} onClick={() => setCounterModal(null)} className="px-4 py-2 rounded-xl text-xs font-semibold" style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C' }}>
+                Cancel
+              </motion.button>
+              <motion.button whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }} onClick={handleCounter} className="px-4 py-2 rounded-xl text-xs font-semibold" style={{ background: '#8BBCAA', color: 'white' }}>
+                Submit Counter-Notice
+              </motion.button>
+            </div>
+          </div>
+        </div>
+      )}
+
+      {/* Dismiss Modal */}
+      {dismissModal && (
+        <div className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center p-4" onClick={() => setDismissModal(null)}>
+          <div
+            className="w-full max-w-md rounded-2xl p-6 shadow-xl"
+            style={{ background: 'var(--color-cream)', border: '1px solid rgba(255,224,203,0.5)' }}
+            onClick={(e) => e.stopPropagation()}
+          >
+            <h3 className="text-sm font-bold mb-3" style={{ color: '#3D405B' }}>Dismiss DMCA Claim</h3>
+            <textarea
+              value={dismissNotes}
+              onChange={(e) => setDismissNotes(e.target.value)}
+              placeholder="Reason for dismissal..."
+              rows={3}
+              className="w-full px-3 py-2 rounded-xl text-sm border outline-none resize-none mb-4"
+              style={{ background: 'rgba(255,255,255,0.8)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+            />
+            <div className="flex gap-2 justify-end">
+              <motion.button whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }} onClick={() => setDismissModal(null)} className="px-4 py-2 rounded-xl text-xs font-semibold" style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C' }}>
+                Cancel
+              </motion.button>
+              <motion.button whileHover={{ scale: 1.03 }} whileTap={{ scale: 0.97 }} onClick={handleDismiss} className="px-4 py-2 rounded-xl text-xs font-semibold" style={{ background: '#F4846F', color: 'white' }}>
+                Confirm Dismiss
+              </motion.button>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Users Tab                                                          */
+/* ------------------------------------------------------------------ */
+
+function UsersTab() {
+  const [users, setUsers] = useState<UserRow[]>([])
+  const [loading, setLoading] = useState(true)
+  const [search, setSearch] = useState('')
+
+  const fetchUsers = useCallback(async () => {
+    setLoading(true)
+    try {
+      const params = new URLSearchParams({ view: 'users' })
+      if (search) params.set('search', search)
+      const res = await fetch(`/api/admin?${params}`)
+      if (res.ok) {
+        const data = await res.json()
+        setUsers(data.users || [])
+      }
+    } catch {
+      // silently fail
+    } finally {
+      setLoading(false)
+    }
+  }, [search])
+
+  useEffect(() => {
+    fetchUsers()
+  }, [fetchUsers])
+
+  return (
+    <div className="flex flex-col gap-3">
+      <div className="flex items-center gap-3">
+        <div className="relative flex-1">
+          <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: '#6B705C' }} />
+          <input
+            type="text"
+            placeholder="Search users by name or email..."
+            value={search}
+            onChange={(e) => setSearch(e.target.value)}
+            className="w-full pl-9 pr-4 py-2.5 rounded-xl text-sm border outline-none"
+            style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#3D405B' }}
+          />
+        </div>
+        <motion.button
+          whileHover={{ scale: 1.05 }}
+          whileTap={{ scale: 0.95 }}
+          onClick={fetchUsers}
+          className="p-2.5 rounded-xl border"
+          style={{ background: 'rgba(255,255,255,0.7)', borderColor: 'rgba(255,224,203,0.5)', color: '#6B705C' }}
+        >
+          <RefreshCw size={14} />
+        </motion.button>
+      </div>
+
+      {loading ? (
+        <LoadingPane text="Loading users..." />
+      ) : users.length === 0 ? (
+        <EmptyState icon={Users} message="No users found" />
+      ) : (
+        <div
+          className="rounded-2xl overflow-hidden"
+          style={{ border: '1px solid rgba(255,224,203,0.5)' }}
+        >
+          {/* Header */}
+          <div
+            className="grid grid-cols-7 gap-4 px-5 py-3 text-[10px] font-bold uppercase tracking-wider"
+            style={{ background: 'rgba(255,224,203,0.3)', color: '#6B705C' }}
+          >
+            <span>User</span>
+            <span>Email</span>
+            <span className="text-center">Uploads</span>
+            <span className="text-center">Photos</span>
+            <span className="text-center">Earnings</span>
+            <span className="text-center">Joined</span>
+            <span className="text-center">Status</span>
+          </div>
+          {/* Rows */}
+          {users.map((user) => (
+            <div
+              key={user.id}
+              className="grid grid-cols-7 gap-4 items-center px-5 py-3.5 border-t hover:bg-white/30 transition-colors"
+              style={{ borderColor: 'rgba(255,224,203,0.3)', background: 'rgba(255,255,255,0.4)' }}
+            >
+              <div className="flex items-center gap-2">
+                <div
+                  className="w-7 h-7 rounded-full flex items-center justify-center text-[10px] font-bold shrink-0"
+                  style={{ background: '#FFB5A7', color: '#3D405B' }}
+                >
+                  {(user.display_name || user.email || '?')
+                    .split(' ')
+                    .map((n: string) => n[0])
+                    .join('')
+                    .slice(0, 2)
+                    .toUpperCase()}
+                </div>
+                <span className="text-xs font-semibold truncate" style={{ color: '#3D405B' }}>
+                  {user.display_name || 'No name'}
+                </span>
+              </div>
+              <span className="text-xs truncate" style={{ color: '#6B705C' }}>{user.email}</span>
+              <span className="text-xs text-center font-semibold" style={{ color: '#3D405B' }}>
+                {user.upload_count}
+                {Number(user.approved_uploads) > 0 && (
+                  <span className="text-[10px] font-normal ml-0.5" style={{ color: '#8BBCAA' }}>
+                    ({user.approved_uploads})
+                  </span>
+                )}
+              </span>
+              <span className="text-xs text-center font-semibold" style={{ color: '#3D405B' }}>{user.photo_count}</span>
+              <span className="text-xs text-center font-semibold" style={{ color: '#3D405B' }}>
+                {formatCurrency(Number(user.total_earnings))}
+              </span>
+              <span className="text-xs text-center" style={{ color: '#6B705C' }}>{formatDate(user.created_at)}</span>
+              <div className="flex justify-center">
+                {user.is_verified ? (
+                  <StatusBadge status="active" />
+                ) : (
+                  <StatusBadge status="pending" />
+                )}
+              </div>
+            </div>
+          ))}
+        </div>
+      )}
+    </div>
+  )
+}
+
+/* ------------------------------------------------------------------ */
+/*  Tab Config                                                         */
+/* ------------------------------------------------------------------ */
+
+const TABS: { id: AdminTab; label: string; icon: typeof Shield }[] = [
+  { id: 'overview', label: 'Overview', icon: BarChart3 },
+  { id: 'queue', label: 'Review Queue', icon: Scale },
+  { id: 'dmca', label: 'DMCA', icon: FileWarning },
+  { id: 'users', label: 'Users', icon: Users },
+]
+
+/* ------------------------------------------------------------------ */
+/*  Main Admin Page                                                    */
+/* ------------------------------------------------------------------ */
+
+export default function AdminPage() {
+  // Auth state
+  const [authChecking, setAuthChecking] = useState(true)
+  const [user, setUser] = useState<AuthUser | null>(null)
+
+  // Dashboard state
+  const [activeTab, setActiveTab] = useState<AdminTab>('overview')
+  const [overview, setOverview] = useState<OverviewData | null>(null)
+  const [overviewLoading, setOverviewLoading] = useState(false)
+  const [actionLoading, setActionLoading] = useState<string | null>(null)
+  const [alert, setAlert] = useState<AlertState | null>(null)
+
+  // Check auth on mount
+  useEffect(() => {
+    fetch('/api/auth')
+      .then((res) => res.json())
+      .then((data) => {
+        if (data.user?.role === 'admin') {
+          setUser(data.user)
+        }
+      })
+      .catch(() => {})
+      .finally(() => setAuthChecking(false))
+  }, [])
+
+  // Load overview when authenticated and on overview tab
+  const fetchOverview = useCallback(async () => {
+    setOverviewLoading(true)
+    try {
+      const res = await fetch('/api/admin?view=overview')
+      if (res.ok) {
+        const data = await res.json()
+        setOverview(data)
+      }
+    } catch {
+      // silently fail
+    } finally {
+      setOverviewLoading(false)
+    }
+  }, [])
+
+  useEffect(() => {
+    if (user && activeTab === 'overview') {
+      fetchOverview()
+    }
+  }, [user, activeTab, fetchOverview])
+
+  // Generic action handler for POST actions
+  const handleAction = useCallback(
+    async (action: string, payload: Record<string, string>) => {
+      const targetId = payload.upload_id || payload.claim_id || payload.user_id || ''
+      setActionLoading(targetId)
+      try {
+        const res = await fetch('/api/admin', {
+          method: 'POST',
+          headers: { 'Content-Type': 'application/json' },
+          body: JSON.stringify({ action, ...payload }),
+        })
+        const data = await res.json()
+
+        if (res.ok && data.success) {
+          setAlert({ type: 'success', message: `Action "${action.replace(/_/g, ' ')}" completed successfully` })
+          // Refresh overview stats in background
+          fetchOverview()
+        } else {
+          setAlert({ type: 'error', message: data.error || 'Action failed' })
+        }
+      } catch {
+        setAlert({ type: 'error', message: 'Network error. Please try again.' })
+      } finally {
+        setActionLoading(null)
+      }
+    },
+    [fetchOverview]
+  )
+
+  // Handle logout
+  const handleLogout = async () => {
+    await fetch('/api/auth', {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({ action: 'logout' }),
+    })
+    setUser(null)
+    setOverview(null)
+  }
+
+  // Auth checking spinner
+  if (authChecking) {
+    return (
+      <div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--color-cream)' }}>
+        <LoadingPane text="Checking authentication..." />
+      </div>
+    )
+  }
+
+  // Not authenticated — show login
+  if (!user) {
+    return <LoginForm onLogin={setUser} />
+  }
+
+  // Badge counts from overview data
+  const pendingCount = overview?.stats.pending ?? 0
+  const dmcaCount = overview?.stats.dmca_open ?? 0
+
+  return (
+    <div className="min-h-screen flex" style={{ background: 'var(--color-cream)' }}>
+      {/* Alert */}
+      {alert && <AlertBanner alert={alert} onDismiss={() => setAlert(null)} />}
+
+      {/* Sidebar */}
+      <aside
+        className="w-56 shrink-0 flex flex-col justify-between p-4"
+        style={{ background: '#1a1d2e' }}
+      >
+        <div>
+          <div className="flex items-center gap-2 px-3 py-3 mb-6">
+            <Shield size={18} style={{ color: '#FFB5A7' }} />
+            <span className="text-sm font-black tracking-tighter" style={{ color: 'white' }}>
+              i was cute
+            </span>
+            <span className="text-[9px] font-bold px-1.5 py-0.5 rounded" style={{ background: 'rgba(255,181,167,0.2)', color: '#FFB5A7' }}>
+              ADMIN
+            </span>
+          </div>
+
+          <div className="flex flex-col gap-1">
+            {TABS.map((tab) => {
+              const Icon = tab.icon
+              const active = activeTab === tab.id
+              return (
+                <button
+                  key={tab.id}
+                  onClick={() => setActiveTab(tab.id)}
+                  className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-xs font-semibold transition-all text-left"
+                  style={{
+                    background: active ? 'rgba(255,181,167,0.12)' : 'transparent',
+                    color: active ? '#FFB5A7' : 'rgba(255,255,255,0.45)',
+                  }}
+                >
+                  <Icon size={15} />
+                  {tab.label}
+                  {tab.id === 'queue' && pendingCount > 0 && (
+                    <span className="ml-auto text-[9px] font-bold px-1.5 py-0.5 rounded-full" style={{ background: 'rgba(244,132,111,0.25)', color: '#F4846F' }}>
+                      {pendingCount}
+                    </span>
+                  )}
+                  {tab.id === 'dmca' && dmcaCount > 0 && (
+                    <span className="ml-auto text-[9px] font-bold px-1.5 py-0.5 rounded-full" style={{ background: 'rgba(244,132,111,0.25)', color: '#F4846F' }}>
+                      {dmcaCount}
+                    </span>
+                  )}
+                </button>
+              )
+            })}
+          </div>
+        </div>
+
+        <div className="flex flex-col gap-1">
+          {/* Logged-in user info */}
+          <div className="px-3 py-2 mb-2">
+            <p className="text-[10px] font-semibold truncate" style={{ color: 'rgba(255,255,255,0.5)' }}>
+              {user.display_name || user.email}
+            </p>
+            <p className="text-[9px] truncate" style={{ color: 'rgba(255,255,255,0.3)' }}>
+              {user.email}
+            </p>
+          </div>
+          <button className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-xs font-medium text-left" style={{ color: 'rgba(255,255,255,0.35)' }}>
+            <Settings size={14} /> Settings
+          </button>
+          <Link href="/" className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-xs font-medium no-underline" style={{ color: 'rgba(255,255,255,0.35)' }}>
+            <LogOut size={14} /> Exit Admin
+          </Link>
+          <button
+            onClick={handleLogout}
+            className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-xs font-medium text-left"
+            style={{ color: 'rgba(244,132,111,0.7)' }}
+          >
+            <LogOut size={14} /> Log Out
+          </button>
+        </div>
+      </aside>
+
+      {/* Main content */}
+      <main className="flex-1 px-6 py-6 md:px-10 md:py-8 overflow-y-auto">
+        <div className="max-w-6xl">
+          <div className="flex items-center justify-between mb-6">
+            <div>
+              <h1 className="text-2xl font-black tracking-tighter" style={{ color: '#3D405B' }}>
+                {TABS.find((t) => t.id === activeTab)?.label}
+              </h1>
+              <p className="text-xs mt-1" style={{ color: '#6B705C' }}>
+                {activeTab === 'overview' && 'Platform health and activity at a glance.'}
+                {activeTab === 'queue' && 'Review uploads for dual-rights clearance.'}
+                {activeTab === 'dmca' && 'Manage takedown requests and counter-notices.'}
+                {activeTab === 'users' && 'Manage licensor accounts and activity.'}
+              </p>
+            </div>
+            {activeTab === 'overview' && (
+              <motion.button
+                whileHover={{ scale: 1.05 }}
+                whileTap={{ scale: 0.95 }}
+                onClick={fetchOverview}
+                className="flex items-center gap-2 px-3 py-2 rounded-xl text-xs font-semibold"
+                style={{ background: 'rgba(255,255,255,0.6)', color: '#6B705C', border: '1px solid rgba(255,224,203,0.5)' }}
+              >
+                <RefreshCw size={12} /> Refresh
+              </motion.button>
+            )}
+          </div>
+
+          {activeTab === 'overview' && (
+            <OverviewTab
+              data={overview}
+              loading={overviewLoading}
+              onSwitchTab={setActiveTab}
+            />
+          )}
+          {activeTab === 'queue' && (
+            <QueueTab
+              onAction={handleAction}
+              actionLoading={actionLoading}
+            />
+          )}
+          {activeTab === 'dmca' && (
+            <DMCATab
+              onAction={handleAction}
+              actionLoading={actionLoading}
+            />
+          )}
+          {activeTab === 'users' && <UsersTab />}
+        </div>
+      </main>
+    </div>
+  )
+}
diff --git a/src/app/api/admin/route.ts b/src/app/api/admin/route.ts
new file mode 100644
index 0000000..2a89946
--- /dev/null
+++ b/src/app/api/admin/route.ts
@@ -0,0 +1,283 @@
+import { NextRequest, NextResponse } from 'next/server'
+import { query } from '@/lib/db'
+import { requireAdmin } from '@/lib/auth'
+
+export async function GET(request: NextRequest) {
+  const admin = await requireAdmin()
+  if (!admin) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
+
+  const { searchParams } = new URL(request.url)
+  const view = searchParams.get('view') || 'overview'
+
+  switch (view) {
+    case 'overview':
+      return getOverview()
+    case 'queue':
+      return getQueue(searchParams)
+    case 'dmca':
+      return getDMCA(searchParams)
+    case 'users':
+      return getUsers(searchParams)
+    case 'upload':
+      return getUploadDetail(searchParams)
+    default:
+      return NextResponse.json({ error: `Unknown view: ${view}` }, { status: 400 })
+  }
+}
+
+async function getOverview() {
+  const [pending, approved, rejected, users, revenue, dmca] = await Promise.all([
+    query(`SELECT COUNT(*) FROM uploads WHERE status IN ('pending', 'flagged')`),
+    query(`SELECT COUNT(*) FROM uploads WHERE status = 'approved'`),
+    query(`SELECT COUNT(*) FROM uploads WHERE status = 'rejected'`),
+    query(`SELECT COUNT(*) FROM users WHERE role != 'admin'`),
+    query(`SELECT COALESCE(SUM(total_earnings), 0) as total FROM photos`),
+    query(`SELECT COUNT(*) FROM dmca_claims WHERE status IN ('open', 'investigating')`),
+  ])
+
+  // Recent queue
+  const recentQueue = await query(`
+    SELECT u.id, u.status, u.created_at, u.copyright_relationship, u.copyright_permission,
+           usr.display_name, usr.email,
+           COUNT(p.id) as photo_count
+    FROM uploads u
+    JOIN users usr ON u.user_id = usr.id
+    LEFT JOIN photos p ON p.upload_id = u.id
+    WHERE u.status IN ('pending', 'flagged')
+    GROUP BY u.id, usr.display_name, usr.email
+    ORDER BY u.created_at DESC
+    LIMIT 5
+  `)
+
+  // Recent DMCA
+  const recentDMCA = await query(`
+    SELECT d.*, u.id as upload_uuid
+    FROM dmca_claims d
+    LEFT JOIN uploads u ON d.upload_id = u.id
+    ORDER BY d.filed_at DESC
+    LIMIT 5
+  `)
+
+  return NextResponse.json({
+    stats: {
+      pending: parseInt(pending.rows[0].count),
+      approved: parseInt(approved.rows[0].count),
+      rejected: parseInt(rejected.rows[0].count),
+      users: parseInt(users.rows[0].count),
+      revenue: parseFloat(revenue.rows[0].total),
+      dmca_open: parseInt(dmca.rows[0].count),
+    },
+    recent_queue: recentQueue.rows,
+    recent_dmca: recentDMCA.rows,
+  })
+}
+
+async function getQueue(params: URLSearchParams) {
+  const status = params.get('status')
+  const search = params.get('search')
+
+  const conditions = [`u.status IN ('pending', 'flagged', 'in_review')`]
+  const values: unknown[] = []
+  let idx = 1
+
+  if (status && status !== 'all') {
+    conditions.push(`u.status = $${idx}`)
+    values.push(status)
+    idx++
+  }
+  if (search) {
+    conditions.push(`(usr.display_name ILIKE $${idx} OR u.id::text ILIKE $${idx})`)
+    values.push(`%${search}%`)
+    idx++
+  }
+
+  const where = conditions.join(' AND ')
+
+  const result = await query(`
+    SELECT u.*, usr.display_name, usr.email,
+           COUNT(p.id) as photo_count,
+           json_agg(json_build_object('id', p.id, 'filename', p.filename, 'exif_year', p.exif_year) ORDER BY p.created_at) FILTER (WHERE p.id IS NOT NULL) as photos
+    FROM uploads u
+    JOIN users usr ON u.user_id = usr.id
+    LEFT JOIN photos p ON p.upload_id = u.id
+    WHERE ${where}
+    GROUP BY u.id, usr.display_name, usr.email
+    ORDER BY CASE WHEN u.status = 'flagged' THEN 0 ELSE 1 END, u.created_at DESC
+  `, values)
+
+  return NextResponse.json({ queue: result.rows })
+}
+
+async function getDMCA(params: URLSearchParams) {
+  const status = params.get('status')
+  const conditions: string[] = []
+  const values: unknown[] = []
+
+  if (status && status !== 'all') {
+    conditions.push(`d.status = $1`)
+    values.push(status)
+  }
+
+  const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : ''
+
+  const result = await query(`
+    SELECT d.*,
+           u.id as upload_uuid, usr.display_name as upload_user
+    FROM dmca_claims d
+    LEFT JOIN uploads u ON d.upload_id = u.id
+    LEFT JOIN users usr ON u.user_id = usr.id
+    ${where}
+    ORDER BY d.filed_at DESC
+  `, values)
+
+  return NextResponse.json({ claims: result.rows })
+}
+
+async function getUsers(params: URLSearchParams) {
+  const search = params.get('search')
+  const conditions = [`u.role != 'admin'`]
+  const values: unknown[] = []
+
+  if (search) {
+    conditions.push(`(u.display_name ILIKE $1 OR u.email ILIKE $1)`)
+    values.push(`%${search}%`)
+  }
+
+  const where = conditions.join(' AND ')
+
+  const result = await query(`
+    SELECT u.id, u.email, u.display_name, u.role, u.is_verified, u.created_at,
+           COUNT(DISTINCT up.id) as upload_count,
+           COUNT(DISTINCT p.id) as photo_count,
+           COALESCE(SUM(p.total_earnings), 0) as total_earnings,
+           COUNT(DISTINCT CASE WHEN up.status = 'approved' THEN up.id END) as approved_uploads
+    FROM users u
+    LEFT JOIN uploads up ON up.user_id = u.id
+    LEFT JOIN photos p ON p.user_id = u.id
+    WHERE ${where}
+    GROUP BY u.id
+    ORDER BY u.created_at DESC
+  `, values)
+
+  return NextResponse.json({ users: result.rows })
+}
+
+async function getUploadDetail(params: URLSearchParams) {
+  const id = params.get('id')
+  if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 })
+
+  const upload = await query(`
+    SELECT u.*, usr.display_name, usr.email
+    FROM uploads u JOIN users usr ON u.user_id = usr.id
+    WHERE u.id = $1
+  `, [id])
+
+  const photos = await query(`SELECT * FROM photos WHERE upload_id = $1 ORDER BY created_at`, [id])
+
+  return NextResponse.json({
+    upload: upload.rows[0] || null,
+    photos: photos.rows,
+  })
+}
+
+// POST — Admin actions
+export async function POST(request: NextRequest) {
+  const admin = await requireAdmin()
+  if (!admin) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
+
+  const body = await request.json()
+  const { action } = body
+
+  switch (action) {
+    case 'approve_upload': {
+      const { upload_id, notes } = body
+      await query(
+        `UPDATE uploads SET status = 'approved', reviewed_by = $1, reviewed_at = NOW(), review_notes = $2, updated_at = NOW() WHERE id = $3`,
+        [admin.user_id, notes || null, upload_id]
+      )
+      // Set clearance level on photos
+      await query(
+        `UPDATE photos SET clearance_level = CASE
+          WHEN (SELECT likeness_ai_training FROM uploads WHERE id = $1) THEN 'ai_training'
+          WHEN (SELECT likeness_commercial FROM uploads WHERE id = $1) THEN 'commercial'
+          ELSE 'editorial'
+        END WHERE upload_id = $1`,
+        [upload_id]
+      )
+      await auditLog(admin.user_id, 'upload.approve', 'upload', upload_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'reject_upload': {
+      const { upload_id, reason } = body
+      await query(
+        `UPDATE uploads SET status = 'rejected', reviewed_by = $1, reviewed_at = NOW(), rejection_reason = $2, updated_at = NOW() WHERE id = $3`,
+        [admin.user_id, reason || 'Rights verification failed', upload_id]
+      )
+      await auditLog(admin.user_id, 'upload.reject', 'upload', upload_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'flag_upload': {
+      const { upload_id, notes } = body
+      await query(
+        `UPDATE uploads SET status = 'flagged', review_notes = $1, updated_at = NOW() WHERE id = $2`,
+        [notes, upload_id]
+      )
+      await auditLog(admin.user_id, 'upload.flag', 'upload', upload_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'dmca_takedown': {
+      const { claim_id } = body
+      await query(
+        `UPDATE dmca_claims SET status = 'taken_down', resolved_by = $1, resolved_at = NOW(), updated_at = NOW() WHERE id = $2`,
+        [admin.user_id, claim_id]
+      )
+      // Also update the associated upload
+      await query(
+        `UPDATE uploads SET status = 'dmca_removed', updated_at = NOW() WHERE id = (SELECT upload_id FROM dmca_claims WHERE id = $1)`,
+        [claim_id]
+      )
+      await auditLog(admin.user_id, 'dmca.takedown', 'dmca_claim', claim_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'dmca_counter': {
+      const { claim_id, notes } = body
+      await query(
+        `UPDATE dmca_claims SET status = 'counter_noticed', resolution_notes = $1, updated_at = NOW() WHERE id = $2`,
+        [notes, claim_id]
+      )
+      await auditLog(admin.user_id, 'dmca.counter_notice', 'dmca_claim', claim_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'dmca_dismiss': {
+      const { claim_id, notes } = body
+      await query(
+        `UPDATE dmca_claims SET status = 'dismissed', resolved_by = $1, resolved_at = NOW(), resolution_notes = $2, updated_at = NOW() WHERE id = $3`,
+        [admin.user_id, notes, claim_id]
+      )
+      await auditLog(admin.user_id, 'dmca.dismiss', 'dmca_claim', claim_id)
+      return NextResponse.json({ success: true })
+    }
+
+    case 'suspend_user': {
+      const { user_id } = body
+      // In a real app, add a suspended_at column. For now, log it.
+      await auditLog(admin.user_id, 'user.suspend', 'user', user_id)
+      return NextResponse.json({ success: true })
+    }
+
+    default:
+      return NextResponse.json({ error: `Unknown action: ${action}` }, { status: 400 })
+  }
+}
+
+async function auditLog(userId: string, action: string, targetType: string, targetId: string) {
+  await query(
+    `INSERT INTO audit_log (user_id, action, target_type, target_id) VALUES ($1, $2, $3, $4)`,
+    [userId, action, targetType, targetId]
+  )
+}
diff --git a/src/app/api/auth/route.ts b/src/app/api/auth/route.ts
new file mode 100644
index 0000000..ce935c9
--- /dev/null
+++ b/src/app/api/auth/route.ts
@@ -0,0 +1,72 @@
+import { NextRequest, NextResponse } from 'next/server'
+import { query } from '@/lib/db'
+import { cookies } from 'next/headers'
+
+export async function POST(request: NextRequest) {
+  const body = await request.json()
+  const { action, email, password } = body
+
+  if (action === 'login') {
+    if (!email || !password) {
+      return NextResponse.json({ error: 'Email and password required' }, { status: 400 })
+    }
+
+    const result = await query(
+      `SELECT id, email, display_name, role
+       FROM users
+       WHERE email = $1 AND password_hash = crypt($2, password_hash)`,
+      [email, password]
+    )
+
+    if (result.rows.length === 0) {
+      return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 })
+    }
+
+    const user = result.rows[0]
+
+    // Create session
+    const session = await query(
+      `INSERT INTO sessions (user_id) VALUES ($1) RETURNING token, expires_at`,
+      [user.id]
+    )
+
+    const cookieStore = await cookies()
+    cookieStore.set('iwascute-session', session.rows[0].token, {
+      httpOnly: true,
+      sameSite: 'lax',
+      path: '/',
+      maxAge: 60 * 60 * 24 * 7, // 7 days
+    })
+
+    return NextResponse.json({ user })
+  }
+
+  if (action === 'logout') {
+    const cookieStore = await cookies()
+    const token = cookieStore.get('iwascute-session')?.value
+    if (token) {
+      await query('DELETE FROM sessions WHERE token = $1', [token])
+      cookieStore.delete('iwascute-session')
+    }
+    return NextResponse.json({ success: true })
+  }
+
+  return NextResponse.json({ error: 'Unknown action' }, { status: 400 })
+}
+
+export async function GET() {
+  const cookieStore = await cookies()
+  const token = cookieStore.get('iwascute-session')?.value
+  if (!token) {
+    return NextResponse.json({ user: null })
+  }
+
+  const result = await query(
+    `SELECT u.id, u.email, u.display_name, u.role
+     FROM sessions s JOIN users u ON s.user_id = u.id
+     WHERE s.token = $1 AND s.expires_at > NOW()`,
+    [token]
+  )
+
+  return NextResponse.json({ user: result.rows[0] || null })
+}
diff --git a/src/app/browse/page.tsx b/src/app/browse/page.tsx
new file mode 100644
index 0000000..db15a45
--- /dev/null
+++ b/src/app/browse/page.tsx
@@ -0,0 +1,281 @@
+'use client'
+
+import { useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import {
+  Search,
+  SlidersHorizontal,
+  Grid3X3,
+  LayoutList,
+  ShieldCheck,
+  ChevronDown,
+  ImageIcon,
+  Tag,
+  Calendar,
+} from 'lucide-react'
+import Navbar from '@/components/shared/Navbar'
+
+type ViewMode = 'grid' | 'list'
+type LicenseFilter = 'all' | 'editorial' | 'commercial' | 'ai-training'
+
+const LICENSE_FILTERS: { id: LicenseFilter; label: string }[] = [
+  { id: 'all', label: 'All' },
+  { id: 'editorial', label: 'Editorial' },
+  { id: 'commercial', label: 'Commercial' },
+  { id: 'ai-training', label: 'AI Training' },
+]
+
+// Placeholder categories for when photos are available
+const CATEGORIES = [
+  'All Photos',
+  'Baby & Toddler',
+  'School Age',
+  'Teenager',
+  'Family',
+  'Holidays',
+  'Outdoor',
+  'Portraits',
+]
+
+function SearchBar({
+  value,
+  onChange,
+}: {
+  value: string
+  onChange: (v: string) => void
+}) {
+  return (
+    <div className="relative flex-1">
+      <Search
+        size={16}
+        className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none"
+        style={{ color: 'var(--color-warm-gray)' }}
+      />
+      <input
+        type="text"
+        value={value}
+        onChange={(e) => onChange(e.target.value)}
+        placeholder="Search photos by keyword, era, or description..."
+        className="w-full pl-11 pr-4 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
+        style={{
+          background: 'rgba(255,255,255,0.8)',
+          borderColor: 'var(--color-cream-dark)',
+          color: 'var(--color-brown)',
+        }}
+      />
+    </div>
+  )
+}
+
+function EmptyMarketplace() {
+  return (
+    <div
+      className="flex flex-col items-center justify-center gap-8 py-24 text-center"
+    >
+      {/* Decorative placeholder grid */}
+      <div className="grid grid-cols-3 gap-3 opacity-30">
+        {Array.from({ length: 9 }).map((_, i) => (
+          <div
+            key={i}
+            className="w-24 h-24 md:w-32 md:h-32 rounded-2xl flex items-center justify-center"
+            style={{ background: 'var(--color-cream-dark)' }}
+          >
+            <ImageIcon size={24} style={{ color: 'var(--color-warm-gray)' }} />
+          </div>
+        ))}
+      </div>
+
+      <div className="max-w-md">
+        <h2 className="text-2xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
+          The marketplace is coming soon
+        </h2>
+        <p className="mt-3 text-base leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+          Photos are being uploaded and reviewed. The first rights-cleared childhood photos
+          will appear here shortly. Every photo has both copyright and likeness rights verified.
+        </p>
+      </div>
+
+      {/* Trust badges */}
+      <div className="flex flex-wrap justify-center gap-4 mt-4">
+        {[
+          { icon: ShieldCheck, label: 'Dual-rights verified' },
+          { icon: Tag, label: 'Clear licensing tiers' },
+          { icon: Calendar, label: 'AI training available' },
+        ].map(({ icon: Icon, label }) => (
+          <div
+            key={label}
+            className="flex items-center gap-2 px-4 py-2.5 rounded-2xl"
+            style={{ background: 'rgba(181, 213, 197, 0.2)', border: '1px solid var(--color-sage)' }}
+          >
+            <Icon size={14} style={{ color: 'var(--color-sage-dark)' }} />
+            <span className="text-xs font-semibold" style={{ color: 'var(--color-sage-dark)' }}>
+              {label}
+            </span>
+          </div>
+        ))}
+      </div>
+    </div>
+  )
+}
+
+export default function BrowsePage() {
+  const [search, setSearch] = useState('')
+  const [viewMode, setViewMode] = useState<ViewMode>('grid')
+  const [licenseFilter, setLicenseFilter] = useState<LicenseFilter>('all')
+  const [activeCategory, setActiveCategory] = useState('All Photos')
+  const [showFilters, setShowFilters] = useState(false)
+
+  // No photos yet — marketplace is pre-launch
+  const photos: unknown[] = []
+
+  return (
+    <div className="min-h-screen" style={{ background: 'var(--color-cream)' }}>
+      <Navbar />
+
+      <div className="max-w-6xl mx-auto px-4 py-8 md:px-8 md:py-12">
+        {/* Header */}
+        <div className="mb-8">
+          <h1 className="text-3xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
+            Browse Photos
+          </h1>
+          <p className="mt-1 text-sm" style={{ color: 'var(--color-warm-gray)' }}>
+            Every photo is dual-rights cleared — copyright and likeness verified.
+          </p>
+        </div>
+
+        {/* Search + controls */}
+        <div className="flex flex-col gap-4 mb-6">
+          <div className="flex gap-3">
+            <SearchBar value={search} onChange={setSearch} />
+
+            <button
+              onClick={() => setShowFilters(!showFilters)}
+              className="flex items-center gap-2 px-4 py-3 rounded-2xl text-sm font-medium border transition-all hover:scale-105"
+              style={{
+                background: showFilters ? 'var(--color-peach)' : 'rgba(255,255,255,0.8)',
+                borderColor: showFilters ? 'var(--color-peach)' : 'var(--color-cream-dark)',
+                color: showFilters ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+              }}
+            >
+              <SlidersHorizontal size={14} />
+              Filters
+              <ChevronDown
+                size={12}
+                className="transition-transform"
+                style={{ transform: showFilters ? 'rotate(180deg)' : 'none' }}
+              />
+            </button>
+
+            {/* View toggle */}
+            <div
+              className="flex rounded-2xl border overflow-hidden"
+              style={{ borderColor: 'var(--color-cream-dark)' }}
+            >
+              <button
+                onClick={() => setViewMode('grid')}
+                className="px-3 py-3 transition-all"
+                style={{
+                  background: viewMode === 'grid' ? 'var(--color-cream-dark)' : 'rgba(255,255,255,0.8)',
+                  color: 'var(--color-warm-gray)',
+                }}
+                aria-label="Grid view"
+              >
+                <Grid3X3 size={16} />
+              </button>
+              <button
+                onClick={() => setViewMode('list')}
+                className="px-3 py-3 transition-all"
+                style={{
+                  background: viewMode === 'list' ? 'var(--color-cream-dark)' : 'rgba(255,255,255,0.8)',
+                  color: 'var(--color-warm-gray)',
+                }}
+                aria-label="List view"
+              >
+                <LayoutList size={16} />
+              </button>
+            </div>
+          </div>
+
+          {/* Filter panel */}
+          <AnimatePresence>
+            {showFilters && (
+              <motion.div
+                initial={{ opacity: 0, height: 0 }}
+                animate={{ opacity: 1, height: 'auto' }}
+                exit={{ opacity: 0, height: 0 }}
+                transition={{ duration: 0.2 }}
+                className="overflow-hidden"
+              >
+                <div
+                  className="p-5 rounded-2xl flex flex-col gap-4"
+                  style={{ background: 'rgba(255,255,255,0.6)', border: '1px solid var(--color-cream-dark)' }}
+                >
+                  {/* License type */}
+                  <div>
+                    <p className="text-xs font-bold tracking-widest uppercase mb-2" style={{ color: 'var(--color-warm-gray)' }}>
+                      License Type
+                    </p>
+                    <div className="flex gap-2 flex-wrap">
+                      {LICENSE_FILTERS.map((f) => (
+                        <button
+                          key={f.id}
+                          onClick={() => setLicenseFilter(f.id)}
+                          className="px-3.5 py-1.5 rounded-xl text-xs font-semibold transition-all hover:scale-105"
+                          style={{
+                            background: licenseFilter === f.id ? 'var(--color-peach)' : 'var(--color-cream-dark)',
+                            color: licenseFilter === f.id ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+                          }}
+                        >
+                          {f.label}
+                        </button>
+                      ))}
+                    </div>
+                  </div>
+
+                  {/* Category chips */}
+                  <div>
+                    <p className="text-xs font-bold tracking-widest uppercase mb-2" style={{ color: 'var(--color-warm-gray)' }}>
+                      Category
+                    </p>
+                    <div className="flex gap-2 flex-wrap">
+                      {CATEGORIES.map((cat) => (
+                        <button
+                          key={cat}
+                          onClick={() => setActiveCategory(cat)}
+                          className="px-3.5 py-1.5 rounded-xl text-xs font-semibold transition-all hover:scale-105"
+                          style={{
+                            background: activeCategory === cat ? 'var(--color-sage)' : 'var(--color-cream-dark)',
+                            color: activeCategory === cat ? 'white' : 'var(--color-warm-gray)',
+                          }}
+                        >
+                          {cat}
+                        </button>
+                      ))}
+                    </div>
+                  </div>
+                </div>
+              </motion.div>
+            )}
+          </AnimatePresence>
+        </div>
+
+        {/* Results */}
+        {photos.length === 0 ? (
+          <EmptyMarketplace />
+        ) : (
+          <div
+            className="grid gap-4"
+            style={{
+              gridTemplateColumns:
+                viewMode === 'grid'
+                  ? 'repeat(auto-fill, minmax(220px, 1fr))'
+                  : '1fr',
+            }}
+          >
+            {/* Photo cards will render here when marketplace launches */}
+          </div>
+        )}
+      </div>
+    </div>
+  )
+}
diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
new file mode 100644
index 0000000..f325b2d
--- /dev/null
+++ b/src/app/dashboard/page.tsx
@@ -0,0 +1,320 @@
+'use client'
+
+import { useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import {
+  Camera,
+  DollarSign,
+  Clock,
+  CheckCircle2,
+  AlertCircle,
+  Eye,
+  TrendingUp,
+  Plus,
+  ChevronRight,
+  Shield,
+  ImageIcon,
+} from 'lucide-react'
+import Link from 'next/link'
+import Navbar from '@/components/shared/Navbar'
+
+const DEMO_STATS = [
+  { label: 'Total Photos', value: '0', icon: Camera, color: 'var(--color-peach)' },
+  { label: 'Cleared', value: '0', icon: CheckCircle2, color: 'var(--color-sage-dark)' },
+  { label: 'Pending Review', value: '0', icon: Clock, color: 'var(--color-warm-gray)' },
+  { label: 'Total Earnings', value: '$0.00', icon: DollarSign, color: 'var(--color-peach-dark)' },
+]
+
+const DEMO_UPLOADS: {
+  id: string
+  name: string
+  status: 'pending' | 'cleared' | 'rejected'
+  submittedAt: string
+  views: number
+  earnings: number
+}[] = []
+
+type Tab = 'overview' | 'photos' | 'earnings'
+
+function StatCard({
+  label,
+  value,
+  icon: Icon,
+  color,
+}: {
+  label: string
+  value: string
+  icon: typeof Camera
+  color: string
+}) {
+  return (
+    <div
+      className="flex items-center gap-4 p-5 rounded-3xl"
+      style={{ background: 'rgba(255,255,255,0.6)', border: '1px solid var(--color-cream-dark)' }}
+    >
+      <div
+        className="w-12 h-12 rounded-2xl flex items-center justify-center shrink-0"
+        style={{ background: color, opacity: 0.9 }}
+      >
+        <Icon size={22} style={{ color: 'white' }} />
+      </div>
+      <div>
+        <p className="text-2xl font-black tracking-tight" style={{ color: 'var(--color-brown)' }}>
+          {value}
+        </p>
+        <p className="text-xs font-medium tracking-wide" style={{ color: 'var(--color-warm-gray)' }}>
+          {label}
+        </p>
+      </div>
+    </div>
+  )
+}
+
+function EmptyState() {
+  return (
+    <div className="flex flex-col items-center justify-center gap-6 py-20 text-center">
+      <div
+        className="w-20 h-20 rounded-3xl flex items-center justify-center"
+        style={{ background: 'var(--color-cream-dark)' }}
+      >
+        <ImageIcon size={36} style={{ color: 'var(--color-warm-gray)' }} />
+      </div>
+      <div className="max-w-sm">
+        <h3 className="text-xl font-bold tracking-tight" style={{ color: 'var(--color-brown)' }}>
+          No photos yet
+        </h3>
+        <p className="mt-2 text-sm leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+          Upload your first childhood photo to start building your portfolio and earning royalties.
+        </p>
+      </div>
+      <Link
+        href="/licensor"
+        className="flex items-center gap-2 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
+        style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+      >
+        <Plus size={16} />
+        Upload Photos
+      </Link>
+    </div>
+  )
+}
+
+function StatusBadge({ status }: { status: 'pending' | 'cleared' | 'rejected' }) {
+  const config = {
+    pending: { bg: 'rgba(255, 181, 167, 0.2)', color: 'var(--color-peach-dark)', label: 'Pending' },
+    cleared: { bg: 'rgba(181, 213, 197, 0.3)', color: 'var(--color-sage-dark)', label: 'Cleared' },
+    rejected: { bg: 'rgba(107, 112, 92, 0.15)', color: 'var(--color-warm-gray)', label: 'Rejected' },
+  }
+  const c = config[status]
+  return (
+    <span
+      className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-semibold"
+      style={{ background: c.bg, color: c.color }}
+    >
+      {status === 'pending' && <Clock size={10} />}
+      {status === 'cleared' && <CheckCircle2 size={10} />}
+      {status === 'rejected' && <AlertCircle size={10} />}
+      {c.label}
+    </span>
+  )
+}
+
+export default function DashboardPage() {
+  const [activeTab, setActiveTab] = useState<Tab>('overview')
+
+  const tabs: { id: Tab; label: string }[] = [
+    { id: 'overview', label: 'Overview' },
+    { id: 'photos', label: 'My Photos' },
+    { id: 'earnings', label: 'Earnings' },
+  ]
+
+  return (
+    <div className="min-h-screen" style={{ background: 'var(--color-cream)' }}>
+      <Navbar />
+
+      <div className="max-w-5xl mx-auto px-4 py-8 md:px-8 md:py-12">
+        {/* Header */}
+        <div className="flex items-center justify-between mb-8">
+          <div>
+            <h1 className="text-3xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
+              Dashboard
+            </h1>
+            <p className="mt-1 text-sm" style={{ color: 'var(--color-warm-gray)' }}>
+              Manage your photos, track rights clearance, and monitor earnings.
+            </p>
+          </div>
+          <Link
+            href="/licensor"
+            className="flex items-center gap-2 px-6 py-3 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
+            style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+          >
+            <Plus size={14} />
+            <span className="hidden sm:inline">Upload Photos</span>
+          </Link>
+        </div>
+
+        {/* Stats grid */}
+        <div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8">
+          {DEMO_STATS.map((stat) => (
+            <StatCard key={stat.label} {...stat} />
+          ))}
+        </div>
+
+        {/* Tab bar */}
+        <div
+          className="flex gap-1 p-1 rounded-2xl mb-8"
+          style={{ background: 'var(--color-cream-dark)' }}
+        >
+          {tabs.map((tab) => (
+            <button
+              key={tab.id}
+              onClick={() => setActiveTab(tab.id)}
+              className="relative flex-1 py-2.5 text-sm font-semibold rounded-xl transition-all"
+              style={{
+                color: activeTab === tab.id ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+              }}
+            >
+              {activeTab === tab.id && (
+                <motion.div
+                  layoutId="dashboard-tab"
+                  className="absolute inset-0 rounded-xl"
+                  style={{ background: 'white', boxShadow: '0 1px 3px rgba(0,0,0,0.08)' }}
+                  transition={{ type: 'spring', stiffness: 380, damping: 30 }}
+                />
+              )}
+              <span className="relative z-10">{tab.label}</span>
+            </button>
+          ))}
+        </div>
+
+        {/* Tab content */}
+        <AnimatePresence mode="wait">
+          {activeTab === 'overview' && (
+            <div key="overview">
+              {DEMO_UPLOADS.length === 0 ? (
+                <EmptyState />
+              ) : (
+                <div className="flex flex-col gap-4" />
+              )}
+
+              {/* How it works card */}
+              <div
+                className="mt-8 rounded-3xl p-8"
+                style={{ background: 'rgba(181, 213, 197, 0.15)', border: '1px solid var(--color-sage)' }}
+              >
+                <div className="flex items-center gap-3 mb-6">
+                  <Shield size={20} style={{ color: 'var(--color-sage-dark)' }} />
+                  <h3 className="text-base font-bold tracking-wide" style={{ color: 'var(--color-brown)' }}>
+                    How Dual-Rights Clearance Works
+                  </h3>
+                </div>
+                <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
+                  {[
+                    {
+                      step: '1',
+                      title: 'Copyright Clearance',
+                      desc: 'We verify the photographer or copyright owner has granted permission for your photo to be licensed.',
+                    },
+                    {
+                      step: '2',
+                      title: 'Likeness Release',
+                      desc: 'Your signed model release confirms you are the person depicted and consent to licensing.',
+                    },
+                    {
+                      step: '3',
+                      title: 'Start Earning',
+                      desc: 'Once both rights are cleared, your photo enters the marketplace. You earn royalties on every license.',
+                    },
+                  ].map(({ step, title, desc }) => (
+                    <div key={step} className="flex flex-col gap-2">
+                      <div
+                        className="w-8 h-8 rounded-full flex items-center justify-center text-xs font-black"
+                        style={{ background: 'var(--color-sage)', color: 'white' }}
+                      >
+                        {step}
+                      </div>
+                      <h4 className="text-sm font-bold" style={{ color: 'var(--color-brown)' }}>
+                        {title}
+                      </h4>
+                      <p className="text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+                        {desc}
+                      </p>
+                    </div>
+                  ))}
+                </div>
+              </div>
+            </div>
+          )}
+
+          {activeTab === 'photos' && (
+            <div key="photos">
+              {DEMO_UPLOADS.length === 0 ? (
+                <EmptyState />
+              ) : (
+                <div className="flex flex-col gap-3">
+                  {DEMO_UPLOADS.map((upload) => (
+                    <div
+                      key={upload.id}
+                      className="flex items-center gap-4 p-4 rounded-2xl transition-all hover:scale-[1.01]"
+                      style={{
+                        background: 'rgba(255,255,255,0.6)',
+                        border: '1px solid var(--color-cream-dark)',
+                      }}
+                    >
+                      <div
+                        className="w-14 h-14 rounded-xl flex items-center justify-center shrink-0"
+                        style={{ background: 'var(--color-cream-dark)' }}
+                      >
+                        <ImageIcon size={20} style={{ color: 'var(--color-warm-gray)' }} />
+                      </div>
+                      <div className="flex-1 min-w-0">
+                        <p className="text-sm font-semibold truncate" style={{ color: 'var(--color-brown)' }}>
+                          {upload.name}
+                        </p>
+                        <p className="text-xs" style={{ color: 'var(--color-warm-gray)' }}>
+                          {upload.submittedAt}
+                        </p>
+                      </div>
+                      <StatusBadge status={upload.status} />
+                      <div className="flex items-center gap-3 text-xs" style={{ color: 'var(--color-warm-gray)' }}>
+                        <span className="flex items-center gap-1">
+                          <Eye size={12} /> {upload.views}
+                        </span>
+                        <span className="flex items-center gap-1">
+                          <DollarSign size={12} /> ${upload.earnings.toFixed(2)}
+                        </span>
+                      </div>
+                      <ChevronRight size={16} style={{ color: 'var(--color-warm-gray)' }} />
+                    </div>
+                  ))}
+                </div>
+              )}
+            </div>
+          )}
+
+          {activeTab === 'earnings' && (
+            <div
+              key="earnings"
+              className="flex flex-col items-center gap-6 py-16 text-center"
+            >
+              <div
+                className="w-20 h-20 rounded-3xl flex items-center justify-center"
+                style={{ background: 'var(--color-cream-dark)' }}
+              >
+                <TrendingUp size={36} style={{ color: 'var(--color-warm-gray)' }} />
+              </div>
+              <div className="max-w-sm">
+                <h3 className="text-xl font-bold tracking-tight" style={{ color: 'var(--color-brown)' }}>
+                  No earnings yet
+                </h3>
+                <p className="mt-2 text-sm leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+                  Upload photos and get them cleared to start earning royalties from licenses.
+                </p>
+              </div>
+            </div>
+          )}
+        </AnimatePresence>
+      </div>
+    </div>
+  )
+}
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/src/app/favicon.ico differ
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..e676935
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,79 @@
+@import "tailwindcss";
+
+:root {
+  --background: #FFF1E6;
+  --foreground: #3D405B;
+
+  /* Brand palette */
+  --color-peach: #FFB5A7;
+  --color-peach-dark: #F4846F;
+  --color-sage: #B5D5C5;
+  --color-sage-dark: #8BBCAA;
+  --color-cream: #FFF1E6;
+  --color-cream-dark: #FFE0CB;
+  --color-warm-gray: #6B705C;
+  --color-brown: #3D405B;
+  --color-brown-light: #5A5F80;
+}
+
+@theme inline {
+  --color-background: var(--background);
+  --color-foreground: var(--foreground);
+  --font-sans: var(--font-geist-sans);
+  --font-mono: var(--font-geist-mono);
+
+  /* Expose brand colors to Tailwind utilities */
+  --color-peach: var(--color-peach);
+  --color-peach-dark: var(--color-peach-dark);
+  --color-sage: var(--color-sage);
+  --color-sage-dark: var(--color-sage-dark);
+  --color-cream: var(--color-cream);
+  --color-cream-dark: var(--color-cream-dark);
+  --color-warm-gray: var(--color-warm-gray);
+  --color-brown: var(--color-brown);
+  --color-brown-light: var(--color-brown-light);
+}
+
+body {
+  background: var(--background);
+  color: var(--foreground);
+  font-family: var(--font-geist-sans, Arial, Helvetica, sans-serif);
+  overflow-x: hidden;
+}
+
+/* Smooth scrolling + prevent horizontal overflow from decorative blobs */
+html {
+  scroll-behavior: smooth;
+  overflow-x: hidden;
+}
+
+/* Custom checkbox styling */
+input[type="checkbox"] {
+  accent-color: var(--color-peach);
+}
+
+/* File input hiding */
+input[type="file"] {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+/* Drag-over highlight */
+.drag-over {
+  border-color: var(--color-peach-dark) !important;
+  background-color: rgba(255, 181, 167, 0.1) !important;
+}
+
+/* Signature field font */
+.signature-field {
+  font-family: "Georgia", "Times New Roman", serif;
+  font-style: italic;
+  letter-spacing: 0.05em;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..67c7cd8
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,37 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+import VersionSwitcher from "@/components/shared/VersionSwitcher";
+
+const geistSans = Geist({
+  variable: "--font-geist-sans",
+  subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+  variable: "--font-geist-mono",
+  subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+  title: "I was Cute — Your Childhood Photos, Legally Licensed",
+  description: "Upload your childhood photos, clear the rights, and earn royalties.",
+};
+
+export default function RootLayout({
+  children,
+}: Readonly<{
+  children: React.ReactNode;
+}>) {
+  return (
+    <html
+      lang="en"
+      className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
+    >
+      <body className="min-h-full flex flex-col">
+        {children}
+        <VersionSwitcher />
+      </body>
+    </html>
+  );
+}
diff --git a/src/app/licensor/layout.tsx b/src/app/licensor/layout.tsx
new file mode 100644
index 0000000..619ee3a
--- /dev/null
+++ b/src/app/licensor/layout.tsx
@@ -0,0 +1,76 @@
+import type { Metadata } from 'next'
+import Link from 'next/link'
+
+export const metadata: Metadata = {
+  title: 'Upload Your Photos — I was Cute',
+  description:
+    'Upload your childhood photos, clear copyright and likeness rights, and start earning royalties.',
+}
+
+export default function LicensorLayout({
+  children,
+}: {
+  children: React.ReactNode
+}) {
+  return (
+    <div className="min-h-screen flex flex-col" style={{ background: 'var(--color-cream)' }}>
+      {/* Header */}
+      <header
+        className="sticky top-0 z-50 flex items-center justify-between px-6 py-4 md:px-12 border-b"
+        style={{
+          background: 'rgba(255, 241, 230, 0.92)',
+          borderColor: 'var(--color-cream-dark)',
+          backdropFilter: 'blur(12px)',
+          WebkitBackdropFilter: 'blur(12px)',
+        }}
+      >
+        <Link
+          href="/"
+          className="text-xl font-black tracking-tighter transition-opacity hover:opacity-70"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          i was cute
+        </Link>
+
+        <div
+          className="text-sm font-medium tracking-wide"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Licensor Upload
+        </div>
+      </header>
+
+      {/* Page content */}
+      <div className="flex-1">{children}</div>
+
+      {/* Footer */}
+      <footer
+        className="py-6 px-6 text-center text-xs border-t"
+        style={{
+          color: 'var(--color-warm-gray)',
+          borderColor: 'var(--color-cream-dark)',
+        }}
+      >
+        <p>
+          By uploading, you agree to our{' '}
+          <Link
+            href="/terms"
+            className="underline hover:opacity-70 transition-opacity"
+            style={{ color: 'var(--color-peach-dark)' }}
+          >
+            Terms of Service
+          </Link>{' '}
+          and{' '}
+          <Link
+            href="/privacy"
+            className="underline hover:opacity-70 transition-opacity"
+            style={{ color: 'var(--color-peach-dark)' }}
+          >
+            Privacy Policy
+          </Link>
+          .
+        </p>
+      </footer>
+    </div>
+  )
+}
diff --git a/src/app/licensor/page.tsx b/src/app/licensor/page.tsx
new file mode 100644
index 0000000..08dbebc
--- /dev/null
+++ b/src/app/licensor/page.tsx
@@ -0,0 +1,95 @@
+'use client'
+
+import { useState } from 'react'
+import Stepper from '@/components/licensor/Stepper'
+import PhotoUpload from '@/components/licensor/PhotoUpload'
+import CopyrightInfo from '@/components/licensor/CopyrightInfo'
+import LikenessRelease from '@/components/licensor/LikenessRelease'
+import ReviewSubmit from '@/components/licensor/ReviewSubmit'
+import type { PhotoFile } from '@/components/licensor/PhotoUpload'
+import type { CopyrightData } from '@/components/licensor/CopyrightInfo'
+import type { LikenessData } from '@/components/licensor/LikenessRelease'
+
+const TOTAL_STEPS = 4
+
+function todayISOString() {
+  const d = new Date()
+  return d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
+}
+
+export default function LicensorPage() {
+  const [step, setStep] = useState(0)
+
+  // Step 1 — Photos
+  const [photos, setPhotos] = useState<PhotoFile[]>([])
+
+  // Step 2 — Copyright
+  const [copyrightInfo, setCopyrightInfo] = useState<CopyrightData>({
+    photographer: '',
+    relationship: '',
+    year: '',
+    hasPermission: '',
+    estateRights: false,
+    applyToAll: true,
+  })
+
+  // Step 3 — Likeness
+  const [likenessRelease, setLikenessRelease] = useState<LikenessData>({
+    isSubject: false,
+    isAdult: false,
+    editorial: false,
+    commercial: false,
+    aiTraining: false,
+    signature: '',
+    date: todayISOString(),
+  })
+
+  const next = () => setStep((s) => Math.min(s + 1, TOTAL_STEPS - 1))
+  const back = () => setStep((s) => Math.max(s - 1, 0))
+
+  return (
+    <div className="px-4 py-8 md:px-8 md:py-12 max-w-2xl mx-auto">
+      {/* Stepper */}
+      <Stepper currentStep={step} />
+
+      {/* Step content */}
+      <div className="mt-8">
+        {step === 0 && (
+          <PhotoUpload
+            photos={photos}
+            onPhotosChange={setPhotos}
+            onNext={next}
+          />
+        )}
+
+        {step === 1 && (
+          <CopyrightInfo
+            photos={photos}
+            copyrightInfo={copyrightInfo}
+            onCopyrightChange={setCopyrightInfo}
+            onNext={next}
+            onBack={back}
+          />
+        )}
+
+        {step === 2 && (
+          <LikenessRelease
+            likenessRelease={likenessRelease}
+            onLikenessChange={setLikenessRelease}
+            onNext={next}
+            onBack={back}
+          />
+        )}
+
+        {step === 3 && (
+          <ReviewSubmit
+            photos={photos}
+            copyrightInfo={copyrightInfo}
+            likenessRelease={likenessRelease}
+            onBack={back}
+          />
+        )}
+      </div>
+    </div>
+  )
+}
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
new file mode 100644
index 0000000..a4a2490
--- /dev/null
+++ b/src/app/login/page.tsx
@@ -0,0 +1,242 @@
+'use client'
+
+import { useState } from 'react'
+import { motion } from 'framer-motion'
+import { Mail, Lock, Eye, EyeOff, ArrowRight } from 'lucide-react'
+import Link from 'next/link'
+
+export default function LoginPage() {
+  const [email, setEmail] = useState('')
+  const [password, setPassword] = useState('')
+  const [showPassword, setShowPassword] = useState(false)
+  const [mode, setMode] = useState<'login' | 'signup'>('login')
+
+  return (
+    <div
+      className="min-h-screen flex flex-col items-center justify-center px-4 py-12 relative overflow-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)' }}
+      />
+
+      {/* Logo */}
+      <div className="mb-10 text-center">
+        <Link
+          href="/"
+          className="text-3xl font-black tracking-tighter transition-opacity hover:opacity-70"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          i was cute
+        </Link>
+      </div>
+
+      {/* Card */}
+      <div
+        className="relative z-10 w-full max-w-sm rounded-3xl p-8"
+        style={{ background: 'rgba(255,255,255,0.7)', border: '1px solid var(--color-cream-dark)' }}
+      >
+        {/* Mode toggle */}
+        <div
+          className="flex gap-1 p-1 rounded-2xl mb-8"
+          style={{ background: 'var(--color-cream-dark)' }}
+        >
+          {(['login', 'signup'] as const).map((m) => (
+            <button
+              key={m}
+              onClick={() => setMode(m)}
+              className="relative flex-1 py-2.5 text-sm font-semibold rounded-xl transition-all"
+              style={{
+                color: mode === m ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+              }}
+            >
+              {mode === m && (
+                <motion.div
+                  layoutId="auth-tab"
+                  className="absolute inset-0 rounded-xl"
+                  style={{ background: 'white', boxShadow: '0 1px 3px rgba(0,0,0,0.08)' }}
+                  transition={{ type: 'spring', stiffness: 380, damping: 30 }}
+                />
+              )}
+              <span className="relative z-10">
+                {m === 'login' ? 'Sign In' : 'Sign Up'}
+              </span>
+            </button>
+          ))}
+        </div>
+
+        <form
+          onSubmit={(e) => {
+            e.preventDefault()
+            // TODO: wire up NextAuth.js
+          }}
+          className="flex flex-col gap-4"
+        >
+          {/* Email */}
+          <div className="flex flex-col gap-1.5">
+            <label
+              htmlFor="email"
+              className="text-sm font-semibold"
+              style={{ color: 'var(--color-brown)' }}
+            >
+              Email
+            </label>
+            <div className="relative">
+              <Mail
+                size={16}
+                className="absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none"
+                style={{ color: 'var(--color-warm-gray)' }}
+              />
+              <input
+                id="email"
+                type="email"
+                value={email}
+                onChange={(e) => setEmail(e.target.value)}
+                placeholder="you@example.com"
+                autoComplete="email"
+                required
+                className="w-full pl-10 pr-4 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
+                style={{
+                  background: 'rgba(255,255,255,0.8)',
+                  borderColor: 'var(--color-cream-dark)',
+                  color: 'var(--color-brown)',
+                }}
+              />
+            </div>
+          </div>
+
+          {/* Password */}
+          <div className="flex flex-col gap-1.5">
+            <label
+              htmlFor="password"
+              className="text-sm font-semibold"
+              style={{ color: 'var(--color-brown)' }}
+            >
+              Password
+            </label>
+            <div className="relative">
+              <Lock
+                size={16}
+                className="absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none"
+                style={{ color: 'var(--color-warm-gray)' }}
+              />
+              <input
+                id="password"
+                type={showPassword ? 'text' : 'password'}
+                value={password}
+                onChange={(e) => setPassword(e.target.value)}
+                placeholder={mode === 'signup' ? 'Create a password' : 'Your password'}
+                autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}
+                required
+                className="w-full pl-10 pr-12 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
+                style={{
+                  background: 'rgba(255,255,255,0.8)',
+                  borderColor: 'var(--color-cream-dark)',
+                  color: 'var(--color-brown)',
+                }}
+              />
+              <button
+                type="button"
+                onClick={() => setShowPassword(!showPassword)}
+                className="absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-lg transition-all hover:scale-110"
+                style={{ color: 'var(--color-warm-gray)' }}
+                aria-label={showPassword ? 'Hide password' : 'Show password'}
+              >
+                {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
+              </button>
+            </div>
+          </div>
+
+          {mode === 'signup' && (
+            <motion.div
+              initial={{ opacity: 0, height: 0 }}
+              animate={{ opacity: 1, height: 'auto' }}
+              className="overflow-hidden"
+            >
+              <label className="flex items-start gap-3 cursor-pointer">
+                <input
+                  type="checkbox"
+                  required
+                  className="mt-1"
+                />
+                <p className="text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+                  I am 18 years or older and agree to the{' '}
+                  <Link href="/terms" className="underline" style={{ color: 'var(--color-peach-dark)' }}>
+                    Terms of Service
+                  </Link>{' '}
+                  and{' '}
+                  <Link href="/privacy" className="underline" style={{ color: 'var(--color-peach-dark)' }}>
+                    Privacy Policy
+                  </Link>
+                  .
+                </p>
+              </label>
+            </motion.div>
+          )}
+
+          {/* Submit */}
+          <motion.button
+            whileHover={{ scale: 1.02 }}
+            whileTap={{ scale: 0.98 }}
+            type="submit"
+            className="flex items-center justify-center gap-2 w-full py-3.5 rounded-3xl text-sm font-semibold tracking-wide mt-2 transition-all"
+            style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+          >
+            {mode === 'login' ? 'Sign In' : 'Create Account'}
+            <ArrowRight size={14} />
+          </motion.button>
+        </form>
+
+        {/* Divider */}
+        <div className="flex items-center gap-3 my-6">
+          <div className="flex-1 h-px" style={{ background: 'var(--color-cream-dark)' }} />
+          <span className="text-xs font-medium" style={{ color: 'var(--color-warm-gray)' }}>
+            or
+          </span>
+          <div className="flex-1 h-px" style={{ background: 'var(--color-cream-dark)' }} />
+        </div>
+
+        {/* Google OAuth */}
+        <button
+          className="flex items-center justify-center gap-3 w-full py-3 rounded-2xl text-sm font-medium border transition-all hover:scale-[1.02]"
+          style={{
+            background: 'rgba(255,255,255,0.8)',
+            borderColor: 'var(--color-cream-dark)',
+            color: 'var(--color-brown)',
+          }}
+        >
+          <svg width="18" height="18" viewBox="0 0 24 24">
+            <path
+              d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
+              fill="#4285F4"
+            />
+            <path
+              d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
+              fill="#34A853"
+            />
+            <path
+              d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
+              fill="#FBBC05"
+            />
+            <path
+              d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
+              fill="#EA4335"
+            />
+          </svg>
+          Continue with Google
+        </button>
+      </div>
+
+      {/* Footer note */}
+      <p className="mt-8 text-xs text-center" style={{ color: 'var(--color-warm-gray)' }}>
+        Must be 18+ to create an account. We never sell your data.
+      </p>
+    </div>
+  )
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..dce293f
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,284 @@
+'use client'
+
+import Link from 'next/link'
+import { motion } from 'framer-motion'
+import { ArrowRight, Moon, Layers, Camera } from 'lucide-react'
+
+const VERSIONS = [
+  {
+    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',
+  },
+  {
+    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',
+  },
+  {
+    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',
+  },
+]
+
+function VersionCard({
+  version,
+  index,
+}: {
+  version: (typeof VERSIONS)[number]
+  index: number
+}) {
+  const Icon = version.icon
+  const isDark = index === 0
+
+  return (
+    <motion.div
+      whileHover={{ y: -8, scale: 1.02 }}
+      whileTap={{ scale: 0.98 }}
+      className="relative group"
+    >
+      <Link href={version.path} className="block no-underline">
+        <div
+          className="rounded-3xl overflow-hidden transition-shadow duration-300 group-hover:shadow-2xl"
+          style={{
+            border: isDark ? '1px solid rgba(255,255,255,0.1)' : '1px solid var(--color-cream-dark)',
+          }}
+        >
+          {/* Preview header — simulates the page's vibe */}
+          <div
+            className="relative h-56 flex flex-col items-center justify-center gap-3 px-6"
+            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)',
+            }}
+          >
+            {/* 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>
+              </>
+            )}
+
+            {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>
+            )}
+
+            {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>
+            )}
+          </div>
+
+          {/* Card body */}
+          <div
+            className="px-6 py-5"
+            style={{
+              background: isDark ? '#1a1d2e' : 'white',
+            }}
+          >
+            <div className="flex items-center justify-between mb-3">
+              <div className="flex items-center gap-2">
+                <div
+                  className="w-8 h-8 rounded-xl flex items-center justify-center"
+                  style={{
+                    background: isDark ? 'rgba(255,181,167,0.15)' : 'var(--color-cream-dark)',
+                  }}
+                >
+                  <Icon size={16} style={{ color: isDark ? '#FFB5A7' : 'var(--color-brown)' }} />
+                </div>
+                <h3
+                  className="text-base font-bold"
+                  style={{ color: isDark ? 'white' : 'var(--color-brown)' }}
+                >
+                  {version.label}
+                </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 mb-4"
+              style={{ color: isDark ? 'rgba(255,255,255,0.5)' : 'var(--color-warm-gray)' }}
+            >
+              {version.desc}
+            </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>
+  )
+}
+
+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)' }}
+        >
+          i was{' '}
+          <span style={{ color: 'var(--color-peach-dark)' }}>cute</span>
+        </h1>
+        <p
+          className="mt-4 text-lg md:text-xl font-light"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Choose your experience
+        </p>
+        <p
+          className="mt-2 text-sm max-w-md mx-auto"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          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>
+    </main>
+  )
+}
diff --git a/src/app/v1/page.tsx b/src/app/v1/page.tsx
new file mode 100644
index 0000000..10db494
--- /dev/null
+++ b/src/app/v1/page.tsx
@@ -0,0 +1,259 @@
+'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>
+  )
+}
+
+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>
+  )
+}
diff --git a/src/app/v2/page.tsx b/src/app/v2/page.tsx
new file mode 100644
index 0000000..451335f
--- /dev/null
+++ b/src/app/v2/page.tsx
@@ -0,0 +1,506 @@
+'use client'
+
+import Link from 'next/link'
+import { motion } from 'framer-motion'
+import { Camera, Shield, DollarSign, ArrowRight, Sparkles } from 'lucide-react'
+import clsx from 'clsx'
+
+// ---------------------------------------------------------------------------
+// AnimationStyles — injects CSS keyframes for blob motion + glass utilities.
+// Content is a static compile-time string with NO user-supplied data, so
+// rendering it via innerHTML carries zero XSS risk (same as any <style> tag
+// in a CSS file or styled-component). This is a standard Next.js / React
+// pattern for scoped global keyframes in a Client Component.
+// ---------------------------------------------------------------------------
+function AnimationStyles() {
+  const css = [
+    '@keyframes blob-float-a{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(40px,-60px) scale(1.08)}}',
+    '@keyframes blob-float-b{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(-50px,40px) scale(1.05)}}',
+    '@keyframes blob-float-c{0%{transform:translate(0px,0px) scale(1.02)}100%{transform:translate(30px,50px) scale(0.96)}}',
+    '@keyframes blob-float-d{0%{transform:translate(0px,0px) scale(1)}100%{transform:translate(-30px,-40px) scale(1.06)}}',
+    '@keyframes blob-float-e{0%{transform:translate(0px,0px) scale(0.98)}100%{transform:translate(20px,30px) scale(1.04)}}',
+    '@keyframes shimmer{0%{background-position:-200% center}100%{background-position:200% center}}',
+    '.blob-a{animation:blob-float-a 18s ease-in-out infinite alternate}',
+    '.blob-b{animation:blob-float-b 22s ease-in-out infinite alternate}',
+    '.blob-c{animation:blob-float-c 16s ease-in-out infinite alternate}',
+    '.blob-d{animation:blob-float-d 20s ease-in-out infinite alternate}',
+    '.blob-e{animation:blob-float-e 14s ease-in-out infinite alternate}',
+    '.glass{background:rgba(255,255,255,0.38);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,0.32)}',
+    '.glass-dark{background:rgba(255,255,255,0.22);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(255,255,255,0.24)}',
+    '.shimmer-text{background:linear-gradient(90deg,#3D405B 0%,#F4846F 30%,#8BBCAA 55%,#3D405B 80%);background-size:200% auto;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:shimmer 5s linear infinite}',
+    '.btn-primary{background:linear-gradient(135deg,#FFB5A7 0%,#F4846F 100%);transition:transform 0.2s ease,box-shadow 0.2s ease,filter 0.2s ease}',
+    '.btn-primary:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 12px 40px rgba(244,132,111,0.45);filter:brightness(1.05)}',
+    '.btn-secondary{transition:transform 0.2s ease,box-shadow 0.2s ease,background 0.2s ease}',
+    '.btn-secondary:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 12px 32px rgba(139,188,170,0.35);background:rgba(255,255,255,0.55)}',
+    '.divider-v{width:1px;background:rgba(61,64,91,0.12);align-self:stretch}',
+  ].join('\n')
+
+  // React renders <style> tags safely — this is static content, not user data.
+  return <style>{css}</style>
+}
+
+// ---------------------------------------------------------------------------
+// Animated gradient mesh — five large blobs using pure CSS keyframes.
+// Fixed so it covers the entire viewport regardless of page scroll length.
+// ---------------------------------------------------------------------------
+function GradientMeshBackground() {
+  return (
+    <div
+      aria-hidden="true"
+      className="pointer-events-none fixed inset-0 overflow-hidden"
+      style={{ zIndex: 0 }}
+    >
+      {/* Blob A — top-left, peach */}
+      <div
+        className="blob-a absolute -top-24 -left-24 w-[520px] h-[520px] rounded-full blur-3xl"
+        style={{ background: '#FFB5A7', opacity: 0.42 }}
+      />
+      {/* Blob B — top-right, sage */}
+      <div
+        className="blob-b absolute -top-12 right-0 w-[480px] h-[480px] rounded-full blur-3xl"
+        style={{ background: '#B5D5C5', opacity: 0.38 }}
+      />
+      {/* Blob C — center, cream-dark */}
+      <div
+        className="blob-c absolute top-1/3 left-1/4 w-[400px] h-[400px] rounded-full blur-3xl"
+        style={{ background: '#FFE0CB', opacity: 0.50 }}
+      />
+      {/* Blob D — bottom-left, peach-dark */}
+      <div
+        className="blob-d absolute bottom-0 -left-12 w-[440px] h-[440px] rounded-full blur-3xl"
+        style={{ background: '#F4846F', opacity: 0.28 }}
+      />
+      {/* Blob E — bottom-right, sage-dark */}
+      <div
+        className="blob-e absolute -bottom-16 right-8 w-[460px] h-[460px] rounded-full blur-3xl"
+        style={{ background: '#8BBCAA', opacity: 0.35 }}
+      />
+    </div>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// NavBar
+// ---------------------------------------------------------------------------
+function NavBar() {
+  return (
+    <nav
+      className="glass relative z-20 flex items-center justify-between px-6 py-4 md:px-12 mx-4 mt-4 rounded-2xl"
+      style={{ color: 'var(--color-brown)' }}
+    >
+      <div className="flex items-center gap-2">
+        <Sparkles
+          size={18}
+          strokeWidth={1.8}
+          style={{ color: 'var(--color-peach-dark)' }}
+        />
+        <span
+          className="text-xl font-black tracking-tighter"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          i was cute
+        </span>
+      </div>
+
+      <div
+        className="hidden sm:flex items-center gap-6 text-sm font-medium"
+        style={{ color: 'var(--color-warm-gray)' }}
+      >
+        <Link href="/browse" className="hover:opacity-70 transition-opacity">
+          Browse
+        </Link>
+        <Link href="/licensor" className="hover:opacity-70 transition-opacity">
+          Upload
+        </Link>
+        <Link href="/login" className="hover:opacity-70 transition-opacity">
+          Login
+        </Link>
+      </div>
+
+      <Link
+        href="/licensor"
+        className="btn-primary inline-flex items-center gap-1.5 px-5 py-2.5 rounded-xl text-sm font-semibold"
+        style={{ color: 'white' }}
+      >
+        Get Started
+        <ArrowRight size={14} strokeWidth={2.5} />
+      </Link>
+    </nav>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// Hero Card — glassmorphism panel, centered on the page
+// ---------------------------------------------------------------------------
+function HeroCard() {
+  return (
+    <section className="relative z-10 flex flex-col items-center justify-center px-4 pt-16 pb-8 md:pt-24">
+      <motion.div
+        whileHover={{ scale: 1.005 }}
+        transition={{ type: 'spring', stiffness: 300, damping: 30 }}
+        className="glass w-full max-w-3xl rounded-3xl px-8 py-14 md:px-16 md:py-20 flex flex-col items-center text-center gap-7"
+        style={{
+          boxShadow:
+            '0 32px 80px rgba(61,64,91,0.10), 0 0 0 1px rgba(255,255,255,0.30)',
+        }}
+      >
+        {/* Badge */}
+        <div
+          className="glass-dark inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-xs font-semibold tracking-widest uppercase"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          <span
+            className="w-1.5 h-1.5 rounded-full"
+            style={{ background: 'var(--color-peach-dark)' }}
+          />
+          Childhood Photo Marketplace
+        </div>
+
+        {/* Headline */}
+        <h1 className="text-6xl md:text-8xl font-black tracking-tighter leading-[0.9] select-none">
+          <span className="shimmer-text">i was</span>
+          <br />
+          <span style={{ color: 'var(--color-peach-dark)' }}>cute</span>
+        </h1>
+
+        {/* Tagline */}
+        <p
+          className="text-xl md:text-2xl font-light tracking-wide max-w-md leading-relaxed"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Your childhood photos, legally licensed and earning for you.
+        </p>
+
+        {/* Description */}
+        <p
+          className="text-base max-w-lg leading-relaxed"
+          style={{ color: 'var(--color-warm-gray)', opacity: 0.85 }}
+        >
+          Turn nostalgic memories into royalties. Upload, verify rights, and
+          let advertisers, filmmakers, and designers celebrate the real you —
+          at your most adorable.
+        </p>
+
+        {/* CTA buttons */}
+        <div className="flex flex-col sm:flex-row gap-3 mt-2 w-full sm:w-auto">
+          <Link
+            href="/licensor"
+            className="btn-primary inline-flex items-center justify-center gap-2 px-8 py-4 rounded-2xl text-base font-bold tracking-wide"
+            style={{ color: 'white' }}
+          >
+            <Camera size={18} strokeWidth={2} />
+            Upload Your Photos
+          </Link>
+          <Link
+            href="/browse"
+            className="btn-secondary glass-dark inline-flex items-center justify-center gap-2 px-8 py-4 rounded-2xl text-base font-semibold tracking-wide"
+            style={{ color: 'var(--color-brown)' }}
+          >
+            Browse Library
+            <ArrowRight size={16} strokeWidth={2} />
+          </Link>
+        </div>
+
+        {/* Trust micro-copy */}
+        <p
+          className="text-xs font-medium tracking-wide"
+          style={{ color: 'var(--color-warm-gray)', opacity: 0.6 }}
+        >
+          No credit card required&nbsp;&middot;&nbsp;Free to upload&nbsp;&middot;&nbsp;You
+          keep all rights
+        </p>
+      </motion.div>
+    </section>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// Stats Bar — glassmorphism strip with 3 metrics
+// ---------------------------------------------------------------------------
+const STATS = [
+  { value: '10K+', label: 'Photos Licensed' },
+  { value: '500+', label: 'Verified Licensors' },
+  { value: '100%', label: 'Rights Verified' },
+] as const
+
+function StatsBar() {
+  return (
+    <section className="relative z-10 px-4 py-4">
+      <div
+        className="glass mx-auto max-w-2xl rounded-2xl px-6 py-5 flex items-center justify-around gap-4"
+        style={{ boxShadow: '0 8px 32px rgba(61,64,91,0.08)' }}
+      >
+        {STATS.map((stat, i) => (
+          <div key={stat.label} className="flex items-center gap-4">
+            <div className="flex flex-col items-center gap-0.5">
+              <span
+                className="text-2xl md:text-3xl font-black tracking-tight"
+                style={{ color: 'var(--color-peach-dark)' }}
+              >
+                {stat.value}
+              </span>
+              <span
+                className="text-xs font-semibold tracking-widest uppercase"
+                style={{ color: 'var(--color-warm-gray)', opacity: 0.75 }}
+              >
+                {stat.label}
+              </span>
+            </div>
+            {i < STATS.length - 1 && (
+              <div className="divider-v hidden sm:block" />
+            )}
+          </div>
+        ))}
+      </div>
+    </section>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// Feature Grid — three glassmorphism cards
+// ---------------------------------------------------------------------------
+interface Feature {
+  icon: React.ReactNode
+  title: string
+  description: string
+  accent: string
+  step: string
+}
+
+const FEATURES: Feature[] = [
+  {
+    icon: <Camera size={28} strokeWidth={1.6} />,
+    title: 'Upload Photos',
+    description:
+      'Drag and drop your childhood photos in any format. We handle compression, metadata extraction, and secure storage.',
+    accent: '#FFB5A7',
+    step: '01',
+  },
+  {
+    icon: <Shield size={28} strokeWidth={1.6} />,
+    title: 'Verify Rights',
+    description:
+      'Our guided rights-clearance wizard walks you through copyright and likeness release in under five minutes.',
+    accent: '#B5D5C5',
+    step: '02',
+  },
+  {
+    icon: <DollarSign size={28} strokeWidth={1.6} />,
+    title: 'Earn Royalties',
+    description:
+      'When your photo is licensed by a brand or filmmaker, royalties land directly in your account. Automatically.',
+    accent: '#FFE0CB',
+    step: '03',
+  },
+]
+
+function FeatureGrid() {
+  return (
+    <section className="relative z-10 px-4 py-10 md:py-16">
+      <div className="max-w-4xl mx-auto">
+        {/* Section header */}
+        <div className="text-center mb-10">
+          <h2
+            className="text-3xl md:text-4xl font-black tracking-tight mb-3"
+            style={{ color: 'var(--color-brown)' }}
+          >
+            How it works
+          </h2>
+          <p
+            className="text-base max-w-md mx-auto leading-relaxed"
+            style={{ color: 'var(--color-warm-gray)', opacity: 0.8 }}
+          >
+            Three steps from photo to paycheck.
+          </p>
+        </div>
+
+        {/* Cards */}
+        <div className="grid grid-cols-1 md:grid-cols-3 gap-5">
+          {FEATURES.map((feature) => (
+            <motion.div
+              key={feature.title}
+              whileHover={{ y: -6, scale: 1.01 }}
+              transition={{ type: 'spring', stiffness: 400, damping: 28 }}
+              className={clsx(
+                'glass rounded-3xl p-8 flex flex-col gap-5 cursor-default',
+              )}
+              style={{ boxShadow: '0 8px 32px rgba(61,64,91,0.07)' }}
+            >
+              {/* Icon bubble */}
+              <div
+                className="w-14 h-14 rounded-2xl flex items-center justify-center flex-shrink-0"
+                style={{ background: feature.accent, color: 'var(--color-brown)' }}
+              >
+                {feature.icon}
+              </div>
+
+              {/* Step label */}
+              <span
+                className="text-xs font-bold tracking-widest uppercase"
+                style={{ color: 'var(--color-warm-gray)', opacity: 0.45 }}
+              >
+                Step {feature.step}
+              </span>
+
+              <div className="flex flex-col gap-2">
+                <h3
+                  className="text-lg font-bold tracking-tight"
+                  style={{ color: 'var(--color-brown)' }}
+                >
+                  {feature.title}
+                </h3>
+                <p
+                  className="text-sm leading-relaxed"
+                  style={{ color: 'var(--color-warm-gray)', opacity: 0.85 }}
+                >
+                  {feature.description}
+                </p>
+              </div>
+            </motion.div>
+          ))}
+        </div>
+      </div>
+    </section>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// CTA Banner — second conversion touchpoint
+// ---------------------------------------------------------------------------
+function CallToActionBanner() {
+  return (
+    <section className="relative z-10 px-4 py-8 md:py-12">
+      <motion.div
+        whileHover={{ scale: 1.008 }}
+        transition={{ type: 'spring', stiffness: 300, damping: 30 }}
+        className="glass max-w-3xl mx-auto rounded-3xl px-8 py-12 md:px-16 flex flex-col items-center text-center gap-6"
+        style={{
+          boxShadow:
+            '0 20px 60px rgba(244,132,111,0.14), 0 0 0 1px rgba(255,255,255,0.28)',
+          background: 'rgba(255,241,230,0.45)',
+        }}
+      >
+        <h2
+          className="text-3xl md:text-4xl font-black tracking-tight"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Ready to be remembered?
+        </h2>
+        <p
+          className="text-base max-w-md leading-relaxed"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Join hundreds of people who have already turned their most charming
+          memories into a passive income stream.
+        </p>
+        <Link
+          href="/licensor"
+          className="btn-primary inline-flex items-center gap-2 px-10 py-4 rounded-2xl text-base font-bold"
+          style={{ color: 'white' }}
+        >
+          Start Uploading — It&apos;s Free
+          <ArrowRight size={16} strokeWidth={2.5} />
+        </Link>
+      </motion.div>
+    </section>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// Footer
+// ---------------------------------------------------------------------------
+const FOOTER_LINKS = [
+  { href: '/browse', label: 'Browse' },
+  { href: '/licensor', label: 'Upload' },
+  { href: '/login', label: 'Login' },
+  { href: '#', label: 'Privacy' },
+  { href: '#', label: 'Terms' },
+] as const
+
+function Footer() {
+  return (
+    <footer className="relative z-10 px-6 py-10 flex flex-col items-center gap-4">
+      <div className="flex items-center gap-2 mb-1">
+        <Sparkles
+          size={14}
+          strokeWidth={1.8}
+          style={{ color: 'var(--color-peach-dark)' }}
+        />
+        <span
+          className="text-sm font-black tracking-tighter"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          i was cute
+        </span>
+      </div>
+
+      <nav className="flex flex-wrap items-center justify-center gap-5">
+        {FOOTER_LINKS.map((link) => (
+          <Link
+            key={link.label}
+            href={link.href}
+            className="text-xs font-medium hover:opacity-70 transition-opacity"
+            style={{ color: 'var(--color-warm-gray)' }}
+          >
+            {link.label}
+          </Link>
+        ))}
+      </nav>
+
+      <p
+        className="text-xs"
+        style={{ color: 'var(--color-warm-gray)', opacity: 0.55 }}
+      >
+        &copy; {new Date().getFullYear()} I was Cute. All rights reserved.
+      </p>
+    </footer>
+  )
+}
+
+// ---------------------------------------------------------------------------
+// Page — root export
+// ---------------------------------------------------------------------------
+
+/**
+ * /v2 — Glassmorphism + Animated Gradient Mesh landing page.
+ *
+ * Usage: navigate to /v2 in the running Next.js dev server.
+ *
+ * Architecture notes:
+ * - 'use client' is required for framer-motion whileHover.
+ * - Blob animations are pure CSS (@keyframes), zero JS runtime cost.
+ * - Glass utility classes are injected via <style>{css}</style> — static
+ *   string, no XSS surface — using the same mechanism as styled-jsx.
+ * - framer-motion is used ONLY for whileHover on interactive cards.
+ *   No initial/animate props to avoid SSR hydration mismatches.
+ * - Fixed-position mesh background sits at z-index 0; page content at z-10+.
+ */
+export default function V2Page() {
+  return (
+    <>
+      <AnimationStyles />
+
+      {/*
+        Root wrapper: `position: relative` establishes a stacking context so
+        the fixed blobs render behind page content. overflow-x-hidden prevents
+        the translated blobs from creating a horizontal scrollbar.
+      */}
+      <div
+        className="relative min-h-screen flex flex-col overflow-x-hidden"
+        style={{ background: 'var(--color-cream)' }}
+      >
+        <GradientMeshBackground />
+
+        {/* Page content — stacks above the fixed mesh */}
+        <div className="relative z-10 flex flex-col flex-1">
+          <NavBar />
+          <HeroCard />
+          <StatsBar />
+          <FeatureGrid />
+          <CallToActionBanner />
+          <Footer />
+        </div>
+      </div>
+    </>
+  )
+}
diff --git a/src/app/v3/page.tsx b/src/app/v3/page.tsx
new file mode 100644
index 0000000..e44d36e
--- /dev/null
+++ b/src/app/v3/page.tsx
@@ -0,0 +1,417 @@
+'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>
+  )
+}
+
+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&apos;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' }}
+          >
+            &ldquo;
+          </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&apos;t even sure it would work — now I check my
+              dashboard every morning like it&apos;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' }}
+          >
+            &rdquo;
+          </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&apos;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">
+          &copy; 2025 I was Cute. All rights reserved. Your memories, your royalties.
+        </p>
+      </footer>
+    </main>
+  )
+}
diff --git a/src/components/licensor/CopyrightInfo.tsx b/src/components/licensor/CopyrightInfo.tsx
new file mode 100644
index 0000000..3a78505
--- /dev/null
+++ b/src/components/licensor/CopyrightInfo.tsx
@@ -0,0 +1,534 @@
+'use client'
+
+import { useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import { ChevronDown, ChevronLeft, ChevronRight, Info } from 'lucide-react'
+import clsx from 'clsx'
+import type { PhotoFile } from './PhotoUpload'
+
+export interface CopyrightData {
+  photographer: string
+  relationship: string
+  year: string
+  hasPermission: string
+  estateRights: boolean
+  applyToAll: boolean
+}
+
+interface CopyrightInfoProps {
+  photos: PhotoFile[]
+  copyrightInfo: CopyrightData
+  onCopyrightChange: (info: CopyrightData) => void
+  onNext: () => void
+  onBack: () => void
+}
+
+const RELATIONSHIP_OPTIONS = [
+  { value: '', label: 'Select who took this photo' },
+  { value: 'parent', label: 'My parent' },
+  { value: 'family', label: 'Other family member' },
+  { value: 'professional', label: 'Professional photographer' },
+  { value: 'unknown', label: "I don't know" },
+]
+
+const PERMISSION_OPTIONS = [
+  { value: '', label: 'Select an option' },
+  { value: 'yes', label: 'Yes, I have permission' },
+  { value: 'no', label: 'No, I do not have explicit permission' },
+  { value: 'deceased', label: 'The photographer is deceased' },
+]
+
+const CURRENT_YEAR = new Date().getFullYear()
+
+function SelectField({
+  label,
+  value,
+  options,
+  onChange,
+  required,
+  id,
+}: {
+  label: string
+  value: string
+  options: { value: string; label: string }[]
+  onChange: (v: string) => void
+  required?: boolean
+  id: string
+}) {
+  return (
+    <div className="flex flex-col gap-1.5">
+      <label
+        htmlFor={id}
+        className="text-sm font-semibold tracking-wide"
+        style={{ color: 'var(--color-brown)' }}
+      >
+        {label}
+        {required && (
+          <span className="ml-1" style={{ color: 'var(--color-peach-dark)' }} aria-hidden>
+            *
+          </span>
+        )}
+      </label>
+      <div className="relative">
+        <select
+          id={id}
+          value={value}
+          onChange={(e) => onChange(e.target.value)}
+          required={required}
+          className={clsx(
+            'w-full appearance-none px-4 py-3 pr-10 rounded-2xl text-sm',
+            'border outline-none transition-all duration-200',
+            'focus:ring-2'
+          )}
+          style={{
+            background: 'rgba(255,255,255,0.8)',
+            borderColor: 'var(--color-cream-dark)',
+            color: value ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+          }}
+        >
+          {options.map((opt) => (
+            <option key={opt.value} value={opt.value} disabled={opt.value === ''}>
+              {opt.label}
+            </option>
+          ))}
+        </select>
+        <ChevronDown
+          size={16}
+          className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none"
+          style={{ color: 'var(--color-warm-gray)' }}
+        />
+      </div>
+    </div>
+  )
+}
+
+function TextField({
+  label,
+  value,
+  onChange,
+  placeholder,
+  type = 'text',
+  min,
+  max,
+  id,
+  optional,
+}: {
+  label: string
+  value: string
+  onChange: (v: string) => void
+  placeholder?: string
+  type?: string
+  min?: number
+  max?: number
+  id: string
+  optional?: boolean
+}) {
+  return (
+    <div className="flex flex-col gap-1.5">
+      <label
+        htmlFor={id}
+        className="text-sm font-semibold tracking-wide"
+        style={{ color: 'var(--color-brown)' }}
+      >
+        {label}
+        {optional && (
+          <span className="ml-1.5 text-xs font-normal" style={{ color: 'var(--color-warm-gray)' }}>
+            (optional)
+          </span>
+        )}
+      </label>
+      <input
+        id={id}
+        type={type}
+        value={value}
+        onChange={(e) => onChange(e.target.value)}
+        placeholder={placeholder}
+        min={min}
+        max={max}
+        className={clsx(
+          'w-full px-4 py-3 rounded-2xl text-sm',
+          'border outline-none transition-all duration-200',
+          'focus:ring-2'
+        )}
+        style={{
+          background: 'rgba(255,255,255,0.8)',
+          borderColor: 'var(--color-cream-dark)',
+          color: 'var(--color-brown)',
+        }}
+      />
+    </div>
+  )
+}
+
+function CopyrightForm({
+  data,
+  onChange,
+  photoIndex,
+  totalPhotos,
+}: {
+  data: CopyrightData
+  onChange: (patch: Partial<CopyrightData>) => void
+  photoIndex?: number
+  totalPhotos?: number
+}) {
+  const idSuffix = photoIndex !== undefined ? `-${photoIndex}` : '-all'
+
+  return (
+    <div className="flex flex-col gap-5">
+      {photoIndex !== undefined && totalPhotos !== undefined && totalPhotos > 1 && (
+        <p
+          className="text-sm font-medium"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Photo {photoIndex + 1} of {totalPhotos}
+        </p>
+      )}
+
+      <SelectField
+        id={`relationship${idSuffix}`}
+        label="Who took this photo?"
+        value={data.relationship}
+        options={RELATIONSHIP_OPTIONS}
+        onChange={(v) => onChange({ relationship: v })}
+        required
+      />
+
+      <AnimatePresence>
+        {data.relationship !== 'unknown' && (
+          <motion.div
+            initial={{ opacity: 0, height: 0 }}
+            animate={{ opacity: 1, height: 'auto' }}
+            exit={{ opacity: 0, height: 0 }}
+            transition={{ duration: 0.2 }}
+            className="overflow-hidden"
+          >
+            <TextField
+              id={`photographer${idSuffix}`}
+              label="Photographer name"
+              value={data.photographer}
+              onChange={(v) => onChange({ photographer: v })}
+              placeholder="e.g. Mom, Grandpa Joe, Jane Smith Photography"
+              optional
+            />
+          </motion.div>
+        )}
+      </AnimatePresence>
+
+      <TextField
+        id={`year${idSuffix}`}
+        label="Approximate year photo was taken"
+        value={data.year}
+        onChange={(v) => onChange({ year: v })}
+        type="number"
+        placeholder={`e.g. ${CURRENT_YEAR - 25}`}
+        min={1900}
+        max={CURRENT_YEAR}
+      />
+
+      <SelectField
+        id={`permission${idSuffix}`}
+        label="Do you have permission from the photographer?"
+        value={data.hasPermission}
+        options={PERMISSION_OPTIONS}
+        onChange={(v) => onChange({ hasPermission: v })}
+        required
+      />
+
+      <AnimatePresence>
+        {data.hasPermission === 'deceased' && (
+          <motion.div
+            initial={{ opacity: 0, height: 0 }}
+            animate={{ opacity: 1, height: 'auto' }}
+            exit={{ opacity: 0, height: 0 }}
+            transition={{ duration: 0.2 }}
+            className="overflow-hidden"
+          >
+            <label
+              className="flex items-start gap-3 cursor-pointer group"
+              htmlFor={`estate${idSuffix}`}
+            >
+              <div className="relative mt-0.5">
+                <input
+                  id={`estate${idSuffix}`}
+                  type="checkbox"
+                  checked={data.estateRights}
+                  onChange={(e) => onChange({ estateRights: e.target.checked })}
+                  className="sr-only"
+                />
+                <div
+                  className={clsx(
+                    'w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all',
+                    data.estateRights ? 'border-transparent' : ''
+                  )}
+                  style={{
+                    background: data.estateRights ? 'var(--color-sage)' : 'white',
+                    borderColor: data.estateRights ? 'var(--color-sage)' : 'var(--color-cream-dark)',
+                  }}
+                  aria-hidden
+                >
+                  {data.estateRights && (
+                    <svg width="11" height="11" viewBox="0 0 11 11" fill="none">
+                      <path
+                        d="M2 5.5l2.5 2.5L9 3"
+                        stroke="white"
+                        strokeWidth="1.6"
+                        strokeLinecap="round"
+                        strokeLinejoin="round"
+                      />
+                    </svg>
+                  )}
+                </div>
+              </div>
+              <div>
+                <p
+                  className="text-sm font-semibold"
+                  style={{ color: 'var(--color-brown)' }}
+                >
+                  I have evidence of estate rights
+                </p>
+                <p className="text-xs mt-0.5" style={{ color: 'var(--color-warm-gray)' }}>
+                  e.g. will, inheritance document, or written permission from the estate executor
+                </p>
+              </div>
+            </label>
+          </motion.div>
+        )}
+      </AnimatePresence>
+    </div>
+  )
+}
+
+export default function CopyrightInfo({
+  photos,
+  copyrightInfo,
+  onCopyrightChange,
+  onNext,
+  onBack,
+}: CopyrightInfoProps) {
+  const [activePhotoIndex, setActivePhotoIndex] = useState(0)
+  const multiPhoto = photos.length > 1
+
+  // Per-photo overrides (index → CopyrightData)
+  const [perPhoto, setPerPhoto] = useState<Record<number, Partial<CopyrightData>>>({})
+
+  const handleBatchChange = (patch: Partial<CopyrightData>) => {
+    onCopyrightChange({ ...copyrightInfo, ...patch })
+  }
+
+  const handlePerPhotoChange = (index: number, patch: Partial<CopyrightData>) => {
+    setPerPhoto((prev) => ({
+      ...prev,
+      [index]: { ...(prev[index] ?? {}), ...patch },
+    }))
+  }
+
+  const getPhotoData = (index: number): CopyrightData => ({
+    ...copyrightInfo,
+    ...(perPhoto[index] ?? {}),
+  })
+
+  const canProceed =
+    copyrightInfo.relationship !== '' && copyrightInfo.hasPermission !== ''
+
+  return (
+    <div className="flex flex-col gap-6">
+      {/* Header */}
+      <div>
+        <h2
+          className="text-2xl md:text-3xl font-bold tracking-tight"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Copyright Information
+        </h2>
+        <p className="mt-2 text-base" style={{ color: 'var(--color-warm-gray)' }}>
+          Help us understand who owns the copyright to your photos.
+        </p>
+      </div>
+
+      {/* Info banner */}
+      <div
+        className="flex items-start gap-3 p-4 rounded-2xl"
+        style={{ background: 'rgba(181, 213, 197, 0.25)', border: '1px solid var(--color-sage)' }}
+        role="note"
+      >
+        <Info size={16} className="mt-0.5 shrink-0" style={{ color: 'var(--color-sage-dark)' }} />
+        <p className="text-sm leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+          In most cases, the person who pressed the shutter button owns the copyright — not the
+          person in the photo. We need this information to ensure your uploads are compliant.
+        </p>
+      </div>
+
+      {/* Apply-to-all toggle */}
+      {multiPhoto && (
+        <label
+          className="flex items-center gap-3 cursor-pointer"
+          htmlFor="apply-all"
+        >
+          <div className="relative">
+            <input
+              id="apply-all"
+              type="checkbox"
+              checked={copyrightInfo.applyToAll}
+              onChange={(e) => handleBatchChange({ applyToAll: e.target.checked })}
+              className="sr-only"
+            />
+            <div
+              className="w-10 h-6 rounded-full transition-colors relative"
+              style={{
+                background: copyrightInfo.applyToAll
+                  ? 'var(--color-peach)'
+                  : 'var(--color-cream-dark)',
+              }}
+              aria-hidden
+            >
+              <div
+                className="absolute top-1 w-4 h-4 rounded-full bg-white shadow transition-all duration-200"
+                style={{
+                  left: copyrightInfo.applyToAll ? '1.25rem' : '0.25rem',
+                }}
+              />
+            </div>
+          </div>
+          <div>
+            <p
+              className="text-sm font-semibold"
+              style={{ color: 'var(--color-brown)' }}
+            >
+              Apply same info to all {photos.length} photos
+            </p>
+            <p className="text-xs" style={{ color: 'var(--color-warm-gray)' }}>
+              Toggle off to fill copyright info per photo
+            </p>
+          </div>
+        </label>
+      )}
+
+      {/* Form */}
+      <div
+        className="p-6 rounded-3xl"
+        style={{ background: 'rgba(255,255,255,0.6)', border: '1px solid var(--color-cream-dark)' }}
+      >
+        {!multiPhoto || copyrightInfo.applyToAll ? (
+          <CopyrightForm
+            data={copyrightInfo}
+            onChange={handleBatchChange}
+            totalPhotos={photos.length}
+          />
+        ) : (
+          <div className="flex flex-col gap-6">
+            {/* Per-photo nav */}
+            <div className="flex items-center justify-between">
+              <button
+                onClick={() => setActivePhotoIndex((i) => Math.max(0, i - 1))}
+                disabled={activePhotoIndex === 0}
+                className="flex items-center gap-1 text-sm font-medium px-3 py-2 rounded-xl transition-all disabled:opacity-30"
+                style={{ color: 'var(--color-warm-gray)' }}
+                aria-label="Previous photo"
+              >
+                <ChevronLeft size={16} />
+                Previous
+              </button>
+
+              {/* Thumbnail strip */}
+              <div className="flex gap-2">
+                {photos.map((photo, i) => (
+                  <button
+                    key={photo.id}
+                    onClick={() => setActivePhotoIndex(i)}
+                    className={clsx(
+                      'w-10 h-10 rounded-xl overflow-hidden border-2 transition-all',
+                      i === activePhotoIndex
+                        ? 'scale-110'
+                        : 'opacity-60 hover:opacity-80'
+                    )}
+                    style={{
+                      borderColor:
+                        i === activePhotoIndex
+                          ? 'var(--color-peach)'
+                          : 'transparent',
+                    }}
+                    aria-label={`Select photo ${i + 1}`}
+                    aria-pressed={i === activePhotoIndex}
+                  >
+                    {/* eslint-disable-next-line @next/next/no-img-element */}
+                    <img
+                      src={photo.preview}
+                      alt={`Photo ${i + 1}`}
+                      className="w-full h-full object-cover"
+                    />
+                  </button>
+                ))}
+              </div>
+
+              <button
+                onClick={() =>
+                  setActivePhotoIndex((i) => Math.min(photos.length - 1, i + 1))
+                }
+                disabled={activePhotoIndex === photos.length - 1}
+                className="flex items-center gap-1 text-sm font-medium px-3 py-2 rounded-xl transition-all disabled:opacity-30"
+                style={{ color: 'var(--color-warm-gray)' }}
+                aria-label="Next photo"
+              >
+                Next
+                <ChevronRight size={16} />
+              </button>
+            </div>
+
+            {/* Active photo form */}
+            <AnimatePresence mode="wait">
+              <motion.div
+                key={activePhotoIndex}
+                initial={{ opacity: 0, x: 20 }}
+                animate={{ opacity: 1, x: 0 }}
+                exit={{ opacity: 0, x: -20 }}
+                transition={{ duration: 0.2 }}
+              >
+                <CopyrightForm
+                  data={getPhotoData(activePhotoIndex)}
+                  onChange={(patch) => handlePerPhotoChange(activePhotoIndex, patch)}
+                  photoIndex={activePhotoIndex}
+                  totalPhotos={photos.length}
+                />
+              </motion.div>
+            </AnimatePresence>
+          </div>
+        )}
+      </div>
+
+      {/* Navigation */}
+      <div className="flex items-center justify-between pt-2">
+        <button
+          onClick={onBack}
+          className="flex items-center gap-2 px-6 py-3 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
+          style={{
+            background: 'var(--color-cream-dark)',
+            color: 'var(--color-warm-gray)',
+          }}
+        >
+          <ChevronLeft size={16} />
+          Back
+        </button>
+
+        <motion.button
+          whileHover={canProceed ? { scale: 1.03 } : {}}
+          whileTap={canProceed ? { scale: 0.97 } : {}}
+          onClick={onNext}
+          disabled={!canProceed}
+          className={clsx(
+            'flex items-center gap-2 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide',
+            'transition-all duration-200',
+            !canProceed && 'opacity-40 cursor-not-allowed'
+          )}
+          style={{
+            background: canProceed ? 'var(--color-peach)' : 'var(--color-cream-dark)',
+            color: 'var(--color-brown)',
+          }}
+          aria-disabled={!canProceed}
+        >
+          Continue to Likeness Release
+          <ChevronRight size={16} />
+        </motion.button>
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/licensor/LikenessRelease.tsx b/src/components/licensor/LikenessRelease.tsx
new file mode 100644
index 0000000..905ad51
--- /dev/null
+++ b/src/components/licensor/LikenessRelease.tsx
@@ -0,0 +1,364 @@
+'use client'
+
+import { useState } from 'react'
+import { motion } from 'framer-motion'
+import { ChevronLeft, ChevronRight, ShieldCheck, AlertTriangle } from 'lucide-react'
+import clsx from 'clsx'
+
+export interface LikenessData {
+  isSubject: boolean
+  isAdult: boolean
+  editorial: boolean
+  commercial: boolean
+  aiTraining: boolean
+  signature: string
+  date: string
+}
+
+interface LikenessReleaseProps {
+  likenessRelease: LikenessData
+  onLikenessChange: (data: LikenessData) => void
+  onNext: () => void
+  onBack: () => void
+}
+
+function CheckboxField({
+  id,
+  label,
+  description,
+  checked,
+  onChange,
+  required,
+  error,
+}: {
+  id: string
+  label: string
+  description?: string
+  checked: boolean
+  onChange: (v: boolean) => void
+  required?: boolean
+  error?: boolean
+}) {
+  return (
+    <label
+      htmlFor={id}
+      className="flex items-start gap-4 p-4 rounded-2xl cursor-pointer transition-all"
+      style={{
+        background: checked
+          ? 'rgba(181, 213, 197, 0.2)'
+          : error
+            ? 'rgba(255, 181, 167, 0.12)'
+            : 'rgba(255,255,255,0.5)',
+        border: `2px solid ${
+          checked
+            ? 'var(--color-sage)'
+            : error
+              ? 'var(--color-peach)'
+              : 'var(--color-cream-dark)'
+        }`,
+      }}
+    >
+      <div className="relative mt-0.5 shrink-0">
+        <input
+          id={id}
+          type="checkbox"
+          checked={checked}
+          onChange={(e) => onChange(e.target.checked)}
+          required={required}
+          className="sr-only"
+          aria-required={required}
+        />
+        <div
+          className="w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all"
+          style={{
+            background: checked ? 'var(--color-sage-dark)' : 'white',
+            borderColor: checked ? 'var(--color-sage-dark)' : 'var(--color-cream-dark)',
+          }}
+          aria-hidden
+        >
+          {checked && (
+            <motion.svg
+              initial={{ scale: 0 }}
+              animate={{ scale: 1 }}
+              width="11"
+              height="11"
+              viewBox="0 0 11 11"
+              fill="none"
+            >
+              <path
+                d="M2 5.5l2.5 2.5L9 3"
+                stroke="white"
+                strokeWidth="1.6"
+                strokeLinecap="round"
+                strokeLinejoin="round"
+              />
+            </motion.svg>
+          )}
+        </div>
+      </div>
+
+      <div className="flex-1">
+        <p
+          className="text-sm font-semibold"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          {label}
+          {required && (
+            <span className="ml-1" style={{ color: 'var(--color-peach-dark)' }} aria-hidden>
+              *
+            </span>
+          )}
+        </p>
+        {description && (
+          <p className="mt-0.5 text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+            {description}
+          </p>
+        )}
+      </div>
+    </label>
+  )
+}
+
+export default function LikenessRelease({
+  likenessRelease,
+  onLikenessChange,
+  onNext,
+  onBack,
+}: LikenessReleaseProps) {
+  const [attempted, setAttempted] = useState(false)
+
+  const patch = (updates: Partial<LikenessData>) => {
+    onLikenessChange({ ...likenessRelease, ...updates })
+  }
+
+  const atLeastOneUse =
+    likenessRelease.editorial ||
+    likenessRelease.commercial ||
+    likenessRelease.aiTraining
+
+  const canProceed =
+    likenessRelease.isSubject &&
+    likenessRelease.isAdult &&
+    atLeastOneUse &&
+    likenessRelease.signature.trim().length >= 2
+
+  const handleNext = () => {
+    setAttempted(true)
+    if (canProceed) onNext()
+  }
+
+  return (
+    <div className="flex flex-col gap-6">
+      {/* Header */}
+      <div>
+        <h2
+          className="text-2xl md:text-3xl font-bold tracking-tight"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Likeness Release
+        </h2>
+        <p className="mt-2 text-base" style={{ color: 'var(--color-warm-gray)' }}>
+          Confirm you are the person in these photos and grant licensing permissions.
+        </p>
+      </div>
+
+      {/* Security badge */}
+      <div
+        className="flex items-center gap-3 p-4 rounded-2xl"
+        style={{ background: 'rgba(181, 213, 197, 0.2)', border: '1px solid var(--color-sage)' }}
+        role="note"
+      >
+        <ShieldCheck size={20} style={{ color: 'var(--color-sage-dark)' }} />
+        <p className="text-sm" style={{ color: 'var(--color-warm-gray)' }}>
+          Your signature is stored securely and is legally binding. You can revoke permissions at
+          any time from your account.
+        </p>
+      </div>
+
+      {/* Identity confirmations */}
+      <section aria-labelledby="identity-heading">
+        <h3
+          id="identity-heading"
+          className="text-base font-bold tracking-wide mb-3"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Identity Confirmation
+        </h3>
+        <div className="flex flex-col gap-3">
+          <CheckboxField
+            id="is-subject"
+            label="I confirm I am the person depicted in these photos"
+            description="These are photos of me as a child. I am the subject whose likeness will be licensed."
+            checked={likenessRelease.isSubject}
+            onChange={(v) => patch({ isSubject: v })}
+            required
+            error={attempted && !likenessRelease.isSubject}
+          />
+          <CheckboxField
+            id="is-adult"
+            label="I am 18 years of age or older"
+            description="You must be an adult to legally grant rights to your own likeness."
+            checked={likenessRelease.isAdult}
+            onChange={(v) => patch({ isAdult: v })}
+            required
+            error={attempted && !likenessRelease.isAdult}
+          />
+        </div>
+      </section>
+
+      {/* Usage permissions */}
+      <section aria-labelledby="usage-heading">
+        <h3
+          id="usage-heading"
+          className="text-base font-bold tracking-wide mb-1"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          I grant permission for these photos to be licensed for:
+        </h3>
+        <p className="text-xs mb-3" style={{ color: 'var(--color-warm-gray)' }}>
+          Select at least one use type. You earn royalties for each license granted.
+        </p>
+
+        {attempted && !atLeastOneUse && (
+          <motion.div
+            initial={{ opacity: 0, y: -4 }}
+            animate={{ opacity: 1, y: 0 }}
+            className="flex items-center gap-2 mb-3 p-3 rounded-xl"
+            style={{ background: 'rgba(255, 181, 167, 0.2)', border: '1px solid var(--color-peach)' }}
+            role="alert"
+          >
+            <AlertTriangle size={14} style={{ color: 'var(--color-peach-dark)' }} />
+            <p className="text-xs font-medium" style={{ color: 'var(--color-brown)' }}>
+              Please select at least one use type.
+            </p>
+          </motion.div>
+        )}
+
+        <div className="flex flex-col gap-3">
+          <CheckboxField
+            id="editorial"
+            label="Editorial use"
+            description="News articles, blog posts, non-commercial publications, and journalism."
+            checked={likenessRelease.editorial}
+            onChange={(v) => patch({ editorial: v })}
+            error={attempted && !atLeastOneUse}
+          />
+          <CheckboxField
+            id="commercial"
+            label="Commercial use"
+            description="Advertisements, product packaging, marketing materials, and brand campaigns."
+            checked={likenessRelease.commercial}
+            onChange={(v) => patch({ commercial: v })}
+            error={attempted && !atLeastOneUse}
+          />
+          <CheckboxField
+            id="ai-training"
+            label="AI training use"
+            description="Use in AI model training datasets. Higher royalty rate applies."
+            checked={likenessRelease.aiTraining}
+            onChange={(v) => patch({ aiTraining: v })}
+            error={attempted && !atLeastOneUse}
+          />
+        </div>
+      </section>
+
+      {/* Signature */}
+      <section aria-labelledby="signature-heading">
+        <h3
+          id="signature-heading"
+          className="text-base font-bold tracking-wide mb-3"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Digital Signature
+        </h3>
+
+        <div className="flex flex-col gap-4">
+          <div className="flex flex-col gap-1.5">
+            <label
+              htmlFor="signature"
+              className="text-sm font-semibold"
+              style={{ color: 'var(--color-brown)' }}
+            >
+              Type your full legal name to sign
+              <span className="ml-1" style={{ color: 'var(--color-peach-dark)' }} aria-hidden>
+                *
+              </span>
+            </label>
+            <input
+              id="signature"
+              type="text"
+              value={likenessRelease.signature}
+              onChange={(e) => patch({ signature: e.target.value })}
+              placeholder="Your full legal name"
+              autoComplete="name"
+              className={clsx(
+                'w-full px-5 py-4 rounded-2xl text-lg signature-field',
+                'border-2 outline-none transition-all duration-200'
+              )}
+              style={{
+                background: 'rgba(255,255,255,0.8)',
+                borderColor:
+                  attempted && likenessRelease.signature.trim().length < 2
+                    ? 'var(--color-peach)'
+                    : likenessRelease.signature.trim().length >= 2
+                      ? 'var(--color-sage)'
+                      : 'var(--color-cream-dark)',
+                color: 'var(--color-brown)',
+              }}
+              aria-required="true"
+              aria-invalid={attempted && likenessRelease.signature.trim().length < 2}
+            />
+            {attempted && likenessRelease.signature.trim().length < 2 && (
+              <p className="text-xs" style={{ color: 'var(--color-peach-dark)' }}>
+                Please enter your full legal name.
+              </p>
+            )}
+          </div>
+
+          {/* Date */}
+          <div
+            className="flex items-center justify-between px-5 py-3 rounded-2xl"
+            style={{ background: 'var(--color-cream-dark)' }}
+          >
+            <span className="text-sm font-medium" style={{ color: 'var(--color-warm-gray)' }}>
+              Date signed
+            </span>
+            <span className="text-sm font-semibold" style={{ color: 'var(--color-brown)' }}>
+              {likenessRelease.date}
+            </span>
+          </div>
+        </div>
+      </section>
+
+      {/* Navigation */}
+      <div className="flex items-center justify-between pt-2">
+        <button
+          onClick={onBack}
+          className="flex items-center gap-2 px-6 py-3 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
+          style={{
+            background: 'var(--color-cream-dark)',
+            color: 'var(--color-warm-gray)',
+          }}
+        >
+          <ChevronLeft size={16} />
+          Back
+        </button>
+
+        <motion.button
+          whileHover={{ scale: 1.03 }}
+          whileTap={{ scale: 0.97 }}
+          onClick={handleNext}
+          className="flex items-center gap-2 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide transition-all duration-200"
+          style={{
+            background: 'var(--color-peach)',
+            color: 'var(--color-brown)',
+            opacity: canProceed ? 1 : 0.7,
+          }}
+        >
+          Review &amp; Submit
+          <ChevronRight size={16} />
+        </motion.button>
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/licensor/PhotoUpload.tsx b/src/components/licensor/PhotoUpload.tsx
new file mode 100644
index 0000000..c25e3c3
--- /dev/null
+++ b/src/components/licensor/PhotoUpload.tsx
@@ -0,0 +1,797 @@
+'use client'
+
+import { useCallback, useRef, useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import {
+  Upload,
+  X,
+  ImageIcon,
+  Plus,
+  AlertCircle,
+  FolderOpen,
+  Calendar,
+  MapPin,
+  Camera,
+  Info,
+} from 'lucide-react'
+import clsx from 'clsx'
+import exifr from 'exifr'
+
+export interface PhotoMetadata {
+  dateTaken?: string
+  year?: number
+  latitude?: number
+  longitude?: number
+  locationName?: string
+  cameraMake?: string
+  cameraModel?: string
+  width?: number
+  height?: number
+}
+
+export interface PhotoFile {
+  id: string
+  file: File
+  preview: string
+  progress: number
+  error?: string
+  metadata?: PhotoMetadata
+  directory?: string
+}
+
+interface PhotoUploadProps {
+  photos: PhotoFile[]
+  onPhotosChange: React.Dispatch<React.SetStateAction<PhotoFile[]>>
+  onNext: () => void
+}
+
+const MAX_FILE_SIZE_MB = 25
+const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024
+const ACCEPTED_TYPES = [
+  'image/jpeg',
+  'image/png',
+  'image/webp',
+  'image/heic',
+  'image/gif',
+  'image/tiff',
+]
+
+function generateId() {
+  return Math.random().toString(36).slice(2) + Date.now().toString(36)
+}
+
+function simulateUpload(
+  id: string,
+  onProgress: (id: string, progress: number) => void
+): Promise<void> {
+  return new Promise((resolve) => {
+    let progress = 0
+    const interval = setInterval(() => {
+      progress += Math.random() * 30 + 10
+      if (progress >= 100) {
+        clearInterval(interval)
+        onProgress(id, 100)
+        resolve()
+      } else {
+        onProgress(id, Math.min(Math.round(progress), 99))
+      }
+    }, 120)
+  })
+}
+
+async function extractMetadata(file: File): Promise<PhotoMetadata> {
+  const meta: PhotoMetadata = {}
+
+  try {
+    const exif = await exifr.parse(file, {
+      pick: [
+        'DateTimeOriginal',
+        'CreateDate',
+        'GPSLatitude',
+        'GPSLongitude',
+        'Make',
+        'Model',
+        'ImageWidth',
+        'ImageHeight',
+        'ExifImageWidth',
+        'ExifImageHeight',
+      ],
+      gps: true,
+    })
+
+    if (!exif) return meta
+
+    // Date
+    const dateVal = exif.DateTimeOriginal || exif.CreateDate
+    if (dateVal instanceof Date) {
+      meta.dateTaken = dateVal.toLocaleDateString('en-US', {
+        year: 'numeric',
+        month: 'short',
+        day: 'numeric',
+      })
+      meta.year = dateVal.getFullYear()
+    }
+
+    // GPS
+    if (typeof exif.latitude === 'number' && typeof exif.longitude === 'number') {
+      meta.latitude = exif.latitude
+      meta.longitude = exif.longitude
+    }
+
+    // Camera
+    if (exif.Make) meta.cameraMake = String(exif.Make).trim()
+    if (exif.Model) meta.cameraModel = String(exif.Model).trim()
+
+    // Dimensions
+    meta.width = exif.ExifImageWidth || exif.ImageWidth
+    meta.height = exif.ExifImageHeight || exif.ImageHeight
+  } catch {
+    // EXIF parsing can fail for PNGs, WEBPs without metadata — that's fine
+  }
+
+  return meta
+}
+
+function MetadataBadge({
+  icon: Icon,
+  label,
+}: {
+  icon: typeof Calendar
+  label: string
+}) {
+  return (
+    <span
+      className="inline-flex items-center gap-1 px-2 py-0.5 rounded-lg text-[10px] font-medium"
+      style={{ background: 'rgba(61, 64, 91, 0.7)', color: 'white' }}
+    >
+      <Icon size={9} />
+      {label}
+    </span>
+  )
+}
+
+function PhotoCard({
+  photo,
+  onRemove,
+  onSelect,
+  isSelected,
+}: {
+  photo: PhotoFile
+  onRemove: () => void
+  onSelect: () => void
+  isSelected: boolean
+}) {
+  const m = photo.metadata
+
+  return (
+    <motion.div
+      layout
+      key={photo.id}
+      initial={{ opacity: 0, scale: 0.85 }}
+      animate={{ opacity: 1, scale: 1 }}
+      exit={{ opacity: 0, scale: 0.85 }}
+      transition={{ type: 'spring', stiffness: 300, damping: 25 }}
+      className={clsx(
+        'relative group rounded-2xl overflow-hidden cursor-pointer',
+        'transition-all duration-200'
+      )}
+      style={{
+        aspectRatio: '1',
+        background: 'var(--color-cream-dark)',
+        outline: isSelected ? '3px solid var(--color-peach)' : 'none',
+        outlineOffset: '2px',
+      }}
+      onClick={onSelect}
+    >
+      {/* Thumbnail */}
+      {/* eslint-disable-next-line @next/next/no-img-element */}
+      <img
+        src={photo.preview}
+        alt={photo.file.name}
+        className="w-full h-full object-cover"
+      />
+
+      {/* Upload progress overlay */}
+      {photo.progress < 100 && (
+        <div
+          className="absolute inset-0 flex flex-col items-center justify-center gap-2"
+          style={{ background: 'rgba(61, 64, 91, 0.55)' }}
+        >
+          <ImageIcon size={20} className="text-white" />
+          <div
+            className="w-3/4 h-1.5 rounded-full overflow-hidden"
+            style={{ background: 'rgba(255,255,255,0.3)' }}
+          >
+            <motion.div
+              className="h-full rounded-full"
+              style={{ background: 'var(--color-peach)' }}
+              initial={{ width: 0 }}
+              animate={{ width: `${photo.progress}%` }}
+              transition={{ duration: 0.2 }}
+            />
+          </div>
+          <span className="text-white text-xs font-medium">{photo.progress}%</span>
+        </div>
+      )}
+
+      {/* Metadata badges (bottom-left) */}
+      {photo.progress === 100 && m && (m.dateTaken || m.latitude) && (
+        <div className="absolute bottom-1.5 left-1.5 flex flex-col gap-0.5">
+          {m.dateTaken && <MetadataBadge icon={Calendar} label={m.dateTaken} />}
+          {m.latitude && (
+            <MetadataBadge
+              icon={MapPin}
+              label={`${m.latitude.toFixed(2)}, ${m.longitude?.toFixed(2)}`}
+            />
+          )}
+        </div>
+      )}
+
+      {/* Remove button */}
+      {photo.progress === 100 && (
+        <button
+          className="absolute top-1.5 right-1.5 w-6 h-6 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
+          style={{ background: 'rgba(61, 64, 91, 0.8)' }}
+          onClick={(e) => {
+            e.stopPropagation()
+            onRemove()
+          }}
+          aria-label={`Remove ${photo.file.name}`}
+        >
+          <X size={12} className="text-white" />
+        </button>
+      )}
+
+      {/* Complete checkmark */}
+      {photo.progress === 100 && (
+        <div
+          className="absolute top-1.5 left-1.5 w-5 h-5 rounded-full flex items-center justify-center"
+          style={{ background: 'var(--color-sage-dark)' }}
+        >
+          <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
+            <path
+              d="M2 5l2.5 2.5L8 3"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+          </svg>
+        </div>
+      )}
+
+      {/* Directory label */}
+      {photo.directory && (
+        <div
+          className="absolute top-1.5 right-1.5 flex items-center gap-0.5 px-1.5 py-0.5 rounded-md"
+          style={{
+            background: 'rgba(61, 64, 91, 0.6)',
+            fontSize: '9px',
+            color: 'white',
+          }}
+        >
+          <FolderOpen size={8} />
+          {photo.directory}
+        </div>
+      )}
+    </motion.div>
+  )
+}
+
+function MetadataPanel({ photo }: { photo: PhotoFile }) {
+  const m = photo.metadata
+  if (!m) return null
+
+  const hasAny = m.dateTaken || m.cameraMake || m.latitude || m.width
+
+  if (!hasAny) {
+    return (
+      <div
+        className="flex items-center gap-2 p-4 rounded-2xl"
+        style={{ background: 'rgba(255,255,255,0.5)', border: '1px solid var(--color-cream-dark)' }}
+      >
+        <Info size={14} style={{ color: 'var(--color-warm-gray)' }} />
+        <p className="text-xs" style={{ color: 'var(--color-warm-gray)' }}>
+          No EXIF metadata found. You can manually enter the year in the next step.
+        </p>
+      </div>
+    )
+  }
+
+  return (
+    <div
+      className="rounded-2xl overflow-hidden"
+      style={{ border: '1px solid var(--color-cream-dark)' }}
+    >
+      <div
+        className="flex items-center gap-2 px-4 py-2.5 border-b"
+        style={{
+          borderColor: 'var(--color-cream-dark)',
+          background: 'rgba(255,255,255,0.4)',
+        }}
+      >
+        <Info size={12} style={{ color: 'var(--color-warm-gray)' }} />
+        <p className="text-xs font-semibold" style={{ color: 'var(--color-brown)' }}>
+          {photo.file.name}
+        </p>
+      </div>
+      <div className="flex flex-wrap gap-x-6 gap-y-2 px-4 py-3" style={{ background: 'rgba(255,255,255,0.5)' }}>
+        {m.dateTaken && (
+          <div className="flex items-center gap-1.5">
+            <Calendar size={12} style={{ color: 'var(--color-peach-dark)' }} />
+            <span className="text-xs" style={{ color: 'var(--color-brown)' }}>
+              {m.dateTaken}
+            </span>
+          </div>
+        )}
+        {m.cameraMake && (
+          <div className="flex items-center gap-1.5">
+            <Camera size={12} style={{ color: 'var(--color-sage-dark)' }} />
+            <span className="text-xs" style={{ color: 'var(--color-brown)' }}>
+              {m.cameraMake} {m.cameraModel || ''}
+            </span>
+          </div>
+        )}
+        {m.latitude && m.longitude && (
+          <div className="flex items-center gap-1.5">
+            <MapPin size={12} style={{ color: 'var(--color-warm-gray)' }} />
+            <span className="text-xs" style={{ color: 'var(--color-brown)' }}>
+              {m.latitude.toFixed(4)}, {m.longitude.toFixed(4)}
+            </span>
+          </div>
+        )}
+        {m.width && m.height && (
+          <div className="flex items-center gap-1.5">
+            <ImageIcon size={12} style={{ color: 'var(--color-warm-gray)' }} />
+            <span className="text-xs" style={{ color: 'var(--color-brown)' }}>
+              {m.width} x {m.height}
+            </span>
+          </div>
+        )}
+      </div>
+    </div>
+  )
+}
+
+export default function PhotoUpload({
+  photos,
+  onPhotosChange,
+  onNext,
+}: PhotoUploadProps) {
+  const [isDragOver, setIsDragOver] = useState(false)
+  const [errors, setErrors] = useState<string[]>([])
+  const [selectedId, setSelectedId] = useState<string | null>(null)
+  const fileInputRef = useRef<HTMLInputElement>(null)
+  const dirInputRef = useRef<HTMLInputElement>(null)
+
+  const processFiles = useCallback(
+    async (files: File[], directoryName?: string) => {
+      const newErrors: string[] = []
+      const validFiles: File[] = []
+
+      for (const file of files) {
+        if (
+          !ACCEPTED_TYPES.includes(file.type) &&
+          !file.name.match(/\.(jpe?g|png|webp|heic|gif|tiff?)$/i)
+        ) {
+          newErrors.push(`"${file.name}" is not a supported image type.`)
+          continue
+        }
+        if (file.size > MAX_FILE_SIZE_BYTES) {
+          newErrors.push(
+            `"${file.name}" exceeds the ${MAX_FILE_SIZE_MB}MB limit.`
+          )
+          continue
+        }
+        validFiles.push(file)
+      }
+
+      if (newErrors.length > 0) {
+        setErrors(newErrors)
+        setTimeout(() => setErrors([]), 5000)
+      }
+
+      if (validFiles.length === 0) return
+
+      // Build photo objects + extract EXIF metadata in parallel
+      const newPhotos: PhotoFile[] = await Promise.all(
+        validFiles.map(async (file) => {
+          const metadata = await extractMetadata(file)
+          // Extract directory from webkitRelativePath
+          const relPath = (file as File & { webkitRelativePath?: string })
+            .webkitRelativePath
+          const dir =
+            directoryName ||
+            (relPath ? relPath.split('/').slice(0, -1).join('/') : undefined)
+
+          return {
+            id: generateId(),
+            file,
+            preview: URL.createObjectURL(file),
+            progress: 0,
+            metadata,
+            directory: dir || undefined,
+          }
+        })
+      )
+
+      // Merge with existing
+      onPhotosChange((prev) => [...prev, ...newPhotos])
+
+      // Simulate upload progress
+      await Promise.all(
+        newPhotos.map((photo) =>
+          simulateUpload(photo.id, (id, progress) => {
+            onPhotosChange((prev) =>
+              prev.map((p) => (p.id === id ? { ...p, progress } : p))
+            )
+          })
+        )
+      )
+
+      // Auto-select first photo
+      if (newPhotos.length > 0) {
+        setSelectedId(newPhotos[0].id)
+      }
+    },
+    [onPhotosChange]
+  )
+
+  const handleDrop = useCallback(
+    async (e: React.DragEvent<HTMLDivElement>) => {
+      e.preventDefault()
+      setIsDragOver(false)
+
+      // Check for directory drops via DataTransferItemList
+      const items = e.dataTransfer.items
+      const allFiles: File[] = []
+
+      if (items) {
+        const entries: FileSystemEntry[] = []
+        for (let i = 0; i < items.length; i++) {
+          const entry = items[i].webkitGetAsEntry?.()
+          if (entry) entries.push(entry)
+        }
+
+        // Recursively read directories
+        for (const entry of entries) {
+          if (entry.isFile) {
+            const file = await new Promise<File>((resolve) =>
+              (entry as FileSystemFileEntry).file(resolve)
+            )
+            allFiles.push(file)
+          } else if (entry.isDirectory) {
+            const dirReader = (
+              entry as FileSystemDirectoryEntry
+            ).createReader()
+            const dirFiles = await new Promise<FileSystemEntry[]>(
+              (resolve) => dirReader.readEntries(resolve)
+            )
+            for (const dirEntry of dirFiles) {
+              if (dirEntry.isFile) {
+                const file = await new Promise<File>((resolve) =>
+                  (dirEntry as FileSystemFileEntry).file(resolve)
+                )
+                allFiles.push(file)
+              }
+            }
+          }
+        }
+      }
+
+      if (allFiles.length > 0) {
+        processFiles(allFiles)
+      } else {
+        // Fallback to standard files
+        processFiles(Array.from(e.dataTransfer.files))
+      }
+    },
+    [processFiles]
+  )
+
+  const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
+    e.preventDefault()
+    setIsDragOver(true)
+  }
+
+  const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
+    if (!e.currentTarget.contains(e.relatedTarget as Node)) {
+      setIsDragOver(false)
+    }
+  }
+
+  const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const files = Array.from(e.target.files ?? [])
+    processFiles(files)
+    if (fileInputRef.current) fileInputRef.current.value = ''
+  }
+
+  const handleDirInput = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const files = Array.from(e.target.files ?? [])
+    // Extract directory name from first file's path
+    const firstPath = (files[0] as File & { webkitRelativePath?: string })
+      ?.webkitRelativePath
+    const dirName = firstPath ? firstPath.split('/')[0] : undefined
+    processFiles(files, dirName)
+    if (dirInputRef.current) dirInputRef.current.value = ''
+  }
+
+  const removePhoto = (id: string) => {
+    const photo = photos.find((p) => p.id === id)
+    if (photo) URL.revokeObjectURL(photo.preview)
+    onPhotosChange((prev) => prev.filter((p) => p.id !== id))
+    if (selectedId === id) setSelectedId(null)
+  }
+
+  const selectedPhoto = photos.find((p) => p.id === selectedId)
+  const canProceed =
+    photos.length >= 1 && photos.every((p) => p.progress === 100)
+
+  // Count unique directories
+  const directories = new Set(photos.map((p) => p.directory).filter(Boolean))
+
+  return (
+    <div className="flex flex-col gap-6">
+      {/* Header */}
+      <div>
+        <h2
+          className="text-2xl md:text-3xl font-bold tracking-tight"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Upload Your Photos
+        </h2>
+        <p className="mt-2 text-base" style={{ color: 'var(--color-warm-gray)' }}>
+          Add childhood photos or entire folders. We&apos;ll extract date, location, and
+          camera info automatically.
+        </p>
+      </div>
+
+      {/* Drop zone */}
+      <div
+        role="button"
+        tabIndex={0}
+        aria-label="Click or drag photos to upload"
+        onDrop={handleDrop}
+        onDragOver={handleDragOver}
+        onDragLeave={handleDragLeave}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') fileInputRef.current?.click()
+        }}
+        onClick={() => fileInputRef.current?.click()}
+        className={clsx(
+          'relative flex flex-col items-center justify-center gap-4 px-8 py-12 rounded-3xl',
+          'border-2 border-dashed cursor-pointer',
+          'transition-all duration-200',
+          'outline-none focus-visible:ring-2',
+          isDragOver && 'drag-over scale-[1.01]'
+        )}
+        style={{
+          borderColor: isDragOver
+            ? 'var(--color-peach-dark)'
+            : 'var(--color-cream-dark)',
+          background: isDragOver
+            ? 'rgba(255, 181, 167, 0.08)'
+            : 'rgba(255, 255, 255, 0.6)',
+        }}
+      >
+        <input
+          ref={fileInputRef}
+          type="file"
+          multiple
+          accept="image/*,.heic"
+          onChange={handleFileInput}
+          aria-hidden="true"
+        />
+        <input
+          ref={dirInputRef}
+          type="file"
+          // @ts-expect-error webkitdirectory is non-standard but widely supported
+          webkitdirectory=""
+          onChange={handleDirInput}
+          aria-hidden="true"
+          style={{
+            position: 'absolute',
+            width: '1px',
+            height: '1px',
+            overflow: 'hidden',
+            clip: 'rect(0,0,0,0)',
+          }}
+        />
+
+        <motion.div
+          animate={{ scale: isDragOver ? 1.15 : 1 }}
+          transition={{ type: 'spring', stiffness: 300, damping: 25 }}
+          className="w-16 h-16 rounded-2xl flex items-center justify-center"
+          style={{ background: 'var(--color-peach)' }}
+        >
+          <Upload size={28} style={{ color: 'var(--color-brown)' }} />
+        </motion.div>
+
+        <div className="text-center">
+          <p
+            className="text-lg font-semibold tracking-wide"
+            style={{ color: 'var(--color-brown)' }}
+          >
+            {isDragOver
+              ? 'Drop them here!'
+              : 'Drag & drop photos or folders'}
+          </p>
+          <p
+            className="mt-1 text-sm"
+            style={{ color: 'var(--color-warm-gray)' }}
+          >
+            or{' '}
+            <span
+              className="underline font-medium"
+              style={{ color: 'var(--color-peach-dark)' }}
+            >
+              browse to choose files
+            </span>
+          </p>
+          <p
+            className="mt-2 text-xs"
+            style={{ color: 'var(--color-warm-gray)' }}
+          >
+            JPG, PNG, WEBP, HEIC up to {MAX_FILE_SIZE_MB}MB each
+          </p>
+        </div>
+      </div>
+
+      {/* Upload folder button */}
+      <div className="flex gap-3">
+        <button
+          onClick={(e) => {
+            e.stopPropagation()
+            dirInputRef.current?.click()
+          }}
+          className="flex items-center gap-2 px-4 py-2.5 rounded-2xl text-sm font-medium transition-all hover:scale-105"
+          style={{
+            background: 'var(--color-cream-dark)',
+            color: 'var(--color-warm-gray)',
+          }}
+        >
+          <FolderOpen size={14} />
+          Upload entire folder
+        </button>
+        {directories.size > 0 && (
+          <span
+            className="flex items-center text-xs"
+            style={{ color: 'var(--color-warm-gray)' }}
+          >
+            {directories.size} folder{directories.size !== 1 ? 's' : ''} loaded
+          </span>
+        )}
+      </div>
+
+      {/* Errors */}
+      <AnimatePresence>
+        {errors.length > 0 && (
+          <motion.div
+            initial={{ opacity: 0, y: -8 }}
+            animate={{ opacity: 1, y: 0 }}
+            exit={{ opacity: 0, y: -8 }}
+            className="flex flex-col gap-2 p-4 rounded-2xl"
+            style={{
+              background: 'rgba(255, 181, 167, 0.2)',
+              border: '1px solid var(--color-peach)',
+            }}
+            role="alert"
+          >
+            {errors.map((err, i) => (
+              <div key={i} className="flex items-start gap-2">
+                <AlertCircle
+                  size={16}
+                  className="mt-0.5 shrink-0"
+                  style={{ color: 'var(--color-peach-dark)' }}
+                />
+                <p className="text-sm" style={{ color: 'var(--color-brown)' }}>
+                  {err}
+                </p>
+              </div>
+            ))}
+          </motion.div>
+        )}
+      </AnimatePresence>
+
+      {/* Photo grid + metadata panel */}
+      {photos.length > 0 && (
+        <div className="flex flex-col gap-4">
+          <div className="flex items-center justify-between">
+            <p
+              className="text-sm font-semibold"
+              style={{ color: 'var(--color-brown)' }}
+            >
+              {photos.length} photo{photos.length !== 1 ? 's' : ''} selected
+              {photos.filter((p) => p.metadata?.dateTaken).length > 0 && (
+                <span
+                  className="ml-2 font-normal"
+                  style={{ color: 'var(--color-warm-gray)' }}
+                >
+                  ({photos.filter((p) => p.metadata?.dateTaken).length} with
+                  date metadata)
+                </span>
+              )}
+            </p>
+            <button
+              onClick={(e) => {
+                e.stopPropagation()
+                fileInputRef.current?.click()
+              }}
+              className="flex items-center gap-1.5 text-sm font-medium px-4 py-2 rounded-2xl transition-all hover:scale-105"
+              style={{
+                background: 'var(--color-cream-dark)',
+                color: 'var(--color-warm-gray)',
+              }}
+              aria-label="Add more photos"
+            >
+              <Plus size={14} />
+              Add more
+            </button>
+          </div>
+
+          <div
+            className="grid gap-3"
+            style={{
+              gridTemplateColumns: 'repeat(auto-fill, minmax(130px, 1fr))',
+            }}
+          >
+            <AnimatePresence>
+              {photos.map((photo) => (
+                <PhotoCard
+                  key={photo.id}
+                  photo={photo}
+                  onRemove={() => removePhoto(photo.id)}
+                  onSelect={() => setSelectedId(photo.id)}
+                  isSelected={selectedId === photo.id}
+                />
+              ))}
+            </AnimatePresence>
+          </div>
+
+          {/* Metadata panel for selected photo */}
+          <AnimatePresence>
+            {selectedPhoto && selectedPhoto.progress === 100 && (
+              <motion.div
+                initial={{ opacity: 0, height: 0 }}
+                animate={{ opacity: 1, height: 'auto' }}
+                exit={{ opacity: 0, height: 0 }}
+                transition={{ duration: 0.2 }}
+                className="overflow-hidden"
+              >
+                <MetadataPanel photo={selectedPhoto} />
+              </motion.div>
+            )}
+          </AnimatePresence>
+        </div>
+      )}
+
+      {/* Proceed */}
+      <div className="flex justify-end pt-2">
+        <motion.button
+          whileHover={canProceed ? { scale: 1.03 } : {}}
+          whileTap={canProceed ? { scale: 0.97 } : {}}
+          onClick={onNext}
+          disabled={!canProceed}
+          className={clsx(
+            'px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide',
+            'transition-all duration-200',
+            !canProceed && 'opacity-40 cursor-not-allowed'
+          )}
+          style={{
+            background: canProceed
+              ? 'var(--color-peach)'
+              : 'var(--color-cream-dark)',
+            color: 'var(--color-brown)',
+          }}
+          aria-disabled={!canProceed}
+        >
+          {photos.length === 0
+            ? 'Upload at least 1 photo'
+            : photos.some((p) => p.progress < 100)
+              ? 'Uploading…'
+              : 'Continue to Copyright Info'}
+        </motion.button>
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/licensor/ReviewSubmit.tsx b/src/components/licensor/ReviewSubmit.tsx
new file mode 100644
index 0000000..7894385
--- /dev/null
+++ b/src/components/licensor/ReviewSubmit.tsx
@@ -0,0 +1,359 @@
+'use client'
+
+import { useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import {
+  ChevronLeft,
+  Camera,
+  Copyright,
+  ShieldCheck,
+  CheckCircle2,
+  Loader2,
+  AlertCircle,
+  ExternalLink,
+} from 'lucide-react'
+import clsx from 'clsx'
+import type { PhotoFile } from './PhotoUpload'
+import type { CopyrightData } from './CopyrightInfo'
+import type { LikenessData } from './LikenessRelease'
+
+interface ReviewSubmitProps {
+  photos: PhotoFile[]
+  copyrightInfo: CopyrightData
+  likenessRelease: LikenessData
+  onBack: () => void
+}
+
+type SubmitState = 'idle' | 'submitting' | 'success' | 'error'
+
+function SectionCard({
+  icon,
+  title,
+  children,
+}: {
+  icon: React.ReactNode
+  title: string
+  children: React.ReactNode
+}) {
+  return (
+    <div
+      className="rounded-3xl overflow-hidden"
+      style={{ border: '1px solid var(--color-cream-dark)', background: 'rgba(255,255,255,0.6)' }}
+    >
+      <div
+        className="flex items-center gap-3 px-6 py-4 border-b"
+        style={{ borderColor: 'var(--color-cream-dark)', background: 'rgba(255,255,255,0.4)' }}
+      >
+        <div
+          className="w-8 h-8 rounded-xl flex items-center justify-center"
+          style={{ background: 'var(--color-peach)' }}
+        >
+          {icon}
+        </div>
+        <h3 className="text-sm font-bold tracking-wide" style={{ color: 'var(--color-brown)' }}>
+          {title}
+        </h3>
+      </div>
+      <div className="px-6 py-5">{children}</div>
+    </div>
+  )
+}
+
+function ReviewRow({ label, value }: { label: string; value: string | React.ReactNode }) {
+  return (
+    <div className="flex items-start justify-between gap-4 py-2.5 border-b last:border-b-0" style={{ borderColor: 'var(--color-cream-dark)' }}>
+      <span className="text-sm shrink-0" style={{ color: 'var(--color-warm-gray)' }}>
+        {label}
+      </span>
+      <span className="text-sm font-medium text-right" style={{ color: 'var(--color-brown)' }}>
+        {value}
+      </span>
+    </div>
+  )
+}
+
+const RELATIONSHIP_LABELS: Record<string, string> = {
+  parent: 'My parent',
+  family: 'Other family member',
+  professional: 'Professional photographer',
+  unknown: "Don't know",
+}
+
+const PERMISSION_LABELS: Record<string, string> = {
+  yes: 'Yes — has permission',
+  no: 'No explicit permission',
+  deceased: 'Photographer deceased',
+}
+
+export default function ReviewSubmit({
+  photos,
+  copyrightInfo,
+  likenessRelease,
+  onBack,
+}: ReviewSubmitProps) {
+  const [submitState, setSubmitState] = useState<SubmitState>('idle')
+  const [errorMsg, setErrorMsg] = useState('')
+
+  const usageTypes = [
+    likenessRelease.editorial && 'Editorial',
+    likenessRelease.commercial && 'Commercial',
+    likenessRelease.aiTraining && 'AI Training',
+  ]
+    .filter(Boolean)
+    .join(', ')
+
+  const handleSubmit = async () => {
+    setSubmitState('submitting')
+    setErrorMsg('')
+
+    // Simulated async submission
+    await new Promise((r) => setTimeout(r, 2200))
+
+    // In production: POST to /api/licensor/submit with FormData
+    const success = Math.random() > 0.05 // 95% success rate for demo
+    if (success) {
+      setSubmitState('success')
+    } else {
+      setSubmitState('error')
+      setErrorMsg('Something went wrong. Please try again.')
+    }
+  }
+
+  if (submitState === 'success') {
+    return (
+      <motion.div
+        initial={{ opacity: 0, scale: 0.96 }}
+        animate={{ opacity: 1, scale: 1 }}
+        transition={{ type: 'spring', stiffness: 300, damping: 28 }}
+        className="flex flex-col items-center justify-center gap-8 py-16 text-center"
+      >
+        <motion.div
+          initial={{ scale: 0 }}
+          animate={{ scale: 1 }}
+          transition={{ type: 'spring', stiffness: 300, damping: 22, delay: 0.1 }}
+          className="w-24 h-24 rounded-full flex items-center justify-center"
+          style={{ background: 'var(--color-sage)' }}
+        >
+          <CheckCircle2 size={48} style={{ color: 'white' }} />
+        </motion.div>
+
+        <div className="flex flex-col gap-3 max-w-md">
+          <h2 className="text-3xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
+            You&apos;re in the queue!
+          </h2>
+          <p className="text-base leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
+            Your {photos.length} photo{photos.length !== 1 ? 's' : ''}{' '}
+            {photos.length !== 1 ? 'have' : 'has'} been submitted for rights review. We&apos;ll
+            notify you by email when they&apos;re cleared for licensing.
+          </p>
+        </div>
+
+        <div
+          className="w-full max-w-sm rounded-3xl p-6 flex flex-col gap-3"
+          style={{ background: 'var(--color-cream-dark)' }}
+        >
+          <p className="text-xs font-bold tracking-widest uppercase" style={{ color: 'var(--color-warm-gray)' }}>
+            What happens next
+          </p>
+          {[
+            { step: '1', text: 'Rights review (24–48 hours)' },
+            { step: '2', text: 'Copyright clearance notification' },
+            { step: '3', text: 'Photos go live in marketplace' },
+            { step: '4', text: 'Royalties deposited when licensed' },
+          ].map(({ step, text }) => (
+            <div key={step} className="flex items-center gap-3">
+              <div
+                className="w-6 h-6 rounded-full flex items-center justify-center shrink-0 text-xs font-bold"
+                style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+              >
+                {step}
+              </div>
+              <p className="text-sm" style={{ color: 'var(--color-brown)' }}>
+                {text}
+              </p>
+            </div>
+          ))}
+        </div>
+
+        <a
+          href="/dashboard"
+          className="inline-flex items-center gap-2 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
+          style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
+        >
+          Go to My Dashboard
+          <ExternalLink size={14} />
+        </a>
+      </motion.div>
+    )
+  }
+
+  return (
+    <div className="flex flex-col gap-6">
+      {/* Header */}
+      <div>
+        <h2
+          className="text-2xl md:text-3xl font-bold tracking-tight"
+          style={{ color: 'var(--color-brown)' }}
+        >
+          Review &amp; Submit
+        </h2>
+        <p className="mt-2 text-base" style={{ color: 'var(--color-warm-gray)' }}>
+          Please review everything below before submitting for rights review.
+        </p>
+      </div>
+
+      {/* Photos section */}
+      <SectionCard icon={<Camera size={16} style={{ color: 'var(--color-brown)' }} />} title={`${photos.length} Photo${photos.length !== 1 ? 's' : ''}`}>
+        <div
+          className="flex gap-2 flex-wrap"
+        >
+          {photos.map((photo, i) => (
+            <div
+              key={photo.id}
+              className="relative rounded-xl overflow-hidden"
+              style={{ width: 72, height: 72, flexShrink: 0 }}
+            >
+              {/* eslint-disable-next-line @next/next/no-img-element */}
+              <img
+                src={photo.preview}
+                alt={`Photo ${i + 1}`}
+                className="w-full h-full object-cover"
+              />
+            </div>
+          ))}
+        </div>
+      </SectionCard>
+
+      {/* Copyright section */}
+      <SectionCard
+        icon={<Copyright size={16} style={{ color: 'var(--color-brown)' }} />}
+        title="Copyright Information"
+      >
+        <ReviewRow
+          label="Photographer"
+          value={RELATIONSHIP_LABELS[copyrightInfo.relationship] ?? copyrightInfo.relationship}
+        />
+        {copyrightInfo.photographer && (
+          <ReviewRow label="Name" value={copyrightInfo.photographer} />
+        )}
+        {copyrightInfo.year && (
+          <ReviewRow label="Year taken" value={copyrightInfo.year} />
+        )}
+        <ReviewRow
+          label="Permission"
+          value={PERMISSION_LABELS[copyrightInfo.hasPermission] ?? copyrightInfo.hasPermission}
+        />
+        {copyrightInfo.hasPermission === 'deceased' && (
+          <ReviewRow
+            label="Estate rights"
+            value={copyrightInfo.estateRights ? 'Has evidence' : 'No evidence'}
+          />
+        )}
+      </SectionCard>
+
+      {/* Likeness section */}
+      <SectionCard
+        icon={<ShieldCheck size={16} style={{ color: 'var(--color-brown)' }} />}
+        title="Likeness Release"
+      >
+        <ReviewRow label="Subject confirmed" value={likenessRelease.isSubject ? 'Yes' : 'No'} />
+        <ReviewRow label="Age confirmed (18+)" value={likenessRelease.isAdult ? 'Yes' : 'No'} />
+        <ReviewRow label="Licensed for" value={usageTypes || 'None selected'} />
+        <ReviewRow
+          label="Signed by"
+          value={
+            <span className="signature-field text-base">
+              {likenessRelease.signature}
+            </span>
+          }
+        />
+        <ReviewRow label="Date" value={likenessRelease.date} />
+      </SectionCard>
+
+      {/* Legal notice */}
+      <div
+        className="flex items-start gap-3 p-4 rounded-2xl text-xs leading-relaxed"
+        style={{ background: 'rgba(61, 64, 91, 0.05)', border: '1px solid var(--color-cream-dark)' }}
+        role="note"
+      >
+        <AlertCircle size={14} className="shrink-0 mt-0.5" style={{ color: 'var(--color-warm-gray)' }} />
+        <p style={{ color: 'var(--color-warm-gray)' }}>
+          By submitting, you certify that all information above is accurate and that you have the
+          legal authority to license these photos. False submissions may result in account termination
+          and legal liability.
+        </p>
+      </div>
+
+      {/* Error */}
+      <AnimatePresence>
+        {submitState === 'error' && (
+          <motion.div
+            initial={{ opacity: 0, y: -8 }}
+            animate={{ opacity: 1, y: 0 }}
+            exit={{ opacity: 0, y: -8 }}
+            className="flex items-center gap-3 p-4 rounded-2xl"
+            style={{ background: 'rgba(255, 181, 167, 0.2)', border: '1px solid var(--color-peach)' }}
+            role="alert"
+          >
+            <AlertCircle size={16} style={{ color: 'var(--color-peach-dark)' }} />
+            <p className="text-sm font-medium" style={{ color: 'var(--color-brown)' }}>
+              {errorMsg}
+            </p>
+          </motion.div>
+        )}
+      </AnimatePresence>
+
+      {/* Navigation */}
+      <div className="flex items-center justify-between pt-2">
+        <button
+          onClick={onBack}
+          disabled={submitState === 'submitting'}
+          className={clsx(
+            'flex items-center gap-2 px-6 py-3 rounded-3xl text-sm font-semibold tracking-wide',
+            'transition-all hover:scale-105 disabled:opacity-40 disabled:cursor-not-allowed'
+          )}
+          style={{
+            background: 'var(--color-cream-dark)',
+            color: 'var(--color-warm-gray)',
+          }}
+        >
+          <ChevronLeft size={16} />
+          Back
+        </button>
+
+        <motion.button
+          whileHover={submitState === 'idle' ? { scale: 1.03 } : {}}
+          whileTap={submitState === 'idle' ? { scale: 0.97 } : {}}
+          onClick={handleSubmit}
+          disabled={submitState === 'submitting'}
+          className={clsx(
+            'flex items-center gap-2.5 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide',
+            'transition-all duration-200',
+            submitState === 'submitting' && 'cursor-not-allowed'
+          )}
+          style={{
+            background:
+              submitState === 'error'
+                ? 'var(--color-peach-dark)'
+                : 'var(--color-peach)',
+            color: 'var(--color-brown)',
+          }}
+        >
+          {submitState === 'submitting' ? (
+            <>
+              <Loader2 size={16} className="animate-spin" />
+              Submitting…
+            </>
+          ) : submitState === 'error' ? (
+            'Try Again'
+          ) : (
+            <>
+              <CheckCircle2 size={16} />
+              Submit for Review
+            </>
+          )}
+        </motion.button>
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/licensor/Stepper.tsx b/src/components/licensor/Stepper.tsx
new file mode 100644
index 0000000..e8f3471
--- /dev/null
+++ b/src/components/licensor/Stepper.tsx
@@ -0,0 +1,139 @@
+'use client'
+
+import { motion } from 'framer-motion'
+import { Check } from 'lucide-react'
+import clsx from 'clsx'
+
+const STEPS = [
+  { label: 'Upload Photos', shortLabel: 'Upload' },
+  { label: 'Copyright Info', shortLabel: 'Copyright' },
+  { label: 'Likeness Release', shortLabel: 'Release' },
+  { label: 'Review & Submit', shortLabel: 'Review' },
+]
+
+interface StepperProps {
+  currentStep: number
+}
+
+export default function Stepper({ currentStep }: StepperProps) {
+  return (
+    <div className="w-full px-4 py-6 md:px-8 md:py-8">
+      <div className="max-w-2xl mx-auto">
+        <div className="flex items-center justify-between relative">
+          {/* Connecting lines (behind circles) */}
+          <div
+            className="absolute top-5 left-0 right-0 h-0.5 mx-12 md:mx-16"
+            style={{ background: 'var(--color-cream-dark)' }}
+          />
+          {/* Animated filled progress line */}
+          <div
+            className="absolute top-5 left-0 h-0.5 mx-12 md:mx-16 transition-all duration-700 ease-in-out"
+            style={{
+              background: 'var(--color-sage)',
+              right: 0,
+              width: `calc(${(currentStep / (STEPS.length - 1)) * 100}% - 6rem)`,
+            }}
+          />
+
+          {STEPS.map((step, index) => {
+            const isCompleted = index < currentStep
+            const isActive = index === currentStep
+            const isUpcoming = index > currentStep
+
+            return (
+              <div
+                key={step.label}
+                className="relative flex flex-col items-center gap-2 z-10"
+              >
+                {/* Circle */}
+                <motion.div
+                  initial={false}
+                  animate={{
+                    scale: isActive ? 1.12 : 1,
+                  }}
+                  transition={{ type: 'spring', stiffness: 300, damping: 25 }}
+                  className={clsx(
+                    'w-10 h-10 rounded-full flex items-center justify-center',
+                    'text-sm font-bold transition-colors duration-300',
+                    'shadow-sm',
+                    {
+                      'text-white': isCompleted || isActive,
+                    }
+                  )}
+                  style={{
+                    background: isCompleted
+                      ? 'var(--color-sage-dark)'
+                      : isActive
+                        ? 'var(--color-peach)'
+                        : 'var(--color-cream)',
+                    border: isUpcoming
+                      ? '2px solid var(--color-cream-dark)'
+                      : 'none',
+                    color: isUpcoming
+                      ? 'var(--color-warm-gray)'
+                      : 'var(--color-brown)',
+                  }}
+                  aria-current={isActive ? 'step' : undefined}
+                  aria-label={`Step ${index + 1}: ${step.label}${isCompleted ? ' (completed)' : isActive ? ' (current)' : ''}`}
+                >
+                  {isCompleted ? (
+                    <Check size={16} strokeWidth={3} />
+                  ) : (
+                    <span>{index + 1}</span>
+                  )}
+                </motion.div>
+
+                {/* Label */}
+                <span
+                  className={clsx(
+                    'text-xs font-medium tracking-wide text-center',
+                    'hidden sm:block',
+                    {
+                      'font-semibold': isActive,
+                    }
+                  )}
+                  style={{
+                    color: isActive
+                      ? 'var(--color-brown)'
+                      : isCompleted
+                        ? 'var(--color-sage-dark)'
+                        : 'var(--color-warm-gray)',
+                  }}
+                >
+                  {step.label}
+                </span>
+                {/* Short label for mobile */}
+                <span
+                  className={clsx(
+                    'text-xs font-medium tracking-wide text-center',
+                    'sm:hidden',
+                    {
+                      'font-semibold': isActive,
+                    }
+                  )}
+                  style={{
+                    color: isActive
+                      ? 'var(--color-brown)'
+                      : isCompleted
+                        ? 'var(--color-sage-dark)'
+                        : 'var(--color-warm-gray)',
+                  }}
+                >
+                  {step.shortLabel}
+                </span>
+              </div>
+            )
+          })}
+        </div>
+
+        {/* Step counter (mobile) */}
+        <p
+          className="mt-4 text-center text-xs sm:hidden"
+          style={{ color: 'var(--color-warm-gray)' }}
+        >
+          Step {currentStep + 1} of {STEPS.length}: {STEPS[currentStep].label}
+        </p>
+      </div>
+    </div>
+  )
+}
diff --git a/src/components/shared/Navbar.tsx b/src/components/shared/Navbar.tsx
new file mode 100644
index 0000000..2481607
--- /dev/null
+++ b/src/components/shared/Navbar.tsx
@@ -0,0 +1,86 @@
+'use client'
+
+import Link from 'next/link'
+import { usePathname } from 'next/navigation'
+import { motion } from 'framer-motion'
+import { Camera, Search, LayoutDashboard, LogIn } from 'lucide-react'
+import clsx from 'clsx'
+
+const NAV_LINKS = [
+  { href: '/browse', label: 'Browse', icon: Search },
+  { href: '/licensor', label: 'Upload', icon: Camera },
+  { href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
+]
+
+export default function Navbar() {
+  const pathname = usePathname()
+
+  return (
+    <nav
+      className="sticky top-0 z-50 flex items-center justify-between px-6 py-4 md:px-12 border-b"
+      style={{
+        background: 'rgba(255, 241, 230, 0.92)',
+        borderColor: 'var(--color-cream-dark)',
+        backdropFilter: 'blur(12px)',
+        WebkitBackdropFilter: 'blur(12px)',
+      }}
+    >
+      <Link
+        href="/"
+        className="text-xl font-black tracking-tighter transition-opacity hover:opacity-70"
+        style={{ color: 'var(--color-brown)' }}
+      >
+        i was cute
+      </Link>
+
+      <div className="flex items-center gap-1">
+        {NAV_LINKS.map(({ href, label, icon: Icon }) => {
+          const active = pathname.startsWith(href)
+          return (
+            <Link
+              key={href}
+              href={href}
+              className={clsx(
+                'relative flex items-center gap-1.5 px-4 py-2 rounded-2xl text-sm font-medium',
+                'transition-all duration-200 hover:scale-105'
+              )}
+              style={{
+                color: active ? 'var(--color-brown)' : 'var(--color-warm-gray)',
+              }}
+            >
+              {active && (
+                <motion.div
+                  layoutId="nav-active"
+                  className="absolute inset-0 rounded-2xl"
+                  style={{ background: 'var(--color-cream-dark)' }}
+                  transition={{ type: 'spring', stiffness: 380, damping: 30 }}
+                />
+              )}
+              <span className="relative z-10 flex items-center gap-1.5">
+                <Icon size={14} />
+                <span className="hidden sm:inline">{label}</span>
+              </span>
+            </Link>
+          )
+        })}
+
+        <div
+          className="w-px h-6 mx-2"
+          style={{ background: 'var(--color-cream-dark)' }}
+        />
+
+        <Link
+          href="/login"
+          className="flex items-center gap-1.5 px-4 py-2 rounded-2xl text-sm font-semibold transition-all hover:scale-105"
+          style={{
+            background: 'var(--color-peach)',
+            color: 'var(--color-brown)',
+          }}
+        >
+          <LogIn size={14} />
+          <span className="hidden sm:inline">Sign In</span>
+        </Link>
+      </div>
+    </nav>
+  )
+}
diff --git a/src/components/shared/VersionSwitcher.tsx b/src/components/shared/VersionSwitcher.tsx
new file mode 100644
index 0000000..19872e9
--- /dev/null
+++ b/src/components/shared/VersionSwitcher.tsx
@@ -0,0 +1,53 @@
+'use client'
+
+import { usePathname } from 'next/navigation'
+import { Palette, Check } from 'lucide-react'
+
+const VERSIONS = [
+  { path: '/', label: 'Original' },
+  { path: '/v1', label: 'V1 Silk' },
+  { path: '/v2', label: 'V2 Glass' },
+  { path: '/v3', label: 'V3 Polaroid' },
+]
+
+export default function VersionSwitcher() {
+  const pathname = usePathname()
+
+  const isLanding = pathname === '/' || pathname.startsWith('/v')
+  if (!isLanding) return null
+
+  return (
+    <div
+      className="fixed bottom-0 left-0 right-0 flex items-center justify-center gap-2 px-4 py-3"
+      style={{
+        zIndex: 99999,
+        background: 'rgba(61, 64, 91, 0.92)',
+        backdropFilter: 'blur(12px)',
+        pointerEvents: 'auto',
+      }}
+    >
+      <Palette size={14} style={{ color: 'var(--color-peach)' }} />
+      <span className="text-xs font-medium mr-1" style={{ color: 'rgba(255,255,255,0.6)' }}>
+        Design:
+      </span>
+      {VERSIONS.map((v) => {
+        const active = v.path === pathname
+        return (
+          <a
+            key={v.path}
+            href={v.path}
+            className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-semibold transition-all hover:scale-105 no-underline"
+            style={{
+              background: active ? 'var(--color-peach)' : 'rgba(255,255,255,0.12)',
+              color: active ? 'var(--color-brown)' : 'rgba(255,255,255,0.7)',
+              pointerEvents: 'auto',
+            }}
+          >
+            {active && <Check size={10} />}
+            {v.label}
+          </a>
+        )
+      })}
+    </div>
+  )
+}
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
new file mode 100644
index 0000000..56ec324
--- /dev/null
+++ b/src/lib/auth.ts
@@ -0,0 +1,23 @@
+import { cookies } from 'next/headers'
+import { query } from './db'
+
+export async function getSession() {
+  const cookieStore = await cookies()
+  const token = cookieStore.get('iwascute-session')?.value
+  if (!token) return null
+
+  const result = await query(
+    `SELECT s.*, u.id as user_id, u.email, u.display_name, u.role
+     FROM sessions s JOIN users u ON s.user_id = u.id
+     WHERE s.token = $1 AND s.expires_at > NOW()`,
+    [token]
+  )
+
+  return result.rows[0] || null
+}
+
+export async function requireAdmin() {
+  const session = await getSession()
+  if (!session || session.role !== 'admin') return null
+  return session
+}
diff --git a/src/lib/db.ts b/src/lib/db.ts
new file mode 100644
index 0000000..50eeb33
--- /dev/null
+++ b/src/lib/db.ts
@@ -0,0 +1,11 @@
+import { Pool } from 'pg'
+
+const pool = new Pool({
+  connectionString: 'postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/iwascute',
+  max: 10,
+})
+
+export async function query(text: string, params?: unknown[]) {
+  const result = await pool.query(text, params)
+  return result
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..cf9c65d
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,34 @@
+{
+  "compilerOptions": {
+    "target": "ES2017",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "strict": true,
+    "noEmit": true,
+    "esModuleInterop": true,
+    "module": "esnext",
+    "moduleResolution": "bundler",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "jsx": "react-jsx",
+    "incremental": true,
+    "plugins": [
+      {
+        "name": "next"
+      }
+    ],
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "include": [
+    "next-env.d.ts",
+    "**/*.ts",
+    "**/*.tsx",
+    ".next/types/**/*.ts",
+    ".next/dev/types/**/*.ts",
+    "**/*.mts"
+  ],
+  "exclude": ["node_modules"]
+}

(oldest)  ·  back to IWasCute  ·  snapshot: 2 file(s) changed, +1 new, ~1 modified 12b7f89 →