feat(auth): bring the landing's cloud sky into login/register
Replace AuthShell's deep-blue grid backdrop with AuthSky — the same bright pale-blue gradient (weather stop 0) and the same vendored cloud puff (/cloud.png) as the landing, as lightweight drifting CSS sprites so no three.js lands on the auth routes. The form keeps the shared frosted-glass panel, so login/register now read as one world with the public landing. Cloud drift respects prefers-reduced-motion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -193,8 +193,17 @@ html.lenis body {
|
|||||||
@keyframes abrbanShimmer {
|
@keyframes abrbanShimmer {
|
||||||
to { background-position: 200% center; }
|
to { background-position: 200% center; }
|
||||||
}
|
}
|
||||||
|
/* Gentle horizontal sway for the auth-screen clouds (see AuthSky). */
|
||||||
|
@keyframes abrbanCloudDrift {
|
||||||
|
from { transform: translateX(calc(var(--drift, 6vw) * -1)); }
|
||||||
|
to { transform: translateX(var(--drift, 6vw)); }
|
||||||
|
}
|
||||||
.abrban-float { animation: abrbanFloat 6s ease-in-out infinite; }
|
.abrban-float { animation: abrbanFloat 6s ease-in-out infinite; }
|
||||||
.abrban-scroll-hint { animation: abrbanScrollHint 1.8s ease-in-out infinite; }
|
.abrban-scroll-hint { animation: abrbanScrollHint 1.8s ease-in-out infinite; }
|
||||||
|
.abrban-cloud-drift {
|
||||||
|
animation: abrbanCloudDrift var(--drift-dur, 40s) ease-in-out infinite alternate;
|
||||||
|
animation-delay: var(--drift-delay, 0s);
|
||||||
|
}
|
||||||
.abrban-shimmer {
|
.abrban-shimmer {
|
||||||
background: linear-gradient(100deg, #93c5fd 0%, #ffffff 25%, #2563eb 50%, #ffffff 75%, #93c5fd 100%);
|
background: linear-gradient(100deg, #93c5fd 0%, #ffffff 25%, #2563eb 50%, #ffffff 75%, #93c5fd 100%);
|
||||||
background-size: 200% auto;
|
background-size: 200% auto;
|
||||||
@@ -223,7 +232,8 @@ html.lenis body {
|
|||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.abrban-float,
|
.abrban-float,
|
||||||
.abrban-scroll-hint,
|
.abrban-scroll-hint,
|
||||||
.abrban-shimmer {
|
.abrban-shimmer,
|
||||||
|
.abrban-cloud-drift {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import { Link } from '@/i18n/Link';
|
|||||||
import { useT } from '@/i18n/I18nProvider';
|
import { useT } from '@/i18n/I18nProvider';
|
||||||
import { LanguageSwitcher } from '@/i18n/LanguageSwitcher';
|
import { LanguageSwitcher } from '@/i18n/LanguageSwitcher';
|
||||||
import { Logo } from '@/components/landing/Logo';
|
import { Logo } from '@/components/landing/Logo';
|
||||||
|
import { AuthSky } from './AuthSky';
|
||||||
|
|
||||||
// Auth shell that carries the Abrban brand into a calmer, more premium key: a
|
// Auth shell that carries the Abrban brand straight from the landing: the same
|
||||||
// deep blue gradient, a faint infrastructure grid, soft brand-blue glows and a
|
// bright, cloud-filled sky (see AuthSky) sits behind the form, which lives in the
|
||||||
// vignette — no fluffy clouds — with the form inside the same frosted dark-glass
|
// same frosted dark-glass panel — so login/register read as one world with the
|
||||||
// panel used across the landing. Direction is inherited from the root layout.
|
// public landing. Direction is inherited from the root layout.
|
||||||
export function AuthShell({
|
export function AuthShell({
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
@@ -27,21 +28,9 @@ export function AuthShell({
|
|||||||
}) {
|
}) {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-screen w-full overflow-hidden bg-[#081427] text-white">
|
<div className="relative min-h-screen w-full overflow-hidden bg-[#9fb4d4] text-white">
|
||||||
{/* Background atmosphere */}
|
{/* Bright cloud-filled sky backdrop, shared with the landing */}
|
||||||
<div className="pointer-events-none absolute inset-0">
|
<AuthSky />
|
||||||
{/* Deep brand gradient */}
|
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(125%_120%_at_50%_-10%,#1f5aa0_0%,#0e2c57_45%,#081427_100%)]" />
|
|
||||||
{/* Fine infrastructure grid, faded toward the edges */}
|
|
||||||
<div className="absolute inset-0 opacity-[0.16] [background-image:linear-gradient(rgba(255,255,255,0.10)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.10)_1px,transparent_1px)] [background-size:54px_54px] [mask-image:radial-gradient(ellipse_at_center,black_45%,transparent_92%)] [-webkit-mask-image:radial-gradient(ellipse_at_center,black_45%,transparent_92%)]" />
|
|
||||||
{/* Soft brand-blue glows */}
|
|
||||||
<div className="absolute right-[-8%] top-[-12%] h-[30rem] w-[30rem] rounded-full bg-[radial-gradient(circle,_rgba(59,130,246,0.40),_transparent_70%)] blur-3xl" />
|
|
||||||
<div className="absolute bottom-[-14%] left-[-10%] h-[28rem] w-[28rem] rounded-full bg-[radial-gradient(circle,_rgba(37,99,235,0.34),_transparent_70%)] blur-3xl" />
|
|
||||||
{/* Crisp hairline along the very top */}
|
|
||||||
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/25 to-transparent" />
|
|
||||||
{/* Vignette to focus the centre */}
|
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_transparent_55%,_rgba(4,10,22,0.55)_100%)]" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="relative z-10 mx-auto flex max-w-5xl items-center justify-between px-6 py-5">
|
<header className="relative z-10 mx-auto flex max-w-5xl items-center justify-between px-6 py-5">
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
// A calm, lightweight echo of the landing's cinematic sky — the same pale-blue
|
||||||
|
// gradient (weather stop 0) and the very same vendored cloud puff (/cloud.png),
|
||||||
|
// but as cheap drifting CSS sprites instead of the full WebGL scene. It keeps the
|
||||||
|
// auth screens visually tied to the landing ("ابر" floating behind the form)
|
||||||
|
// without pulling three.js onto the login/register routes.
|
||||||
|
|
||||||
|
// Each cloud is a small cluster of overlapping puffs so the silhouette reads as a
|
||||||
|
// soft fluffy cloud rather than a single blob. Offsets are relative to the
|
||||||
|
// cluster's own box (percent of its width).
|
||||||
|
const PUFFS = [
|
||||||
|
{ l: '0%', t: '32%', w: '52%' },
|
||||||
|
{ l: '20%', t: '6%', w: '62%' },
|
||||||
|
{ l: '46%', t: '24%', w: '56%' },
|
||||||
|
{ l: '28%', t: '40%', w: '66%' },
|
||||||
|
];
|
||||||
|
|
||||||
|
type CloudSpec = {
|
||||||
|
// Anchor: provide either left/right and either top/bottom.
|
||||||
|
left?: string;
|
||||||
|
right?: string;
|
||||||
|
top?: string;
|
||||||
|
bottom?: string;
|
||||||
|
width: string; // cluster width
|
||||||
|
opacity: number;
|
||||||
|
blur: number; // px
|
||||||
|
drift: string; // horizontal sway amplitude
|
||||||
|
dur: string; // drift duration
|
||||||
|
delay: string; // negative to desync
|
||||||
|
};
|
||||||
|
|
||||||
|
const CLOUDS: CloudSpec[] = [
|
||||||
|
{ top: '6%', left: '-8%', width: '30rem', opacity: 0.92, blur: 1, drift: '7vw', dur: '34s', delay: '0s' },
|
||||||
|
{ top: '18%', right: '-10%', width: '36rem', opacity: 0.8, blur: 2, drift: '6vw', dur: '42s', delay: '-7s' },
|
||||||
|
{ top: '50%', left: '2%', width: '24rem', opacity: 0.5, blur: 3, drift: '5vw', dur: '38s', delay: '-14s' },
|
||||||
|
{ top: '62%', right: '0%', width: '30rem', opacity: 0.58, blur: 3, drift: '8vw', dur: '46s', delay: '-4s' },
|
||||||
|
{ bottom: '-6%', left: '18%', width: '38rem', opacity: 0.72, blur: 4, drift: '5vw', dur: '52s', delay: '-22s' },
|
||||||
|
];
|
||||||
|
|
||||||
|
function Cloud({ spec }: { spec: CloudSpec }) {
|
||||||
|
const driftVars = {
|
||||||
|
'--drift': spec.drift,
|
||||||
|
'--drift-dur': spec.dur,
|
||||||
|
'--drift-delay': spec.delay,
|
||||||
|
} as CSSProperties;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="abrban-cloud-drift pointer-events-none absolute"
|
||||||
|
style={{ left: spec.left, right: spec.right, top: spec.top, bottom: spec.bottom, ...driftVars }}
|
||||||
|
>
|
||||||
|
<div className="abrban-float" style={{ animationDelay: spec.delay }}>
|
||||||
|
<div
|
||||||
|
className="relative"
|
||||||
|
style={{
|
||||||
|
width: spec.width,
|
||||||
|
height: `calc(${spec.width} * 0.6)`,
|
||||||
|
opacity: spec.opacity,
|
||||||
|
filter: `blur(${spec.blur}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{PUFFS.map((p, i) => (
|
||||||
|
<span
|
||||||
|
key={i}
|
||||||
|
className="absolute aspect-square bg-contain bg-center bg-no-repeat"
|
||||||
|
style={{ left: p.l, top: p.t, width: p.w, backgroundImage: 'url(/cloud.png)' }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuthSky() {
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-none absolute inset-0 overflow-hidden">
|
||||||
|
{/* Bright sky gradient — landing weather stop 0 (calm, bright overcast). */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-b from-[#9fb4d4] via-[#c3d0e0] to-[#e9f0f8]" />
|
||||||
|
{/* Soft warm sun glow, high and centred, like the landing's hidden sun. */}
|
||||||
|
<div className="absolute left-1/2 top-[-18%] h-[34rem] w-[34rem] -translate-x-1/2 rounded-full bg-[radial-gradient(circle,_rgba(255,242,210,0.55),_transparent_68%)] blur-2xl" />
|
||||||
|
{/* Drifting clouds. */}
|
||||||
|
{CLOUDS.map((spec, i) => (
|
||||||
|
<Cloud key={i} spec={spec} />
|
||||||
|
))}
|
||||||
|
{/* Low fog haze so the bottom melts into the sky like the landing. */}
|
||||||
|
<div className="absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-[#e9f0f8]/80 to-transparent" />
|
||||||
|
{/* Crisp hairline along the very top. */}
|
||||||
|
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/40 to-transparent" />
|
||||||
|
{/* Vignette to focus the centre — matches LandingPage. */}
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_transparent_62%,_rgba(15,23,42,0.34)_100%)]" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user