diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..0f163201b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,29 @@
+# Dependencies
+node_modules
+.pnpm-store
+
+# Build output
+.next
+out
+dist
+
+# Logs
+npm-debug.log*
+yarn-debug.log*
+pnpm-debug.log*
+*.log
+
+# Environment files
+.env
+.env.local
+.env.*
+.envrc
+
+# VCS
+.git
+.gitignore
+
+# Editor/misc
+.vscode
+.DS_Store
+*.swp
diff --git a/.gitignore b/.gitignore
index fd3dbb571..c2dd114d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,6 @@
# dependencies
/node_modules
-/.pnp
-.pnp.js
-.yarn/install-state.gz
-
-# testing
-/coverage
# next.js
/.next/
@@ -16,17 +10,14 @@
# production
/build
-# misc
-.DS_Store
-*.pem
-
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+.pnpm-debug.log*
-# local env files
-.env*.local
+# env files
+.env*
# vercel
.vercel
@@ -34,3 +25,16 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# project temp and large binary assets (avoid committing raw media dumps)
+temp/
+temp/**
+*.mp4
+*.mov
+*.avi
+*.mkv
+*.psd
+*.ai
+*.zip
+*.7z
+*.rar
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..cc0ac9ee5
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,30 @@
+# syntax=docker/dockerfile:1
+
+# 1) Install dependencies
+FROM node:20-alpine AS deps
+WORKDIR /app
+COPY package.json package-lock.json ./
+RUN npm ci --legacy-peer-deps
+
+# 2) Build the Next.js app (standalone output is enabled in next.config.mjs)
+FROM node:20-alpine AS builder
+WORKDIR /app
+ENV NEXT_TELEMETRY_DISABLED=1
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+RUN npm run build
+
+# 3) Production runner (small image using standalone output)
+FROM node:20-alpine AS runner
+WORKDIR /app
+ENV NODE_ENV=production
+ENV PORT=3000
+ENV HOSTNAME=0.0.0.0
+
+# Copy standalone server and static files
+COPY --from=builder /app/.next/standalone ./
+COPY --from=builder /app/.next/static ./.next/static
+COPY --from=builder /app/public ./public
+
+EXPOSE 3000
+CMD ["node", "server.js"]
diff --git a/README.md b/README.md
index e215bc4cc..74bd3ded0 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,125 @@
-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).
+# United Tattoo — Official Website (Next.js + ShadCN UI)
-## Getting Started
+Hi, I’m Nicholai. I built this site for my friend Christy (aka Ink Mama) and the United Tattoo crew in Fountain, CO. The goal was simple: give the studio a site that actually reflects the art, the people, and the experience — not the stiff, generic stuff you usually see. This is also a thank you for everything Christy has done for Amari (my girlfriend and soulmate), who was her apprentice. So yeah, this is personal — and it shows.
-First, run the development server:
+This repo powers the official United Tattoo website, built with:
+- Next.js App Router
+- TypeScript
+- Tailwind CSS
+- ShadCN UI components (used across all pages)
+- Lenis (smooth scroll)
+- Lucide (icons)
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
+Live dev server: http://localhost:3000
-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.
+## Project Structure
-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.
+- app/
+ - page.tsx — homepage (Hero, Artists, Services, Contact sections)
+ - aftercare/page.tsx — aftercare instructions (ShadCN-driven)
+ - deposit/page.tsx — deposit policy + payment options (Afterpay, Stripe)
+ - terms/page.tsx — terms of service
+ - privacy/page.tsx — privacy policy
+ - artists/ — artists listing + dynamic routes for profiles (coming from data)
+ - book/page.tsx — booking flow
+ - specials/page.tsx — promotions (monthly specials, VIP list)
+ - contact/page.tsx — contact
+ - gift-cards/page.tsx — gift card info
-## Learn More
+- components/
+ - hero-section.tsx, artists-section.tsx, services-section.tsx, contact-section.tsx
+ - aftercare-page.tsx, deposit-page.tsx, terms-page.tsx, privacy-page.tsx
+ - booking-form.tsx — multi-step form using ShadCN components
+ - footer.tsx — contains direct links to Aftercare, Deposit Policy, Terms, and Privacy
+ - ui/ — ShadCN UI primitives
-To learn more about Next.js, take a look at the following resources:
+- data/
+ - artists.ts — single source of truth for artist metadata and images used across pages
-- [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.
+- public/
+ - united-logo-*.png/jpg, artists/, and other stable assets
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
-## Deploy on Vercel
+## Content & Assets
-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.
+- All the “real” content, bios, and images are now wired in (not seed placeholders).
+- Artist portraits and tattoo samples live under public/artists/.
+- Pages like Aftercare, Deposit, Terms, and Privacy use consistent styling patterned after the homepage and portfolio pages — powered by ShadCN components.
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+If you need to re-copy images from the temp folder into public (on your machine), there’s a helper script:
+- ./copy-artist-images.sh
+Note: This script expects the temp directory structure to exist locally; it silently skips if a source is missing.
+
+
+## Getting Started (Local Dev)
+
+- Install deps:
+ - npm install
+
+- Run dev server:
+ - npm run dev
+ - Open http://localhost:3000
+
+- Lint (optional):
+ - npm run lint
+
+Build:
+- npm run build
+- npm start
+
+
+## Docker
+
+This repo is docker-ready. We build a standalone Next.js app for a smaller runtime image.
+
+Build image:
+- docker build -t united-tattoo:latest .
+
+Run container (port 3000):
+- docker run --rm -p 3000:3000 -e PORT=3000 united-tattoo:latest
+- Open http://localhost:3000
+
+Notes:
+- next.config.mjs sets output: "standalone"
+- The Dockerfile copies .next/standalone + .next/static and runs the server with HOSTNAME=0.0.0.0
+
+
+## Pages Overview
+
+- Home — Bold, high-contrast, split imagery, parallax accents. This sets the identity.
+- Artists — Grid and profile surfaces wired to data/artists.ts. Each artist shows image, specialties, and sample work.
+- Aftercare — Two flows: General Aftercare and Transparent Bandage Aftercare (accurate, readable, ShadCN cards + alerts).
+- Deposit — Clear policy, payment options, and compliance notes (LW2 Investments, LLC oversight).
+- Terms & Privacy — Straightforward, legally sound, human-readable. Both accessible from the footer.
+- Booking — Multi-step form with ShadCN components and validation-friendly structure.
+- Specials — Marketing surface for time-bound promotions and membership-like advantages.
+
+
+## Design Language
+
+- ShadCN components everywhere possible
+- Monochrome foundation with high contrast and cinematic image splits
+- Type scales + spacing match the homepage/portfolio feeling
+- Lucide icons for affordances
+
+
+## Tech Notes
+
+- TypeScript errors are ignored during build in CI to allow non-blocking content/design iteration (next.config.mjs).
+- Images are unoptimized (no Next image loader), served statically; change if you plan to put this behind a CDN with transforms.
+- Smooth scroll and parallax-style offsets are kept subtle to let the work shine.
+
+
+## Deployment
+
+- Standard Next.js deploys work (Vercel, Node server, Docker)
+- For self-hosting or VPS, use the Dockerfile in the repo
+- The site runs on port 3000 by default
+
+
+## Why This Exists
+
+Because Christy deserved a proper site — and because the previous one was, bluntly, not it. United Tattoo is more than a shop. It’s a community with real people and real art. This site tries to honor that.
+
+— Nicholai
diff --git a/app/ClientLayout.tsx b/app/ClientLayout.tsx
new file mode 100644
index 000000000..926e074e7
--- /dev/null
+++ b/app/ClientLayout.tsx
@@ -0,0 +1,23 @@
+"use client"
+
+import type React from "react"
+import { SmoothScrollProvider } from "@/components/smooth-scroll-provider"
+import { useSearchParams } from "next/navigation"
+import { Suspense } from "react"
+import "./globals.css"
+
+export default function ClientLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode
+}>) {
+ const searchParams = useSearchParams()
+
+ return (
+ <>
+ Loading...}>
+ {children}
+
+ >
+ )
+}
diff --git a/app/aftercare/page.tsx b/app/aftercare/page.tsx
new file mode 100644
index 000000000..07f600ad4
--- /dev/null
+++ b/app/aftercare/page.tsx
@@ -0,0 +1,15 @@
+import { Navigation } from "@/components/navigation"
+import { AftercarePage } from "@/components/aftercare-page"
+import { Footer } from "@/components/footer"
+
+export default function Aftercare() {
+ return (
+
+
+
+
+
+ )
}
diff --git a/app/privacy/page.tsx b/app/privacy/page.tsx
new file mode 100644
index 000000000..cd027d480
--- /dev/null
+++ b/app/privacy/page.tsx
@@ -0,0 +1,15 @@
+import { Navigation } from "@/components/navigation"
+import { PrivacyPage } from "@/components/privacy-page"
+import { Footer } from "@/components/footer"
+
+export default function Privacy() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/specials/page.tsx b/app/specials/page.tsx
new file mode 100644
index 000000000..4ba403b75
--- /dev/null
+++ b/app/specials/page.tsx
@@ -0,0 +1,15 @@
+import { Navigation } from "@/components/navigation"
+import { SpecialsPage } from "@/components/specials-page"
+import { Footer } from "@/components/footer"
+
+export default function Specials() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/terms/page.tsx b/app/terms/page.tsx
new file mode 100644
index 000000000..e8af9e383
--- /dev/null
+++ b/app/terms/page.tsx
@@ -0,0 +1,15 @@
+import { Navigation } from "@/components/navigation"
+import { TermsPage } from "@/components/terms-page"
+import { Footer } from "@/components/footer"
+
+export default function Terms() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/components.json b/components.json
new file mode 100644
index 000000000..4ee62ee10
--- /dev/null
+++ b/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/components/aftercare-page.tsx b/components/aftercare-page.tsx
new file mode 100644
index 000000000..6cd1d116e
--- /dev/null
+++ b/components/aftercare-page.tsx
@@ -0,0 +1,352 @@
+"use client"
+
+import { useState } from "react"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Badge } from "@/components/ui/badge"
+import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import {
+ CheckCircle,
+ Clock,
+ Shield,
+ AlertTriangle,
+ Droplets,
+ Phone,
+ Mail,
+ Heart,
+} from "lucide-react"
+import Link from "next/link"
+
+type Phase = {
+ phase: string
+ icon: any
+ color: string
+ bgColor: string
+ steps: string[]
+}
+
+const generalAftercare: Record = {
+ immediate: {
+ phase: "Immediate Aftercare",
+ icon: Clock,
+ color: "text-red-400",
+ bgColor: "bg-red-950/20 border-red-900/30",
+ steps: [
+ "Keep the bandage or dressing on for 1 to 4 hours to prevent exposure to airborne bacteria.",
+ "Wash your hands thoroughly before removing the bandage.",
+ "Remove the bandage gently and cleanse your tattoo using lukewarm water and mild, unscented antibacterial soap.",
+ "Pat dry with a clean paper towel — never touch your tattoo unless you have just washed your hands.",
+ "Apply a very light layer of the recommended aftercare product or fragrance-free lotion.",
+ ],
+ },
+ general: {
+ phase: "General Aftercare",
+ icon: Shield,
+ color: "text-yellow-400",
+ bgColor: "bg-yellow-950/20 border-yellow-900/30",
+ steps: [
+ "Cleanse your tattoo multiple times a day with lukewarm water and antibacterial soap.",
+ "Apply a thin layer of ointment or lotion to keep your tattoo moisturized.",
+ "After the first few days, transition to a non-scented lotion.",
+ "Avoid wearing tight clothing over your tattoo.",
+ "Avoid immersing your tattoo in pools, oceans, lakes, or hot tubs for 2–4 weeks.",
+ "Minimize activities that lead to excessive sweating and sun exposure.",
+ "Do not pick, peel, or scratch scabbing or hardened layers.",
+ ],
+ },
+ longterm: {
+ phase: "Long-term Aftercare",
+ icon: Heart,
+ color: "text-green-400",
+ bgColor: "bg-green-950/20 border-green-900/30",
+ steps: [
+ "Always use a minimum of SPF 30 sunblock to protect your tattoo from UV rays.",
+ "Keep your tattoos well-moisturized, especially in areas prone to fading (hands, feet, knees, elbows).",
+ "The outermost layer of skin typically takes 2–3 weeks to heal.",
+ "Complete healing may take up to 6 months.",
+ "Ongoing care will contribute to the longevity and vibrancy of your tattoo.",
+ ],
+ },
+}
+
+const transparentBandage: Record = {
+ removal: {
+ phase: "Bandage Removal",
+ icon: Droplets,
+ color: "text-blue-400",
+ bgColor: "bg-blue-950/20 border-blue-900/30",
+ steps: [
+ "Remove bandage in the shower for added comfort — running water helps adhesive detachment.",
+ "Peel back in the direction of hair growth.",
+ "Wash hands before handling your tattoo.",
+ "Cleanse with lukewarm water and mild antibacterial soap multiple times a day.",
+ "If the tattoo feels slippery, carefully remove excess plasma to avoid scab formation.",
+ "Air dry or gently pat with a paper towel.",
+ ],
+ },
+ reapply: {
+ phase: "Bandage Reapplication (If Advised)",
+ icon: Shield,
+ color: "text-purple-400",
+ bgColor: "bg-purple-950/20 border-purple-900/30",
+ steps: [
+ "DO NOT apply ointments or lotions unless directed by your artist.",
+ "Apply the bandage only to the tattoo, avoiding surrounding skin.",
+ "Cut and trim to fit with ~1 inch around all sides (rounded edges adhere better).",
+ "Keep the new bandage on for 3–6 days unless your artist advises otherwise.",
+ "Remove earlier if irritation, fluid buildup, or loosening occurs.",
+ "Avoid reapplying once the tattoo enters the scabbing or flaking phase.",
+ ],
+ },
+}
+
+const infectionWarning = [
+ "Increased redness or swelling that spreads beyond the tattoo",
+ "Pain when touching the tattoo or a throbbing sensation",
+ "Sensation of heat from the tattoo area",
+ "Yellow or green discharge with offensive odor",
+ "Fever or chills",
+ "Red streaking from the tattoo",
+ "Excessive swelling after the first day",
+ "Signs of allergic reaction",
+]
+
+export function AftercarePage() {
+ const [tab, setTab] = useState<"general" | "transparent">("general")
+
+ return (
+
+ {/* Hero / Header */}
+
+
+
+
+
+
+
+
+ Tattoo Aftercare
+
+
+ Proper aftercare is crucial for the healing and longevity of your new tattoo. Follow these
+ instructions carefully to ensure the best results.
+
+
+
+
+
+ {/* Licensing Notice */}
+
+
+
+
+
+ United Tattoo is proudly licensed by the El Paso County Health Department and fully supports
+ health department regulations to protect the health of our customers.
+
+
+
+
+
+ {/* Tabs: General vs Transparent Bandage */}
+
+
+ setTab(v as any)} className="w-full">
+
+
+ General Tattoo Aftercare
+
+
+ Transparent Bandage Aftercare
+
+
+
+ {/* General Aftercare */}
+
+
+
+
+
+
+ Signs of Infection — Seek Medical Attention
+
+
+
+
+ {infectionWarning.map((sign, i) => (
+
+
+ {sign}
+
+ ))}
+
+
+
+
+ Important
+
+ If you experience any of these symptoms, contact our studio immediately at{" "}
+
+ (719) 698-9004
+ {" "}
+ or seek urgent medical attention.
+
+
+
+
+
+
+
+ {/* Healing Timeline */}
+
+
+
+
+
+ Surface Healing
+
+
+
2–3 Weeks
+
+ The outermost layer of skin typically heals in 2–3 weeks. Continue following aftercare during this time.
+
+
+
+
+
+
+ Deep Healing
+
+
+
2–4 Months
+
+ Deeper layers of skin continue healing. Maintain a consistent moisturizing routine.
+
+
+
+
+
+
+ Complete Healing
+
+
+
Up to 6 Months
+
+ Full healing may take up to 6 months. Protect with SPF and keep moisturized.
+
+
+
+
+
+
+
+ {/* Contact / Help */}
+
+
+
+
+
Questions?
+
+ Reach out if you have any aftercare questions or concerns. We’re here to help.
+
+ Explore {artist.name}'s portfolio showcasing {artist.experience} of expertise in{" "}
+ {artist.specialty.toLowerCase()}. Each piece represents a unique collaboration between artist and
+ client.
+
+ Book a consultation with {artist.name} to discuss your next tattoo. Whether you're looking for a
+ traditional piece or something with a modern twist, let's bring your vision to life.
+