project setup

This commit is contained in:
Sone
2025-11-02 10:00:53 +01:00
parent 75bd171804
commit 7cb4c5345e
51 changed files with 3616 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+36
View File
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+111
View File
@@ -0,0 +1,111 @@
import Image from "next/image";
import aboutBg from "@/public/assets/images/our-mission-bg.png";
import avatar from "@/public/assets/avatars/avatar.png";
import avatar1 from "@/public/assets/avatars/avatar_1.png";
import avatar2 from "@/public/assets/avatars/avatar_2.png";
import avatar3 from "@/public/assets/avatars/avatar_3.png";
import TeamMemberCard from "@/components/about/TeamMemberCard";
const AboutPage = () => {
return (
<div>
<section className="relative overflow-hidden w-full min-h-[600px] sm:min-h-[700px] md:aspect-[5/3] md:max-h-[900px] -mt-[75px] pt-[75px]">
<div
className="absolute inset-0 sm:top-[-75px] top-[-35px] overflow-hidden"
style={{
WebkitMaskImage:
"linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 14%, rgba(0,0,0,1) 86%, rgba(0,0,0,0) 100%)",
maskImage:
"linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 14%, rgba(0,0,0,1) 86%, rgba(0,0,0,0) 100%)",
}}
>
<Image
src={aboutBg}
alt="About background"
fill
className="object-cover h-full"
style={{ objectPosition: "50% 20%" }}
priority
/>
{/* Mobile darkening overlay */}
<div className="absolute inset-0 bg-black/35 md:bg-black/0" />
<div className="pointer-events-none absolute left-1/2 top-[-36%] w-[260%] h-[62%] -translate-x-1/2 bg-[radial-gradient(circle_at_50%_-70%,rgba(127,19,236,0.88),rgba(127,19,236,0.52)_58%,rgba(25,16,34,0)_88%)] blur-[180px] opacity-95" />
<div className="pointer-events-none absolute left-1/2 bottom-[-18%] w-[240%] h-[36%] -translate-x-1/2 bg-[radial-gradient(circle_at_50%_135%,rgba(127,19,236,0.82),rgba(127,19,236,0.36)_46%,rgba(25,16,34,0)_86%)] blur-[140px] opacity-[0.9]" />
</div>
<div className="relative z-10 w-full h-full flex items-center justify-center px-6 py-12 sm:py-12 md:py-8">
<div className="max-w-4xl text-center">
<h1 className="text-3xl sm:text-4xl lg:text-6xl font-mono font-extrabold text-white drop-shadow-[0_0_20px_rgba(127,19,236,0.7)]">
Our Mission
</h1>
<p className="mt-4 sm:mt-6 text-sm sm:text-base font-mono leading-relaxed px-4">
Us as human, we better work as a collective no matter the language
or background we have. However we at LAN Codes believe we can
bring one more player to the team, AI. Acceleration of machine
learning language (MLL) models. "AI", is scary. In some way "I
Robot" it may seem more real to some. However, for players in LAN
Codes there is nothing that scares us. We are attempting to use
our critical thinking and MLL's efficiency to remove the busy work
and reduce human errors. While working on creating these systems
our results will produce cost effective security with
uncompromising safety. We believe that safety should not be a
privilege.
</p>
</div>
</div>
</section>
<section className="relative z-10 bg-[var(--color-1)]">
<div className="pointer-events-none absolute -top-16 left-0 right-0 h-16 bg-gradient-to-b from-transparent via-[var(--color-1)]/38 to-[var(--color-1)]" />
<div className="mx-auto px-4 sm:px-6 lg:px-8 pb-4 pt-4 max-w-[82.5%]">
<div className="text-center mb-4">
<h2 className="text-2xl sm:text-5xl font-mono">Founder</h2>
</div>
<div className="flex flex-col md:flex-row md:items-center md:justify-center gap-8 md:gap-12 max-w-3xl mx-auto">
<TeamMemberCard
avatar={avatar2}
name="Milan Vucetic"
position="Founder"
/>
<p className="font-mono text-sm sm:text-base leading-relaxed text-white/90 text-center md:max-w-xl">
I was always told you cannot reinvent the wheel, but you can make
it better. While working in reactive security prevention, then
moved towards proactive security prevention and a sprinkle of
inspiration, I managed to find a gap in many security doors, and
better yet a solution to tighten them up. With that goal I have
managed to organize the team of fearless people and with their
help LAN Codes became a reality from just an idea on a napkin.
</p>
</div>
<div className="mt-6 text-center">
<h3 className="text-xl sm:text-4xl font-mono">Meet the team</h3>
</div>
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-6 sm:gap-12 max-w-3xl mx-auto justify-items-center">
<TeamMemberCard
avatar={avatar1}
name="Tevin Gary-Vincent"
position="Head of Cybersecurity"
/>
<TeamMemberCard
avatar={avatar}
name="Nenad Zivotic"
position="Front-end Developer"
/>
<TeamMemberCard
avatar={avatar3}
name="Stefan Djukelic"
position="Back-end Developer"
/>
</div>
</div>
</section>
</div>
);
};
export default AboutPage;
+41
View File
@@ -0,0 +1,41 @@
import { saveContact } from "@/lib/contactStore";
import { contactSchema } from "@/schemas/schemas";
export async function POST(req: Request) {
try {
const body = await req.json();
const {
firstName,
lastName,
companyName,
address,
email,
phone,
industry,
notes,
subscribe,
} = contactSchema.parse(body);
const saved = await saveContact({
firstName,
lastName,
companyName,
address,
email,
phone,
industry,
notes,
subscribe,
});
return Response.json({ success: true, id: saved.id });
} catch (err: any) {
if (err?.issues) {
return Response.json(
{ success: false, errors: err.issues },
{ status: 400 }
);
}
return Response.json({ success: false }, { status: 500 });
}
}
+33
View File
@@ -0,0 +1,33 @@
const ContactPage = () => {
return (
<div className="mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8 py-16 md:py-32">
<div className="flex flex-col gap-16">
<div className="flex flex-col gap-10">
<p className="font-mono text-sm sm:text-base">
If you have any questions send us an email and we will be happy to
answer them.
<br />
You can expect our team to get back to your with in 24h-48h.
</p>
<p className="font-mono text-xl text-[var(--color-7)]">
<a href="mailto:service@lancodes.ca">service@lancodes.ca</a>
</p>
</div>
<div className="flex flex-col gap-10">
<p className="font-mono text-sm sm:text-base">
Looking for a job? Send us your resume and we will get back to you
when our next round of hiring starts.
</p>
<p className="font-mono text-xl text-[var(--color-10)]">
<a href="mailto:hireme@lancodes.ca">hireme@lancodes.ca</a>
</p>
</div>
</div>
</div>
);
};
export default ContactPage;
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+47
View File
@@ -0,0 +1,47 @@
@font-face {
font-family: 'Fira Code';
src: url('/assets/fonts/static/FiraCode-Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fira Code';
src: url('/assets/fonts/static/FiraCode-Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fira Code';
src: url('/assets/fonts/static/FiraCode-SemiBold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fira Code';
src: url('/assets/fonts/static/FiraCode-Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fira Code';
src: url('/assets/fonts/static/FiraCode-Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fira Code VF';
src: url('/assets/fonts/FiraCode-VariableFont_wght.ttf') format('truetype-variations');
font-weight: 300 700;
font-style: normal;
font-display: swap;
}
+218
View File
@@ -0,0 +1,218 @@
"use client";
import { useForm, SubmitHandler } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { contactSchema } from "@/schemas/schemas";
import { toast } from "react-toastify";
type FormValues = z.infer<typeof contactSchema>;
const GetStartedPage = () => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset,
watch,
setValue,
} = useForm<FormValues>({ resolver: zodResolver(contactSchema) });
const onSubmit: SubmitHandler<FormValues> = async (data) => {
try {
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error("Request failed");
toast.success("Thank you! We'll be in touch.");
reset();
} catch (e) {
toast.error("Failed to save contact. Please try again.");
}
};
return (
<div className="mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8 py-6 sm:py-12">
<div className="text-center mb-6">
<h1 className="text-2xl md:text-3xl font-mono font-extrabold">
Join Us on This Journey
</h1>
<p className="mt-4 mb-4 max-w-4xl mx-auto text-sm sm:text-base font-mono">
Be a part of something innovative. As our early adapters, you will be
one of the key players making a change in how staying safe online is
important yet effortless. You will get exclusive early access to our
newest solutions and influence the development, while staying
protected in the process.
</p>
</div>
<div className="relative rounded-3xl border-4 border-[var(--color-3)] px-10 sm:px-14 py-8 shadow-[0_0_20px_rgba(255,255,255,0.5)]">
<form
onSubmit={handleSubmit(onSubmit)}
className="relative z-10 space-y-4"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="sr-only">First name</label>
<input
placeholder="First name"
{...register("firstName")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.firstName && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.firstName.message}
</p>
)}
</div>
<div>
<label className="sr-only">Last name</label>
<input
placeholder="Last name"
{...register("lastName")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.lastName && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.lastName.message}
</p>
)}
</div>
<div>
<label className="sr-only">Company name</label>
<input
placeholder="Company name"
{...register("companyName")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.companyName && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.companyName.message}
</p>
)}
</div>
<div>
<label className="sr-only">Address</label>
<input
placeholder="Address"
{...register("address")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.address && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.address.message}
</p>
)}
</div>
<div>
<label className="sr-only">Email</label>
<input
placeholder="Email"
type="email"
{...register("email")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.email && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.email.message}
</p>
)}
</div>
<div>
<label className="sr-only">Phone number</label>
<input
placeholder="Phone number"
{...register("phone")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.phone && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.phone.message}
</p>
)}
</div>
</div>
<div>
<label className="sr-only">Industry type</label>
<input
placeholder="Industry type"
{...register("industry")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-0.5 text-sm"
/>
{errors.industry && (
<p className="!text-[var(--color-8)] opacity-100 text-xs mt-1">
{errors.industry.message}
</p>
)}
</div>
<div>
<label className="sr-only">Additional notes</label>
<textarea
placeholder="Additional notes"
rows={8}
{...register("notes")}
className="w-full border border-[var(--color-3)] outline-none rounded-sm px-3 py-1 text-sm"
/>
</div>
<div className="flex items-center gap-2 ml-6">
<div className="relative">
<input
type="checkbox"
id="subscribe"
{...register("subscribe")}
className="sr-only"
/>
<div
className={`w-4 h-4 border-2 border-[var(--color-3)] cursor-pointer flex items-center justify-center transition-colors shadow-[0_0_10px_rgba(255,255,255,0.2)] ${
watch("subscribe") ? "bg-[var(--color-3)]" : "bg-transparent"
}`}
onClick={() => setValue("subscribe", !watch("subscribe"))}
>
{watch("subscribe") && (
<svg
className="w-3 h-3 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</div>
</div>
<label
htmlFor="subscribe"
className="text-xs sm:text-sm font-mono cursor-pointer"
>
Sign up for our email list for updates, promotions, and more.
</label>
</div>
<div className="flex justify-center pt-2">
<button
type="submit"
disabled={isSubmitting}
className="px-6 py-3 font-mono text-sm text-white bg-[var(--color-3)] rounded-xl disabled:opacity-50 cursor-pointer"
>
{isSubmitting ? "Sending..." : "Send"}
</button>
</div>
</form>
{/* White glow */}
<div className="pointer-events-none absolute inset-0 rounded-3xl shadow-[0_0_5px_rgba(255,255,255,0.4)] z-0"></div>
</div>
</div>
);
};
export default GetStartedPage;
+44
View File
@@ -0,0 +1,44 @@
@import "tailwindcss";
@import "./fonts.css";
:root {
/* Base colors */
--white: #ffffff;
--black: #000000;
/* Custom color palette */
--color-1: #191022;
--color-2: #2d114a;
--color-3: #7f13ec;
--color-4: #cfa5e0;
--color-5: #3cf61a;
--color-6: #4ee871;
--color-7: #34d399;
--color-8: #ff007f;
--color-9: #ff31b5;
--color-10: #ec4899;
}
@theme inline {
--color-background: var(--color-1);
--color-foreground: var(--color-2);
--font-sans: var(--font-geist-sans);
--font-mono: "Fira Code", monospace;
--color-primary: var(--color-3);
--color-secondary: var(--color-7);
--color-accent: var(--color-8);
}
code,
pre {
font-family: "Fira Code", monospace;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: var(--color-background);
color: var(--color-white);
font-family: var(--font-mono);
}
+33
View File
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import favicon from "@/public/assets/Logo_Pin_-_with_no_background.ico";
import Navbar from "@/components/navigation/Navbar";
import Footer from "@/components/Footer";
import ToastProvider from "@/components/providers/ToastProvider";
import "./globals.css";
export const metadata: Metadata = {
title: "Lan Codes",
description: "Lan Codes",
icons: {
icon: favicon.src,
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="bg-[var(--color-1)] min-h-screen flex flex-col overflow-x-hidden">
<Navbar />
<main className="flex-1">{children}</main>
<Footer />
<ToastProvider />
</body>
</html>
);
}
+112
View File
@@ -0,0 +1,112 @@
import Image from "next/image";
import { LockKeyhole, RotateCcw, Shield } from "lucide-react";
import heroBanner from "@/public/assets/images/homescreen_bg.png";
import FeatureCard from "@/components/FeatureCard";
import GetStartedButton from "@/components/navigation/GetStartedButton";
const HomePage = () => {
return (
<div>
<main>
<div>
<section className="relative overflow-hidden w-full min-h-[500px] sm:min-h-[600px] md:aspect-[16/9] md:max-h-[900px] -mt-[75px] pt-[75px] md:pt-[75px]">
{/* Background image */}
<div
className="absolute inset-0 top-[-75px] overflow-hidden"
style={{
WebkitMaskImage:
"linear-gradient(to bottom, rgba(0,0,0,0) 10%, rgba(0,0,0,0.3) 12%, rgba(0,0,0,1) 16%, rgba(0,0,0,1) 88%, rgba(0,0,0,0) 100%)",
maskImage:
"linear-gradient(to bottom, rgba(0,0,0,0) 10%, rgba(0,0,0,0.3) 12%, rgba(0,0,0,1) 16%, rgba(0,0,0,1) 88%, rgba(0,0,0,0) 100%)",
}}
>
<Image
src={heroBanner}
alt="Hero background"
fill
priority
className="object-cover h-full"
style={{ objectPosition: "50% 42.5%" }}
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/3 via-black/6 to-black/9" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_35%,rgba(127,19,236,0.03),rgba(16,0,40,0.06)_58%,rgba(0,0,0,0.12))]" />
{/* Mobile darkening overlay */}
<div className="absolute inset-0 bg-black/30 md:bg-black/0" />
</div>
<div className="relative z-10 w-full h-full flex items-center px-6 sm:px-10 md:pl-16 lg:pl-24 py-12 sm:py-12 md:py-8">
<div>
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-6xl">
<span className="font-mono font-semibold w-[100px]">
Cost-Effective{" "}
</span>
<span className="text-[var(--color-8)] font-semibold">
Security
</span>
<br />
<span className="font-mono font-semibold w-[100px]">
Uncompromising{" "}
</span>
<span className="text-[var(--color-5)] font-semibold">
Safety
</span>
</h1>
<p className="mt-2 font-bold max-w-4xl text-lg sm:text-xl md:text-2xl">
Security that is{" "}
<span className="text-[var(--color-9)]">tailored</span>
</p>
<p className="mb-2 font-bold max-w-4xl text-lg sm:text-xl md:text-2xl">
for{" "}
<span className="text-[var(--color-5)]">your business</span>{" "}
needs
</p>
<GetStartedButton />
</div>
</div>
</section>
</div>
<section id="services" className="relative z-10 bg-[var(--color-1)]">
<div className="pointer-events-none absolute -top-16 left-0 right-0 h-16 bg-gradient-to-b from-transparent via-[var(--color-1)]/38 to-[var(--color-1)]" />
<div className="mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8 py-6 sm:py-16 md:py-20">
<div className="text-center">
<h2 className="text-2xl sm:text-3xl font-extrabold font-mono">
Comprehensive Cybersecurity Services
</h2>
<p className="mt-3 max-w-3xl mx-auto text-md sm:text-lg font-mono">
Lan Codes offers a range of services in one simple package
designed to protect your business from cyber threats, ensuring
data security and operational continuity.
</p>
</div>
<div className="mt-6 sm:mt-10 grid grid-cols-1 md:grid-cols-3 gap-4">
<FeatureCard
icon={<Shield color="green" size={30} />}
title="Risk Detection"
description="Be on top of your end users and ensure there are no weak links in the chain. Have active reminders on your high risk users."
/>
<FeatureCard
icon={<LockKeyhole color="red" size={30} />}
title="Data Encryption & Security"
description="Robust data encryption and security measures to protect sensitive information and ensure compliance with industry standard."
/>
<FeatureCard
icon={<RotateCcw color="purple" size={30} />}
title="24/7 Monitoring & Assistance"
description="Round-the-clock monitoring and assistance to address and resolve any security incidents, minimizing downtime and potential damage."
/>
</div>
</div>
</section>
</main>
</div>
);
};
export default HomePage;
+100
View File
@@ -0,0 +1,100 @@
"use client";
import { useState } from "react";
import ProjectItems from "@/components/services/ProjectItems";
import GetStartedButton from "@/components/navigation/GetStartedButton";
import { projectItemsData } from "@/data/StaticData";
const ServicesPage = () => {
const [activeIndex, setActiveIndex] = useState(-1);
return (
<div className="mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8 py-8 sm:pt-32 sm:pb-16">
<div className="grid grid-cols-1 md:grid-cols-12 gap-6 md:gap-0">
<div className="md:col-span-6">
<div className="flex items-center gap-3 mb-3 ml-2">
<span className="inline-block w-6 h-6 rotate-45 bg-[var(--color-7)]" />
<h1 className="text-2xl sm:text-3xl font-mono ml-2">
Bird eye view
</h1>
</div>
<div
className="md:w-3/4 w-full h-[3px] mb-4"
style={{ background: "var(--color-2)" }}
/>
<ProjectItems
items={projectItemsData}
activeIndex={activeIndex}
setActiveIndex={setActiveIndex}
/>
</div>
<div className="md:col-span-6">
<div
className={`bg-[var(--color-2)] p-6 md:p-8 md:min-h-[510px] flex items-start relative
${
activeIndex === projectItemsData.length - 1
? "rounded-tl-lg rounded-tr-lg"
: "rounded-lg"
}
`}
>
{activeIndex === -1 ? (
<>
<div className="w-full text-center pb-16">
<h3 className="text-xl sm:text-2xl font-mono font-extrabold">
Boost Your Security.
<br />
Simplify Your Workflow.
</h3>
<p className="mt-4 max-w-2xl mx-auto text-sm sm:text-base font-mono leading-relaxed">
LAN Codes is designed for organizations that want a smarter,
faster way to strengthen their security posture. Our custom
LLM automatically detects vulnerabilities and unusual
patternsremoving the tedious manual work so your team can
focus on what truly matters: investigating threats and
finding solutions.
</p>
<p className="mt-4 max-w-2xl mx-auto text-sm sm:text-base font-mono leading-relaxed">
Even if a breach occurs, your critical data stays encrypted
and protected, giving you the confidence and time to respond
effectively and prevent future incidents.
</p>
</div>
<div className="absolute bottom-6 left-0 right-0 flex justify-center">
<GetStartedButton />
</div>
</>
) : (
<div className="w-full text-center pb-16">
<p className="font-mono text-sm sm:text-base">
{projectItemsData[activeIndex]?.content}
</p>
</div>
)}
</div>
</div>
</div>
<div className="mt-10">
<div className="relative rounded-lg bg-[var(--color-2)] ring-1 ring-white/10 px-6 py-8 shadow-[0_0_16px_rgba(255,255,255,0.5)]">
<h2 className="text-center text-xl md:text-2xl font-mono mb-4">
Affordable Security for Everyone
</h2>
<p className="text-center mx-auto text-sm sm:text-base font-mono mb-6">
We believe cybersecurity should be accessible to all businesses,
regardless of size. Our solutions are designed to be cost effective
without compromising on security
</p>
<div className="flex justify-center">
<GetStartedButton />
</div>
</div>
</div>
</div>
);
};
export default ServicesPage;
+37
View File
@@ -0,0 +1,37 @@
{
"$schema": "https://biomejs.dev/schemas/2.2.0/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"includes": ["**", "!node_modules", "!.next", "!dist", "!build"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noUnknownAtRules": "off"
}
},
"domains": {
"next": "recommended",
"react": "recommended"
}
},
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
interface ButtonProps {
text: string;
}
const Button = ({ text }: ButtonProps) => {
return (
<button className="px-4 py-3 font-mono text-xs sm:text-sm text-white bg-[var(--color-3)] rounded-xl cursor-pointer">
{text}
</button>
);
};
export default Button;
+19
View File
@@ -0,0 +1,19 @@
import { ReactNode } from "react";
interface FeatureCardProps {
icon: ReactNode;
title: string;
description: string;
}
const FeatureCard = ({ icon, title, description }: FeatureCardProps) => {
return (
<div className="flex flex-col gap-5 border-[3px] border-[var(--color-2)] p-4 rounded-2xl">
<div className="mb-2 bg-transparent">{icon}</div>
<h3 className="font-mono font-bold leading-none mb-2">{title}</h3>
<p className="text-sm sm:text-base font-mono">{description}</p>
</div>
);
};
export default FeatureCard;
+39
View File
@@ -0,0 +1,39 @@
import Image from "next/image";
import Link from "next/link";
const Footer = () => {
const currentYear = new Date().getFullYear();
return (
<footer className="relative h-[125px] flex flex-col justify-center">
<div className="absolute top-0 left-0 right-0 h-px">
<div
className="mx-auto w-[87.5%] h-[3px]"
style={{
background: "var(--color-2)",
}}
/>
</div>
<div className="mx-8 flex flex-col md:flex-row justify-between items-center relative">
<div className="mb-4 md:mb-0">
<Link href="/">
<Image
src="/assets/images/logo_no_bg.png"
alt="Logo"
width={125}
height={125}
className="w-[100px] h-auto sm:w-[120px] lg:w-[125px]"
/>
</Link>
</div>
<div className="text-white text-sm font-mono absolute top-[70%] md:static left-0 right-0 text-center md:text-right">
{currentYear} Lan Codes All Rights Reserved
</div>
</div>
</footer>
);
};
export default Footer;
+31
View File
@@ -0,0 +1,31 @@
import Image, { StaticImageData } from "next/image";
interface TeamMemberCardProps {
avatar: StaticImageData;
name: string;
position: string;
}
const TeamMemberCard = ({ avatar, name, position }: TeamMemberCardProps) => {
return (
<div className="flex flex-col items-center gap-3">
<div className="relative w-40 h-40 sm:w-48 sm:h-48 rounded-full border-2 overflow-hidden">
<Image src={avatar} alt={name} className="object-cover" />
</div>
<div className="text-center">
<p
className={`font-mono text-sm ${
position === "Founder" ? "text-white" : "text-[var(--color-7)]"
}`}
>
{name}
</p>
{position !== "Founder" && (
<p className="font-mono text-sm text-[var(--color-10)]">{position}</p>
)}
</div>
</div>
);
};
export default TeamMemberCard;
@@ -0,0 +1,17 @@
import Link from "next/link";
import Button from "@/components/Button";
interface GetStartedButtonProps {
onClick?: () => void;
}
const GetStartedButton = ({ onClick }: GetStartedButtonProps) => {
return (
<Link href="/get-started" onClick={onClick}>
<Button text="Get Started" />
</Link>
);
};
export default GetStartedButton;
+51
View File
@@ -0,0 +1,51 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
interface NavLinksProps {
mobile?: boolean;
onLinkClick?: () => void;
}
const NavLinks = ({ mobile = false, onLinkClick }: NavLinksProps) => {
const pathname = usePathname();
const containerClass = mobile
? "flex flex-col items-center gap-4 text-lg min-[828px]:flex-row min-[828px]:gap-6"
: "hidden md:flex items-center gap-8 text-lg";
return (
<nav className={containerClass}>
<Link href="/services" onClick={onLinkClick}>
<span
className={`${
pathname === "/services" && "text-[var(--color-8)]"
} font-mono`}
>
Service
</span>
</Link>
<Link href="/about" onClick={onLinkClick}>
<span
className={`${
pathname === "/about" && "text-[var(--color-8)]"
} font-mono`}
>
About us
</span>
</Link>
<Link href="/contact" onClick={onLinkClick}>
<span
className={`${
pathname === "/contact" && "text-[var(--color-8)]"
} font-mono`}
>
Contact
</span>
</Link>
</nav>
);
};
export default NavLinks;
+80
View File
@@ -0,0 +1,80 @@
"use client";
import { useState } from "react";
import { usePathname } from "next/navigation";
import { Menu, X } from "lucide-react";
import NavLinks from "@/components/navigation/NavLinks";
import SiteLogo from "@/components/navigation/SiteLogo";
import GetStartedButton from "@/components/navigation/GetStartedButton";
const Navbar = () => {
const [menuOpen, setMenuOpen] = useState(false);
const pathname = usePathname();
const isGetStartedPage = pathname === "/get-started";
const isHeroOrAboutPage = pathname === "/" || pathname === "/about";
const isAboutPage = pathname === "/about";
return (
<header
className={`relative z-50 ${
isHeroOrAboutPage ? "bg-transparent" : "bg-[var(--color-1)]"
}`}
>
<nav className="flex items-center h-[75px] px-6 md:px-12 relative">
<div className="flex-shrink-0" onClick={() => setMenuOpen(false)}>
<SiteLogo />
</div>
<div className="hidden min-[828px]:flex min-[870px]:hidden items-center gap-6 ml-auto">
<NavLinks mobile={true} onLinkClick={() => setMenuOpen(false)} />
{!isGetStartedPage && <GetStartedButton />}
</div>
<div
className={`hidden min-[870px]:flex ${
isGetStartedPage
? "absolute left-1/2 transform -translate-x-1/2"
: "flex-1 justify-center transform -translate-x-[7.5%]"
}`}
>
<NavLinks onLinkClick={() => setMenuOpen(false)} />
</div>
<div className="hidden min-[870px]:flex flex-shrink-0">
{!isGetStartedPage && <GetStartedButton />}
</div>
<button
onClick={() => setMenuOpen(!menuOpen)}
className="min-[828px]:hidden absolute right-6 top-1/2 transform -translate-y-1/2 p-2 text-[var(--color-8)]"
aria-label="Toggle menu"
>
{menuOpen ? <X size={26} /> : <Menu size={26} />}
</button>
</nav>
{menuOpen && (
<div className="min-[828px]:hidden bg-[var(--color-1)] border-t border-[var(--color-2)]">
<div className="flex flex-col items-center py-4 space-y-4">
<NavLinks mobile={true} onLinkClick={() => setMenuOpen(false)} />
</div>
</div>
)}
<div
className="absolute left-0 right-0 h-[3px] mx-auto w-[87.5%]"
style={{
background: `${
isAboutPage
? "color-mix(in srgb, var(--color-2) 50%, transparent)"
: "var(--color-2)"
}`,
}}
/>
</header>
);
};
export default Navbar;
+31
View File
@@ -0,0 +1,31 @@
import Image from "next/image";
import Link from "next/link";
const SiteLogo = () => {
return (
<Link href="/" className="flex items-center gap-1">
<div
className="relative overflow-hidden w-[100px] h-[100px] sm:w-[120px] sm:h-[120px] lg:w-[150px] lg:h-[150px]"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Image
src="/assets/images/logo_no_bg.png"
alt="Logo"
width={150}
height={150}
priority
style={{ objectFit: "contain", marginTop: 4 }}
/>
</div>
<span className="font-mono text-xl sm:text-2xl lg:text-3xl z-100">
LAN CODES
</span>
</Link>
);
};
export default SiteLogo;
+17
View File
@@ -0,0 +1,17 @@
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
const ToastProvider = () => {
return (
<ToastContainer
position="bottom-right"
autoClose={3000}
newestOnTop
closeOnClick
pauseOnHover={false}
theme="colored"
/>
);
};
export default ToastProvider;
+43
View File
@@ -0,0 +1,43 @@
import { ProjectItem } from "@/data/StaticData";
interface ProjectItemsProps {
items: ProjectItem[];
activeIndex: number;
setActiveIndex: (idx: number) => void;
}
const ProjectItems = ({
items,
activeIndex,
setActiveIndex,
}: ProjectItemsProps) => {
return (
<ul className="space-y-8">
{items.map(({ title }, idx) => {
const isActive = idx === activeIndex;
return (
<li key={title}>
<button
onClick={() => setActiveIndex(idx)}
className={`ml-1 w-full text-left flex items-center gap-4 rounded-md px-2 py-2 transition cursor-pointer ${
isActive && "bg-[var(--color-2)] md:rounded-r-none"
}`}
>
<span
className={`inline-block w-3.5 h-3.5 rotate-45 ${
isActive
? "bg-[var(--color-8)] shadow-[0_0_10px_rgba(127,17,224,1)]"
: "bg-[var(--color-10)]"
}`}
/>
<span className="font-mono text-sm sm:text-base">{title}</span>
</button>
</li>
);
})}
</ul>
);
};
export default ProjectItems;
+42
View File
@@ -0,0 +1,42 @@
export type ProjectItem = {
title: string;
content: string;
};
export const projectItemsData: ProjectItem[] = [
{
title: "Seamless integration",
content:
"Implementation of this product in to your day to day workflow should not present with any challenges for your core team. Only training and understanding of the of the product will be needed by your administrators and/or your IT department.",
},
{
title: "Email protection",
content:
"Identifies and quarantines emails with potential malware or ransomware attachments. Prevents threats from domain impersonation, spoofing, and other misleading phishing attempts. Allows you to create and maintain a list of individual senders or sending domains to allow or block from your business inboxes.",
},
{
title: "Email encryption",
content:
"Fully automated process for both inbound and outbound emails with in the company or B2B communications. Removing a request of end user to check boxes by automating the process based on the email constant. Allowing your team to determent what is considered to be a sensitive information and ensuring that system will encrypt your next critical email.",
},
{
title: "User risk reporting",
content:
"Get a full overview of your end user risk status across all the application at one place. Have one stop shop both for user application risk and use best coerce of action to mitigated any form of user risk. ",
},
{
title: "User activity tracking",
content:
"Allows you to create a full time line of user activity from knowing location to time and action that took place. Keeping track of all user app activity in one place allows you to better track compromised user and act on mitigation of any breaches. ",
},
{
title: "Organization security overview",
content:
"Connect all the web-app tools that are used with in the company and have a full overview of the best course of action on what security changes or tuning need to be done to ensure maximum level of security.",
},
// {
// title: "Malware protection",
// content:
// "Continuous malware detection and response powered by up-to-date threat intelligence.",
// },
];
+55
View File
@@ -0,0 +1,55 @@
import prisma from "@/lib/prisma";
export type ContactRecord = {
id: string;
firstName: string;
lastName: string;
companyName: string;
address: string;
email: string;
phone: string;
industry: string;
notes?: string;
subscribe: boolean;
createdAt: Date;
};
export async function saveContact({
firstName,
lastName,
companyName,
address,
email,
phone,
industry,
notes,
subscribe,
}: Omit<ContactRecord, "id" | "createdAt">): Promise<ContactRecord> {
const { id, createdAt } = await prisma.contact.create({
data: {
firstName,
lastName,
companyName,
address,
email,
phone,
industry,
notes,
subscribe,
},
});
return {
id,
firstName,
lastName,
companyName,
address,
email,
phone,
industry,
notes,
subscribe,
createdAt,
};
}
+13
View File
@@ -0,0 +1,13 @@
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ["error", "warn"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;
+7
View File
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
+2015
View File
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
{
"name": "project-adonis",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "prisma generate && next build --turbopack",
"start": "next start",
"lint": "biome check",
"format": "biome format --write",
"prisma:generate": "prisma generate",
"prisma:migrate:dev": "prisma migrate dev --name init",
"prisma:migrate:deploy": "prisma migrate deploy"
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@prisma/client": "^5.22.0",
"lucide-react": "^0.545.0",
"next": "15.5.5",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-hook-form": "^7.65.0",
"react-toastify": "^11.0.5",
"zod": "^4.1.12"
},
"devDependencies": {
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"prisma": "^5.22.0",
"tailwindcss": "^4",
"typescript": "^5"
}
}
+5
View File
@@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "Contact" (
"id" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"companyName" TEXT NOT NULL,
"address" TEXT NOT NULL,
"email" TEXT NOT NULL,
"phone" TEXT NOT NULL,
"industry" TEXT NOT NULL,
"notes" TEXT,
"subscribe" BOOLEAN NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Contact_pkey" PRIMARY KEY ("id")
);
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
+22
View File
@@ -0,0 +1,22 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Contact {
id String @id @default(cuid())
firstName String
lastName String
companyName String
address String
email String
phone String
industry String
notes String?
subscribe Boolean
createdAt DateTime @default(now())
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014-2020 The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+67
View File
@@ -0,0 +1,67 @@
Fira Code Variable Font
=======================
This download contains Fira Code as both a variable font and static fonts.
Fira Code is a variable font with this axis:
wght
This means all the styles are contained in a single file:
FiraCode-VariableFont_wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Fira Code:
static/FiraCode-Light.ttf
static/FiraCode-Regular.ttf
static/FiraCode-Medium.ttf
static/FiraCode-SemiBold.ttf
static/FiraCode-Bold.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

+21
View File
@@ -0,0 +1,21 @@
import { z } from "zod";
const phoneSchema = z
.string()
.min(6, "Phone number is too short")
.max(20, "Phone number is too long")
.regex(/^\+?[1-9]\d{1,14}$/, "Invalid international phone number");
export const contactSchema = z.object({
firstName: z.string().min(3, "First name must be at least 3 characters long"),
lastName: z.string().min(3, "Last name must be at least 3 characters long"),
companyName: z
.string()
.min(3, "Company name must be at least 3 characters long"),
address: z.string().min(3, "Address must be at least 3 characters long"),
email: z.email("Invalid email address"),
phone: phoneSchema,
industry: z.string().min(1, "Industry is required"),
notes: z.string().optional(),
subscribe: z.boolean(),
});
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}