Initial website build

This commit is contained in:
Nolan Kovacs 2026-04-01 19:49:19 -04:00
parent abff50b0d7
commit 607fa059b9
14 changed files with 1607 additions and 86 deletions

27
app/api/contact/route.ts Normal file
View File

@ -0,0 +1,27 @@
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const body = await req.json();
const { name, email, subject, message } = body;
if (!name || !email || !message) {
return NextResponse.json({ error: "Missing fields" }, { status: 400 });
}
// Forward to VPS API which handles email delivery
// Once email is configured, the VPS API will send to support@verimundsolutions.com
try {
const res = await fetch(`${process.env.API_URL}/contact`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email, subject, message }),
});
if (!res.ok) throw new Error("API error");
return NextResponse.json({ ok: true });
} catch {
// Log the submission server-side so nothing is lost even if email is not yet set up
console.log("[CONTACT SUBMISSION]", { name, email, subject, message, ts: new Date().toISOString() });
return NextResponse.json({ ok: true }); // Still return ok so the user isn't confused
}
}

237
app/contact/page.tsx Normal file
View File

@ -0,0 +1,237 @@
"use client";
import { useState } from "react";
import FadeIn from "@/components/FadeIn";
const subjects = [
"General Inquiry",
"Billing",
"Technical Support",
"Hardware ID Reset",
"Other",
];
export default function Contact() {
const [form, setForm] = useState({
name: "",
email: "",
subject: subjects[0],
message: "",
});
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStatus("sending");
try {
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(form),
});
if (res.ok) {
setStatus("sent");
setForm({ name: "", email: "", subject: subjects[0], message: "" });
} else {
setStatus("error");
}
} catch {
setStatus("error");
}
};
const inputStyle = {
background: "#111111",
border: "1px solid #222222",
color: "#ffffff",
borderRadius: "2px",
padding: "10px 14px",
fontSize: "14px",
width: "100%",
outline: "none",
transition: "border-color 0.2s",
};
return (
<section className="min-h-screen pt-32 pb-24 px-6">
<div className="max-w-5xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Contact
</p>
<h1
className="text-4xl md:text-5xl font-semibold mb-12"
style={{ letterSpacing: "-0.03em" }}
>
Get in Touch
</h1>
</FadeIn>
<div className="grid grid-cols-1 md:grid-cols-2 gap-16">
{/* Contact info */}
<FadeIn delay={0.1} direction="right">
<div>
<p
className="text-xs font-semibold uppercase tracking-widest mb-6"
style={{ color: "#3a3a3a", letterSpacing: "0.15em" }}
>
Direct Contact
</p>
<div
className="p-7 rounded-sm border"
style={{ border: "1px solid #222222", background: "#111111" }}
>
<div className="mb-5">
<p className="text-base font-semibold mb-0.5">Nolan Kovacs</p>
<p className="text-sm" style={{ color: "#888888" }}>
Chief Executive Officer
</p>
<p className="text-xs mt-0.5" style={{ color: "#3a3a3a" }}>
Verimund Solutions
</p>
</div>
<div className="flex flex-col gap-2 pt-5 border-t" style={{ borderColor: "#1a1a1a" }}>
<div className="flex items-center gap-3">
<span className="text-xs w-16" style={{ color: "#3a3a3a" }}>Phone</span>
<a
href="tel:+12482529430"
className="text-sm transition-colors duration-200"
style={{ color: "#888888" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#ffffff")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#888888")}
>
248-252-9430
</a>
</div>
<div className="flex items-center gap-3">
<span className="text-xs w-16" style={{ color: "#3a3a3a" }}>Email</span>
<a
href="mailto:nolankovacs@verimundsolutions.com"
className="text-sm transition-colors duration-200"
style={{ color: "#888888" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#ffffff")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#888888")}
>
nolankovacs@verimundsolutions.com
</a>
</div>
</div>
</div>
</div>
</FadeIn>
{/* Contact form */}
<FadeIn delay={0.15} direction="left">
{status === "sent" ? (
<div
className="p-7 rounded-sm border text-center"
style={{ border: "1px solid #222222", background: "#111111" }}
>
<p
className="text-sm font-semibold mb-2"
style={{ color: "#4ade80" }}
>
Message sent.
</p>
<p className="text-sm" style={{ color: "#888888" }}>
We&apos;ll get back to you as soon as possible.
</p>
</div>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="flex flex-col gap-1.5">
<label className="text-xs" style={{ color: "#888888" }}>Name</label>
<input
name="name"
value={form.name}
onChange={handleChange}
required
placeholder="Your name"
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = "#ffffff")}
onBlur={(e) => (e.currentTarget.style.borderColor = "#222222")}
/>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs" style={{ color: "#888888" }}>Email</label>
<input
name="email"
type="email"
value={form.email}
onChange={handleChange}
required
placeholder="you@email.com"
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = "#ffffff")}
onBlur={(e) => (e.currentTarget.style.borderColor = "#222222")}
/>
</div>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs" style={{ color: "#888888" }}>Subject</label>
<select
name="subject"
value={form.subject}
onChange={handleChange}
style={{ ...inputStyle, cursor: "pointer" }}
onFocus={(e) => (e.currentTarget.style.borderColor = "#ffffff")}
onBlur={(e) => (e.currentTarget.style.borderColor = "#222222")}
>
{subjects.map((s) => (
<option key={s} value={s} style={{ background: "#111111" }}>
{s}
</option>
))}
</select>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs" style={{ color: "#888888" }}>Message</label>
<textarea
name="message"
value={form.message}
onChange={handleChange}
required
rows={5}
placeholder="How can we help?"
style={{ ...inputStyle, resize: "vertical" }}
onFocus={(e) => (e.currentTarget.style.borderColor = "#ffffff")}
onBlur={(e) => (e.currentTarget.style.borderColor = "#222222")}
/>
</div>
{status === "error" && (
<p className="text-xs" style={{ color: "#f87171" }}>
Something went wrong. Please try again or email us directly.
</p>
)}
<button
type="submit"
disabled={status === "sending"}
className="py-3 text-sm font-medium rounded-sm transition-all duration-200"
style={{ background: "#ffffff", color: "#000000" }}
onMouseEnter={(e) => (e.currentTarget.style.background = "#e0e0e0")}
onMouseLeave={(e) => (e.currentTarget.style.background = "#ffffff")}
>
{status === "sending" ? "Sending..." : "Send Message"}
</button>
</form>
)}
</FadeIn>
</div>
</div>
</section>
);
}

