+ {/* Left: the "start testing" form. */}
+
+
+ {/* Gradient border layer — animates out when the demo video is
+ hovered, mirroring the video's frame animating in. */}
+
+
-
+
+
+ {/* App selector — segmented "Your app" / "Demo app" toggle.
+ "Demo app" switches to a demo and opens the picker so a
+ specific demo (Website / E-commerce / Banking) stays
+ selectable. */}
+
+
handleAppTypeChange(AppType.YOUR_APPLICATION)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ handleAppTypeChange(AppType.YOUR_APPLICATION);
+ }
+ }}
+ className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold leading-none cursor-pointer select-none transition-all ${
+ appType === AppType.YOUR_APPLICATION
+ ? "bg-secondary-wopee text-white shadow-sm"
+ : "text-secondary-wopee/70 hover:text-secondary-wopee hover:bg-secondary-wopee/10 dark:text-white/70 dark:hover:text-white dark:hover:bg-white/10"
+ }`}
+ >
+
+ Your app
+
+
+
{
+ if (!DEMO_SCENARIOS.includes(appType)) {
+ handleAppTypeChange(triggerDemo);
+ }
+ setDemoMenuOpen((open) => !open);
+ }}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ if (!DEMO_SCENARIOS.includes(appType)) {
+ handleAppTypeChange(triggerDemo);
+ }
+ setDemoMenuOpen((open) => !open);
+ }
+ }}
+ className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold leading-none cursor-pointer select-none transition-all ${
+ DEMO_SCENARIOS.includes(appType)
+ ? "bg-secondary-wopee text-white shadow-sm"
+ : "text-secondary-wopee/70 hover:text-secondary-wopee hover:bg-secondary-wopee/10 dark:text-white/70 dark:hover:text-white dark:hover:bg-white/10"
+ }`}
+ >
+
+ Demo app
+
+
+ {demoMenuOpen && (
+
+ {DEMO_SCENARIOS.map((type) => {
+ const tpl = appTemplates[type];
+ const Icon = tpl.icon;
+ const selected = appType === type;
+ return (
+
handleAppTypeChange(type)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ handleAppTypeChange(type);
+ }
+ }}
+ className={`flex items-center gap-2 px-3 py-1.5 text-[11px] cursor-pointer select-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 ${selected ? "text-secondary-wopee dark:text-primary-wopee font-semibold" : "text-gray-700 dark:text-gray-300"}`}
+ >
+
+ {tpl.label}
+ {selected && (
+
+ )}
+
+ );
+ })}
+
+ )}
+
+
+
{
return (
<>
-
- {/* Your app chip on the left, then "or try a demos:" label,
- then a single expander showing the selected demo. Clicking
- it reveals the three demo options; picking one pre-fills URL
- + instructions and collapses the menu. One click on "Your
- app" returns to the empty custom-URL state. */}
-
- {(() => {
- const tpl = appTemplates[AppType.YOUR_APPLICATION];
- const Icon = tpl.icon;
- // "Your app" is only visually selected once the visitor has
- // typed a URL in Your-app mode — otherwise the chip would
- // read as active on landing while the CTA is disabled.
- const selected =
- appType === AppType.YOUR_APPLICATION && appUrl.length > 0;
- return (
-
- handleAppTypeChange(AppType.YOUR_APPLICATION)
- }
- onKeyDown={(e) => {
- if (e.key === "Enter" || e.key === " ") {
- e.preventDefault();
- handleAppTypeChange(AppType.YOUR_APPLICATION);
- }
- }}
- className={`scenario-chip ${selected ? "scenario-chip--selected" : ""} inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[11px] font-normal cursor-pointer select-none transition-colors`}
- aria-label={tpl.label}
- >
-
- {tpl.label}
-
- );
- })()}
-
- or try a demos:
-
-
-
- {demoMenuOpen && (
-
- {DEMO_SCENARIOS.map((type) => {
- const tpl = appTemplates[type];
- const Icon = tpl.icon;
- const selected = appType === type;
- return (
-
handleAppTypeChange(type)}
- onKeyDown={(e) => {
- if (e.key === "Enter" || e.key === " ") {
- e.preventDefault();
- handleAppTypeChange(type);
- }
- }}
- className={`flex items-center gap-2 px-3 py-1.5 text-[11px] cursor-pointer select-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 ${selected ? "text-secondary-wopee dark:text-primary-wopee font-semibold" : "text-gray-700 dark:text-gray-300"}`}
- >
-
- {tpl.label}
- {selected && (
-
- )}
-
- );
- })}
-
- )}
-
-
-
+
+
+ No credit card required
+
+
+
+
+ {/* Right: fully-visible demo video. Hovering it fades the video's own
+ gradient frame in while fading the form's gradient border out. */}
+
setVideoHovered(true)}
+ onMouseLeave={() => setVideoHovered(false)}
+ >
+
+
@@ -381,11 +405,6 @@ const HomeHeroVibe = () => {
isOpen={loginDialogState.isOpen}
/>
-
setVideoOpen(false)}
- />
);
};
diff --git a/src/components/home-page/vibe/LoginDialog.tsx b/src/components/home-page/vibe/LoginDialog.tsx
index e3bbcc6..2a1b623 100644
--- a/src/components/home-page/vibe/LoginDialog.tsx
+++ b/src/components/home-page/vibe/LoginDialog.tsx
@@ -1,7 +1,7 @@
import React from "react";
import Icon from "@mdi/react";
import { mdiGithub, mdiGitlab, mdiGoogle } from "@mdi/js";
-import { Globe, Sparkles, ChevronRight } from "lucide-react";
+import { Sparkles } from "lucide-react";
import { cmdBaseUrl } from "../../../../cmdBaseUrl";
import {
@@ -13,19 +13,6 @@ import {
} from "@/components/ui/dialog";
import { AppType } from "./enums";
-// Insert zero-width spaces after each dot so long hostnames can wrap at
-// segment boundaries instead of mid-character.
-const wrapHost = (host: string) => host.replace(/\./g, ".");
-
-const cleanHost = (raw: string) => {
- if (!raw) return "";
- try {
- return new URL(raw).hostname.replace(/^www\./, "");
- } catch {
- return raw.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0];
- }
-};
-
// Real customer logos used elsewhere on the site (homepage trusted-by strip).
// Each logo sits in a fixed slot with object-contain so wildly different source
// aspect ratios (Generali wordmark, Multitude monogram, Synot text mark) all
@@ -71,15 +58,12 @@ const LoginDialog = ({
setIsOpenVibe(false);
};
- const host = cleanHost(appUrl);
const isVibe = mode === "vibe";
return (