From 969f7a82a0596c8e26dd04ef5d92e9fa925ccd84 Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Wed, 7 Oct 2026 06:09:13 +0000 Subject: [PATCH] Gate polish round two: glows, disabled state, help link, modal - Disabled Continue is now the primary style at 45% opacity with cursor: not-allowed; full opacity while validating and once text is entered. - Move "How do I get a key?" below the Continue button (small ? icon, muted, underline on hover) and tighten input+button spacing. - Shorten the subtitle to "One step to freeze your first deck." and balance it. - Replace the faint gradient with two real blurred blobs (top-right and bottom-left, 120px blur, pointer-events: none, behind the card). - Dark: page #0f0f0f, card #1a1a1a, 8% white border. - Phone: 24px heading, 15px subtitle, wider card padding, 40px corner buttons with safe-area-aware offsets, robust 100dvh centering. - Modal: 480px wide with text-pretty steps, stronger tinted free-tier callout, handle removed from the phone sheet. - Tests: gate suite now 49 checks incl. disabled opacity, glow pseudo, card separation and help-link placement; new state screenshots for invalid/offline/validating in both themes. --- app/globals.css | 51 +++++++++++++++++++++++++++------------ components/ApiKeyGate.tsx | 50 ++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/app/globals.css b/app/globals.css index 9f098c0..1566715 100644 --- a/app/globals.css +++ b/app/globals.css @@ -104,11 +104,12 @@ --sidebar-edge: 0 1px 0 var(--hairline), 6px 0 16px -14px rgb(13 13 13 / 0.28); /* API-key gate: near-white stage with soft blank glows, tinted input. */ --gate-base: #f6f6f8; - --gate-glow-a: rgb(88 106 132 / 0.16); - --gate-glow-b: rgb(126 140 160 / 0.12); + --gate-glow-a: rgb(84 102 130 / 0.22); + --gate-glow-b: rgb(124 138 160 / 0.18); --gate-card: #ffffff; --gate-card-border: #e7e7e9; - --gate-callout-border: #d9d9dc; + --gate-callout-bg: #f2f2f5; + --gate-callout-border: #cfcfd6; --field: #f3f3f4; --field-border: #e2e2e4; --shadow-gate: 0 28px 70px -36px rgb(13 13 13 / 0.3), 0 1px 2px rgb(13 13 13 / 0.05); @@ -134,12 +135,13 @@ --sidebar-bottom: #151515; --sidebar-edge: 0 1px 0 var(--hairline), 6px 0 16px -14px rgb(0 0 0 / 0.75); /* API-key gate: near-black stage, monochrome glows, raised input. */ - --gate-base: #0c0c0d; - --gate-glow-a: rgb(255 255 255 / 0.14); - --gate-glow-b: rgb(255 255 255 / 0.09); - --gate-card: #171718; + --gate-base: #0f0f0f; + --gate-glow-a: rgb(255 255 255 / 0.17); + --gate-glow-b: rgb(255 255 255 / 0.12); + --gate-card: #1a1a1a; --gate-card-border: rgb(255 255 255 / 0.08); - --gate-callout-border: rgb(255 255 255 / 0.2); + --gate-callout-bg: rgb(255 255 255 / 0.07); + --gate-callout-border: rgb(255 255 255 / 0.22); --field: #242425; --field-border: rgb(255 255 255 / 0.1); --shadow-gate: 0 30px 80px -40px rgb(0 0 0 / 0.85), 0 1px 2px rgb(0 0 0 / 0.4); @@ -192,14 +194,33 @@ body { background: transparent; } -/* API-key gate stage: monochrome radial glows in opposite corners over a - near-black (dark) or near-white (light) base. Radial gradients are the - softest "blurred glow" available without extra layers. */ +/* API-key gate stage: two big blurred blobs (top-right + bottom-left) over a + near-black (dark) or near-white (light) base. Real blurred elements read as + atmosphere; a flat radial gradient was too faint. Both blobs are decorative, + non-interactive and sit behind the card (z-index 0 vs the card's z-10). */ .gate-bg { - background: - radial-gradient(72rem 54rem at 108% -14%, var(--gate-glow-a), transparent 70%), - radial-gradient(64rem 48rem at -14% 112%, var(--gate-glow-b), transparent 72%), - var(--gate-base); + background: var(--gate-base); +} +.gate-bg::before, +.gate-bg::after { + content: ""; + position: absolute; + z-index: 0; + width: 48rem; + height: 48rem; + border-radius: 9999px; + filter: blur(120px); + pointer-events: none; +} +.gate-bg::before { + top: -20rem; + right: -16rem; + background: var(--gate-glow-a); +} +.gate-bg::after { + bottom: -20rem; + left: -16rem; + background: var(--gate-glow-b); } .app-backdrop { diff --git a/components/ApiKeyGate.tsx b/components/ApiKeyGate.tsx index b07fdd5..bd8626f 100644 --- a/components/ApiKeyGate.tsx +++ b/components/ApiKeyGate.tsx @@ -69,7 +69,7 @@ function CornerButton({ onClick={onClick} aria-label={label} title={title} - className="grid size-9 place-items-center rounded-full border border-[var(--field-border)] bg-[var(--gate-card)] text-[var(--text-muted)] transition hover:bg-[var(--hover)] hover:text-[var(--text)]" + className="grid size-10 place-items-center rounded-full border border-[var(--field-border)] bg-[var(--gate-card)] text-[var(--text-muted)] transition hover:bg-[var(--hover)] hover:text-[var(--text)]" > {children} @@ -123,8 +123,8 @@ function GateScreen() {
{/* card ----------------------------------------------------------- */} -
+
-

+

Connect your Ollama key

-

- One step and you’re ready to freeze your first deck. +

+ One step to freeze your first deck.

@@ -223,20 +223,13 @@ function GateScreen() {

) : null} -
- -
-
+
+ +
+

Your key stays in this browser. Our server never stores it.

@@ -285,10 +289,8 @@ function HelpDialog({ open, onClose }: { open: boolean; onClose: () => void }) { exit={reduceMotion ? { opacity: 0 } : { y: 24, opacity: 0 }} transition={{ type: "spring", stiffness: 340, damping: 32 }} onClick={(event) => event.stopPropagation()} - className="thin-scroll max-h-[90dvh] w-full max-w-md overflow-y-auto rounded-t-[24px] border border-[var(--gate-card-border)] bg-[var(--gate-card)] p-6 pb-safe shadow-[var(--shadow-gate)] sm:rounded-[24px] sm:pb-6" + className="thin-scroll max-h-[90dvh] w-full max-w-[480px] overflow-y-auto rounded-t-[24px] border border-[var(--gate-card-border)] bg-[var(--gate-card)] p-6 pb-safe shadow-[var(--shadow-gate)] sm:rounded-[24px] sm:pb-6" > -
-

void }) {

-
+

You only need the{" "} @@ -375,7 +377,7 @@ function Step({ index, children }: { index: number; children: React.ReactNode }) > {index} - {children} + {children} ); }