@@ -1139,7 +1145,7 @@ export const ImageTo3DExperience = ({
activeTab={activeMode}
onTabChange={(tabId) => setActiveMode(tabId as Mode)}
className="w-fit"
- indicatorClassName="bg-primary/25"
+ indicatorClassName="bg-white/15"
/>
)}
@@ -1196,12 +1202,12 @@ export const ImageTo3DExperience = ({
{previewImage && (
setPreviewImage(null)}
>
diff --git a/frontend/apps/artcraft/app/src/components/generation-feed/CreateEmptyState.tsx b/frontend/apps/artcraft/app/src/components/generation-feed/CreateEmptyState.tsx
new file mode 100644
index 0000000000..68f341ca15
--- /dev/null
+++ b/frontend/apps/artcraft/app/src/components/generation-feed/CreateEmptyState.tsx
@@ -0,0 +1,56 @@
+import { type ReactNode } from "react";
+import { twMerge } from "tailwind-merge";
+
+const CORNERS = [
+ "top-0 left-0",
+ "top-0 right-0",
+ "bottom-0 left-0",
+ "bottom-0 right-0",
+];
+
+interface CreateEmptyStateProps {
+ title: string;
+ subtitle: string;
+ children?: ReactNode;
+ compact?: boolean;
+ className?: string;
+}
+
+/** A quiet editorial section with a fine border and corner registration marks. */
+export function CreateEmptyState({
+ title,
+ subtitle,
+ children,
+ compact = false,
+ className,
+}: CreateEmptyStateProps) {
+ return (
+
+
+ {title}
+
+
+ {subtitle}
+
+ {children && {children}
}
+ {CORNERS.map((corner) => (
+
+ ))}
+
+ );
+}
diff --git a/frontend/apps/artcraft/app/src/components/generation-feed/DesktopCreatePageShell.tsx b/frontend/apps/artcraft/app/src/components/generation-feed/DesktopCreatePageShell.tsx
index f8578c272f..197b7c269c 100644
--- a/frontend/apps/artcraft/app/src/components/generation-feed/DesktopCreatePageShell.tsx
+++ b/frontend/apps/artcraft/app/src/components/generation-feed/DesktopCreatePageShell.tsx
@@ -1,15 +1,16 @@
import { useEffect, useState, type ReactNode } from "react";
import { twMerge } from "tailwind-merge";
+import { CreateEmptyState } from "./CreateEmptyState";
// Shared page chrome for the desktop Generate Image / Generate Video pages,
-// mirroring the webapp's CreateMediaPageShell: a centered hero over the
-// decorative background, and a scrollable generation feed (padded past the
-// fixed promptbox) once there is anything to show.
+// mirroring the webapp's CreateMediaPageShell: a centered empty-state panel,
+// and a scrollable generation feed (padded past the fixed promptbox) once
+// there is anything to show.
//
-// The hero + background behave like a splash screen: they stay up while the
-// feed is still loading (pages remount on tab switches, so this covers the
-// reload) and fade out once content arrives. With a genuinely empty library
-// they simply stay — they ARE the empty state.
+// The empty state (and optional background) behave like a splash screen:
+// they stay up while the feed is still loading (pages remount on tab
+// switches, so this covers the reload) and fade out once content arrives.
+// With a genuinely empty library they simply stay.
const SPLASH_FADE_MS = 700;
@@ -17,7 +18,8 @@ interface DesktopCreatePageShellProps {
hasContent: boolean;
emptyStateTitle: string;
emptyStateSubtitle: string;
- /** Decorative page background (fixed-position); fades out with the hero. */
+ /** Optional decorative page background (fixed-position); fades out with
+ * the empty state. */
background?: ReactNode;
/** Height of the fixed promptbox area, used to pad the feed's bottom. */
bottomOffset: number;
@@ -55,11 +57,11 @@ export function DesktopCreatePageShell({
}, [hasContent, splashState]);
return (
-
+
{hasContent && (
{listContent}
@@ -73,15 +75,14 @@ export function DesktopCreatePageShell({
splashState === "fading" ? "opacity-0" : "opacity-100",
)}
>
-
-
-
- {emptyStateTitle}
-
-
- {emptyStateSubtitle}
-
-
+
+
{background}
@@ -92,7 +93,7 @@ export function DesktopCreatePageShell({
{hasContent && (
)}
diff --git a/frontend/apps/artcraft/app/src/components/generation-feed/DesktopGenerationGallery.tsx b/frontend/apps/artcraft/app/src/components/generation-feed/DesktopGenerationGallery.tsx
index 69fef3c575..327031844a 100644
--- a/frontend/apps/artcraft/app/src/components/generation-feed/DesktopGenerationGallery.tsx
+++ b/frontend/apps/artcraft/app/src/components/generation-feed/DesktopGenerationGallery.tsx
@@ -180,7 +180,7 @@ function SelectionDownloadBar({
type="button"
onClick={handleDownload}
disabled={isDownloading || selectedItems.length === 0}
- className="flex items-center gap-2 rounded-full bg-ui-controls/60 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-ui-controls/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:opacity-60"
+ className="flex items-center gap-2 border border-white/15 bg-ui-controls/60 px-3 py-1.5 font-mono text-[11px] font-semibold uppercase tracking-[0.12em] text-white transition-colors hover:bg-ui-controls/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/60 disabled:opacity-60"
>
{
@@ -305,7 +305,7 @@ function ItemActions({
aria-label="Make Video"
className={buttonClass}
>
-
+
)}
diff --git a/frontend/apps/artcraft/app/src/components/media/UploadEntryCard.tsx b/frontend/apps/artcraft/app/src/components/media/UploadEntryCard.tsx
index d12eeb92c7..82380031ff 100644
--- a/frontend/apps/artcraft/app/src/components/media/UploadEntryCard.tsx
+++ b/frontend/apps/artcraft/app/src/components/media/UploadEntryCard.tsx
@@ -106,8 +106,8 @@ export const UploadEntryCard = ({
return (
-
-
-
-
-
+
+
+
-
-
- {title}
-
-
+
+
{title}
+
{description}
-
+