fix: improve mobile UX, polish artist cards, and enhance contact modal with responsive backgrounds

This commit is contained in:
Nicholai 2025-09-17 08:29:24 -06:00
parent d925ab75cf
commit ebe8fa2407
18 changed files with 252 additions and 62 deletions

43
.clinerules/cloudflare.md Normal file
View File

@ -0,0 +1,43 @@
# NextJS + Cloudflare + OpenNext Deployment
## Setup Requirements
- Node.js 18+ and Cloudflare account required
- **@opennextjs/cloudflare** adapter mandatory (not edge runtime)
- Global Wrangler CLI: `npm install -g wrangler`
- All deployments via OpenNext adapter; no direct NextJS builds
## Project Configuration
- **wrangler.toml**: compatibility_date ≥ "2024-09-23", nodejs_compat flag
- **package.json**: `pages:build` script runs `npx @opennextjs/cloudflare@latest`
- **next.config.js**: `output: 'standalone'`, image optimization configured
- Build output directory: `.vercel/output/static`
## Build & Deploy Process
- Build command: `npm run pages:build` (transforms NextJS → Workers)
- Local testing: `npm run preview` (required before deploy)
- Deploy: `npm run deploy` or Cloudflare Pages Git integration
- Never deploy untested builds; preview mimics production runtime
## Environment & Security
- Environment variables in both Cloudflare Dashboard and `wrangler.toml`
- Secrets via `wrangler secret put SECRET_NAME` (not in wrangler.toml)
- Security headers required in API routes (X-Frame-Options, CSP, etc.)
- Cache headers mandatory for API endpoints: `s-maxage=86400, stale-while-revalidate`
## Performance & Limits
- Bundle size limits: 3MB free tier, 15MB paid
- Dynamic imports for heavy components to reduce cold starts
- Static files in `public/` directory only
- Image optimization via Cloudflare Images or custom loader
## Database & Storage
- Cloudflare D1 binding in wrangler.toml for SQL databases
- Workers KV for key-value storage
- All DB operations via environment bindings (env.DB, env.KV)
- No direct database connections; use Cloudflare services
## CI/CD Integration
- GitHub Actions with CLOUDFLARE_API_TOKEN secret
- Build step: `npm run pages:build`
- Deploy: `wrangler pages deploy .vercel/output/static`
- Fail builds on type/compatibility errors

View File

