404lab 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/core/templates.js +1 -0
- package/package.json +3 -3
- package/templates/AmongUs.tsx +213 -119
- package/templates/BlueGlitch.tsx +190 -100
- package/templates/GeeksforGeeks.tsx +125 -37
- package/templates/Google.tsx +97 -29
- package/templates/MacOs.tsx +116 -212
- package/templates/ModernPage.tsx +73 -11
- package/templates/Particles.tsx +173 -67
- package/templates/Poet.tsx +139 -54
- package/templates/RetroTv.tsx +182 -130
- package/templates/SimplePage.tsx +60 -16
- package/templates/Snow.tsx +113 -179
- package/templates/StoneAge.tsx +91 -23
- package/templates/StrangerThings.tsx +79 -193
- package/templates/Terminal404.tsx +124 -242
- package/templates/Vercel.tsx +127 -0
package/templates/MacOs.tsx
CHANGED
|
@@ -2,264 +2,168 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useState, KeyboardEvent } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import
|
|
5
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
6
|
+
import { cn } from "@/components/ui/cn";
|
|
6
7
|
|
|
7
8
|
type OutputLine = {
|
|
8
|
-
type: "command" | "output" | "error";
|
|
9
|
+
type: "command" | "output" | "error" | "system" | "warning";
|
|
9
10
|
text: string;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
| "help"
|
|
14
|
-
| "exit"
|
|
15
|
-
| "close"
|
|
16
|
-
| "report"
|
|
17
|
-
| "commands"
|
|
18
|
-
| "clear"
|
|
19
|
-
| "cls"
|
|
20
|
-
| "about"
|
|
21
|
-
| "date"
|
|
22
|
-
| "echo";
|
|
23
|
-
|
|
24
|
-
const COMMANDS: Record<
|
|
25
|
-
Exclude<CommandKey, "exit" | "close" | "clear" | "cls" | "date" | "echo">,
|
|
26
|
-
string
|
|
27
|
-
> = {
|
|
28
|
-
help: `This page does not exist. Type <span class="text-yellow-300">commands</span> to see available commands.`,
|
|
29
|
-
report: `<span class="text-green-400">Report sent successfully.</span>`,
|
|
30
|
-
commands: `
|
|
31
|
-
<span class="text-yellow-300">help</span>,
|
|
32
|
-
<span class="text-yellow-300">about</span>,
|
|
33
|
-
<span class="text-yellow-300">date</span>,
|
|
34
|
-
<span class="text-yellow-300">echo</span>,
|
|
35
|
-
<span class="text-yellow-300">clear</span>,
|
|
36
|
-
<span class="text-yellow-300">exit</span>
|
|
37
|
-
`,
|
|
38
|
-
about: `<span class="text-cyan-400">Terminal 404</span> — Interactive MacOS-style terminal.`,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const bootScript = [
|
|
42
|
-
{ text: "user@macbook ~ % pnpm run dev", tone: "text-emerald-400" },
|
|
43
|
-
{ text: "> Starting development server...", tone: "text-white/60" },
|
|
44
|
-
{ text: "✖ Error: Page Not Found (404)", tone: "text-red-400 font-semibold" },
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
const MacOs = () => {
|
|
13
|
+
const MacOs = ({ className }: { className?: string }) => {
|
|
48
14
|
const router = useRouter();
|
|
49
15
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
50
16
|
const terminalRef = useRef<HTMLDivElement>(null);
|
|
51
17
|
|
|
52
|
-
const [printed, setPrinted] = useState<string[]>([]);
|
|
53
|
-
const [typing, setTyping] = useState("");
|
|
54
|
-
const [line, setLine] = useState(0);
|
|
55
|
-
const [char, setChar] = useState(0);
|
|
56
18
|
const [bootDone, setBootDone] = useState(false);
|
|
57
|
-
|
|
58
19
|
const [input, setInput] = useState("");
|
|
59
|
-
const [output, setOutput] = useState<OutputLine[]>([
|
|
20
|
+
const [output, setOutput] = useState<OutputLine[]>([
|
|
21
|
+
{ type: "system", text: "Last login: " + new Date().toUTCString().split(' ').slice(0, 4).join(' ') + " on ttys001" },
|
|
22
|
+
{ type: "system", text: "Restoring session... " },
|
|
23
|
+
{ type: "error", text: "zsh: error 404: route not found" },
|
|
24
|
+
{ type: "warning", text: "Available commands: help, clear, exit, date, whoami, ls, sudo" }
|
|
25
|
+
]);
|
|
60
26
|
|
|
61
27
|
useEffect(() => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
inputRef.current?.focus();
|
|
66
|
-
}, 400);
|
|
67
|
-
return () => clearTimeout(t);
|
|
68
|
-
}
|
|
28
|
+
const timer = setTimeout(() => setBootDone(true), 1200);
|
|
29
|
+
return () => clearTimeout(timer);
|
|
30
|
+
}, []);
|
|
69
31
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
setChar((c) => c + 1);
|
|
74
|
-
}, 26);
|
|
75
|
-
return () => clearTimeout(t);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (bootDone && inputRef.current && window.self === window.top) {
|
|
34
|
+
inputRef.current.focus();
|
|
76
35
|
}
|
|
77
|
-
|
|
78
|
-
const t = setTimeout(() => {
|
|
79
|
-
setPrinted((p) => [...p, typing]);
|
|
80
|
-
setTyping("");
|
|
81
|
-
setChar(0);
|
|
82
|
-
setLine((l) => l + 1);
|
|
83
|
-
}, 350);
|
|
84
|
-
|
|
85
|
-
return () => clearTimeout(t);
|
|
86
|
-
}, [char, line, typing]);
|
|
36
|
+
}, [bootDone]);
|
|
87
37
|
|
|
88
38
|
useEffect(() => {
|
|
89
|
-
terminalRef.current
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
39
|
+
if (terminalRef.current) {
|
|
40
|
+
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
|
41
|
+
}
|
|
93
42
|
}, [output]);
|
|
94
43
|
|
|
95
44
|
const runCommand = (cmd: string) => {
|
|
96
|
-
const trimmed = cmd.trim();
|
|
45
|
+
const trimmed = cmd.trim().toLowerCase();
|
|
97
46
|
if (!trimmed) return;
|
|
98
47
|
|
|
99
|
-
const [raw, ...rest] = trimmed.split(" ");
|
|
100
|
-
const command = raw.toLowerCase() as CommandKey;
|
|
101
|
-
const args = rest.join(" ");
|
|
102
|
-
|
|
103
48
|
setOutput((o) => [...o, { type: "command", text: cmd }]);
|
|
104
49
|
|
|
105
|
-
|
|
106
|
-
setOutput([])
|
|
107
|
-
|
|
108
|
-
|
|
50
|
+
const commands: Record<string, () => void> = {
|
|
51
|
+
help: () => setOutput(o => [...o, { type: "output", text: "Available commands: help, clear, exit, date, whoami, ls, sudo" }]),
|
|
52
|
+
clear: () => setOutput([]),
|
|
53
|
+
exit: () => router.push("/"),
|
|
54
|
+
date: () => setOutput(o => [...o, { type: "output", text: new Date().toString() }]),
|
|
55
|
+
whoami: () => setOutput(o => [...o, { type: "output", text: "guest@macbook-pro" }]),
|
|
56
|
+
ls: () => setOutput(o => [...o, { type: "output", text: "Applications Documents Downloads Public Desktop .hidden_404_key" }]),
|
|
57
|
+
sudo: () => setOutput(o => [...o, { type: "error", text: "Nice try, but you don't have root access to this void." }]),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (commands[trimmed]) {
|
|
61
|
+
commands[trimmed]();
|
|
62
|
+
} else {
|
|
63
|
+
setOutput(o => [...o, { type: "error", text: `zsh: command not found: ${trimmed}` }]);
|
|
109
64
|
}
|
|
110
|
-
|
|
111
|
-
if (command === "exit" || command === "close") {
|
|
112
|
-
router.push("/");
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (command === "date") {
|
|
117
|
-
setOutput((o) => [
|
|
118
|
-
...o,
|
|
119
|
-
{ type: "output", text: new Date().toLocaleString() },
|
|
120
|
-
]);
|
|
121
|
-
setInput("");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (command === "echo") {
|
|
126
|
-
setOutput((o) => [...o, { type: "output", text: args }]);
|
|
127
|
-
setInput("");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!(command in COMMANDS)) {
|
|
132
|
-
setOutput((o) => [
|
|
133
|
-
...o,
|
|
134
|
-
{
|
|
135
|
-
type: "error",
|
|
136
|
-
text: `Command not found. Type <span class="text-yellow-300">commands</span>.`,
|
|
137
|
-
},
|
|
138
|
-
]);
|
|
139
|
-
setInput("");
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
setOutput((o) => [
|
|
144
|
-
...o,
|
|
145
|
-
{ type: "output", text: COMMANDS[command as keyof typeof COMMANDS] },
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
65
|
setInput("");
|
|
149
66
|
};
|
|
150
67
|
|
|
151
|
-
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
152
|
-
if (e.key === "Enter") {
|
|
153
|
-
e.preventDefault();
|
|
154
|
-
runCommand(input);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (e.ctrlKey && e.key.toLowerCase() === "l") {
|
|
158
|
-
e.preventDefault();
|
|
159
|
-
setOutput([]);
|
|
160
|
-
setInput("");
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
68
|
return (
|
|
165
|
-
<main className="min-h-screen flex items-center justify-center bg-[#
|
|
166
|
-
<div className="
|
|
167
|
-
<div className="
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<span className="h-3 w-3 rounded-full bg-green-500" />
|
|
171
|
-
<span className="ml-2 sm:ml-4 text-xs sm:text-sm text-white/60">
|
|
172
|
-
user@MacBook-Pro — zsh
|
|
173
|
-
</span>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
<div
|
|
177
|
-
ref={terminalRef}
|
|
178
|
-
onClick={() => inputRef.current?.focus()}
|
|
179
|
-
className="p-3 sm:p-6 bg-[#0f0f14] text-xs sm:text-sm space-y-2 cursor-text min-h-[60vh] max-h-[80vh] overflow-y-auto"
|
|
180
|
-
>
|
|
181
|
-
{printed.map((l, i) => (
|
|
182
|
-
<p key={i} className={bootScript[i].tone}>
|
|
183
|
-
{l}
|
|
184
|
-
</p>
|
|
185
|
-
))}
|
|
186
|
-
|
|
187
|
-
{!bootDone && (
|
|
188
|
-
<p className={bootScript[line]?.tone}>
|
|
189
|
-
{typing}
|
|
190
|
-
<span className="ml-1 inline-block h-4 w-2 bg-current animate-pulse" />
|
|
191
|
-
</p>
|
|
192
|
-
)}
|
|
193
|
-
|
|
194
|
-
{bootDone && (
|
|
195
|
-
<>
|
|
196
|
-
<p className="text-white/50 italic">
|
|
197
|
-
Tip: type{" "}
|
|
198
|
-
<span className="text-yellow-300 not-italic">commands</span> to
|
|
199
|
-
see available options
|
|
200
|
-
</p>
|
|
69
|
+
<main className={cn("min-h-screen flex items-center justify-center bg-[#0d1117] relative overflow-hidden font-mono", className)}>
|
|
70
|
+
<div className="absolute inset-0 z-0">
|
|
71
|
+
<div className="absolute top-[-10%] right-[-10%] w-[500px] h-[500px] bg-purple-600/10 rounded-full blur-[120px]" />
|
|
72
|
+
<div className="absolute bottom-[-10%] left-[-10%] w-[500px] h-[500px] bg-blue-600/10 rounded-full blur-[120px]" />
|
|
73
|
+
</div>
|
|
201
74
|
|
|
75
|
+
<motion.div
|
|
76
|
+
initial={{ opacity: 0, y: 40, scale: 0.95 }}
|
|
77
|
+
animate={{ opacity: 1, y: 0, scale: 1 }}
|
|
78
|
+
transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
|
79
|
+
className="w-full max-w-4xl mx-4 z-10"
|
|
80
|
+
>
|
|
81
|
+
<div className="rounded-xl overflow-hidden shadow-[0_30px_100px_rgba(0,0,0,0.6)] border border-white/10 backdrop-blur-3xl bg-[#1c1c1e]/80">
|
|
82
|
+
<div className="flex items-center justify-between px-4 py-3 bg-white/5 border-b border-white/5">
|
|
83
|
+
<div className="flex items-center gap-2">
|
|
84
|
+
<div className="w-3.5 h-3.5 rounded-full bg-[#ff5f57] shadow-inner" />
|
|
85
|
+
<div className="w-3.5 h-3.5 rounded-full bg-[#febc2e] shadow-inner" />
|
|
86
|
+
<div className="w-3.5 h-3.5 rounded-full bg-[#28c840] shadow-inner" />
|
|
87
|
+
</div>
|
|
88
|
+
<div className="text-[13px] text-white/40 font-medium tracking-wide">
|
|
89
|
+
guest — zsh — 80×24
|
|
90
|
+
</div>
|
|
91
|
+
<div className="w-12" />
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div
|
|
95
|
+
ref={terminalRef}
|
|
96
|
+
onClick={() => inputRef.current?.focus()}
|
|
97
|
+
className="p-6 h-[500px] overflow-y-auto custom-scrollbar text-[14px] leading-relaxed selection:bg-blue-500/30"
|
|
98
|
+
>
|
|
99
|
+
<AnimatePresence>
|
|
202
100
|
{output.map((line, i) => (
|
|
203
|
-
<div
|
|
101
|
+
<motion.div
|
|
102
|
+
key={i}
|
|
103
|
+
initial={{ opacity: 0, x: -5 }}
|
|
104
|
+
animate={{ opacity: 1, x: 0 }}
|
|
105
|
+
className="mb-1"
|
|
106
|
+
>
|
|
204
107
|
{line.type === "command" && (
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
108
|
+
<div className="flex items-center gap-2">
|
|
109
|
+
<span className="text-[#32d74b] font-bold">➜</span>
|
|
110
|
+
<span className="text-[#64d2ff] font-bold">~</span>
|
|
111
|
+
<span className="text-white">{line.text}</span>
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
{line.type === "output" && (
|
|
115
|
+
<div className="text-white/80 pl-6">{line.text}</div>
|
|
116
|
+
)}
|
|
117
|
+
{line.type === "system" && (
|
|
118
|
+
<div className="text-white/40 italic">{line.text}</div>
|
|
208
119
|
)}
|
|
209
|
-
{line.type
|
|
210
|
-
<
|
|
211
|
-
className={
|
|
212
|
-
line.type === "error" ? "text-red-400" : "text-white/70"
|
|
213
|
-
}
|
|
214
|
-
dangerouslySetInnerHTML={{ __html: line.text }}
|
|
215
|
-
/>
|
|
120
|
+
{line.type === "error" && (
|
|
121
|
+
<div className="text-[#ff453a] pl-6 font-medium">{line.text}</div>
|
|
216
122
|
)}
|
|
217
|
-
|
|
123
|
+
{line.type === "warning" && (
|
|
124
|
+
<div className="text-[#febc2e] pl-6">{line.text}</div>
|
|
125
|
+
)}
|
|
126
|
+
</motion.div>
|
|
218
127
|
))}
|
|
128
|
+
</AnimatePresence>
|
|
219
129
|
|
|
220
|
-
|
|
221
|
-
|
|
130
|
+
{bootDone && (
|
|
131
|
+
<div className="flex items-center gap-2 mt-1">
|
|
132
|
+
<span className="text-[#32d74b] font-bold">➜</span>
|
|
133
|
+
<span className="text-[#64d2ff] font-bold">~</span>
|
|
222
134
|
<input
|
|
223
135
|
ref={inputRef}
|
|
224
136
|
value={input}
|
|
225
137
|
onChange={(e) => setInput(e.target.value)}
|
|
226
|
-
onKeyDown={
|
|
227
|
-
|
|
138
|
+
onKeyDown={(e) => e.key === "Enter" && runCommand(input)}
|
|
139
|
+
className="bg-transparent border-none outline-none flex-1 text-white caret-[#32d74b]"
|
|
228
140
|
spellCheck={false}
|
|
229
|
-
className="
|
|
230
|
-
bg-transparent
|
|
231
|
-
border-0
|
|
232
|
-
outline-none
|
|
233
|
-
ring-0
|
|
234
|
-
shadow-none
|
|
235
|
-
focus:outline-none
|
|
236
|
-
focus:ring-0
|
|
237
|
-
focus:border-0
|
|
238
|
-
appearance-none
|
|
239
|
-
caret-emerald-400
|
|
240
|
-
flex-1
|
|
241
|
-
text-white
|
|
242
|
-
min-w-0
|
|
243
|
-
py-1
|
|
244
|
-
text-xs sm:text-sm
|
|
245
|
-
"
|
|
246
141
|
/>
|
|
247
142
|
</div>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
<Link
|
|
251
|
-
href="/"
|
|
252
|
-
className="text-sky-400 hover:underline text-base sm:text-lg"
|
|
253
|
-
>
|
|
254
|
-
→ Return Home
|
|
255
|
-
</Link>
|
|
256
|
-
</div>
|
|
257
|
-
</>
|
|
258
|
-
)}
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
259
145
|
</div>
|
|
260
|
-
|
|
146
|
+
|
|
147
|
+
</motion.div>
|
|
148
|
+
|
|
149
|
+
<style jsx>{`
|
|
150
|
+
.custom-scrollbar::-webkit-scrollbar {
|
|
151
|
+
width: 8px;
|
|
152
|
+
}
|
|
153
|
+
.custom-scrollbar::-webkit-scrollbar-track {
|
|
154
|
+
background: transparent;
|
|
155
|
+
}
|
|
156
|
+
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
157
|
+
background: rgba(255, 255, 255, 0.1);
|
|
158
|
+
border-radius: 10px;
|
|
159
|
+
}
|
|
160
|
+
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
161
|
+
background: rgba(255, 255, 255, 0.2);
|
|
162
|
+
}
|
|
163
|
+
`}</style>
|
|
261
164
|
</main>
|
|
262
165
|
);
|
|
263
166
|
};
|
|
264
167
|
|
|
265
168
|
export default MacOs;
|
|
169
|
+
|
package/templates/ModernPage.tsx
CHANGED
|
@@ -1,20 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion } from "framer-motion";
|
|
1
4
|
import Link from "next/link";
|
|
5
|
+
import { cn } from "@/components/ui/cn";
|
|
2
6
|
|
|
3
|
-
const ModernPage = () => {
|
|
7
|
+
const ModernPage = ({ className }: { className?: string }) => {
|
|
4
8
|
return (
|
|
5
|
-
<div
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
<div
|
|
10
|
+
className={cn(
|
|
11
|
+
"min-h-screen bg-[#050505] flex items-center justify-center p-6 overflow-hidden relative font-sans",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
>
|
|
15
|
+
{/* Mesh Gradient Background */}
|
|
16
|
+
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
|
17
|
+
<div className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-purple-900/20 blur-[120px] rounded-full animate-pulse" />
|
|
18
|
+
<div className="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-blue-900/20 blur-[120px] rounded-full animate-pulse [animation-delay:2s]" />
|
|
15
19
|
</div>
|
|
20
|
+
|
|
21
|
+
<motion.div
|
|
22
|
+
initial={{ opacity: 0, y: 20 }}
|
|
23
|
+
animate={{ opacity: 1, y: 0 }}
|
|
24
|
+
transition={{ duration: 0.8, ease: "easeOut" }}
|
|
25
|
+
className="relative z-10 w-full max-w-2xl"
|
|
26
|
+
>
|
|
27
|
+
<div className="bg-white/[0.03] backdrop-blur-xl border border-white/10 rounded-[2.5rem] p-12 md:p-20 text-center shadow-2xl overflow-hidden group">
|
|
28
|
+
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.05] to-transparent pointer-events-none" />
|
|
29
|
+
|
|
30
|
+
<motion.div
|
|
31
|
+
initial={{ scale: 0.9, opacity: 0 }}
|
|
32
|
+
animate={{ scale: 1, opacity: 1 }}
|
|
33
|
+
transition={{ delay: 0.2, duration: 0.5 }}
|
|
34
|
+
className="relative inline-block mb-8"
|
|
35
|
+
>
|
|
36
|
+
<h1 className="text-[8rem] md:text-[12rem] font-black leading-none tracking-tighter bg-gradient-to-b from-white to-white/20 bg-clip-text text-transparent select-none">
|
|
37
|
+
404
|
|
38
|
+
</h1>
|
|
39
|
+
<div className="absolute -inset-4 bg-white/5 blur-3xl rounded-full z-[-1] group-hover:bg-white/10 transition-colors" />
|
|
40
|
+
</motion.div>
|
|
41
|
+
|
|
42
|
+
<motion.div
|
|
43
|
+
initial={{ opacity: 0, y: 10 }}
|
|
44
|
+
animate={{ opacity: 1, y: 0 }}
|
|
45
|
+
transition={{ delay: 0.4 }}
|
|
46
|
+
>
|
|
47
|
+
<h2 className="text-2xl md:text-3xl font-bold text-white mb-4 tracking-tight">
|
|
48
|
+
Lost in the Digital Ether.
|
|
49
|
+
</h2>
|
|
50
|
+
<p className="text-gray-400 text-lg mb-12 max-w-md mx-auto leading-relaxed">
|
|
51
|
+
The page you are seeking has drifted beyond our reach.
|
|
52
|
+
Let's navigate you back to solid ground.
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
|
56
|
+
<Link
|
|
57
|
+
href="/"
|
|
58
|
+
className="w-full sm:w-auto px-10 py-4 bg-white text-black font-bold rounded-2xl hover:bg-gray-200 transition-all transform hover:-translate-y-1 active:scale-95 shadow-xl shadow-white/5"
|
|
59
|
+
>
|
|
60
|
+
Go Home
|
|
61
|
+
</Link>
|
|
62
|
+
<button className="w-full sm:w-auto px-10 py-4 border border-white/10 text-white font-bold rounded-2xl hover:bg-white/5 transition-all active:scale-95">
|
|
63
|
+
Take a Tour
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
</motion.div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="mt-12 flex justify-center gap-8 text-[10px] text-white/20 font-bold uppercase tracking-[0.3em]">
|
|
70
|
+
<span>Security Verified</span>
|
|
71
|
+
<span>•</span>
|
|
72
|
+
<span>Node 404_X</span>
|
|
73
|
+
<span>•</span>
|
|
74
|
+
<span>Stable Connection</span>
|
|
75
|
+
</div>
|
|
76
|
+
</motion.div>
|
|
16
77
|
</div>
|
|
17
78
|
);
|
|
18
79
|
};
|
|
19
80
|
|
|
20
81
|
export default ModernPage;
|
|
82
|
+
|