diff --git a/app/layout.tsx b/app/layout.tsx
index 2cc502f..a8e8931 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import Sidebar from "../components/Sidebar";
import styles from "../styles/Layout.module.css";
import "./globals.css";
+import AppContainer from "@/components/Container";
export const metadata: Metadata = {
title: "Hex: Unlocked",
@@ -16,12 +17,14 @@ export default function RootLayout({
return (
-
-
-
- {children}
-
-
+
+
+
+
+ {children}
+
+
+
);
diff --git a/components/Container.tsx b/components/Container.tsx
new file mode 100644
index 0000000..d8f9c37
--- /dev/null
+++ b/components/Container.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useWebsocketStore } from "../store/useWebsocketStore";
+import styles from "../styles/AppContainer.module.css";
+
+export default function AppContainer({ children }: { children: React.ReactNode }) {
+ const { connect, isConnected } = useWebsocketStore();
+ const [showHelp, setShowHelp] = useState(false);
+
+ useEffect(() => {
+ connect();
+
+ const timer = setTimeout(() => {
+ setShowHelp(true);
+ }, 3000);
+
+ return () => clearTimeout(timer);
+ }, [connect]);
+
+ if (!isConnected) {
+ return (
+
+
+
Hex: Unlocked
+
Awaiting unlocker connection
+
+
+
+ {showHelp && (
+
+
Make sure the client is running.
+
+ Don't have it? Download it here
+
+
+ )}
+
+
+ );
+ }
+
+ return <>{children}>;
+}
\ No newline at end of file
diff --git a/styles/AppContainer.module.css b/styles/AppContainer.module.css
new file mode 100644
index 0000000..3e2f370
--- /dev/null
+++ b/styles/AppContainer.module.css
@@ -0,0 +1,83 @@
+.overlay {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ width: 100vw;
+ background: #000000;
+ color: #ffffff;
+ font-family: 'Roboto Condensed', sans-serif;
+ text-align: center;
+}
+
+.card {
+ background: #050505;
+ border: 1px solid #1a1a1a;
+ border-top: 3px solid #a30000;
+ padding: 3rem 4rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.6);
+ min-width: 400px;
+}
+
+.title {
+ font-size: 2.25rem;
+ text-transform: uppercase;
+ color: #ffffff;
+ letter-spacing: 0.05em;
+ margin: 0 0 0.5rem 0;
+ text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.8);
+ font-family: 'Oswald', sans-serif;
+}
+
+.subtitle {
+ font-size: 0.9rem;
+ color: #777777;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ margin: 0 0 2rem 0;
+}
+
+.spinner {
+ width: 36px;
+ height: 36px;
+ border: 3px solid #1a1a1a;
+ border-top: 3px solid #a30000;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 2rem;
+}
+
+.helpText {
+ color: #555555;
+ font-size: 0.85rem;
+ margin-top: 1rem;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ animation: fadeIn 1s ease-in;
+}
+
+.link {
+ color: #a30000;
+ text-decoration: none;
+ font-weight: bold;
+ transition: all 0.2s ease;
+}
+
+.link:hover {
+ color: #ff0000;
+ text-shadow: 0 0 10px rgba(163, 0, 0, 0.4);
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}