@ -6,8 +6,6 @@ import { ArtistsSection } from "@/components/artists-section"
import { ServicesSection } from "@/components/services-section" import { ServicesSection } from "@/components/services-section"
import { ContactSection } from "@/components/contact-section" import { ContactSection } from "@/components/contact-section"
import { Footer } from "@/components/footer" import { Footer } from "@/components/footer"
import { MobileBookingBar } from "@/components/mobile-booking-bar"
export default function HomePage() { export default function HomePage() {
return ( return (
<main className="min-h-screen"> <main className="min-h-screen">
@ -27,7 +25,6 @@ export default function HomePage() {
<ContactSection /> <ContactSection />
</div> </div>
<Footer /> <Footer />
<MobileBookingBar />
</main> </main>
) )
} }

View File

@ -23,7 +23,7 @@ export function ArtistsSection() {
} }
}) })
}, },
{ threshold: 0.1, rootMargin: "0px 0px -50px 0px" }, { threshold: 0.2, rootMargin: "0px 0px 0px 0px" },
) )
const cards = sectionRef.current?.querySelectorAll("[data-index]") const cards = sectionRef.current?.querySelectorAll("[data-index]")
@ -55,29 +55,30 @@ export function ArtistsSection() {
const sectionTop = sectionRef.current?.offsetTop || 0 const sectionTop = sectionRef.current?.offsetTop || 0
const relativeScroll = scrollY - sectionTop const relativeScroll = scrollY - sectionTop
leftColumnRef.current.style.transform = `translateY(${relativeScroll * -0.05}px)` leftColumnRef.current.style.transform = `translateY(${relativeScroll * -0.025}px)`
centerColumnRef.current.style.transform = `translateY(0px)` centerColumnRef.current.style.transform = `translateY(0px)`
rightColumnRef.current.style.transform = `translateY(${relativeScroll * 0.05}px)` rightColumnRef.current.style.transform = `translateY(${relativeScroll * 0.025}px)`
const leftImages = leftColumnRef.current.querySelectorAll(".artist-image") const leftImages = leftColumnRef.current.querySelectorAll(".artist-image")
const centerImages = centerColumnRef.current.querySelectorAll(".artist-image") const centerImages = centerColumnRef.current.querySelectorAll(".artist-image")
const rightImages = rightColumnRef.current.querySelectorAll(".artist-image") const rightImages = rightColumnRef.current.querySelectorAll(".artist-image")
leftImages.forEach((img) => { leftImages.forEach((img) => {
;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.02}px)` ;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.01}px)`
}) })
centerImages.forEach((img) => { centerImages.forEach((img) => {
;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.015}px)` ;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.0075}px)`
}) })
rightImages.forEach((img) => { rightImages.forEach((img) => {
;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.01}px)` ;(img as HTMLElement).style.transform = `translateY(${relativeScroll * -0.005}px)`
}) })
} }
}, [scrollY]) }, [scrollY])
const leftColumn = artists.filter((_, index) => index % 3 === 0) // Better distribution for visual balance
const centerColumn = artists.filter((_, index) => index % 3 === 1) const leftColumn = [artists[0], artists[3], artists[6]] // Christy, Donovan, John
const rightColumn = artists.filter((_, index) => index % 3 === 2) const centerColumn = [artists[1], artists[4], artists[7]] // Angel, EJ, Pako
const rightColumn = [artists[2], artists[5], artists[8]] // Amari, Heather, Sole
return ( return (
<section ref={sectionRef} id="artists" className="relative overflow-hidden bg-black"> <section ref={sectionRef} id="artists" className="relative overflow-hidden bg-black">
@ -112,7 +113,7 @@ export function ArtistsSection() {
</div> </div>
</div> </div>
<div className="relative z-10 px-8 lg:px-16 pb-20"> <div className="relative z-10 px-8 lg:px-16 pb-32">
<div className="max-w-screen-2xl mx-auto"> <div className="max-w-screen-2xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div ref={leftColumnRef} className="space-y-8"> <div ref={leftColumnRef} className="space-y-8">
@ -126,25 +127,33 @@ export function ArtistsSection() {
: "opacity-0 translate-y-8" : "opacity-0 translate-y-8"
}`} }`}
style={{ style={{
transitionDelay: `${artists.indexOf(artist) * 100}ms`, transitionDelay: `${artists.indexOf(artist) * 50}ms`,
}} }}
> >
<div className="relative h-[600px] overflow-hidden rounded-lg shadow-2xl"> <div className="relative w-full aspect-[4/5] overflow-hidden rounded-lg shadow-2xl">
<div className="absolute inset-0 bg-black artist-image"> <div className="absolute inset-0 bg-black artist-image">
<div className="absolute left-0 top-0 w-1/2 h-full"> {/* Portfolio background - full width */}
<img <div className="absolute inset-0">
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
/>
</div>
<div className="absolute right-0 top-0 w-1/2 h-full">
<img <img
src={artist.workImages?.[0] || "/placeholder.svg"} src={artist.workImages?.[0] || "/placeholder.svg"}
alt={`${artist.name} tattoo work`} alt={`${artist.name} tattoo work`}
className="w-full h-full object-cover scale-110" className="w-full h-full object-cover scale-110"
/> />
{/* Darkening overlay to push background further back */}
<div className="absolute inset-0 bg-black/40"></div>
</div>
{/* Artist portrait - with proper feathered mask */}
<div className="absolute left-0 top-0 w-3/5 h-full">
<img
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
style={{
maskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)',
WebkitMaskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)'
}}
/>
</div> </div>
</div> </div>
@ -155,7 +164,7 @@ export function ArtistsSection() {
</span> </span>
</div> </div>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-full group-hover:translate-y-0 transition-transform duration-500"> <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-0 lg:translate-y-full lg:group-hover:translate-y-0 transition-transform duration-500">
<h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3> <h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3>
<p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p> <p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p>
<p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p> <p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p>
@ -194,25 +203,33 @@ export function ArtistsSection() {
: "opacity-0 translate-y-8" : "opacity-0 translate-y-8"
}`} }`}
style={{ style={{
transitionDelay: `${artists.indexOf(artist) * 100}ms`, transitionDelay: `${artists.indexOf(artist) * 50}ms`,
}} }}
> >
<div className="relative h-[600px] overflow-hidden rounded-lg shadow-2xl"> <div className="relative w-full aspect-[4/5] overflow-hidden rounded-lg shadow-2xl">
<div className="absolute inset-0 bg-black artist-image"> <div className="absolute inset-0 bg-black artist-image">
<div className="absolute left-0 top-0 w-1/2 h-full"> {/* Portfolio background - full width */}
<img <div className="absolute inset-0">
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
/>
</div>
<div className="absolute right-0 top-0 w-1/2 h-full">
<img <img
src={artist.workImages?.[0] || "/placeholder.svg"} src={artist.workImages?.[0] || "/placeholder.svg"}
alt={`${artist.name} tattoo work`} alt={`${artist.name} tattoo work`}
className="w-full h-full object-cover scale-110" className="w-full h-full object-cover scale-110"
/> />
{/* Darkening overlay to push background further back */}
<div className="absolute inset-0 bg-black/40"></div>
</div>
{/* Artist portrait - with proper feathered mask */}
<div className="absolute left-0 top-0 w-3/5 h-full">
<img
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
style={{
maskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)',
WebkitMaskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)'
}}
/>
</div> </div>
</div> </div>
@ -223,7 +240,7 @@ export function ArtistsSection() {
</span> </span>
</div> </div>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-full group-hover:translate-y-0 transition-transform duration-500"> <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-0 lg:translate-y-full lg:group-hover:translate-y-0 transition-transform duration-500">
<h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3> <h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3>
<p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p> <p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p>
<p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p> <p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p>
@ -262,25 +279,33 @@ export function ArtistsSection() {
: "opacity-0 translate-y-8" : "opacity-0 translate-y-8"
}`} }`}
style={{ style={{
transitionDelay: `${artists.indexOf(artist) * 100}ms`, transitionDelay: `${artists.indexOf(artist) * 50}ms`,
}} }}
> >
<div className="relative h-[600px] overflow-hidden rounded-lg shadow-2xl"> <div className="relative w-full aspect-[4/5] overflow-hidden rounded-lg shadow-2xl">
<div className="absolute inset-0 bg-black artist-image"> <div className="absolute inset-0 bg-black artist-image">
<div className="absolute left-0 top-0 w-1/2 h-full"> {/* Portfolio background - full width */}
<img <div className="absolute inset-0">
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
/>
</div>
<div className="absolute right-0 top-0 w-1/2 h-full">
<img <img
src={artist.workImages?.[0] || "/placeholder.svg"} src={artist.workImages?.[0] || "/placeholder.svg"}
alt={`${artist.name} tattoo work`} alt={`${artist.name} tattoo work`}
className="w-full h-full object-cover scale-110" className="w-full h-full object-cover scale-110"
/> />
{/* Darkening overlay to push background further back */}
<div className="absolute inset-0 bg-black/40"></div>
</div>
{/* Artist portrait - with proper feathered mask */}
<div className="absolute left-0 top-0 w-3/5 h-full">
<img
src={artist.faceImage || "/placeholder.svg"}
alt={`${artist.name} portrait`}
className="w-full h-full object-cover scale-110"
style={{
maskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)',
WebkitMaskImage: 'linear-gradient(to right, black 0%, black 70%, transparent 100%)'
}}
/>
</div> </div>
</div> </div>
@ -291,7 +316,7 @@ export function ArtistsSection() {
</span> </span>
</div> </div>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-full group-hover:translate-y-0 transition-transform duration-500"> <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/60 to-transparent p-6 translate-y-0 lg:translate-y-full lg:group-hover:translate-y-0 transition-transform duration-500">
<h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3> <h3 className="text-2xl font-bold tracking-tight mb-2 text-white">{artist.name}</h3>
<p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p> <p className="text-sm font-medium text-white/90 mb-3">{artist.specialty}</p>
<p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p> <p className="text-sm text-white/80 mb-4 leading-relaxed">{artist.bio}</p>
@ -322,7 +347,7 @@ export function ArtistsSection() {
</div> </div>
</div> </div>
<div className="bg-black text-white py-20 px-8 lg:px-16"> <div className="relative z-20 bg-black text-white py-20 px-8 lg:px-16">
<div className="max-w-screen-2xl mx-auto text-center"> <div className="max-w-screen-2xl mx-auto text-center">
<h3 className="text-5xl lg:text-7xl font-bold tracking-tight mb-8">READY?</h3> <h3 className="text-5xl lg:text-7xl font-bold tracking-tight mb-8">READY?</h3>
<p className="text-xl text-white/70 mb-12 max-w-2xl mx-auto"> <p className="text-xl text-white/70 mb-12 max-w-2xl mx-auto">

View File

@ -103,7 +103,9 @@ export function ContactModal({ children }: ContactModalProps) {
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{children}</DialogTrigger> <DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto bg-white border-0 shadow-2xl relative">
{/* Solid background overlay to block any background images */}
<div className="absolute inset-0 bg-white -z-10 rounded-lg"></div>
<DialogHeader> <DialogHeader>
<DialogTitle className="font-playfair text-2xl">Get In Touch</DialogTitle> <DialogTitle className="font-playfair text-2xl">Get In Touch</DialogTitle>
</DialogHeader> </DialogHeader>

View File

@ -36,17 +36,23 @@ export function ContactSection() {
return ( return (
<section id="contact" className="min-h-screen bg-black relative overflow-hidden"> <section id="contact" className="min-h-screen bg-black relative overflow-hidden">
{/* Background logo - desktop only */}
<div <div
className="absolute inset-0 opacity-[0.03] bg-cover bg-center bg-no-repeat blur-sm" className="absolute inset-0 opacity-[0.03] bg-cover bg-center bg-no-repeat blur-sm hidden lg:block"
style={{ style={{
backgroundImage: "url('/united-logo-full.jpg')", backgroundImage: "url('/united-logo-full.jpg')",
transform: `translateY(${scrollY * 0.2}px)`, transform: `translateY(${scrollY * 0.2}px)`,
}} }}
/> />
{/* Mobile solid background */}
<div className="absolute inset-0 bg-black lg:hidden"></div>
<div className="flex flex-col lg:flex-row min-h-screen relative z-10"> <div className="flex flex-col lg:flex-row min-h-screen relative z-10">
<div className="w-full lg:w-1/2 bg-black flex items-center justify-center p-8 lg:p-12"> <div className="w-full lg:w-1/2 bg-black flex items-center justify-center p-8 lg:p-12 relative">
<div className="w-full max-w-md"> {/* Mobile background overlay to hide logo */}
<div className="absolute inset-0 bg-black lg:bg-transparent"></div>
<div className="w-full max-w-md relative z-10">
<div className="mb-8"> <div className="mb-8">
<h2 className="text-4xl font-bold text-white mb-2">Let's Talk</h2> <h2 className="text-4xl font-bold text-white mb-2">Let's Talk</h2>
<p className="text-gray-400">Ready to create something amazing?</p> <p className="text-gray-400">Ready to create something amazing?</p>

View File

@ -45,7 +45,7 @@ export function Navigation() {
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-700 ease-out ${ className={`fixed top-0 left-0 right-0 z-50 transition-all duration-700 ease-out ${
isScrolled isScrolled
? "bg-black/95 backdrop-blur-md shadow-lg border-b border-white/10 opacity-100" ? "bg-black/95 backdrop-blur-md shadow-lg border-b border-white/10 opacity-100"
: "bg-transparent lg:opacity-0 lg:pointer-events-none opacity-100 bg-black/80 backdrop-blur-md" : "bg-black/80 backdrop-blur-md lg:bg-transparent lg:opacity-0 lg:pointer-events-none opacity-100"
}`} }`}
> >
<div className="max-w-screen-2xl mx-auto px-6 lg:px-12"> <div className="max-w-screen-2xl mx-auto px-6 lg:px-12">

