diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..936bbc1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM node:20-alpine AS base + +# Install dependencies +FROM base AS deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --only=production + +FROM base AS builder +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Production image +FROM base AS runner +WORKDIR /app +ENV NODE_ENV=production +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7c686ff --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +services: + cadcad-website: + build: . + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.cadcad-staging.rule=Host(`cadcad-staging.jeffemmett.com`)" + - "traefik.http.services.cadcad.loadbalancer.server.port=3000" + networks: + - traefik-public + +networks: + traefik-public: + external: true diff --git a/next.config.ts b/next.config.ts index e9ffa30..68a6c64 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: "standalone", }; export default nextConfig; diff --git a/src/app/globals.css b/src/app/globals.css index a2dc41e..f1573c3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,26 +1,125 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --background: #0a0a0a; + --foreground: #ededed; + --accent: #00d4aa; + --accent-dim: #00b894; + --surface: #141414; + --surface-light: #1e1e1e; + --border: #2a2a2a; + --muted: #888888; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); + --color-accent: var(--accent); + --color-accent-dim: var(--accent-dim); + --color-surface: var(--surface); + --color-surface-light: var(--surface-light); + --color-border: var(--border); + --color-muted: var(--muted); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-sans), Arial, Helvetica, sans-serif; +} + +html { + scroll-behavior: smooth; +} + +/* Animated grid background */ +.grid-bg { + background-image: + linear-gradient(rgba(0, 212, 170, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 212, 170, 0.03) 1px, transparent 1px); + background-size: 60px 60px; +} + +/* Glow effect for hero */ +.glow { + box-shadow: 0 0 60px rgba(0, 212, 170, 0.15), 0 0 120px rgba(0, 212, 170, 0.05); +} + +/* Subtle node connection animation */ +@keyframes pulse-node { + 0%, 100% { opacity: 0.3; } + 50% { opacity: 0.8; } +} + +.animate-pulse-node { + animation: pulse-node 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} + +.animate-float { + animation: float 6s ease-in-out infinite; +} + +/* Code block styling */ +.code-block { + background: #1a1a2e; + border: 1px solid #2a2a3e; + border-radius: 8px; + font-family: var(--font-mono), 'Courier New', monospace; +} + +/* Gradient text */ +.gradient-text { + background: linear-gradient(135deg, #00d4aa, #00b4d8, #00d4aa); + background-size: 200% 200%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Card hover effect */ +.card-hover { + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; +} +.card-hover:hover { + transform: translateY(-2px); + box-shadow: 0 8px 30px rgba(0, 212, 170, 0.1); + border-color: rgba(0, 212, 170, 0.3); +} + +/* Section fade-in */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in-up { + animation: fadeInUp 0.6s ease-out forwards; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 8px; +} +::-webkit-scrollbar-track { + background: var(--background); +} +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--muted); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f7fa87e..f459f71 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,8 +13,23 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "cadCAD - Complex Adaptive Dynamics Computer-Aided Design", + description: + "An open-source Python package that assists in the processes of designing, testing and validating complex systems through simulation.", + openGraph: { + title: "cadCAD - Complex Systems Simulation", + description: + "A Python package for designing, testing and validating complex systems through simulation.", + url: "https://cadcad.org", + siteName: "cadCAD", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "cadCAD - Complex Systems Simulation", + description: + "A Python package for designing, testing and validating complex systems through simulation.", + }, }; export default function RootLayout({ diff --git a/src/app/page.tsx b/src/app/page.tsx index 295f8fd..5ec69c7 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,65 +1,25 @@ -import Image from "next/image"; +import Header from "@/components/Header"; +import Hero from "@/components/Hero"; +import WhyHowWhat from "@/components/WhyHowWhat"; +import UseCases from "@/components/UseCases"; +import CodeExample from "@/components/CodeExample"; +import GettingStarted from "@/components/GettingStarted"; +import Community from "@/components/Community"; +import Footer from "@/components/Footer"; export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
+ <> +
+
+ + + + + +
-
+