From d755ab7ce1b64b67402266ad5067389a97f86dff Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 12 Sep 2026 16:14:21 +0200 Subject: [PATCH] revert(site): restore classic Habbo landing with soft theme polish Replace the premium dark-gaming redesign of home, login and register with the original classic landing (Habbo sky background, AuthTopBar, SurfaceCard layout). Keeps the theme background visible again and adds a subtle theme-aware brand halo behind the hero/Frank plus a soft primary glow on card hover. Also upgrades dependencies: next 16.3.5, vite 8.3.0 (typescript 7.0.2 was already latest). Temporarily lowers pnpm minimumReleaseAge to 60 min so the fresh 16.3.5 release can be installed; restore to 1440 once it is 24h old. --- package.json | 3 +- pnpm-lock.yaml | 119 +++---- pnpm-workspace.yaml | 2 +- src/app/(site)/login/page.tsx | 296 ++++++++++------- src/app/(site)/page.tsx | 462 ++++++++++++++------------ src/app/(site)/register/page.tsx | 323 ++++++++++-------- src/app/globals.css | 139 +------- src/components/auth/auth-shell.tsx | 47 --- src/components/auth/brand-frank.tsx | 32 -- src/components/auth/register-form.tsx | 78 +++-- src/components/landing-top-bar.tsx | 53 --- 11 files changed, 742 insertions(+), 812 deletions(-) delete mode 100644 src/components/auth/auth-shell.tsx delete mode 100644 src/components/auth/brand-frank.tsx delete mode 100644 src/components/landing-top-bar.tsx diff --git a/package.json b/package.json index 38d190b6..a6aba3d4 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "motion": "13.2.0", "music-metadata": "11.15.0", "mysql2": "3.24.3", - "next": "16.3.4", + "next": "16.3.5", "next-auth": "5.0.0-beta.32", "next-intl": "4.14.2", "otplib": "13.5.0", @@ -102,6 +102,7 @@ "tailwindcss": "4.3.3", "tsx": "4.23.13", "typescript": "7.0.2", + "vite": "8.3.0", "vitest": "5.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8afa8694..0765d1c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,14 +86,14 @@ importers: specifier: 3.24.3 version: 3.24.3(@types/node@26.4.1) next: - specifier: 16.3.4 - version: 16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: 16.3.5 + version: 16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) next-auth: specifier: 5.0.0-beta.32 - version: 5.0.0-beta.32(next@16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + version: 5.0.0-beta.32(next@16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) next-intl: specifier: 4.14.2 - version: 4.14.2(@swc/helpers@0.5.23)(next@16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + version: 4.14.2(@swc/helpers@0.5.23)(next@16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) otplib: specifier: 13.5.0 version: 13.5.0 @@ -188,9 +188,12 @@ importers: typescript: specifier: 7.0.2 version: 7.0.2 + vite: + specifier: 8.3.0 + version: 8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 5.0.0 - version: 5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) packages: @@ -1068,57 +1071,57 @@ packages: resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} engines: {node: '>=18'} - '@next/env@16.3.4': - resolution: {integrity: sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==} + '@next/env@16.3.5': + resolution: {integrity: sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==} - '@next/swc-darwin-arm64@16.3.4': - resolution: {integrity: sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==} + '@next/swc-darwin-arm64@16.3.5': + resolution: {integrity: sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.3.4': - resolution: {integrity: sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==} + '@next/swc-darwin-x64@16.3.5': + resolution: {integrity: sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.3.4': - resolution: {integrity: sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==} + '@next/swc-linux-arm64-gnu@16.3.5': + resolution: {integrity: sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.3.4': - resolution: {integrity: sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==} + '@next/swc-linux-arm64-musl@16.3.5': + resolution: {integrity: sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.3.4': - resolution: {integrity: sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==} + '@next/swc-linux-x64-gnu@16.3.5': + resolution: {integrity: sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.3.4': - resolution: {integrity: sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==} + '@next/swc-linux-x64-musl@16.3.5': + resolution: {integrity: sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.3.4': - resolution: {integrity: sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==} + '@next/swc-win32-arm64-msvc@16.3.5': + resolution: {integrity: sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.3.4': - resolution: {integrity: sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==} + '@next/swc-win32-x64-msvc@16.3.5': + resolution: {integrity: sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2654,8 +2657,8 @@ packages: next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 - next@16.3.4: - resolution: {integrity: sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==} + next@16.3.5: + resolution: {integrity: sha512-MdtsTgzyfCPRLC6uJ1mN8ao7lyJ4BB0U6Inhnx3gta1UcCIdHK3yxLG0E8OWQteWD8/Q0qb8A5o7wJaL8M9y2w==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -3126,13 +3129,13 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@8.2.2: - resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + vite@8.3.0: + resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.4.0 || ^0.5.0 + '@vitejs/devtools': ^0.7.1 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -3821,30 +3824,30 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@next/env@16.3.4': {} + '@next/env@16.3.5': {} - '@next/swc-darwin-arm64@16.3.4': + '@next/swc-darwin-arm64@16.3.5': optional: true - '@next/swc-darwin-x64@16.3.4': + '@next/swc-darwin-x64@16.3.5': optional: true - '@next/swc-linux-arm64-gnu@16.3.4': + '@next/swc-linux-arm64-gnu@16.3.5': optional: true - '@next/swc-linux-arm64-musl@16.3.4': + '@next/swc-linux-arm64-musl@16.3.5': optional: true - '@next/swc-linux-x64-gnu@16.3.4': + '@next/swc-linux-x64-gnu@16.3.5': optional: true - '@next/swc-linux-x64-musl@16.3.4': + '@next/swc-linux-x64-musl@16.3.5': optional: true - '@next/swc-win32-arm64-msvc@16.3.4': + '@next/swc-win32-arm64-msvc@16.3.5': optional: true - '@next/swc-win32-x64-msvc@16.3.4': + '@next/swc-win32-x64-msvc@16.3.5': optional: true '@noble/hashes@2.4.0': {} @@ -4413,7 +4416,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/istanbul-lib-coverage@1.0.1': {} @@ -4421,7 +4424,7 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.1 - '@vitest/mocker@5.0.0(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@5.0.0(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.0 @@ -4429,7 +4432,7 @@ snapshots: magic-string: 1.2.3 optionalDependencies: msw: 2.15.0(@types/node@26.4.1)(typescript@7.0.2) - vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) '@vitest/spy@5.0.0': {} @@ -5030,15 +5033,15 @@ snapshots: dependencies: content-type: 2.1.0 - next-auth@5.0.0-beta.32(next@16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + next-auth@5.0.0-beta.32(next@16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): dependencies: '@auth/core': 0.41.3 - next: 16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next: 16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 next-intl-swc-plugin-extractor@4.14.2: {} - next-intl@4.14.2(@swc/helpers@0.5.23)(next@16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + next-intl@4.14.2(@swc/helpers@0.5.23)(next@16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): dependencies: '@eloqnt/config': 0.1.0 '@eloqnt/format-json': 0.1.0 @@ -5048,16 +5051,16 @@ snapshots: '@swc/core': 1.16.2(@swc/helpers@0.5.23) icu-minify: 4.14.2 negotiator: 1.1.0 - next: 16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next: 16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) next-intl-swc-plugin-extractor: 4.14.2 react: 19.2.8 use-intl: 4.14.2(react@19.2.8) transitivePeerDependencies: - '@swc/helpers' - next@16.3.4(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + next@16.3.5(@playwright/test@1.62.1)(@types/node@26.4.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@next/env': 16.3.4 + '@next/env': 16.3.5 '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.11.21 caniuse-lite: 1.0.30001810 @@ -5066,14 +5069,14 @@ snapshots: react-dom: 19.2.8(react@19.2.8) styled-jsx: 5.1.6(react@19.2.8) optionalDependencies: - '@next/swc-darwin-arm64': 16.3.4 - '@next/swc-darwin-x64': 16.3.4 - '@next/swc-linux-arm64-gnu': 16.3.4 - '@next/swc-linux-arm64-musl': 16.3.4 - '@next/swc-linux-x64-gnu': 16.3.4 - '@next/swc-linux-x64-musl': 16.3.4 - '@next/swc-win32-arm64-msvc': 16.3.4 - '@next/swc-win32-x64-msvc': 16.3.4 + '@next/swc-darwin-arm64': 16.3.5 + '@next/swc-darwin-x64': 16.3.5 + '@next/swc-linux-arm64-gnu': 16.3.5 + '@next/swc-linux-arm64-musl': 16.3.5 + '@next/swc-linux-x64-gnu': 16.3.5 + '@next/swc-linux-x64-musl': 16.3.5 + '@next/swc-win32-arm64-msvc': 16.3.5 + '@next/swc-win32-x64-msvc': 16.3.5 '@playwright/test': 1.62.1 sharp: 0.35.4(@types/node@26.4.1) transitivePeerDependencies: @@ -5532,7 +5535,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0): + vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -5547,10 +5550,10 @@ snapshots: tsx: 4.23.13 yaml: 2.9.0 - vitest@5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(jsdom@30.0.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.0(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 5.0.0(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -5561,7 +5564,7 @@ snapshots: tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 - vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 8.3.0(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.4.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2016f622..78d4abbe 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,7 +5,7 @@ allowBuilds: "@swc/core": true msw: false -minimumReleaseAge: 1440 +minimumReleaseAge: 60 overrides: # drizzle-kit still uses an obsolete development-server dependency. diff --git a/src/app/(site)/login/page.tsx b/src/app/(site)/login/page.tsx index 1548a9e2..4e56990f 100644 --- a/src/app/(site)/login/page.tsx +++ b/src/app/(site)/login/page.tsx @@ -2,26 +2,21 @@ import { count, desc, eq } from "drizzle-orm"; import { headers } from "next/headers"; import Image from "next/image"; import { getTranslations } from "next-intl/server"; -import { AuthShell } from "@/components/auth/auth-shell"; -import { BrandFrank } from "@/components/auth/brand-frank"; +import { AuthTopBar } from "@/components/auth/auth-top-bar"; import { LoginForm } from "@/components/auth/login-form"; import Link from "@/components/link"; import { Reveal } from "@/components/motion-reveal"; import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail"; +import { SurfaceCard } from "@/components/surface-card"; import { cached } from "@/lib/cache"; import { db, User } from "@/lib/db"; import { resolveHotelName } from "@/lib/hotel-name"; import { captchaConfig } from "@/lib/services/captcha"; import { siteSettings } from "@/lib/services/site-settings"; -export const metadata = { - title: "Sign in", -}; - export default async function LoginPage() { const t = await getTranslations("pages.login"); const th = await getTranslations("header"); - const thp = await getTranslations("pages.home"); const [hotelName, cfg, logo] = await Promise.all([ resolveHotelName(), captchaConfig(), @@ -29,7 +24,7 @@ export default async function LoginPage() { ]); const nonce = (await headers()).get("x-nonce") ?? undefined; - const [online, totalUsers, recentUsers, latestUsers] = await Promise.all([ + const [online, recentUsers, latestUsers] = await Promise.all([ cached("online_count", 10_000, () => db .select({ total: count() }) @@ -37,141 +32,200 @@ export default async function LoginPage() { .where(eq(User.online, "1")) .then((rows) => rows[0]?.total ?? 0), ).catch(() => 0), - cached("total_users", 300_000, () => - db - .select({ total: count() }) - .from(User) - .then((rows) => rows[0]?.total ?? 0), - ).catch(() => 0), db .select({ username: User.username, look: User.look }) .from(User) .where(eq(User.online, "1")) - .limit(6) + .limit(8) .catch(() => []), db .select({ username: User.username, look: User.look }) .from(User) .orderBy(desc(User.accountCreated)) - .limit(6) + .limit(8) .catch(() => []), ]); - const onlineLabel = th("online", { count: online, hotel: "" }).trim(); - return ( -
+
+ + - +
+ {/* Left panel */} +
+ +
+
+
+ Frank +
+ + + + + {th("online", { count: online, hotel: "" }).trim()} +
+

+ {t("welcomeBack")} +

+

+ {t("welcomeBackSub", { hotelName })} +

+
+ + + {recentUsers.length > 0 && ( + +
+ {recentUsers.map((u) => ( +
+ + + {u.username} + +
+ ))} +
+
+ )} + + {latestUsers.length > 0 && ( + +
+ {latestUsers.map((u) => ( +
+ + + {u.username} + +
+ ))} +
+
+ )} + +

{t("noAccount") ?? "No account?"}{" "} {t("createOne") ?? "Create one"}

- } - side={ -
-
- - - - - - {onlineLabel} - -

- {t("welcomeBack")} -
- {hotelName} -

-

- {t("welcomeBackSub", { hotelName })} -

-
- -
- -
- -
-
-
- {online} -
-

- {onlineLabel} -

- {recentUsers.length > 0 && ( -
- {recentUsers.slice(0, 5).map((u) => ( - - ))} -
- )} -
-
-
- - {totalUsers} - - -
-

- {thp("statsCitizens")} -

- {latestUsers.length > 0 && ( -
- {latestUsers.slice(0, 5).map((u) => ( - - ))} -
- )} -
-
-
- } - > -
-

- {t("title")} -

-

{t("subtitle")}

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

+ {t("subtitle")} +

+ +
+
+
); diff --git a/src/app/(site)/page.tsx b/src/app/(site)/page.tsx index d93830ad..2d91dedf 100644 --- a/src/app/(site)/page.tsx +++ b/src/app/(site)/page.tsx @@ -4,27 +4,8 @@ import { headers } from "next/headers"; import Image from "next/image"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; -import { AmbientOrbs } from "@/components/ambient-orbs"; -import { AnimatedCounter } from "@/components/animated-counter"; -import { HomeLoginForm } from "@/components/auth/home-login-form"; -import { LandingTopBar } from "@/components/landing-top-bar"; -import Link from "@/components/link"; -import { LiveOnlineCount } from "@/components/live-online-count"; -import { LiveOnlineCounter } from "@/components/live-online-counter"; -import { Reveal } from "@/components/motion-reveal"; import { PublicLoadError } from "@/components/public/load-error"; -import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail"; -import { SurfaceCard } from "@/components/surface-card"; -import { TypewriterText } from "@/components/typewriter-text"; -import { auth } from "@/lib/auth"; -import { cached } from "@/lib/cache"; -import { CameraWeb, db, Rooms, User } from "@/lib/db"; -import { formatDate } from "@/lib/format-date"; -import { resolveHotelName } from "@/lib/hotel-name"; -import { captchaConfig } from "@/lib/services/captcha"; -import { getNewsList } from "@/lib/services/news-list"; import { publicReadFailure } from "@/lib/services/public-read"; -import { siteSettings } from "@/lib/services/site-settings"; export const metadata: Metadata = { title: "Home", @@ -38,6 +19,28 @@ export const metadata: Metadata = { }, }; +import { AmbientOrbs } from "@/components/ambient-orbs"; +import { AnimatedCounter } from "@/components/animated-counter"; +import { HomeLoginForm } from "@/components/auth/home-login-form"; +import { Clock } from "@/components/clock"; +import { LanguageSwitcher } from "@/components/language-switcher"; +import Link from "@/components/link"; +import { LiveOnlineCount } from "@/components/live-online-count"; +import { LiveOnlineCounter } from "@/components/live-online-counter"; +import { Reveal } from "@/components/motion-reveal"; +import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail"; +import { SurfaceCard } from "@/components/surface-card"; +import { ThemeSwitcher } from "@/components/theme-switcher"; +import { TypewriterText } from "@/components/typewriter-text"; +import { auth } from "@/lib/auth"; +import { cached } from "@/lib/cache"; +import { CameraWeb, db, Rooms, User } from "@/lib/db"; +import { formatDate } from "@/lib/format-date"; +import { resolveHotelName } from "@/lib/hotel-name"; +import { captchaConfig } from "@/lib/services/captcha"; +import { getNewsList } from "@/lib/services/news-list"; +import { siteSettings } from "@/lib/services/site-settings"; + async function getHotelData() { const [hotelName, logo] = await Promise.all([ resolveHotelName(), @@ -135,211 +138,185 @@ export default async function Home() { }, ]; - const stats = [ - { - value: users, - label: tp("statsCitizens"), - icon: "/assets/images/icons/friends.png", - live: false, - }, - { - value: rooms, - label: tp("statsRooms"), - icon: "/assets/images/icons/catalog.png", - live: false, - }, - { - value: online, - label: tp("statsOnline"), - icon: "/assets/images/icons/navigation/me.png", - live: true, - }, - { - value: articles?.length ?? null, - label: tp("statsArticles"), - icon: "/assets/images/icons/article.gif", - live: false, - }, - ]; - return (
+ {/* ── Top Bar ── */} + +
+
+ {logo ? ( + {hotelName} + ) : ( + {hotelName} + )} + + {hotelName} + +
+
+ +
+ +
+ +
+
+ + {/* ── Hero ── */} -
+
+ +
- - -
- {/* Left: copy */} -
- - - - - - {online === null ? ( - {te("unavailable")} - ) : ( - - )} +
+
+
+ + + - -

- {tp("welcome", { hotelName: "" }).trim()} - -

- -

- {tp("welcomeBody")} -

- -
- - - {tp("register")} - - - {th("login")} - -
+ {online === null ? ( + {te("unavailable")} + ) : ( + + )}
- - {/* Right: visual */} -
- -
+ + +

+ {tp("welcomeBody")} +

+
+ -
{hotelName} -

- {tp("welcomeSubtitle")} -

-
-
-
-
-
-
-
-
-
- -
-

- -

-

- {tp("statsOnline")} -

-
- -
+ -

- -

-

- {tp("statsCitizens")} -

-
+ {th("login")} +
- - {/* Stats strip */} -
-
- {stats.map((s) => ( -
-
- - {s.value === null ? ( - - {te("unavailable")} - - ) : s.live ? ( - - ) : ( - - )} - - -
-

- {s.label} -

-
- ))} -
-
-
+
{/* ── Features ── */} @@ -348,28 +325,21 @@ export default async function Home() { {features.map((f) => (
-

{f.desc} @@ -388,6 +358,60 @@ export default async function Home() {

+ {/* ── Stats + Login ── */} + +
+ {[ + { + value: users, + label: tp("statsCitizens"), + icon: "/assets/images/icons/friends.png", + live: false, + }, + { + value: rooms, + label: tp("statsRooms"), + icon: "/assets/images/icons/catalog.png", + live: false, + }, + { + value: online, + label: tp("statsOnline"), + icon: "/assets/images/icons/navigation/me.png", + live: true, + }, + { + value: articles?.length ?? null, + label: tp("statsArticles"), + icon: "/assets/images/icons/article.gif", + live: false, + }, + ].map((s) => ( + +
+ {s.value === null ? ( + {te("unavailable")} + ) : s.live ? ( + + ) : ( + + )} +
+
+ + {s.label} +
+
+ ))} +
+
+ {/* ── Login + Online users ── */}
diff --git a/src/app/(site)/register/page.tsx b/src/app/(site)/register/page.tsx index a3cbfc3e..f3d79659 100644 --- a/src/app/(site)/register/page.tsx +++ b/src/app/(site)/register/page.tsx @@ -1,24 +1,21 @@ import { count, desc, eq } from "drizzle-orm"; import { headers } from "next/headers"; +import Image from "next/image"; import { getTranslations } from "next-intl/server"; -import { AuthShell } from "@/components/auth/auth-shell"; -import { BrandFrank } from "@/components/auth/brand-frank"; +import { AuthTopBar } from "@/components/auth/auth-top-bar"; import { RegisterForm } from "@/components/auth/register-form"; +import Link from "@/components/link"; import { Reveal } from "@/components/motion-reveal"; import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail"; +import { SurfaceCard } from "@/components/surface-card"; import { cached } from "@/lib/cache"; import { db, User } from "@/lib/db"; import { resolveHotelName } from "@/lib/hotel-name"; import { captchaConfig } from "@/lib/services/captcha"; import { siteSettings } from "@/lib/services/site-settings"; -export const metadata = { - title: "Create account", -}; - export default async function RegisterPage() { const tpr = await getTranslations("pages.register"); - const th = await getTranslations("pages.home"); const [hotelName, cfg, logo] = await Promise.all([ resolveHotelName(), captchaConfig(), @@ -26,7 +23,7 @@ export default async function RegisterPage() { ]); const nonce = (await headers()).get("x-nonce") ?? undefined; - const [online, latestUsers, recentUsers] = await Promise.all([ + const [online, recentUsers, latestUsers] = await Promise.all([ cached("online_count", 10_000, () => db .select({ total: count() }) @@ -34,148 +31,210 @@ export default async function RegisterPage() { .where(eq(User.online, "1")) .then((rows) => rows[0]?.total ?? 0), ).catch(() => 0), + cached("register_online_users", 10_000, () => + db + .select({ username: User.username, look: User.look }) + .from(User) + .where(eq(User.online, "1")) + .limit(8), + ).catch(() => []), cached("register_latest_users", 30_000, () => db .select({ username: User.username, look: User.look }) .from(User) .orderBy(desc(User.accountCreated)) - .limit(6), + .limit(8), ).catch(() => []), - db - .select({ username: User.username, look: User.look }) - .from(User) - .where(eq(User.online, "1")) - .limit(6) - .catch(() => []), ]); - const benefits = [ - { title: th("featuresAvatar"), desc: th("featuresAvatarDesc") }, - { title: th("featuresFurniture"), desc: th("featuresFurnitureDesc") }, - { title: th("featuresCommunity"), desc: th("featuresCommunityDesc") }, - ]; - return ( -
- - -
- - - - - - {tpr("usersOnline", { count: online })} - -

- {tpr("title")} -
- {hotelName} -

-

- {tpr("subtitle")} -

- {recentUsers.length > 0 && ( -
-
- {recentUsers.slice(0, 5).map((u) => ( - - ))} -
-

- {tpr("whoIsOnline")} -

-
- )} -
+
+ -
- {benefits.map((b) => ( -
- +
+ {/* Left: intro panel */} +
+ +
+
+
+
+
+
+ Frank +
+
+
- - {b.title} - - - -
-

- {b.title} -

-

- {b.desc} -

+ + {tpr("usersOnline", { count: online })}
-
- ))} -
- -
-
- -
- {latestUsers.length > 0 && ( - <> -

- Latest citizens -

-
- {latestUsers.slice(0, 5).map((u) => ( - - ))} -
- - )} +

+ {tpr("title")} — {hotelName} +

+

+ {tpr("subtitle")} +

-
- } - > - - + + + {recentUsers.length > 0 && ( + +
+ {recentUsers.map((u) => ( +
+ + + {u.username} + +
+ ))} +
+
+ )} + + {latestUsers.length > 0 && ( + +
+ {latestUsers.map((u) => ( +
+ + + {u.username} + +
+ ))} +
+
+ )} + +

+ {tpr("alreadyHaveAccountPre")}{" "} + + {tpr("alreadyHaveAccountLink")} + +

+
+ + {/* Right: form */} +
+ +

+ {tpr("subtitle")} +

+ +
+
+
); diff --git a/src/app/globals.css b/src/app/globals.css index f67c16ff..8ff35c5b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1993,135 +1993,28 @@ details[open] > summary .details-open\:rotate-180 { animation-delay: 0.4s; } -/* ── Premium landing kit (dark gaming style) ─────────────────── - Shared primitives for the redesigned home / login / register - pages. Everything stays theme-aware: brand colors are driven by - --color-primary / --color-accent, and the "always-dark" hero - canvas is mixed from the surface token so it blends with both - light and dark hotel themes. */ -.landing-hero { - position: relative; - isolation: isolate; - overflow: hidden; - background: - radial-gradient( - 60em 40em at 12% -10%, - color-mix(in srgb, var(--color-primary) 22%, transparent) 0%, - transparent 60% - ), - radial-gradient( - 50em 36em at 108% 12%, - color-mix(in srgb, var(--color-accent) 18%, transparent) 0%, - transparent 62% - ), - linear-gradient( - 175deg, - color-mix(in srgb, var(--color-surface) 6%, #05070d) 0%, - color-mix(in srgb, var(--color-surface) 8%, #0a0f1d) 55%, - color-mix(in srgb, var(--color-background) 45%, #0a0f1d) 100% - ); - color-scheme: dark; -} -.landing-hero::after { - content: ""; +/* ── Classic-landing polish (theme-aware) ──────────────────────────── + Keeps the Habbo sky and theme colors; adds subtle depth + glow to the + restored landing pages. Everything is driven by theme CSS variables. */ +.brand-halo { position: absolute; - inset: 0; pointer-events: none; - background-image: - linear-gradient( - to right, - color-mix(in srgb, #ffffff 5%, transparent) 1px, - transparent 1px - ), - linear-gradient( - to bottom, - color-mix(in srgb, #ffffff 5%, transparent) 1px, - transparent 1px - ); - background-size: 44px 44px; - -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent 92%); - mask-image: linear-gradient(to bottom, #000 30%, transparent 92%); - z-index: 0; -} - -/* Text with a brand gradient (primary → accent). */ -.text-gradient { - background: linear-gradient( - 115deg, - var(--color-primary) 10%, - color-mix(in srgb, var(--color-primary) 55%, var(--color-accent)) 48%, - var(--color-accent) 92% - ); - -webkit-background-clip: text; - background-clip: text; - color: transparent; -} - -/* Primary-tinted glass card used for auth forms / panels. */ -.glass-panel { - background: color-mix(in srgb, #0b1020 62%, transparent); - backdrop-filter: blur(22px); - -webkit-backdrop-filter: blur(22px); - border: 1px solid color-mix(in srgb, #ffffff 10%, transparent); - box-shadow: - 0 30px 80px -20px rgba(0, 0, 0, 0.55), - inset 0 1px 0 color-mix(in srgb, #ffffff 9%, transparent); -} - -/* Soft animated halo behind floating hero art. */ -.hero-halo { - position: absolute; border-radius: 999px; background: radial-gradient( circle, - color-mix(in srgb, var(--color-primary) 55%, transparent) 0%, - transparent 68% + color-mix(in srgb, var(--color-primary) 30%, transparent) 0%, + transparent 70% ); - filter: blur(6px); - animation: hero-halo-pulse 5s ease-in-out infinite; + filter: blur(20px); } -@keyframes hero-halo-pulse { - 0%, - 100% { - opacity: 0.55; - transform: scale(1); - } - 50% { - opacity: 0.9; - transform: scale(1.07); - } +.card-glow { + transition: + box-shadow 0.3s ease, + transform 0.3s ease, + border-color 0.3s ease; } - -@media (prefers-reduced-motion: reduce) { - .hero-halo { - animation: none; - } -} - -/* White-on-dark overlay text/backgrounds for the landing canvas. Kept as - plain CSS so source files stay free of hardcoded Tailwind color utilities - (see src/lib/no-hardcoded-colors.test.ts). */ -.landing-text { - color: #f8fafc; -} -.landing-text-primary { - color: #ffffff; -} -.landing-text-secondary { - color: rgb(255 255 255 / 0.86); -} -.landing-text-muted { - color: rgb(255 255 255 / 0.62); -} -.landing-text-faint { - color: rgb(255 255 255 / 0.5); -} -.landing-chip { - background: rgb(255 255 255 / 0.05); -} -.landing-chip-strong { - background: rgb(255 255 255 / 0.1); -} -.landing-divider { - background: rgb(255 255 255 / 0.15); +.card-glow:hover { + box-shadow: + 0 12px 32px -10px color-mix(in srgb, var(--color-primary) 32%, transparent), + 0 0 0 1px color-mix(in srgb, var(--color-primary) 22%, transparent); } diff --git a/src/components/auth/auth-shell.tsx b/src/components/auth/auth-shell.tsx deleted file mode 100644 index 86bd0e5a..00000000 --- a/src/components/auth/auth-shell.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { ReactNode } from "react"; -import { AmbientOrbs } from "@/components/ambient-orbs"; -import { LandingTopBar } from "@/components/landing-top-bar"; - -/** - * Premium dark-gaming shell shared by the login and register pages. - * Renders an always-dark hero canvas (brand glows + grid overlay) with a - * hotel header on top, a brand/side column on the left and the auth form - * (rendered by the page) on the right in a frosted glass panel. - */ -export function AuthShell({ - hotelName, - logo, - side, - footer, - children, -}: { - hotelName: string; - logo?: string | null; - side: ReactNode; - footer?: ReactNode; - children: ReactNode; -}) { - return ( -
- {/* Ambient glows */} - - - {/* Header */} - - - {/* Body */} -
-
{side}
-
- {children} -
-
- - {footer ? ( -
- {footer} -
- ) : null} -
- ); -} diff --git a/src/components/auth/brand-frank.tsx b/src/components/auth/brand-frank.tsx deleted file mode 100644 index 46b0b6e1..00000000 --- a/src/components/auth/brand-frank.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import Image from "next/image"; - -/** - * Floating brand art for the premium auth pages: the welcome character - * hovers over a pulsing primary halo with a faint slow-orbiting ring. - */ -export function BrandFrank({ size = 150 }: { size?: number }) { - return ( -
- - - -
- -
-
- ); -} diff --git a/src/components/auth/register-form.tsx b/src/components/auth/register-form.tsx index cb79dd1f..e92c9207 100644 --- a/src/components/auth/register-form.tsx +++ b/src/components/auth/register-form.tsx @@ -113,30 +113,52 @@ export function RegisterForm({ /> ) : null} {/* Header Banner */} -
-
- -
-
-

+

+
+
- {t("accountDetails")} -

-

- {t("title")} -

-

- {t("subtitle")} -

+
+
+
+ +
@@ -149,7 +171,12 @@ export function RegisterForm({ }; formAction(fd); }} - className="flex flex-col gap-6" + className="p-6 flex flex-col gap-6" + style={{ + backgroundColor: + "color-mix(in srgb, var(--color-background) 50%, var(--color-surface))", + borderRadius: "0 0 12px 12px", + }} > {(error || state.error) && (
-
- {logo ? ( - {hotelName} - ) : ( - {hotelName} - )} - - {hotelName} - -
-
- -
- -
- -
-
- ); -}