diff --git a/src/app/(site)/login/page.tsx b/src/app/(site)/login/page.tsx
index 71ca3756..02dea8b8 100644
--- a/src/app/(site)/login/page.tsx
+++ b/src/app/(site)/login/page.tsx
@@ -5,10 +5,10 @@ import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { LoginForm } from "@/components/auth/login-form";
import { Clock } from "@/components/clock";
-import { SectionCard } from "@/components/home-section";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Reveal } from "@/components/motion-reveal";
import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
+import { SurfaceCard } from "@/components/surface-card";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { cached } from "@/lib/cache";
import { db, User } from "@/lib/db";
@@ -123,13 +123,12 @@ export default async function LoginPage() {
{/* Left panel */}
-
+
{recentUsers.length > 0 && (
-
))}
-
+
)}
{latestUsers.length > 0 && (
-
))}
-
+
)}
-
-
+
diff --git a/src/app/(site)/me/page.tsx b/src/app/(site)/me/page.tsx
index 53008c93..7aee9cca 100644
--- a/src/app/(site)/me/page.tsx
+++ b/src/app/(site)/me/page.tsx
@@ -254,8 +254,8 @@ export default async function MePage({
) : null}
-
-
+
{alertRaw ? (
-
{alertRaw}
-
+
) : null}
diff --git a/src/app/(site)/page.tsx b/src/app/(site)/page.tsx
index a1d9718f..3c0c0e69 100644
--- a/src/app/(site)/page.tsx
+++ b/src/app/(site)/page.tsx
@@ -8,10 +8,10 @@ import { AmbientOrbs } from "@/components/ambient-orbs";
import { AnimatedCounter } from "@/components/animated-counter";
import { HomeLoginForm } from "@/components/auth/home-login-form";
import { Clock } from "@/components/clock";
-import { SectionCard } from "@/components/home-section";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Reveal } from "@/components/motion-reveal";
import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
+import { SurfaceCard } from "@/components/surface-card";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { TypewriterText } from "@/components/typewriter-text";
import { auth } from "@/lib/auth";
@@ -321,15 +321,9 @@ export default async function Home() {
icon: "/assets/images/icons/catalog.png",
},
].map((s) => (
-
+
))}
@@ -373,7 +367,7 @@ export default async function Home() {
{/* Left: Login & Register */}
-
-
+
-
+
-
+
{latestUsers.length > 0 && (
-
))}
-
+
)}
{/* Right: News */}
-
)}
-
+
{/* ── Online users ── */}
{recentUsers.length > 0 && (
-
))}
-
+
)}
@@ -601,7 +589,7 @@ export default async function Home() {
{/* ── Recent photos ── */}
{recentPhotos.length > 0 && (
-
@@ -627,7 +615,7 @@ export default async function Home() {
))}
-
+
)}
diff --git a/src/app/(site)/register/page.tsx b/src/app/(site)/register/page.tsx
index 713bac2e..38595044 100644
--- a/src/app/(site)/register/page.tsx
+++ b/src/app/(site)/register/page.tsx
@@ -5,10 +5,10 @@ import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { RegisterForm } from "@/components/auth/register-form";
import { Clock } from "@/components/clock";
-import { SectionCard } from "@/components/home-section";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Reveal } from "@/components/motion-reveal";
import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
+import { SurfaceCard } from "@/components/surface-card";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { cached } from "@/lib/cache";
import { db, User } from "@/lib/db";
@@ -125,13 +125,12 @@ export default async function RegisterPage() {
{/* Left panel */}
-
+
{recentUsers.length > 0 && (
-
))}
-
+
)}
{latestUsers.length > 0 && (
-
))}
-
+
)}
-
-
+
diff --git a/src/app/(site)/settings/page.tsx b/src/app/(site)/settings/page.tsx
index b4071599..1fb926f3 100644
--- a/src/app/(site)/settings/page.tsx
+++ b/src/app/(site)/settings/page.tsx
@@ -4,8 +4,8 @@ import Link from "next/link";
import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { updateMotto } from "@/actions/user-settings";
-import { SectionCard } from "@/components/home-section";
import { Reveal } from "@/components/motion-reveal";
+import { SurfaceCard } from "@/components/surface-card";
import { auth } from "@/lib/auth";
import { db, User } from "@/lib/db";
import { avatarImageUrl } from "@/lib/format";
@@ -47,8 +47,8 @@ export default async function SettingsPage() {
-
+
{/* Profile card */}
-
-
+
{/* Motto form */}
-
-
+
{/* Security card */}
-
{t("twoFactorLink")}
-
+
{/* Sessions link */}
-
-
-
-
-
-
-
-
- Session Management
-
-
- View active sessions and sign out everywhere
-
-
-
-
+
+
- →
-
-
+
+
+
+
+
+
+ Session Management
+
+
+ View active sessions and sign out everywhere
+
+
+
+
+ →
+
+
+
diff --git a/src/components/home-section.tsx b/src/components/home-section.tsx
deleted file mode 100644
index 644904c0..00000000
--- a/src/components/home-section.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import Image from "next/image";
-import Link from "next/link";
-import type { ReactNode } from "react";
-
-interface SectionCardProps {
- title: string;
- icon?: string;
- actionHref?: string;
- actionLabel?: string;
- children: ReactNode;
- className?: string;
- bodyClassName?: string;
-}
-
-/**
- * Consistent, restrained panel used across the home page. Replaces the
- * per-section neon gradients with a single calm surface + hairline divider so
- * the page reads as professional rather than game-like.
- */
-export function SectionCard({
- title,
- icon,
- actionHref,
- actionLabel,
- children,
- className,
- bodyClassName,
-}: SectionCardProps) {
- return (
-
-
-
- {icon && (
-
- )}
-
- {title}
-
-
- {actionHref && actionLabel && (
-
- {actionLabel}
-
- )}
-
-
{children}
-
- );
-}
diff --git a/src/components/surface-card.tsx b/src/components/surface-card.tsx
index 631bda20..6782784f 100644
--- a/src/components/surface-card.tsx
+++ b/src/components/surface-card.tsx
@@ -1,22 +1,48 @@
+import Image from "next/image";
+import Link from "next/link";
import type { CSSProperties, ReactNode } from "react";
const CARD_BORDER =
"color-mix(in srgb, var(--color-text-muted) 12%, transparent)";
+const CARD_DIVIDER =
+ "color-mix(in srgb, var(--color-text-muted) 8%, transparent)";
+const CARD_HEADER_BG =
+ "color-mix(in srgb, var(--color-primary) 10%, var(--color-surface))";
-/**
- * Public-site surface card. Mirrors the CSS .content-card / SectionCard visual
- * language (--radius-lg corners, --shadow-card, hairline border) so every page
- * that opts out of ContentCard still shares one consistent component.
- */
-export function SurfaceCard({
- className = "",
- style,
- children,
-}: {
+export interface SurfaceCardProps {
+ children: ReactNode;
+ /** Optional header title. */
+ title?: string;
+ /** Optional header icon (image URL). */
+ icon?: string;
+ /** Optional header action link. */
+ actionHref?: string;
+ actionLabel?: string;
+ /** Class for the body wrapper (only used when a header is present). */
+ bodyClassName?: string;
className?: string;
style?: CSSProperties;
- children: ReactNode;
-}) {
+}
+
+/**
+ * The single public-site card component. Mirrors the CSS `.content-card` visual
+ * language (--radius-lg corners, --shadow-card, hairline border). Pass `title`
+ * / `icon` / `actionHref` to render a section header, exactly like the old
+ * a card header. Without a header, children render directly so callers control
+ * padding via `className`.
+ */
+export function SurfaceCard({
+ children,
+ title,
+ icon,
+ actionHref,
+ actionLabel,
+ bodyClassName,
+ className = "",
+ style,
+}: SurfaceCardProps) {
+ const hasHeader = Boolean(title || icon || (actionHref && actionLabel));
+
return (
- {children}
+ {hasHeader && (
+
+
+ {icon && (
+
+ )}
+ {title && (
+
+ {title}
+
+ )}
+
+ {actionHref && actionLabel && (
+
+ {actionLabel}
+
+ )}
+
+ )}
+ {hasHeader ? (
+
{children}
+ ) : (
+ children
+ )}
);
}
-
-/** Padded body for a SurfaceCard. Override padding via className. */
-export function SurfaceCardBody({
- className = "p-5",
- children,
-}: {
- className?: string;
- children: ReactNode;
-}) {
- return {children}
;
-}