diff --git a/src/app/(site)/page.tsx b/src/app/(site)/page.tsx index c52e7899..18d1c42f 100644 --- a/src/app/(site)/page.tsx +++ b/src/app/(site)/page.tsx @@ -27,7 +27,7 @@ async function getHotelData() { cached("total_users", 30_000, () => prisma.user.count()).catch(() => 0), cached("total_rooms", 30_000, () => prisma.rooms.count()).catch(() => 0), prisma.websiteArticles - .findMany({ orderBy: { createdAt: "desc" }, take: 5 }) + .findMany({ orderBy: { createdAt: "desc" }, take: 6 }) .catch(() => []), prisma.user .findMany({ @@ -37,7 +37,7 @@ async function getHotelData() { }) .catch(() => []), prisma.cameraWeb - .findMany({ orderBy: { timestamp: "desc" }, take: 4 }) + .findMany({ orderBy: { timestamp: "desc" }, take: 6 }) .catch(() => []), ]); @@ -65,150 +65,255 @@ export default async function Home() { recentUsers, recentPhotos, } = await getHotelData(); - - const featuredArticle = articles[0]; - const remainingArticles = articles.slice(1, 5); + const featured = articles[0]; + const rest = articles.slice(1, 5); return ( -
- {/* ── Hero ── */} -
-
-
-
+
+ {/* ── 1. Cinematic Hero ── */} +
+
+
+
- - {online.toLocaleString()} online now -
-

- Welcome to{" "} - - {hotelName} - -

-

- {t("tagline")} -

-
- - - Enter {hotelName} - - - Play now - -
-
-
- Frank -
- {recentUsers.slice(0, 4).map((u) => ( -
- - - {u.username} +
+
+
+ + + + {online.toLocaleString()} guests online
- ))} + +

+ Your story starts{" "} + + + here + + + +

+

+ {t("tagline")} +

+
+ + + + Enter {hotelName} + + + + Play now + +
+
+ +
+
+
+ Frank +
+

+ Hey! 👋 +

+

+ Ready for an adventure? +

+
+
+
+ {recentUsers.slice(0, 5).map((u, i) => ( +
+ +
+

+ {u.username} +

+
+ + + Online + +
+
+
+ ))} +
+
- {/* ── Stats bar ── */} -
+ {/* ── 2. Stats ── */} +
{[ - { value: online.toLocaleString(), label: "Guests online" }, - { value: users.toLocaleString(), label: "Total citizens" }, - { value: rooms.toLocaleString(), label: "Rooms built" }, + { + value: online.toLocaleString(), + label: "Guests online", + sub: "Exploring right now", + icon: "🟢", + }, + { + value: users.toLocaleString(), + label: "Total citizens", + sub: "And counting", + icon: "👥", + }, + { + value: rooms.toLocaleString(), + label: "Rooms built", + sub: "Waiting for you", + icon: "🏠", + }, ].map((s) => (
- {s.value} -
-
- {s.label} + className="absolute -top-8 -right-8 w-20 h-20 rounded-full opacity-[0.05] group-hover:opacity-[0.1] transition-opacity duration-500" + style={{ background: "var(--color-primary)" }} + /> +
+
+ {s.value} +
+
+ {s.label} +
+
+ {s.sub} +
))}
- {/* ── Main content: News + Sidebar ── */} -
- {/* News feed (left, 2/3) */} -
- {featuredArticle && ( + {/* ── 3. News + Sidebar ── */} +
+ {/* Main feed */} +
+
+
+

+ Latest news +

+

+ Stay up to date with {hotelName} +

+
+ View all + +
+ + {featured && ( + -
- {featuredArticle.image ? ( +
+ {featured.image ? ( ) : ( @@ -270,44 +414,55 @@ export default async function Home() { }} /> )} -
-
-

+
+
- {featuredArticle.title} -

-

- {formatDate(featuredArticle.createdAt, "date", "")} + Featured +

+

+ {featured.title} +

+

+ {formatDate(featured.createdAt, "date", "")}

)} - {remainingArticles.length > 0 && ( + {rest.length > 0 && (
- {remainingArticles.map((a) => ( + {rest.map((a, i) => ( -
+
{a.image ? ( ) : ( @@ -321,13 +476,13 @@ export default async function Home() {

{a.title}

)} - - Browse all articles → - +

+ + View all articles → + +
- {/* Sidebar (right, 1/3) */} -
- {/* ── CTA ── */} -
-

- See you inside! -

-

+

- {hotelName} is free, forever. Create your account and start your - adventure today. -

- - - Join for free - +
+

+ The doors are open +

+

+ {hotelName} is free, forever. Your first room is waiting — come + join us! +

+ + + + Claim your room + +
+
); diff --git a/src/app/(site)/register/page.tsx b/src/app/(site)/register/page.tsx index f8641872..b2d2d128 100644 --- a/src/app/(site)/register/page.tsx +++ b/src/app/(site)/register/page.tsx @@ -36,84 +36,111 @@ export default async function RegisterPage() { ]); return ( -
- {/* ── Left panel: Frank + avatars ── */} +
+ {/* ── Left: Reception ── */}
-
- Frank -

- Join{" "} - - {hotelName} - -

-

+

- {t("tagline")} -

-
- - {online.toLocaleString()} online now +
+
+ Frank +
+ + {online.toLocaleString()} online +
+

+ Join{" "} + + {hotelName} + +

+

+ {t("tagline")} +

+
{recentUsers.length > 0 && (

- Who's online + Who's online

{recentUsers.map((u) => (
{u.username} @@ -158,11 +185,11 @@ export default async function RegisterPage() {

- {/* ── Right: Registration form ── */} + {/* ── Right: Registration ── */}
-
+

Create your account @@ -174,7 +201,7 @@ export default async function RegisterPage() { "var(--color-text-muted-readable, var(--color-text-muted))", }} > - Free and takes less than a minute + Free and takes less than a minute — no email required

* { @@ -305,31 +343,66 @@ summary, } html.dark .site-bg { - background: - linear-gradient( - 180deg, - color-mix(in srgb, var(--color-primary) 6%, #0a0e1a) 0%, - color-mix(in srgb, var(--color-primary) 8%, #10152a) 30%, - color-mix(in srgb, var(--color-primary) 10%, #161c30) 60%, - color-mix(in srgb, var(--color-primary) 12%, #0f141f) 100% - ); + background: linear-gradient( + 180deg, + color-mix(in srgb, var(--color-primary) 6%, #0a0e1a) 0%, + color-mix(in srgb, var(--color-primary) 8%, #10152a) 30%, + color-mix(in srgb, var(--color-primary) 10%, #161c30) 60%, + color-mix(in srgb, var(--color-primary) 12%, #0f141f) 100% + ); background-attachment: fixed; } html.dark .site-bg::before { opacity: 0.08; background: - radial-gradient(ellipse 240px 60px at 15% 8%, rgba(200,210,255,0.5) 0%, transparent 100%), - radial-gradient(ellipse 320px 70px at 35% 12%, rgba(200,210,255,0.4) 0%, transparent 100%), - radial-gradient(ellipse 200px 50px at 55% 6%, rgba(200,210,255,0.45) 0%, transparent 100%), - radial-gradient(ellipse 280px 65px at 75% 10%, rgba(200,210,255,0.5) 0%, transparent 100%), - radial-gradient(ellipse 180px 45px at 90% 5%, rgba(200,210,255,0.35) 0%, transparent 100%), - radial-gradient(ellipse 260px 55px at 50% 18%, rgba(200,210,255,0.25) 0%, transparent 100%), - radial-gradient(ellipse 200px 40px at 20% 22%, rgba(200,210,255,0.2) 0%, transparent 100%); + radial-gradient( + ellipse 240px 60px at 15% 8%, + rgba(200, 210, 255, 0.5) 0%, + transparent 100% + ), + radial-gradient( + ellipse 320px 70px at 35% 12%, + rgba(200, 210, 255, 0.4) 0%, + transparent 100% + ), + radial-gradient( + ellipse 200px 50px at 55% 6%, + rgba(200, 210, 255, 0.45) 0%, + transparent 100% + ), + radial-gradient( + ellipse 280px 65px at 75% 10%, + rgba(200, 210, 255, 0.5) 0%, + transparent 100% + ), + radial-gradient( + ellipse 180px 45px at 90% 5%, + rgba(200, 210, 255, 0.35) 0%, + transparent 100% + ), + radial-gradient( + ellipse 260px 55px at 50% 18%, + rgba(200, 210, 255, 0.25) 0%, + transparent 100% + ), + radial-gradient( + ellipse 200px 40px at 20% 22%, + rgba(200, 210, 255, 0.2) 0%, + transparent 100% + ); } html.dark .site-bg::after { background: - radial-gradient(ellipse 600px 400px at 20% 0%, color-mix(in srgb, var(--color-primary) 14%, transparent) 0%, transparent 100%), - radial-gradient(ellipse 500px 300px at 80% 0%, color-mix(in srgb, var(--color-primary) 8%, transparent) 0%, transparent 100%); + radial-gradient( + ellipse 600px 400px at 20% 0%, + color-mix(in srgb, var(--color-primary) 14%, transparent) 0%, + transparent 100% + ), + radial-gradient( + ellipse 500px 300px at 80% 0%, + color-mix(in srgb, var(--color-primary) 8%, transparent) 0%, + transparent 100% + ); } /* Admin must not inherit the public wallpaper — solid HK canvas only. */ @@ -1346,7 +1419,8 @@ body:has([data-admin]) .btn-outline:hover { color: var(--admin-text); } /* Override public html.dark input hardcodes inside admin. */ -html.dark body:has([data-admin]) +html.dark + body:has([data-admin]) input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not( [type="file"] ):not([type="color"]), @@ -1455,7 +1529,8 @@ details[open] > summary .details-open\:rotate-180 { text-align: center; padding: 2.25rem 1.75rem 2rem; border-radius: 1.25rem; - border: 1px solid color-mix(in srgb, var(--color-primary) 28%, var(--border-subtle)); + border: 1px solid + color-mix(in srgb, var(--color-primary) 28%, var(--border-subtle)); background: linear-gradient( 165deg, color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)), @@ -1475,7 +1550,8 @@ details[open] > summary .details-open\:rotate-180 { letter-spacing: 0.06em; line-height: 1; color: var(--color-primary-readable, var(--color-primary)); - text-shadow: 0 0 36px color-mix(in srgb, var(--color-primary) 45%, transparent); + text-shadow: 0 0 36px + color-mix(in srgb, var(--color-primary) 45%, transparent); } .error-screen-title { @@ -1537,3 +1613,166 @@ details[open] > summary .details-open\:rotate-180 { animation: none; } } + +/* ── Premium effects ── */ +@keyframes float { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } +} +@keyframes shimmer-glow { + 0% { + opacity: 0.4; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.4; + } +} +@keyframes gradient-shift { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} +@keyframes fade-in-up { + from { + opacity: 0; + transform: translateY(16px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes scale-in { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} +@keyframes pulse-glow { + 0%, + 100% { + box-shadow: 0 0 8px + color-mix(in srgb, var(--color-primary) 25%, transparent); + } + 50% { + box-shadow: 0 0 24px + color-mix(in srgb, var(--color-primary) 45%, transparent); + } +} + +.animate-float { + animation: float 4s ease-in-out infinite; +} +.animate-shimmer { + animation: shimmer-glow 2.5s ease-in-out infinite; +} +.animate-gradient { + background-size: 200% 200%; + animation: gradient-shift 8s ease infinite; +} +.animate-fade-in-up { + animation: fade-in-up 0.6s ease-out both; +} +.animate-scale-in { + animation: scale-in 0.4s ease-out both; +} +.animate-pulse-glow { + animation: pulse-glow 2s ease-in-out infinite; +} + +/* Glass morphism */ +.glass { + background: color-mix(in srgb, var(--color-surface) 70%, transparent); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid color-mix(in srgb, var(--color-text-muted) 10%, transparent); +} +.glass-strong { + background: color-mix(in srgb, var(--color-surface) 85%, transparent); + backdrop-filter: blur(32px); + -webkit-backdrop-filter: blur(32px); + border: 1px solid color-mix(in srgb, var(--color-text-muted) 12%, transparent); +} + +/* Premium card with depth */ +.card-premium { + background: var(--color-surface); + border: 1px solid color-mix(in srgb, var(--color-text-muted) 10%, transparent); + border-radius: var(--radius-lg); + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.04), + 0 4px 12px rgba(0, 0, 0, 0.04), + 0 12px 32px rgba(0, 0, 0, 0.04); + transition: + transform 0.25s ease, + box-shadow 0.25s ease; +} +.card-premium:hover { + transform: translateY(-3px); + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.04), + 0 6px 16px rgba(0, 0, 0, 0.06), + 0 16px 40px rgba(0, 0, 0, 0.08); +} + +/* Gradient border wrapper */ +.gradient-border { + position: relative; + border-radius: var(--radius-lg); + background: linear-gradient( + 135deg, + var(--color-primary), + color-mix(in srgb, var(--color-primary) 50%, var(--color-accent)) + ); + padding: 1px; +} +.gradient-border > * { + border-radius: calc(var(--radius-lg) - 1px); +} + +/* Staggered children animation */ +.stagger-children > * { + animation: fade-in-up 0.5s ease-out both; +} +.stagger-children > *:nth-child(1) { + animation-delay: 0.05s; +} +.stagger-children > *:nth-child(2) { + animation-delay: 0.1s; +} +.stagger-children > *:nth-child(3) { + animation-delay: 0.15s; +} +.stagger-children > *:nth-child(4) { + animation-delay: 0.2s; +} +.stagger-children > *:nth-child(5) { + animation-delay: 0.25s; +} +.stagger-children > *:nth-child(6) { + animation-delay: 0.3s; +} +.stagger-children > *:nth-child(7) { + animation-delay: 0.35s; +} +.stagger-children > *:nth-child(8) { + animation-delay: 0.4s; +}