114
app/faq/page.tsx Normal file
View File

@ -0,0 +1,114 @@
"use client";
import { useState } from "react";
import FadeIn from "@/components/FadeIn";
const faqs = [
{
q: "How do I receive my license key?",
a: "After completing your purchase through Stripe, your license key is emailed to the address you provided at checkout. Delivery is immediate upon payment confirmation.",
},
{
q: "What is a license key and how does it work?",
a: "A license key is a unique code that activates UIT on your machine. When you first launch the application, you enter your key and it binds to your hardware. No account creation or login is required.",
},
{
q: "Can I use my license on multiple devices?",
a: "Each license key is bound to a single machine (hardware ID). If you need to transfer your license to a new machine, contact support and we will reset your hardware binding.",
},
{
q: "What happens when my license expires?",
a: "Weekly and monthly licenses expire after their respective period. The application will notify you before expiry. You can renew at any time by purchasing a new license key.",
},
{
q: "How do I renew my license?",
a: "Simply purchase a new license key from the UIT pricing page. Enter it in the application when prompted and your access will be extended.",
},
{
q: "How do I reset my hardware ID?",
a: "Contact us via the Contact page or email support@verimundsolutions.com with your license key and a brief description of your situation. We will process your hardware reset promptly.",
},
{
q: "Do you offer refunds?",
a: "Due to the digital nature of license keys, all sales are final. If you experience a technical issue preventing the software from functioning, contact support and we will work to resolve it.",
},
{
q: "Is UIT financial advice?",
a: "No. UIT is an informational tool that provides algorithmic scoring and data analysis. Nothing produced by UIT constitutes financial advice or a recommendation to buy or sell any security. Always consult a licensed financial advisor before making investment decisions.",
},
];
function Accordion({ q, a }: { q: string; a: string }) {
const [open, setOpen] = useState(false);
return (
<div
className="border-b cursor-pointer"
style={{ borderColor: "#1a1a1a" }}
onClick={() => setOpen(!open)}
>
<div className="flex items-center justify-between py-5 gap-4">
<p className="text-sm font-medium" style={{ color: "#ffffff" }}>
{q}
</p>
<span
className="text-lg flex-shrink-0 transition-transform duration-200"
style={{
color: "#888888",
transform: open ? "rotate(45deg)" : "none",
}}
>
+
</span>
</div>
{open && (
<p className="text-sm pb-5 leading-relaxed" style={{ color: "#888888" }}>
{a}
</p>
)}
</div>
);
}
export default function FAQ() {
return (
<section className="min-h-screen pt-32 pb-24 px-6">
<div className="max-w-2xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Support
</p>
<h1
className="text-4xl md:text-5xl font-semibold mb-4"
style={{ letterSpacing: "-0.03em" }}
>
Frequently Asked Questions
</h1>
<p className="text-sm mb-12" style={{ color: "#888888" }}>
Can&apos;t find what you&apos;re looking for?{" "}
<a
href="/contact"
className="underline underline-offset-4 transition-colors duration-200"
style={{ color: "#888888" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#ffffff")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#888888")}
>
Contact us.
</a>
</p>
</FadeIn>
<FadeIn delay={0.1}>
<div className="border-t" style={{ borderColor: "#1a1a1a" }}>
{faqs.map((item) => (
<Accordion key={item.q} {...item} />
))}
</div>
</FadeIn>
</div>
</section>
);
}

View File

@ -1,26 +1,45 @@
@import "tailwindcss"; @import "tailwindcss";
:root { :root {
--background: #ffffff; --bg: #0a0a0a;
--foreground: #171717; --bg2: #111111;
--bg3: #181818;
--border: #222222;
--fg: #ffffff;
--fg2: #888888;
--fg3: #3a3a3a;
--green: #4ade80;
} }
@theme inline { * {
--color-background: var(--background); box-sizing: border-box;
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
} }
@media (prefers-color-scheme: dark) { html {
:root { scroll-behavior: smooth;
--background: #0a0a0a;
--foreground: #ededed;
}
} }
body { body {
background: var(--background); background-color: #0a0a0a;
color: var(--foreground); color: #ffffff;
font-family: Arial, Helvetica, sans-serif; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
::selection {
background: rgba(255, 255, 255, 0.15);
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #0a0a0a;
}
::-webkit-scrollbar-thumb {
background: #222222;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #3a3a3a;
} }

View File

@ -1,20 +1,24 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
import Nav from "@/components/Nav";
import Footer from "@/components/Footer";
const geistSans = Geist({ const inter = Inter({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"], subsets: ["latin"],
variable: "--font-inter",
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "Verimund Solutions",
description: "Generated by create next app", description: "Finance-specialized SaaS tools for serious investors.",
openGraph: {
title: "Verimund Solutions",
description: "Finance-specialized SaaS tools for serious investors.",
url: "https://verimundsolutions.com",
siteName: "Verimund Solutions",
type: "website",
},
}; };
export default function RootLayout({ export default function RootLayout({
@ -23,11 +27,12 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html <html lang="en" className={`${inter.variable}`}>
lang="en" <body className="min-h-screen flex flex-col" style={{ fontFamily: "var(--font-inter), sans-serif" }}>
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`} <Nav />
> <main className="flex-1">{children}</main>
<body className="min-h-full flex flex-col">{children}</body> <Footer />
</body>
</html> </html>
); );
} }

View File

@ -1,65 +1,208 @@
import Image from "next/image"; "use client";
import Link from "next/link";
import { motion } from "framer-motion";
import FadeIn from "@/components/FadeIn";
const products = [
{
name: "UIT",
tagline: "Institutional-grade stock screening for independent investors.",
href: "/uit",
badge: "Available Now",
badgeColor: "#4ade80",
},
{
name: "More Coming",
tagline: "Additional tools are in development. Stay tuned.",
href: "#",
badge: "Soon",
badgeColor: "#888888",
},
];
export default function Home() { export default function Home() {
return ( return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black"> <>
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start"> {/* ── Hero ─────────────────────────────────────────────────────────── */}
<Image <section className="relative min-h-screen flex flex-col items-center justify-center text-center px-6 overflow-hidden">
className="dark:invert" {/* Background grid */}
src="/next.svg" <div
alt="Next.js logo" className="absolute inset-0 pointer-events-none"
width={100} style={{
height={20} backgroundImage:
priority "linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px)",
backgroundSize: "60px 60px",
}}
/> />
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left"> {/* Radial glow */}
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50"> <div
To get started, edit the page.tsx file. className="absolute inset-0 pointer-events-none"
</h1> style={{
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"> background:
Looking for a starting point or more instructions? Head over to{" "} "radial-gradient(ellipse 80% 50% at 50% 40%, rgba(255,255,255,0.04) 0%, transparent 70%)",
<a }}
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" />
className="font-medium text-zinc-950 dark:text-zinc-50"
> <div className="relative z-10 max-w-3xl mx-auto">
Templates <motion.div
</a>{" "} initial={{ opacity: 0, y: 16 }}
or the{" "} animate={{ opacity: 1, y: 0 }}
<a transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
> >
<Image <p
className="dark:invert" className="text-xs font-semibold uppercase tracking-widest mb-6"
src="/vercel.svg" style={{ color: "#4ade80", letterSpacing: "0.2em" }}
alt="Vercel logomark" >
width={16} Verimund Solutions
height={16} </p>
/> </motion.div>
Deploy Now
</a> <motion.h1
<a className="text-5xl md:text-7xl font-semibold leading-tight mb-6"
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]" style={{ letterSpacing: "-0.03em" }}
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" initial={{ opacity: 0, y: 20 }}
target="_blank" animate={{ opacity: 1, y: 0 }}
rel="noopener noreferrer" transition={{ duration: 0.6, delay: 0.1, ease: [0.22, 1, 0.36, 1] }}
> >
Documentation Precision tools for{" "}
</a> <span style={{ color: "#888888" }}>serious investors.</span>
</motion.h1>
<motion.p
className="text-base md:text-lg mb-10 max-w-xl mx-auto"
style={{ color: "#888888", lineHeight: 1.7 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2, ease: [0.22, 1, 0.36, 1] }}
>
Verimund Solutions builds finance-focused software that brings
institutional-level analysis to independent investors.
</motion.p>
<motion.div
className="flex flex-col sm:flex-row gap-3 justify-center"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3, ease: [0.22, 1, 0.36, 1] }}
>
<Link
href="/uit"
className="px-6 py-3 text-sm font-medium rounded-sm transition-all duration-200"
style={{ background: "#ffffff", color: "#000000" }}
onMouseEnter={(e) => (e.currentTarget.style.background = "#e0e0e0")}
onMouseLeave={(e) => (e.currentTarget.style.background = "#ffffff")}
>
Explore UIT
</Link>
<Link
href="/contact"
className="px-6 py-3 text-sm font-medium rounded-sm border transition-all duration-200"
style={{ border: "1px solid #222222", color: "#888888" }}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "#888888";
e.currentTarget.style.color = "#ffffff";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "#222222";
e.currentTarget.style.color = "#888888";
}}
>
Contact Us
</Link>
</motion.div>
</div> </div>
</main>
</div> {/* Scroll indicator */}
<motion.div
className="absolute bottom-10 left-1/2 -translate-x-1/2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
>
<motion.div
className="w-px h-10 mx-auto"
style={{ background: "linear-gradient(to bottom, #333333, transparent)" }}
animate={{ scaleY: [1, 0.5, 1] }}
transition={{ duration: 1.8, repeat: Infinity, ease: "easeInOut" }}
/>
</motion.div>
</section>
{/* ── About ────────────────────────────────────────────────────────── */}
<section className="py-24 px-6 border-t" style={{ borderColor: "#111111" }}>
<FadeIn>
<div className="max-w-2xl mx-auto text-center">
<p
className="text-xs font-semibold uppercase tracking-widest mb-5"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
About
</p>
<p className="text-base md:text-lg leading-relaxed" style={{ color: "#888888" }}>
We build software that makes institutional-grade financial analysis
accessible without a Bloomberg terminal. No account needed.
Every product runs on a single license key, activated directly on your machine.
</p>
</div>
</FadeIn>
</section>
{/* ── Products ─────────────────────────────────────────────────────── */}
<section className="py-24 px-6 border-t" style={{ borderColor: "#111111" }}>
<div className="max-w-5xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-12 text-center"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Products
</p>
</FadeIn>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
{products.map((p, i) => (
<FadeIn key={p.name} delay={i * 0.1}>
<Link
href={p.href}
className="block p-8 rounded-sm border transition-all duration-300"
style={{ border: "1px solid #222222", background: "#111111" }}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.borderColor = "#333333";
(e.currentTarget as HTMLElement).style.background = "#181818";
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.borderColor = "#222222";
(e.currentTarget as HTMLElement).style.background = "#111111";
}}
>
<div className="flex items-start justify-between mb-4">
<h3 className="text-xl font-semibold">{p.name}</h3>
<span
className="text-xs px-2 py-0.5 rounded-full font-medium"
style={{
background: `${p.badgeColor}18`,
color: p.badgeColor,
border: `1px solid ${p.badgeColor}30`,
}}
>
{p.badge}
</span>
</div>
<p className="text-sm leading-relaxed" style={{ color: "#888888" }}>
{p.tagline}
</p>
{p.href !== "#" && (
<p className="mt-6 text-xs font-medium" style={{ color: "#3a3a3a" }}>
Learn more
</p>
)}
</Link>
</FadeIn>
))}
</div>
</div>
</section>
</>
); );
} }

123
app/privacy/page.tsx Normal file
View File

@ -0,0 +1,123 @@
import FadeIn from "@/components/FadeIn";
const EFFECTIVE_DATE = "2026";
export default function Privacy() {
return (
<section className="min-h-screen pt-32 pb-24 px-6">
<div className="max-w-2xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Legal
</p>
<h1
className="text-4xl font-semibold mb-2"
style={{ letterSpacing: "-0.03em" }}
>
Privacy Policy
</h1>
<p className="text-xs mb-12" style={{ color: "#3a3a3a" }}>
Effective: {EFFECTIVE_DATE} · Verimund Solutions
</p>
</FadeIn>
<FadeIn delay={0.1}>
<div className="flex flex-col gap-10" style={{ color: "#888888", lineHeight: 1.8 }}>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>1. Overview</h2>
<p className="text-sm">
Verimund Solutions (&quot;we,&quot; &quot;us&quot;) operates verimundsolutions.com and related software products.
This Privacy Policy explains what data we collect, how we use it, and your rights
regarding that data.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>2. Data We Collect</h2>
<p className="text-sm mb-3">We collect the following data:</p>
<ul className="text-sm flex flex-col gap-2 pl-4" style={{ listStyleType: "disc" }}>
<li>
<strong style={{ color: "#ffffff" }}>Email address</strong> collected at purchase,
used to deliver your license key and send important account notices.
</li>
<li>
<strong style={{ color: "#ffffff" }}>Payment information</strong> processed entirely
by Stripe. We never see or store your card number, CVV, or banking details.
</li>
<li>
<strong style={{ color: "#ffffff" }}>Hardware ID (HWID)</strong> a non-reversible
fingerprint of your machine, used to bind your license key. It cannot be used to
identify you personally.
</li>
<li>
<strong style={{ color: "#ffffff" }}>IP address and timestamp</strong> logged on
each authentication request for security and abuse prevention.
</li>
<li>
<strong style={{ color: "#ffffff" }}>Contact form submissions</strong> name, email,
and message content submitted through our Contact page.
</li>
</ul>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>3. How We Use Your Data</h2>
<p className="text-sm">
Data collected is used solely to: deliver license keys, authenticate your software,
respond to support requests, and detect and prevent abuse or unauthorized access.
We do not use your data for advertising or sell it to any third party.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>4. Data Storage</h2>
<p className="text-sm">
Your data is stored on a private server hosted by Hetzner in the EU. The database
is not publicly accessible. Authentication logs are retained for up to 90 days.
Email addresses and license records are retained for the life of your license and
a reasonable period after for support purposes.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>5. Third Parties</h2>
<p className="text-sm">
We use Stripe for payment processing. Stripe&apos;s privacy policy governs how they
handle your payment data. We do not share your personal data with any other third party.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>6. Your Rights</h2>
<p className="text-sm">
You may request deletion of your personal data (email address and authentication logs)
at any time by contacting support@verimundsolutions.com. Note that deleting your data
will also invalidate any associated license keys.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>7. Cookies</h2>
<p className="text-sm">
This website does not use tracking cookies or third-party analytics. No cookie consent
banner is required.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>8. Contact</h2>
<p className="text-sm">
Privacy-related inquiries may be directed to support@verimundsolutions.com.
</p>
</div>
</div>
</FadeIn>
</div>
</section>
);
}

130
app/terms/page.tsx Normal file
View File

@ -0,0 +1,130 @@
import FadeIn from "@/components/FadeIn";
const EFFECTIVE_DATE = "2026";
export default function Terms() {
return (
<section className="min-h-screen pt-32 pb-24 px-6">
<div className="max-w-2xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Legal
</p>
<h1
className="text-4xl font-semibold mb-2"
style={{ letterSpacing: "-0.03em" }}
>
Terms of Service
</h1>
<p className="text-xs mb-12" style={{ color: "#3a3a3a" }}>
Effective: {EFFECTIVE_DATE} · Verimund Solutions
</p>
</FadeIn>
<FadeIn delay={0.1}>
<div className="flex flex-col gap-10" style={{ color: "#888888", lineHeight: 1.8 }}>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>1. Acceptance</h2>
<p className="text-sm">
By purchasing or using any Verimund Solutions software, you agree to these Terms of Service.
If you do not agree, do not purchase or use our products.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>2. License Grant</h2>
<p className="text-sm">
Upon purchase, Verimund Solutions grants you a non-exclusive, non-transferable license to
use the purchased software on a single machine. The license is bound to the hardware ID
of the machine on which it is first activated. Each license key may only be used on one
device at a time.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>3. Prohibited Use</h2>
<p className="text-sm">
You may not: (a) share, resell, or transfer your license key to any third party; (b) reverse
engineer, decompile, or attempt to extract source code from any Verimund Solutions software;
(c) use automated systems to abuse or overload our API or data services; (d) use the software
for any unlawful purpose.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>4. Financial Disclaimer</h2>
<p className="text-sm">
Verimund Solutions software, including UIT, is provided for informational purposes only.
All scores, ratings, rankings, and any buy, sell, or hold designations produced by our
software are algorithmic outputs based on publicly available data. Nothing produced by
our software constitutes financial advice, investment advice, or a recommendation to buy,
sell, or hold any security. Past performance of any scoring methodology does not guarantee
future results. You should consult a licensed financial advisor before making any investment
decision. Verimund Solutions accepts no liability for investment decisions made based on
the output of our software.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>5. Refund Policy</h2>
<p className="text-sm">
Due to the digital nature of license keys, all sales are final. If you experience a
verified technical issue that prevents the software from functioning on a supported
system and we are unable to resolve it, we will issue a refund at our discretion.
Contact support@verimundsolutions.com with your license key and a description of the issue.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>6. License Expiry and Renewal</h2>
<p className="text-sm">
Weekly and monthly licenses expire at the end of their respective period. Verimund Solutions
is not obligated to provide advance notice of expiry, though we may do so at our discretion.
Expired licenses may be renewed by purchasing a new license key.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>7. Termination</h2>
<p className="text-sm">
Verimund Solutions reserves the right to revoke any license key without refund if we
determine the license is being used in violation of these Terms, including but not limited
to sharing, resale, or automated abuse of our services.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>8. Limitation of Liability</h2>
<p className="text-sm">
To the maximum extent permitted by applicable law, Verimund Solutions shall not be liable
for any indirect, incidental, special, or consequential damages arising from your use of
our software, including any investment losses. Our total liability to you for any claim
shall not exceed the amount you paid for the license key in question.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>9. Changes to Terms</h2>
<p className="text-sm">
We may update these Terms at any time. Continued use of the software after updated Terms
are posted constitutes acceptance of the revised Terms.
</p>
</div>
<div>
<h2 className="text-sm font-semibold mb-2" style={{ color: "#ffffff" }}>10. Contact</h2>
<p className="text-sm">
Questions regarding these Terms may be directed to support@verimundsolutions.com.
</p>
</div>
</div>
</FadeIn>
</div>
</section>
);
}

400
app/uit/page.tsx Normal file
View File

@ -0,0 +1,400 @@
"use client";
import Link from "next/link";
import FadeIn from "@/components/FadeIn";
const dimensions = [
{ name: "Value", methods: ["Sector Z-Score Normalization"] },
{ name: "Growth", methods: ["Revenue & Earnings Trend Analysis"] },
{ name: "Momentum", methods: ["Short/Long-Term Trend Divergence"] },
{ name: "Quality", methods: ["Piotroski F-Score"] },
{ name: "Profitability", methods: ["Sloan Accruals Ratio"] },
{ name: "Sentiment", methods: ["VADER NLP"] },
{ name: "Analyst", methods: ["Analyst Consensus Normalization"] },
{ name: "Risk", methods: ["FINRA RegSHO Short-Volume Analysis"] },
];
const profiles = [
"Balanced",
"High Growth",
"Value / Defensive",
"Momentum / Swing",
"Dividend Income",
"Turnaround / Contrarian",
"Early-Stage",
];
const dataSources = [
"SEC EDGAR XBRL",
"FINRA RegSHO",
"13F Hedge Fund Filings",
"SEC Form 4 Insider Transactions",
"Live Analyst Consensus",
];
const pricingTiers = [
{
name: "Weekly",
price: "$6",
period: "7-day access",
sub: null,
discount: null,
popular: false,
priceId: "STRIPE_PRICE_ID_WEEKLY",
},
{
name: "Monthly",
price: "$19",
period: "30-day access",
sub: null,
discount: null,
popular: false,
priceId: "STRIPE_PRICE_ID_MONTHLY",
},
{
name: "Annual",
price: "$149",
period: "12-month access",
sub: "$12.42 / mo",
discount: "Save 35%",
popular: true,
priceId: "STRIPE_PRICE_ID_ANNUAL",
},
{
name: "Lifetime",
price: "$299",
period: "Never expires",
sub: null,
discount: null,
popular: false,
priceId: "STRIPE_PRICE_ID_LIFETIME",
},
];
export default function UIT() {
const handleBuy = (priceId: string) => {
// Stripe Checkout — wired once live keys are in place
console.log("Checkout:", priceId);
alert("Purchase flow coming soon. Check back shortly.");
};
return (
<>
{/* ── Hero ─────────────────────────────────────────────────────────── */}
<section className="relative min-h-[60vh] flex flex-col items-center justify-center text-center px-6 pt-24 overflow-hidden">
<div
className="absolute inset-0 pointer-events-none"
style={{
backgroundImage:
"linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px)",
backgroundSize: "60px 60px",
}}
/>
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
"radial-gradient(ellipse 70% 50% at 50% 30%, rgba(74,222,128,0.04) 0%, transparent 70%)",
}}
/>
<div className="relative z-10 max-w-2xl mx-auto">
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#4ade80", letterSpacing: "0.2em" }}
>
Product
</p>
<h1
className="text-5xl md:text-6xl font-semibold mb-5"
style={{ letterSpacing: "-0.03em" }}
>
UIT
</h1>
<p className="text-base md:text-lg mb-8" style={{ color: "#888888", lineHeight: 1.7 }}>
A multi-dimensional stock screener that evaluates every major US-listed
equity across 8 scoring dimensions weighted automatically to match
your investor profile.
</p>
<a
href="#pricing"
className="inline-block px-6 py-3 text-sm font-medium rounded-sm transition-all duration-200"
style={{ background: "#ffffff", color: "#000000" }}
onMouseEnter={(e) => (e.currentTarget.style.background = "#e0e0e0")}
onMouseLeave={(e) => (e.currentTarget.style.background = "#ffffff")}
>
Get Started
</a>
</div>
</section>
{/* ── Evaluation Methods ───────────────────────────────────────────── */}
<section className="py-24 px-6 border-t" style={{ borderColor: "#111111" }}>
<div className="max-w-5xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-3 text-center"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Methodology
</p>
<h2
className="text-2xl md:text-3xl font-semibold text-center mb-3"
style={{ letterSpacing: "-0.02em" }}
>
8-Dimension Composite Score
</h2>
<p className="text-sm text-center mb-12" style={{ color: "#888888" }}>
Every stock is scored 0100 independently. Weights shift automatically based on your selected investor profile.
</p>
</FadeIn>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{dimensions.map((d, i) => (
<FadeIn key={d.name} delay={i * 0.05}>
<div
className="p-5 rounded-sm border h-full"
style={{ border: "1px solid #222222", background: "#111111" }}
>
<p className="text-sm font-semibold mb-3" style={{ color: "#ffffff" }}>
{d.name}
</p>
<div className="flex flex-col gap-1">
{d.methods.map((m) => (
<p key={m} className="text-xs" style={{ color: "#888888" }}>
{m}
</p>
))}
</div>
</div>
</FadeIn>
))}
</div>
{/* Investor profiles */}
<FadeIn delay={0.2}>
<div className="mt-10 p-6 rounded-sm border" style={{ border: "1px solid #222222", background: "#111111" }}>
<p className="text-xs font-semibold uppercase tracking-widest mb-4" style={{ color: "#3a3a3a", letterSpacing: "0.15em" }}>
Investor Profiles
</p>
<div className="flex flex-wrap gap-2">
{profiles.map((p) => (
<span
key={p}
className="text-xs px-3 py-1 rounded-full border"
style={{ border: "1px solid #222222", color: "#888888" }}
>
{p}
</span>
))}
</div>
</div>
</FadeIn>
{/* Data sources */}
<FadeIn delay={0.25}>
<div className="mt-4 p-6 rounded-sm border" style={{ border: "1px solid #222222", background: "#111111" }}>
<p className="text-xs font-semibold uppercase tracking-widest mb-4" style={{ color: "#3a3a3a", letterSpacing: "0.15em" }}>
Data Sources
</p>
<div className="flex flex-wrap gap-2">
{dataSources.map((s) => (
<span
key={s}
className="text-xs px-3 py-1 rounded-full border"
style={{ border: "1px solid #1a1a1a", color: "#4ade80", background: "#4ade8010" }}
>
{s}
</span>
))}
</div>
</div>
</FadeIn>
</div>
</section>
{/* ── Pricing ──────────────────────────────────────────────────────── */}
<section id="pricing" className="py-24 px-6 border-t" style={{ borderColor: "#111111" }}>
<div className="max-w-4xl mx-auto">
<FadeIn>
<p
className="text-xs font-semibold uppercase tracking-widest mb-3 text-center"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Pricing
</p>
<h2
className="text-2xl md:text-3xl font-semibold text-center mb-3"
style={{ letterSpacing: "-0.02em" }}
>
One-time payment. No subscription required.
</h2>
<p className="text-sm text-center mb-12" style={{ color: "#888888" }}>
Purchase a license key. Your key is emailed to you immediately after checkout.
</p>
</FadeIn>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{pricingTiers.map((tier, i) => (
<FadeIn key={tier.name} delay={i * 0.08}>
<div
className="relative p-6 rounded-sm border flex flex-col h-full"
style={{
border: tier.popular ? "1px solid #ffffff" : "1px solid #222222",
background: tier.popular ? "#181818" : "#111111",
}}
>
{tier.popular && (
<div
className="absolute -top-px left-1/2 -translate-x-1/2 px-3 py-0.5 text-xs font-semibold rounded-b-sm whitespace-nowrap"
style={{ background: "#ffffff", color: "#000000" }}
>
Most Popular
</div>
)}
{/* Header */}
<div className="flex items-center justify-between mb-2 mt-1">
<p className="text-sm font-semibold">{tier.name}</p>
{tier.discount && (
<span
className="text-xs px-2 py-0.5 rounded-full font-semibold"
style={{ background: "#4ade8018", color: "#4ade80", border: "1px solid #4ade8030" }}
>
{tier.discount}
</span>
)}
</div>
{/* Price */}
<p className="text-4xl font-semibold mb-0.5" style={{ letterSpacing: "-0.03em" }}>
{tier.price}
</p>
{tier.sub && (
<p className="text-xs mb-0.5" style={{ color: "#4ade80" }}>
{tier.sub}
</p>
)}
<p className="text-xs mb-6" style={{ color: "#888888" }}>
{tier.period}
</p>
{/* Features */}
<ul className="flex flex-col gap-2 mb-6 flex-1">
{[
"Full screener access",
"All 7 investor profiles",
"Hedge fund & insider data",
"License key via email",
].map((f) => (
<li key={f} className="text-xs flex items-center gap-2" style={{ color: "#888888" }}>
<span style={{ color: "#4ade80" }}></span> {f}
</li>
))}
</ul>
{/* CTA */}
<button
onClick={() => handleBuy(tier.priceId)}
className="w-full py-2.5 text-sm font-medium rounded-sm transition-all duration-200"
style={
tier.popular
? { background: "#ffffff", color: "#000000" }
: { border: "1px solid #222222", color: "#ffffff", background: "transparent" }
}
onMouseEnter={(e) => {
if (tier.popular) {
e.currentTarget.style.background = "#e0e0e0";
} else {
e.currentTarget.style.borderColor = "#ffffff";
}
}}
onMouseLeave={(e) => {
if (tier.popular) {
e.currentTarget.style.background = "#ffffff";
} else {
e.currentTarget.style.borderColor = "#222222";
}
}}
>
Buy Now
</button>
</div>
</FadeIn>
))}
</div>
<FadeIn delay={0.35}>
<p className="text-xs text-center mt-8" style={{ color: "#3a3a3a" }}>
Powered by Stripe. Your payment information is never stored on our servers.
</p>
</FadeIn>
{/* ── Active development strip ─────────────────────────────────── */}
<FadeIn delay={0.4}>
<div
className="mt-8 flex items-start sm:items-center gap-4 px-6 py-5 rounded-sm border"
style={{ border: "1px solid #1a1a1a", background: "#0f0f0f" }}
>
<div className="flex-shrink-0 flex items-center gap-2 pt-0.5 sm:pt-0">
<span
className="relative flex h-2 w-2"
>
<span
className="animate-ping absolute inline-flex h-full w-full rounded-full opacity-60"
style={{ background: "#4ade80" }}
/>
<span
className="relative inline-flex rounded-full h-2 w-2"
style={{ background: "#4ade80" }}
/>
</span>
<span className="text-xs font-semibold whitespace-nowrap" style={{ color: "#4ade80" }}>
Actively developed
</span>
</div>
<p className="text-xs leading-relaxed" style={{ color: "#888888" }}>
UIT is updated regularly with new scoring signals, data sources, and improvements.
All updates are included with every license what you buy today keeps getting better.
</p>
</div>
</FadeIn>
</div>
</section>
{/* ── Discord ──────────────────────────────────────────────────────── */}
<section className="py-20 px-6 border-t" style={{ borderColor: "#111111" }}>
<FadeIn>
<div
className="max-w-3xl mx-auto rounded-sm border p-10 text-center"
style={{ border: "1px solid #222222", background: "#111111" }}
>
<p
className="text-xs font-semibold uppercase tracking-widest mb-4"
style={{ color: "#3a3a3a", letterSpacing: "0.2em" }}
>
Community
</p>
<h3 className="text-2xl font-semibold mb-3" style={{ letterSpacing: "-0.02em" }}>
Join the UIT Discord
</h3>
<p className="text-sm mb-7" style={{ color: "#888888" }}>
Get support, announcements, and connect with other UIT users.
</p>
<a
href="DISCORD_INVITE_LINK"
target="_blank"
rel="noopener noreferrer"
className="inline-block px-6 py-3 text-sm font-medium rounded-sm border transition-all duration-200"
style={{ border: "1px solid #222222", color: "#ffffff" }}
onMouseEnter={(e) => (e.currentTarget.style.borderColor = "#ffffff")}
onMouseLeave={(e) => (e.currentTarget.style.borderColor = "#222222")}
>
Join Server
</a>
</div>
</FadeIn>
</section>
</>
);
}

43
components/FadeIn.tsx Normal file
View File

@ -0,0 +1,43 @@
"use client";
import { motion, useInView } from "framer-motion";
import { useRef } from "react";
interface FadeInProps {
children: React.ReactNode;
delay?: number;
direction?: "up" | "down" | "left" | "right" | "none";
className?: string;
}
export default function FadeIn({
children,
delay = 0,
direction = "up",
className,
}: FadeInProps) {
const ref = useRef(null);
const inView = useInView(ref, { once: true, margin: "-80px" });
const offsets = {
up: { y: 28, x: 0 },
down: { y: -28, x: 0 },
left: { y: 0, x: 28 },
right: { y: 0, x: -28 },
none: { y: 0, x: 0 },
};
const { x, y } = offsets[direction];
return (
<motion.div
ref={ref}
className={className}
initial={{ opacity: 0, x, y }}
animate={inView ? { opacity: 1, x: 0, y: 0 } : {}}
transition={{ duration: 0.6, delay, ease: [0.22, 1, 0.36, 1] }}
>
{children}
</motion.div>
);
}

110
components/Footer.tsx Normal file
View File

@ -0,0 +1,110 @@
"use client";
import Link from "next/link";
export default function Footer() {
const year = new Date().getFullYear();
return (
<footer
className="border-t mt-auto"
style={{ borderColor: "#222222", background: "#0a0a0a" }}
>
{/* Disclaimer bar */}
<div
className="border-b px-6 py-3 text-center text-xs"
style={{ borderColor: "#1a1a1a", color: "#3a3a3a" }}
>
UIT is for informational purposes only. Nothing on this site constitutes
financial advice or a recommendation to buy or sell any security. Past
performance does not guarantee future results. Consult a licensed financial
advisor before making investment decisions.
</div>
{/* Main footer */}
<div className="max-w-6xl mx-auto px-6 py-10">
<div className="grid grid-cols-1 md:grid-cols-3 gap-10">
{/* Brand */}
<div>
<div className="mb-3">
<span className="text-sm font-semibold tracking-widest uppercase" style={{ letterSpacing: "0.15em" }}>
Verimund
</span>{" "}
<span className="text-sm font-light tracking-widest uppercase" style={{ color: "#888888", letterSpacing: "0.15em" }}>
Solutions
</span>
</div>
<p className="text-xs leading-relaxed" style={{ color: "#888888" }}>
Finance-specialized SaaS tools built for serious investors.
</p>
</div>
{/* Navigation */}
<div>
<p className="text-xs font-semibold uppercase tracking-widest mb-3" style={{ color: "#3a3a3a", letterSpacing: "0.12em" }}>
Navigation
</p>
<div className="flex flex-col gap-2">
{[
{ href: "/", label: "Home" },
{ href: "/uit", label: "UIT" },
{ href: "/faq", label: "FAQ" },
{ href: "/contact", label: "Contact" },
].map(({ href, label }) => (
<Link
key={href}
href={href}
className="text-xs transition-colors duration-200"
style={{ color: "#888888" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#ffffff")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#888888")}
>
{label}
</Link>
))}
</div>
</div>
{/* Contact + Legal */}
<div>
<p className="text-xs font-semibold uppercase tracking-widest mb-3" style={{ color: "#3a3a3a", letterSpacing: "0.12em" }}>
Contact
</p>
<div className="flex flex-col gap-1 mb-5">
<p className="text-xs" style={{ color: "#888888" }}>Nolan Kovacs CEO</p>
<p className="text-xs" style={{ color: "#888888" }}>248-252-9430</p>
<p className="text-xs" style={{ color: "#888888" }}>
{/* Placeholder until email is set up */}
nolankovacs@verimundsolutions.com
</p>
</div>
<div className="flex gap-4">
<Link href="/terms" className="text-xs transition-colors duration-200" style={{ color: "#3a3a3a" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#888888")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#3a3a3a")}
>
Terms of Service
</Link>
<Link href="/privacy" className="text-xs transition-colors duration-200" style={{ color: "#3a3a3a" }}
onMouseEnter={(e) => (e.currentTarget.style.color = "#888888")}
onMouseLeave={(e) => (e.currentTarget.style.color = "#3a3a3a")}
>
Privacy Policy
</Link>
</div>
</div>
</div>
{/* Bottom row */}
<div
className="mt-10 pt-6 border-t flex flex-col md:flex-row items-center justify-between gap-3"
style={{ borderColor: "#1a1a1a" }}
>
<p className="text-xs" style={{ color: "#3a3a3a" }}>
© {year} Verimund Solutions. All rights reserved.
</p>
</div>
</div>
</footer>
);
}

126
components/Nav.tsx Normal file
View File

@ -0,0 +1,126 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
const links = [
{ href: "/", label: "Home" },
{ href: "/uit", label: "UIT" },
{ href: "/faq", label: "FAQ" },
{ href: "/contact", label: "Contact" },
];
export default function Nav() {
const pathname = usePathname();
const [scrolled, setScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<header
className="fixed top-0 left-0 right-0 z-50 transition-all duration-300"
style={{
background: scrolled ? "rgba(10,10,10,0.85)" : "transparent",
backdropFilter: scrolled ? "blur(12px)" : "none",
borderBottom: scrolled ? "1px solid #222222" : "1px solid transparent",
}}
>
<div className="max-w-6xl mx-auto px-6 h-16 flex items-center justify-between">
{/* Logo */}
<Link href="/" className="flex items-center gap-2 group">
<span
className="text-sm font-semibold tracking-widest uppercase"
style={{ color: "#ffffff", letterSpacing: "0.15em" }}
>
Verimund
</span>
<span
className="text-sm font-light tracking-widest uppercase"
style={{ color: "#888888", letterSpacing: "0.15em" }}
>
Solutions
</span>
</Link>
{/* Desktop nav */}
<nav className="hidden md:flex items-center gap-8">
{links.map(({ href, label }) => {
const active = pathname === href;
return (
<Link
key={href}
href={href}
className="text-sm transition-colors duration-200"
style={{
color: active ? "#ffffff" : "#888888",
fontWeight: active ? 500 : 400,
}}
onMouseEnter={(e) => (e.currentTarget.style.color = "#ffffff")}
onMouseLeave={(e) =>
(e.currentTarget.style.color = active ? "#ffffff" : "#888888")
}
>
{label}
</Link>
);
})}
</nav>
{/* Mobile hamburger */}
<button
className="md:hidden flex flex-col gap-1.5 p-2"
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Toggle menu"
>
<span
className="block w-5 h-px transition-all duration-200"
style={{
background: "#ffffff",
transform: menuOpen ? "rotate(45deg) translate(2px, 2px)" : "none",
}}
/>
<span
className="block w-5 h-px transition-all duration-200"
style={{
background: "#ffffff",
opacity: menuOpen ? 0 : 1,
}}
/>
<span
className="block w-5 h-px transition-all duration-200"
style={{
background: "#ffffff",
transform: menuOpen ? "rotate(-45deg) translate(2px, -2px)" : "none",
}}
/>
</button>
</div>
{/* Mobile menu */}
{menuOpen && (
<div
className="md:hidden border-t px-6 py-4 flex flex-col gap-4"
style={{ background: "#0a0a0a", borderColor: "#222222" }}
>
{links.map(({ href, label }) => (
<Link
key={href}
href={href}
className="text-sm"
style={{ color: pathname === href ? "#ffffff" : "#888888" }}
onClick={() => setMenuOpen(false)}
>
{label}
</Link>
))}
</div>
)}
</header>
);
}

43
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "website", "name": "website",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"framer-motion": "^12.38.0",
"next": "16.2.2", "next": "16.2.2",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4"
@ -3621,6 +3622,33 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/framer-motion": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
"integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.38.0",
"motion-utils": "^12.36.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -4936,6 +4964,21 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/motion-dom": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
"integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.36.0"
}
},
"node_modules/motion-utils": {
"version": "12.36.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
"integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
"license": "MIT"
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",

View File

@ -9,6 +9,7 @@
"lint": "eslint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"framer-motion": "^12.38.0",
"next": "16.2.2", "next": "16.2.2",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4"