diff --git a/apps/web/src/components/loading-overlay.tsx b/apps/web/src/components/loading-overlay.tsx
new file mode 100644
index 0000000..64edff3
--- /dev/null
+++ b/apps/web/src/components/loading-overlay.tsx
@@ -0,0 +1,21 @@
+import { Icon } from "./icon";
+
+interface LoadingOverlayProps {
+ visible: boolean;
+ label?: string;
+}
+
+export function LoadingOverlay({ visible, label }: LoadingOverlayProps) {
+ if (!visible) return null;
+
+ return (
+
+
+
+ {label && (
+ {label}
+ )}
+
+
+ );
+}
diff --git a/apps/web/src/styles/utilities.css b/apps/web/src/styles/utilities.css
index a6a6922..18ed2e0 100644
--- a/apps/web/src/styles/utilities.css
+++ b/apps/web/src/styles/utilities.css
@@ -383,6 +383,51 @@ a.nav-item,
align-items: center;
}
+/* ============================================
+ Loading Overlay
+ ============================================ */
+
+.loading-overlay {
+ position: absolute;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.12);
+ backdrop-filter: blur(1px);
+ border-radius: inherit;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10;
+}
+
+.loading-overlay-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--space-2);
+ color: var(--text, currentColor);
+}
+
+.loading-overlay-label {
+ font-size: var(--text-sm);
+ font-weight: 500;
+}
+
+.icon-spin {
+ animation: icon-spin 1s linear infinite;
+}
+
+@keyframes icon-spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.session-card,
+.tool-starter,
+.create-session-form-wrapper {
+ position: relative;
+}
+
/* ============================================
Terminal Styles
============================================ */