"use client" /** * AuthShell — the two-column frame every pre-auth surface wears. * * Left: a promo rail. Right: the form, and a support footer that stays reachable * whether or not sign-in succeeds. * * The rail is the first thing to go below `lg`. It carries no task, so on a * phone it would push the one control that matters below the fold; the brand * lockup moves inline above the form instead so the page still identifies itself. * * Heading contract: the rail's promo is an `h2` and the page supplies the single * `h1`, because the reason you are here is signing in, not the conference. */ import * as React from "react" import { MarketingBanner } from "@/components/ui/marketing-banner" import { ProductMark, ProductWordmark } from "@/components/product-wordmark" import { EXXAT_CORPORATE_BRAND } from "@/lib/product-brand" import { cn } from "@/lib/utils" /** * One promo, held in code rather than rotated. * * A carousel here would move the page under someone mid-password and give a * screen reader three competing pitches beside a single form. When this needs to * change per season, change the object. */ const AUTH_PROMO = { title: "Exxat User Conference 2026", description: "Baltimore, MD. September 23 to 25, 2026.", action: { label: "Register Today", href: "https://www.exxat.com/" }, /** * Which slide artwork the promo wears. Production ships one of these per * carousel slide; both live in `public/auth`, so swapping the promo to the * Prism story is this line plus the copy above, not a layout change: * `/auth/exxat-prism-visual.svg`. * * Purely decorative, so the alt text is empty and the overlay is `aria-hidden` * — the promo already says what it is in the heading beside it. */ visual: "/auth/cohere-slide-visual.svg", } as const /** Corporate lockup: mark plus the word "Exxat", no product suffix. */ export function AuthBrandLockup({ className }: { className?: string }) { return ( Exxat ) } /** * The slide: a pastel canvas with the artwork laid over it, filling the panel * from the top-left, which is how the production rail composes it. The vector is * drawn to sit *on* that wash rather than to replace it — its own pink wedge * supplies the saturated colour, and the canvas is what shows through wherever * `contain` leaves the panel uncovered. * * Both layers live in one `decorativeOverlay` so they stay a single ordered * stack under the copy. Painting the canvas on the banner root instead would put * it behind the border and corner radius, and leave the wash and the art it * belongs to in two different files. */ function AuthPromoVisual({ src }: { src: string }) { return ( <>
{/* Bottom-anchored, where the reference says top. Identical on any panel wider than the art's 0.87 ratio, because `contain` then fits by height and there is no vertical gap to place. On a taller panel it fits by width instead and letterboxes, and top-anchoring drops that gap under the copy — bare pastel behind white text, which measured 1.11:1. Putting the gap above the art instead costs nothing: no copy sits up there. */} {/* The copy is white because the slide is, and the art's wedge is a mid-pink that gives white only 3.9:1 at its deepest — over the 3:1 a large title needs, under the 4.5:1 the meta line needs. Deepening the wedge under the copy with its own hue family buys that back without reading as a grey veil dropped on the artwork. Nothing here is decorative: at 15% it is the difference between a passing and a failing line, and axe cannot see it, because it does not evaluate what an image paints behind text. */}
) } function AuthPromoRail() { return ( ) } function AuthSupportFooter() { return ( // `text-xs`, not `text-sm`. The form column is under half the width now, and // at 14px the help line wrapped to a second row, which turned a two-line // footer into three and pushed the seam up into the form.