View File

@ -61,8 +61,8 @@ export function ServicesMobileCarousel() {
> >
<CarouselContent className="-ml-2 md:-ml-4"> <CarouselContent className="-ml-2 md:-ml-4">
{services.map((service, index) => ( {services.map((service, index) => (
<CarouselItem key={index} className="pl-2 md:pl-4 basis-[85%] sm:basis-[75%]"> <CarouselItem key={index} className="pl-2 md:pl-4 basis-[85%] sm:basis-[75%] md:basis-[70%]">
<div className="min-h-[70vh] flex items-center justify-center p-6 relative"> <div className="min-h-[50vh] flex items-center justify-center p-4 relative">
<div className="max-w-sm relative"> <div className="max-w-sm relative">
<div className="mb-6"> <div className="mb-6">
<span className="text-sm font-medium tracking-widest text-white/60 uppercase"> <span className="text-sm font-medium tracking-widest text-white/60 uppercase">

View File

@ -0,0 +1,116 @@
"use client"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel"
import Link from "next/link"
const services = [
{
title: "Black & Grey Realism",
description: "Photorealistic tattoos with incredible depth and detail using black and grey shading techniques.",
features: ["Lifelike portraits", "Detailed shading", "3D effects"],
price: "Starting at $250",
},
{
title: "Cover-ups & Blackout",
description: "Transform old tattoos into stunning new pieces with expert cover-up techniques or bold blackout designs.",
features: ["Free consultation", "Creative solutions", "Complete coverage"],
price: "Starting at $300",
},
{
title: "Fine Line & Micro Realism",
description: "Delicate, precise linework and tiny realistic designs that showcase incredible detail.",
features: ["Single needle work", "Intricate details", "Minimalist aesthetic"],
price: "Starting at $150",
},
{
title: "Traditional & Neo-Traditional",
description: "Bold American traditional and neo-traditional styles with vibrant colors and strong lines.",
features: ["Classic designs", "Bold color palettes", "Timeless appeal"],
price: "Starting at $200",
},
{
title: "Anime & Watercolor",
description: "Vibrant anime characters and painterly watercolor effects that bring art to life on skin.",
features: ["Character designs", "Soft color blends", "Artistic techniques"],
price: "Starting at $250",
},
]
export function ServicesMobileOnly() {
return (
<section className="lg:hidden bg-black text-white py-16">
{/* Header */}
<div className="px-6 mb-12 text-center">
<div className="mb-4">
<span className="text-sm font-medium tracking-widest text-white/60 uppercase">Our Services</span>
</div>
<h2 className="text-4xl font-bold tracking-tight mb-4">Choose Your Style</h2>
<p className="text-white/70 max-w-md mx-auto">
From custom designs to cover-ups, we offer comprehensive tattoo services with the highest standards.
</p>
</div>
{/* Carousel */}
<div className="px-4">
<Carousel
opts={{
align: "start",
loop: true,
}}
className="w-full max-w-sm mx-auto"
>
<CarouselContent className="-ml-2">
{services.map((service, index) => (
<CarouselItem key={index} className="pl-2 basis-full">
<Card className="bg-black border-white/20 text-white h-full">
<CardHeader className="pb-4">
<div className="text-xs font-medium tracking-widest text-white/60 uppercase mb-2">
Service {String(index + 1).padStart(2, "0")}
</div>
<CardTitle className="text-2xl font-bold leading-tight">
{service.title}
</CardTitle>
<CardDescription className="text-white/80 text-base leading-relaxed">
{service.description}
</CardDescription>
</CardHeader>
<CardContent className="pb-4">
<div className="space-y-2 mb-6">
{service.features.map((feature, idx) => (
<div key={idx} className="flex items-center text-white/70">
<span className="w-1.5 h-1.5 bg-white/40 rounded-full mr-3 flex-shrink-0"></span>
<span className="text-sm">{feature}</span>
</div>
))}
</div>
<div className="text-xl font-bold text-white mb-4">
{service.price}
</div>
</CardContent>
<CardFooter className="pt-0">
<Button
asChild
className="w-full bg-white text-black hover:bg-gray-100 !text-black font-medium"
>
<Link href="/book">BOOK NOW</Link>
</Button>
</CardFooter>
</Card>
</CarouselItem>
))}
</CarouselContent>
<div className="flex justify-center mt-8 gap-4">
<CarouselPrevious className="relative translate-y-0 left-0 bg-white/10 border-white/20 text-white hover:bg-white/20" />
<CarouselNext className="relative translate-y-0 right-0 bg-white/10 border-white/20 text-white hover:bg-white/20" />
</div>
</Carousel>
</div>
</section>
)
}

View File

@ -3,7 +3,7 @@
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import Link from "next/link" import Link from "next/link"
import { ServicesMobileCarousel } from "@/components/services-mobile-carousel" import { ServicesMobileOnly } from "@/components/services-mobile-only"
const services = [ const services = [
{ {
@ -101,10 +101,10 @@ export function ServicesSection() {
</div> </div>
</div> </div>
<div className="bg-black text-white relative z-10"> <div className="hidden lg:block bg-black text-white relative z-10">
<div className="flex"> <div className="flex">
{/* Left Side - Enhanced with split composition styling */} {/* Left Side - Enhanced with split composition styling */}
<div className="hidden lg:block w-1/2 sticky top-0 h-screen bg-black relative"> <div className="w-1/2 sticky top-0 h-screen bg-black relative">
<div className="absolute right-0 top-0 w-px h-full bg-white/10"></div> <div className="absolute right-0 top-0 w-px h-full bg-white/10"></div>
<div className="h-full flex flex-col justify-center p-16 relative"> <div className="h-full flex flex-col justify-center p-16 relative">
<div className="space-y-8"> <div className="space-y-8">
@ -201,8 +201,9 @@ export function ServicesSection() {
</div> </div>
</div> </div>
<ServicesMobileCarousel />
</div> </div>
<ServicesMobileOnly />
</section> </section>
) )
} }

View File

@ -269,7 +269,7 @@ export const artists: Artist[] = [
name: "Steven 'Sole' Cedre", name: "Steven 'Sole' Cedre",
title: "It has to have soul, Sole!", title: "It has to have soul, Sole!",
specialty: "Gritty Realism & Comic Art", specialty: "Gritty Realism & Comic Art",
faceImage: "/artists/sole-cedre-portrait.jpg", faceImage: "/artists/steven-sole-cedre.jpg",
workImages: [ workImages: [
"/artists/sole-cedre-work-1.jpg", "/artists/sole-cedre-work-1.jpg",
"/artists/sole-cedre-work-2.jpg", "/artists/sole-cedre-work-2.jpg",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB