+ )
+}
diff --git a/frontend/src/components/ui/Panel.tsx b/frontend/src/components/ui/Panel.tsx
new file mode 100644
index 0000000..a25c12f
--- /dev/null
+++ b/frontend/src/components/ui/Panel.tsx
@@ -0,0 +1,13 @@
+import type { HTMLAttributes } from 'react'
+import { cn } from '../../lib/cn'
+
+type PanelProps = HTMLAttributes
+
+export default function Panel({ className, ...props }: PanelProps) {
+ return (
+
+ )
+}
diff --git a/frontend/src/lib/cn.ts b/frontend/src/lib/cn.ts
new file mode 100644
index 0000000..5499bf7
--- /dev/null
+++ b/frontend/src/lib/cn.ts
@@ -0,0 +1,13 @@
+/**
+ * Minimal className joiner — filters out falsy values and joins with spaces.
+ * Avoids pulling clsx / tailwind-merge for the small primitive set here.
+ *
+ * Note: this does NOT resolve conflicting Tailwind utilities. If a consumer
+ * passes a class that conflicts with a default (e.g. bg-blue-500 over bg-black),
+ * both land in the DOM and CSS source order decides the winner — not "last wins".
+ */
+export function cn(
+ ...classes: Array
+): string {
+ return classes.filter(Boolean).join(' ')
+}