'use client'; import Link from 'next/link'; import {BrandLogo} from '@/shared/components/brand/BrandLogo'; import {LoginCredentialsForm} from './LoginCredentialsForm'; import {LoginHeroPanel} from './LoginHeroPanel'; /** * The sign-in screen's frame: the ivory canvas, the ambient yellow shapes, the * split card, and the two halves that fill it. * * This file used to be 314 lines holding the photograph, the form, the fake * submit and the page chrome at once. It is now the composition only — * behaviour lives in useLoginForm, the form markup in LoginCredentialsForm, * the carousel in LoginHeroPanel — which is what makes each of them separately * readable and separately changeable. * * ── On the literal colour values here ──────────────────────────────────── * The workspace is monochrome and token-driven; this screen deliberately is * not. It predates the workspace, it is the only page a merchant sees before * the product, it renders outside the Astryx shell, and it is pinned to the * light palette whatever the theme cookie says (see `.login-surface` in * globals.css). The one hue used is #F4C430 — the same Loyaly yellow as * `--color-brand-warm`, kept literal because no Astryx token is in scope here. */ /** * `/join` renders in the same frame: redeeming an invitation is the other way * into the console, and a second design for it would be a second thing to * keep in step. Called with no props, this is exactly the sign-in screen. */ export function LoginSplit({ title = 'Welcome back', description = 'Access your Merchant Operating System to manage stores, rewards, staff and AI insights.', children, }: { title?: string; description?: string; children?: React.ReactNode; } = {}) { return (
{/* The ambient layer: a warm wash at the outer edges, then three large curved shapes cropped by the viewport so only an arc of each is ever visible. The brief is "felt, not seen" — nothing here should read as a distinct object behind the card. Painted with radial gradients rather than blurred elements, and that is a performance fix rather than a style choice. Three `blur-[110px]` divs are three compositing layers the size of the viewport, and the carousel crossfading on top of them invalidated all three every frame for a full second every five seconds — enough to lock the renderer. A gradient is soft for free: no filter, no layer, no repaint. */}
Merchant OS

{title}

{description}

{children ?? ( <>

Got an invitation code?{' '} Join your team

)}
{/* In normal flow rather than pinned to the viewport bottom: the card grows with the form's error messages, and an absolutely positioned footer sat on top of it as soon as it did. */}

By continuing, you agree to Loyaly's{' '} Terms of Service {' '} and{' '} Privacy Policy .

); }