diff --git a/public/manifest.json b/public/manifest.json index 8983420..74d69d4 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -4,8 +4,8 @@ "description": "Free gay cruising map, chat & shop", "start_url": "/", "display": "standalone", - "background_color": "#0d0a0f", - "theme_color": "#ff2d6b", + "background_color": "#08001C", + "theme_color": "#F92B9B", "orientation": "any", "icons": [ { diff --git a/src/app/globals.css b/src/app/globals.css index 0f123fc..62ef96b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -4,25 +4,25 @@ @layer base { :root { - --background: 280 30% 6%; - --foreground: 0 0% 98%; - --card: 280 25% 10%; - --card-foreground: 0 0% 98%; - --popover: 280 25% 10%; - --popover-foreground: 0 0% 98%; - --primary: 340 100% 59%; + --background: 265 100% 5%; + --foreground: 228 100% 97%; + --card: 268 100% 11%; + --card-foreground: 228 100% 97%; + --popover: 268 100% 11%; + --popover-foreground: 228 100% 97%; + --primary: 330 94% 57%; --primary-foreground: 0 0% 100%; - --secondary: 280 40% 18%; - --secondary-foreground: 0 0% 98%; - --muted: 280 20% 16%; - --muted-foreground: 280 10% 65%; - --accent: 280 60% 45%; + --secondary: 268 55% 16%; + --secondary-foreground: 228 100% 97%; + --muted: 268 35% 14%; + --muted-foreground: 228 12% 60%; + --accent: 280 72% 47%; --accent-foreground: 0 0% 100%; --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; - --border: 280 20% 20%; - --input: 280 20% 20%; - --ring: 340 100% 59%; + --border: 220 38% 20%; + --input: 220 38% 20%; + --ring: 330 94% 57%; --radius: 0.75rem; } } @@ -37,17 +37,59 @@ } } -/* Leaflet dark theme overrides */ +@layer components { + .gradient-text { + @apply bg-pulse-gradient bg-clip-text text-transparent; + } + + .glass-card { + @apply rounded-xl border border-white/[0.06] bg-pulse-midnight/70 backdrop-blur-md; + transition: background 0.2s, border-color 0.2s; + } + + .glass-card:hover { + @apply border-primary/25 bg-pulse-glow/40; + } + + .pulse-pill { + @apply inline-flex items-center justify-center rounded-full px-4 py-2 text-sm font-bold transition-all touch-manipulation; + } + + .pulse-pill-active { + @apply text-white shadow-pulse-glow; + background: linear-gradient(135deg, #ff2d9b, #9b30ff); + } + + .pulse-pill-inactive { + color: #8b92a8; + background: rgba(30, 45, 74, 0.4); + border: 1px solid rgba(155, 48, 255, 0.3); + } + + .pulse-pill-inactive:hover { + color: #ff6ec7; + border-color: rgba(255, 45, 155, 0.35); + background: rgba(255, 45, 155, 0.08); + } + + .pulse-pill-tag { + @apply text-xs font-bold px-3 py-1 rounded-full; + color: #ff6ec7; + background: rgba(255, 45, 155, 0.15); + border: 1px solid rgba(255, 45, 155, 0.3); + } +} + +/* Leaflet — Pink Pulse dark palette */ .leaflet-container { - background: #0a1628 !important; + background: #08001c !important; font-family: inherit; } .sniffies-map .leaflet-tile-pane { - filter: saturate(0.85) brightness(0.95); + filter: saturate(0.9) brightness(0.92) hue-rotate(-8deg); } -/* Sniffies-style photo markers */ .sniffies-marker-wrap, .sniffies-you-wrap, .sniffies-spot-wrap { @@ -68,15 +110,15 @@ width: 44px; height: 44px; border-radius: 50%; - border: 2px solid #3b82f6; + border: 2px solid #f92b9b; padding: 2px; - background: #0a1628; + background: #08001c; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6); } .sniffies-avatar-ring.sniffies-online { - border-color: #3b82f6; - box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.35); + border-color: #f92b9b; + box-shadow: 0 0 0 2px rgba(249, 43, 155, 0.35); } .sniffies-avatar { @@ -93,7 +135,7 @@ height: 16px; padding: 0 4px; border-radius: 8px; - background: #f97316; + background: linear-gradient(135deg, #ff2d9b, #c084fc); color: white; font-size: 10px; font-weight: 700; @@ -113,9 +155,9 @@ width: 40px; height: 40px; border-radius: 50%; - background: #ef4444; + background: #f92b9b; border: 3px solid white; - box-shadow: 0 2px 12px rgba(239, 68, 68, 0.6); + box-shadow: 0 2px 12px rgba(249, 43, 155, 0.6); display: flex; align-items: center; justify-content: center; @@ -133,7 +175,7 @@ .sniffies-you-label { font-size: 11px; font-weight: 700; - color: #3b82f6; + color: #ff6ec7; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); } @@ -142,8 +184,8 @@ width: 36px; height: 36px; border-radius: 50%; - background: rgba(10, 22, 40, 0.9); - border: 2px solid #3b82f6; + background: rgba(26, 0, 58, 0.95); + border: 2px solid #8b22cc; display: flex; align-items: center; justify-content: center; @@ -158,7 +200,7 @@ min-width: 16px; height: 16px; border-radius: 8px; - background: #3b82f6; + background: linear-gradient(135deg, #ff2d9b, #8b22cc); color: white; font-size: 9px; font-weight: 700; @@ -166,91 +208,114 @@ text-align: center; padding: 0 3px; } + .leaflet-popup-content-wrapper { - background: hsl(280 25% 10%); - color: hsl(0 0% 98%); + background: hsl(268 100% 11%); + color: hsl(228 100% 97%); border-radius: 0.75rem; - border: 1px solid hsl(280 20% 20%); + border: 1px solid hsl(220 38% 20%); } + .leaflet-popup-tip { - background: hsl(280 25% 10%); + background: hsl(268 100% 11%); } + .marker-cluster-small, .marker-cluster-medium, .marker-cluster-large { - background-color: rgba(255, 45, 107, 0.3) !important; + background-color: rgba(249, 43, 155, 0.3) !important; } + .marker-cluster-small div, .marker-cluster-medium div, .marker-cluster-large div { - background-color: rgba(255, 45, 107, 0.8) !important; + background-color: rgba(249, 43, 155, 0.85) !important; color: white !important; font-weight: 700; } -/* Vanilla mode blur */ .vanilla-blur img, .vanilla-blur video, .vanilla-blur .explicit-content { filter: blur(24px); transition: filter 0.3s ease; } + .vanilla-blur:hover img, .vanilla-blur:hover video, .vanilla-blur:hover .explicit-content { filter: blur(8px); } -/* Scrollbar */ ::-webkit-scrollbar { width: 6px; height: 6px; } + ::-webkit-scrollbar-track { - background: hsl(280 30% 6%); + background: hsl(265 100% 5%); } + ::-webkit-scrollbar-thumb { - background: hsl(340 100% 59% / 0.5); + background: rgba(249, 43, 155, 0.45); border-radius: 3px; } -/* PWA safe areas */ +::-webkit-scrollbar-thumb:hover { + background: rgba(249, 43, 155, 0.65); +} + +.scrollbar-none { + scrollbar-width: none; + -ms-overflow-style: none; +} + +.scrollbar-none::-webkit-scrollbar { + display: none; +} + .safe-bottom { padding-bottom: max(env(safe-area-inset-bottom, 0px), 4px); } + .safe-top { padding-top: env(safe-area-inset-top, 0); } -/* ID verification blur — nude photos in restricted states */ .id-blur img, .id-blur .explicit-content { filter: blur(28px) !important; } + .id-blur:hover img, .id-blur:hover .explicit-content { filter: blur(20px) !important; } -/* Mobile touch targets & scroll */ .touch-manipulation { touch-action: manipulation; } + @supports (height: 100dvh) { .min-h-screen { min-height: 100dvh; } } -/* Prevent iOS zoom on input focus */ @media (max-width: 767px) { - input, select, textarea { + input, + select, + textarea { font-size: 16px !important; } } -/* Overscroll containment for chat */ .overscroll-contain { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; +} + +:focus-visible { + outline: 2px solid rgba(249, 43, 155, 0.6); + outline-offset: 2px; } \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a631eba..5f9fac7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,9 +1,12 @@ import type { Metadata, Viewport } from "next"; -import { Inter } from "next/font/google"; +import { Manrope } from "next/font/google"; import { Providers } from "@/components/Providers"; import "./globals.css"; -const inter = Inter({ subsets: ["latin"] }); +const manrope = Manrope({ + subsets: ["latin"], + variable: "--font-manrope", +}); export const metadata: Metadata = { title: "ExposedGays — Free Gay Cruising & Shop", @@ -22,7 +25,7 @@ export const metadata: Metadata = { }; export const viewport: Viewport = { - themeColor: "#ff2d6b", + themeColor: "#F92B9B", width: "device-width", initialScale: 1, viewportFit: "cover", @@ -35,7 +38,7 @@ export default function RootLayout({ }) { return ( - + {children} diff --git a/src/components/AgeGate.tsx b/src/components/AgeGate.tsx index a10b11f..1b63473 100644 --- a/src/components/AgeGate.tsx +++ b/src/components/AgeGate.tsx @@ -73,7 +73,7 @@ export function AgeGate({ onVerified }: AgeGateProps) {
-

+

ExposedGays

@@ -149,9 +149,9 @@ export function AgeGate({ onVerified }: AgeGateProps) { )} -

-

- No account needed. Verify your age +

+

+ No account needed. Verify your age with date of birth only — browse the map, spots, and profiles free. Explicit photos and chat require a free signup. Government ID is only needed for nude content in certain states. diff --git a/src/components/CannedMessagesPanel.tsx b/src/components/CannedMessagesPanel.tsx index e5a5f97..3cd79b6 100644 --- a/src/components/CannedMessagesPanel.tsx +++ b/src/components/CannedMessagesPanel.tsx @@ -97,7 +97,7 @@ export function CannedMessagesPanel({ compact, onSelect }: CannedMessagesPanelPr key={msg.id} type="button" onClick={() => onSelect?.(msg.content)} - className="text-xs px-2.5 py-1.5 rounded-full border border-border hover:border-horny-pink/50 hover:bg-horny-pink/10 transition-colors truncate max-w-[140px]" + className="text-xs px-2.5 py-1.5 rounded-full pulse-pill-inactive truncate max-w-[140px]" title={msg.content} > {msg.title} diff --git a/src/components/CruiserProfileView.tsx b/src/components/CruiserProfileView.tsx index b2a39c4..649dbc6 100644 --- a/src/components/CruiserProfileView.tsx +++ b/src/components/CruiserProfileView.tsx @@ -111,14 +111,14 @@ export function CruiserProfileView({ return (

-
+
{/* Header — Sniffies style */}
{filteredProfiles.filter((p) => p.status !== "offline").length} cruising @@ -202,7 +202,7 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
{showFilters && ( -
+
Filters {isGuest ? ( - + Guest ) : ( diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx index 29ce081..87dbd8d 100644 --- a/src/components/Nav.tsx +++ b/src/components/Nav.tsx @@ -99,7 +99,7 @@ export function Nav({ Vanilla {isGuest && ( - + Guest )} diff --git a/src/components/Shop.tsx b/src/components/Shop.tsx index fbf9be3..fd075fd 100644 --- a/src/components/Shop.tsx +++ b/src/components/Shop.tsx @@ -48,7 +48,7 @@ export function Shop() { return (
{/* Hero */} -
+

ExposedGays Shop diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index cf5734b..ff57f2c 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -3,19 +3,25 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { - default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", - secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", - outline: "text-foreground", - top: "border-transparent bg-blue-500/20 text-blue-400", - bottom: "border-transparent bg-orange-500/20 text-orange-400", - vers: "border-transparent bg-purple-500/20 text-purple-400", - side: "border-transparent bg-green-500/20 text-green-400", - online: "border-transparent bg-green-500/20 text-green-400 animate-pulse", + default: + "border-transparent bg-pulse-gradient-bold text-white shadow-sm", + secondary: + "border-transparent bg-secondary text-secondary-foreground", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow", + outline: "border-pulse-border/60 text-foreground bg-pulse-midnight/30", + pill: + "border-primary/30 bg-primary/15 text-pulse-pink-light", + top: "border-transparent bg-primary/20 text-pulse-pink-light", + bottom: "border-transparent bg-orange-500/20 text-orange-300", + vers: "border-transparent bg-pulse-violet/25 text-pulse-lavender", + side: "border-transparent bg-pulse-teal/20 text-pulse-teal", + online: + "border-transparent bg-emerald-500/20 text-emerald-300 animate-pulse", }, }, defaultVariants: { variant: "default" }, diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index d85e951..0db569f 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -4,23 +4,33 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-semibold transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 touch-manipulation active:scale-[0.98]", { variants: { variant: { - default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", - destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", - outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", - secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - horny: "bg-horny-gradient text-white shadow-lg hover:opacity-90 font-semibold", + default: + "rounded-full bg-pulse-gradient-bold text-white shadow-pulse-glow hover:opacity-90 hover:-translate-y-px", + destructive: + "rounded-full bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "rounded-full border border-pulse-border/80 bg-pulse-midnight/40 text-foreground hover:border-primary/40 hover:bg-primary/10", + secondary: + "rounded-full bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "rounded-full hover:bg-primary/10 hover:text-primary", + link: "text-primary underline-offset-4 hover:underline rounded-none", + horny: + "rounded-full bg-pulse-gradient-bold text-white shadow-pulse-glow hover:opacity-90 hover:-translate-y-px font-bold", + pill: + "rounded-full pulse-pill-inactive font-bold hover:border-primary/35", + "pill-active": + "rounded-full pulse-pill-active font-bold", }, size: { - default: "h-10 px-4 py-2", - sm: "h-8 rounded-md px-3 text-xs", - lg: "h-12 rounded-md px-8 text-base", - icon: "h-10 w-10", + default: "h-10 px-5 py-2", + sm: "h-8 px-4 text-xs", + lg: "h-12 px-8 text-base", + icon: "h-10 w-10 rounded-full", }, }, defaultVariants: { variant: "default", size: "default" }, diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 7d39cf1..ef73f14 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -3,7 +3,14 @@ import { cn } from "@/lib/utils"; const Card = React.forwardRef>( ({ className, ...props }, ref) => ( -
+
) ); Card.displayName = "Card"; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index b4d5dd6..e3aed8d 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -6,7 +6,7 @@ const Input = React.forwardRef>( (({ className, ...props }, ref) => ( )); diff --git a/src/types/index.ts b/src/types/index.ts index e9da584..de91c6c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -171,7 +171,7 @@ export const KINK_OPTIONS = [ ] as const; export const POSITION_COLORS: Record = { - top: "#3b82f6", + top: "#F92B9B", bottom: "#f97316", vers: "#a855f7", side: "#22c55e", diff --git a/tailwind.config.ts b/tailwind.config.ts index f0d6796..b2fa70f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -43,20 +43,43 @@ const config: Config = { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, + pulse: { + pink: "#F92B9B", + "pink-light": "#FF6EC7", + "pink-dark": "#CC1A7A", + violet: "#8B22CC", + lavender: "#C084FC", + teal: "#03A4A8", + "deep-space": "#08001C", + midnight: "#1A003A", + glow: "#470070", + border: "#1E2D4A", + muted: "#8B92A8", + }, horny: { - pink: "#ff2d6b", - purple: "#8b2fc9", - dark: "#0d0a0f", - surface: "#1a1220", + pink: "#F92B9B", + purple: "#8B22CC", + dark: "#08001C", + surface: "#1A003A", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", + pill: "9999px", }, backgroundImage: { - "horny-gradient": "linear-gradient(135deg, #ff2d6b 0%, #8b2fc9 50%, #ff2d6b 100%)", + "pulse-gradient": "linear-gradient(135deg, #FF2D9B 0%, #C084FC 50%, #FF2D9B 100%)", + "pulse-gradient-bold": "linear-gradient(135deg, #FF2D9B 0%, #9B30FF 100%)", + "horny-gradient": "linear-gradient(135deg, #FF2D9B 0%, #C084FC 50%, #FF2D9B 100%)", + }, + boxShadow: { + "pulse-glow": "0 0 16px rgba(249, 43, 155, 0.4), 0 0 32px rgba(249, 43, 155, 0.15)", + "pulse-card": "0 12px 40px rgba(249, 43, 155, 0.12)", + }, + fontFamily: { + sans: ["var(--font-manrope)", "system-ui", "sans-serif"], }, }, },