Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions apps/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,77 @@ html, body {
}
}

/* ─── Landing redesign ─────────────────────────────────────────────
Top-anchored amber spotlight + faded dot grid. Static (no JS, no
animation) so it's cheap; lives only behind the hero, rest stays
clean dark. Content must sit in a relative z-10 wrapper above these. */
.hero-spotlight {
position: relative;
overflow: hidden;
}
.hero-spotlight::before {
/* faint dotted grid, masked so it fades out below the fold */
content: "";
position: absolute;
inset: 0;
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.05) 1px, transparent 1.4px);
background-size: 26px 26px;
-webkit-mask-image: radial-gradient(110% 70% at 50% 0%, #000 0%, transparent 65%);
mask-image: radial-gradient(110% 70% at 50% 0%, #000 0%, transparent 65%);
pointer-events: none;
}
.hero-spotlight::after {
/* two stacked amber radials: a tight strong core + a wide soft wash */
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(55% 45% at 50% -8%, rgba(245, 158, 11, 0.24) 0%, transparent 60%),
radial-gradient(120% 70% at 50% -10%, rgba(245, 158, 11, 0.07) 0%, transparent 70%);
pointer-events: none;
}

/* Feature card: dark surface, border brightens on hover, with a single
diagonal light sweep that runs only on hover (not a looping shimmer).
Reduced-motion users get the border change without the sweep. */
.feat-card {
position: relative;
overflow: hidden;
background: var(--gradient-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
transition:
border-color var(--dur-mid) var(--ease-out),
transform var(--dur-mid) var(--ease-out);
}
.feat-card:hover {
border-color: var(--color-border-default);
transform: translateY(-2px);
}
.feat-card > * {
position: relative;
z-index: 1;
}
.feat-card::after {
content: "";
position: absolute;
inset: 0;
z-index: 0;
background: linear-gradient(
120deg,
transparent 30%,
rgba(255, 255, 255, 0.06) 50%,
transparent 70%
);
transform: translateX(-120%);
transition: transform 700ms var(--ease-out);
pointer-events: none;
}
.feat-card:hover::after {
transform: translateX(120%);
}

/* Honor reduced-motion globally — covers landing animations + existing transitions */
@media (prefers-reduced-motion: reduce) {
*,
Expand Down
65 changes: 43 additions & 22 deletions apps/web/src/screens/LandingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ export function LandingScreen() {

return (
<div className="landing-scroll" style={{ background: "var(--color-bg)" }}>
{/* Ambient hero glow */}
<div style={{ background: "var(--gradient-hero)" }}>
<div className="pt-safe">
{/* Hero owns the amber spotlight + dot-grid backdrop */}
<div className="hero-spotlight pt-safe">
<div className="relative z-10">
<Nav loading={loading} onSignIn={handleSignIn} />
<Hero
loading={loading}
error={error}
Expand All @@ -51,6 +52,27 @@ export function LandingScreen() {
);
}

/* ─────────────────────────── Nav ─────────────────────────── */

function Nav({ loading, onSignIn }: { loading: boolean; onSignIn: () => void }) {
return (
<nav className="mx-auto flex w-full max-w-screen-lg items-center justify-between px-5 pt-5">
<span className="flex items-center gap-2">
<BrandMark size={28} />
<span
className="text-base font-semibold tracking-tight"
style={{ color: "var(--color-text-primary)", fontFamily: "var(--font-mono)" }}
>
Khata
</span>
</span>
<Button variant="secondary" size="sm" loading={loading} onClick={onSignIn}>
Sign in
</Button>
</nav>
);
}

/* ─────────────────────────── Hero ─────────────────────────── */

function Hero({
Expand All @@ -65,25 +87,31 @@ function Hero({
onHowItWorks: () => void;
}) {
return (
<header className="mx-auto w-full max-w-screen-lg px-5 pt-10 pb-12 lg:pt-16 lg:pb-20">
<header className="mx-auto w-full max-w-screen-lg px-5 pt-10 pb-12 lg:pt-14 lg:pb-20">
<div className="flex flex-col items-center gap-12 lg:flex-row lg:items-center lg:gap-16">
{/* Copy */}
<div
className="flex w-full flex-col items-center text-center lg:items-start lg:text-left"
style={{ animation: "khata-rise var(--dur-slow) var(--ease-out) both" }}
>
<span className="flex items-center gap-2">
<BrandMark size={32} />
{/* Pill badge */}
<span
className="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-medium"
style={{
background: "var(--color-accent-subtle)",
border: "1px solid var(--color-accent-border)",
color: "var(--color-accent)",
}}
>
<span
className="text-base font-semibold tracking-tight"
style={{ color: "var(--color-text-primary)", fontFamily: "var(--font-mono)" }}
>
Khata
</span>
className="h-1.5 w-1.5 rounded-full"
style={{ background: "var(--color-accent)" }}
/>
UPI-aware · Private · On your device
</span>

<h1
className="mt-4 text-4xl font-bold leading-[1.1] tracking-tight sm:text-5xl"
className="mt-5 text-4xl font-bold leading-[1.1] tracking-tight sm:text-5xl"
style={{ color: "var(--color-text-primary)" }}
>
Split trips.
Expand Down Expand Up @@ -318,21 +346,14 @@ function Features() {
</h2>
<div className="mt-10 grid grid-cols-1 gap-4 sm:grid-cols-2">
{FEATURES.map((f) => (
<div
key={f.title}
className="flex flex-col gap-3 p-6"
style={{
background: "var(--gradient-surface)",
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border-subtle)",
boxShadow: "var(--shadow-card)",
}}
>
<div key={f.title} className="feat-card flex flex-col gap-3 p-6">
{/* Ringed icon badge */}
<span
className="flex h-11 w-11 items-center justify-center"
style={{
borderRadius: "var(--radius-md)",
background: "var(--color-accent-bg)",
border: "1px solid var(--color-accent-border)",
color: "var(--color-accent)",
}}
>
Expand Down
Loading