AI Systems Playground Portfolio

Build an accessible AI portfolio with Next.js, Motion, and Vercel.

Introduction

30 Second Summary

Hiring teams often meet your work through a link before they meet you. A page filled with unsupported claims gives them little reason to trust what you can build.

In this project, you will build a single-page AI systems portfolio with Next.js, React, Tailwind CSS, and Motion. The finished site will run at a public Vercel URL backed by GitHub.

What You'll Build

Your public portfolio guides a hiring manager from an animated introduction through concrete project evidence to a clear contact link.

By the end of this project, you'll have:

  • A responsive hero that states your position for Applied AI and AI Engineering work beside an animated system-status panel.
  • Three evidence-led project cards that show a problem, approach, technology set, and result. A capability map makes your wider AI skills easy to scan.
  • An accessible public experience with purposeful interaction feedback. Reduced-motion settings keep every section readable. A shareable Vercel URL gives visitors a direct contact path.
  • Secret Mission: Replace one sample card with an evidence-based case study from your own work.

Are there any prerequisites?

You need a Windows computer with Node.js, npm, Git, Claude Code, and a code editor already available. You also need personal GitHub and Vercel accounts.

Before We Start

Your portfolio needs a clear professional message before any design or code takes shape. This step helps you commit to the Applied AI or AI Engineering opportunity you want to attract.

Set Up the Portfolio Workspace

Your professional positioning now has a clear target. It needs a predictable local workspace before code can turn it into a portfolio.

A version-pinned workspace keeps Claude Code from choosing outdated packages or an unfamiliar structure. In this step, you will prepare a minimal Next.js project that you can inspect before building the full page.

In this step, get ready to:
  • Verify Node.js, npm, Git, and Claude Code.
  • Create the portfolio workspace with pinned dependencies.
  • Generate a minimal App Router project with Claude Code.
Verify the local tools

The project depends on Node.js version 20.9.0 or newer. You will also confirm that Git and the other command-line tools respond before creating any files.

  • Press the Windows key to open the search bar.
  • Type PowerShell into the search bar.
  • Press Enter to open Windows PowerShell.

Before you check, which tool do you expect to have a minimum version requirement?

  • Check all four tools by running these commands:
node --version
npm --version
git --version
claude --version

What do these commands check?

  • The node command confirms that your JavaScript runtime meets the Next.js requirement.
  • The npm command confirms that the package manager is available.
  • The git command confirms that source control is available for publishing later.
  • The claude command confirms that Claude Code can run from your terminal.

✔️ I see the required Node.js version

Your Node.js result shows 20.9.0 or a newer version. That clears the main compatibility check for this workspace.

  • Keep this PowerShell window open for the workspace commands.

ⓧ I see an older Node.js version

Your installed Node.js version is below 20.9.0. Update it before installing the project dependencies.

  • Use the official Node.js download page to install a version at or above 20.9.0.
  • Close the current PowerShell window after the installation finishes.
  • Start a fresh PowerShell session through the Windows search bar.
  • Run the four version checks above again.

ⓧ The Node.js command is missing

Windows cannot find the Node.js executable yet. Install Node.js before continuing with the workspace.

  • Use the official Node.js download page to install a version at or above 20.9.0.
  • Close the current PowerShell window after the installation finishes.
  • Start a fresh PowerShell session through the Windows search bar.
  • Run the four version checks above again.

The npm, git, and claude checks should each print version information. This proves that every local tool responds from PowerShell.

Is another tool missing?

Close PowerShell after installing or updating the missing tool. Start a fresh session so Windows can load the updated command path.

Share the command that failed with the output it displayed when you ask for help diagnosing the missing tool.

Create and pin the workspace

The workspace belongs on your Desktop so you can find it easily. Initializing it manually also keeps every generated file visible for review.

  • Create the ai-systems-portfolio folder on your Desktop by running these commands:
Set-Location ~/Desktop
mkdir ai-systems-portfolio
cd ai-systems-portfolio
npm init -y

What did these commands create?

  • The first command moves PowerShell to your Desktop.
  • The mkdir command creates the ai-systems-portfolio folder.
  • The cd command makes that folder your current location.
  • The npm init -y command creates the initial package.json file.
  • Confirm your current folder by running this command:
Get-Location

What should you see?

The path should end with Desktop\ai-systems-portfolio. The earlier initialization output should also identify the new package.json file.

The runtime dependency group gives the app its framework, rendering layer, and animation library. Exact versions keep your workspace aligned with the project code.

  • Install the pinned runtime dependencies by running this command:
npm install next@16.4.2 react@19.3.1 react-dom@19.3.1 motion@14.6.0

What did you install?

  • Next.js 16.4.2 provides the App Router framework.
  • React 19.3.1 provides the component model.
  • React DOM 19.3.1 renders those components in the browser.
  • Motion 14.6.0 provides the animation tools used later in the portfolio.
  • Wait for PowerShell to return to its command prompt.

The completed install reports added packages. Your package.json now contains the runtime dependency group.

Did the runtime install fail?

Check that Get-Location ends with ai-systems-portfolio. A network or package download problem can also interrupt the installation.

Include the final lines from PowerShell when you ask for help with the runtime installation.

The development dependency group adds styling, type checking, and editor support. These packages shape how you build the app without becoming part of its browser runtime.

  • Install the pinned development dependencies by running this command:
npm install --save-dev tailwindcss@4.3.3 @tailwindcss/postcss@4.3.3 typescript@7.0.2 @types/node@26.6.5 @types/react@19.3.0 @types/react-dom@19.3.0

What completes the toolchain?

  • Tailwind CSS 4.3.3 provides utility-first styling.
  • The @tailwindcss/postcss package at 4.3.3 connects Tailwind CSS to the stylesheet build.
  • TypeScript 7.0.2 checks the project code.
  • The three type packages describe the Node.js, React, and React DOM APIs to TypeScript.
  • Wait for PowerShell to return to its command prompt.

The second completed install adds the development dependency group to package.json. Your pinned package set is now ready for the starter files.

Did the development install stop?

Keep every package version unchanged when retrying. Check your connection if PowerShell reports that it could not download a package.

Share the final output when you ask for help with the development dependencies.

Generate and run the starter page

The dependencies are ready, but the folder still needs a minimal App Router structure. A constrained prompt gives Claude Code clear boundaries before it creates those files.

  • Start Claude Code inside ai-systems-portfolio by running this command:
claude

What does this command do?

The command starts an interactive Claude Code session in your current folder. This folder boundary gives the assistant the local package.json to inspect before editing.

  • Complete the on-screen authentication flow if Claude Code asks for access.
  • Review each file-edit request before approving it.
  • Paste this complete prompt into the Claude Code session:
First inspect package.json and report the dependencies you found. Do not install, remove, or upgrade any package. Set up a minimal Next.js App Router project in the current folder for Windows development. Create app/layout.tsx, app/page.tsx, app/globals.css, postcss.config.mjs, tsconfig.json, and .gitignore. Update package.json so the scripts are exactly dev: next dev, build: next build, and start: next start while preserving the pinned dependencies. Configure Tailwind CSS through @tailwindcss/postcss and import Tailwind in app/globals.css. The starter page should display “AI Systems Playground: workspace ready.” Keep this first page static and do not import Motion yet. After editing, summarize every file you changed and explain what each one does. Do not run destructive commands.

How does the prompt control the setup?

  • The inspection request makes Claude Code read the installed dependency set before editing.
  • The file list limits the generated structure to the minimal App Router workspace.
  • The package restriction protects every pinned version from silent upgrades.
  • The static-page instruction keeps Motion out of the starter page until the interaction step.
  • Wait for Claude Code to finish editing the project files.
  • Review Claude Code's summary of every changed file.

The summary should identify .gitignore, package.json, postcss.config.mjs, tsconfig.json, app/layout.tsx, app/page.tsx, and app/globals.css.

Did Claude Code change the package set?

Stop before running the app if the summary mentions an added, removed, or upgraded dependency. Ask Claude Code to restore the dependency versions from the original prompt without installing anything.

Include the reported file changes when you ask for help checking the generated workspace.

Claude Code occupies the first terminal session while you review its work. A second PowerShell window lets you run the development server without closing that session.

  • Press the Windows key to open the search bar.
  • Type PowerShell into the search bar.
  • Press Enter to open a second PowerShell window.
  • Return to the portfolio folder by running this command:
Set-Location ~/Desktop/ai-systems-portfolio

Why use a second terminal?

This command points the new PowerShell session at the same project folder. The separate session can remain occupied by the development server while Claude Code stays available in the first window.

Before you start the server, what message do you expect the first browser page to display?

  • Start the Next.js development server by running this command:
npm run dev

What does the development command do?

The npm run dev command starts the Next.js development server. Keep this PowerShell window running while you view the local page.

  • Press the Windows key to open the search bar.
  • Type the name of your installed browser into the search bar.
  • Press Enter to open the browser.
  • Press Ctrl+L to focus the address bar.
  • Paste http://localhost:3000 into the address bar.
  • Press Enter to load the local page.

You should see AI Systems Playground: workspace ready. in the browser. The page proves that the generated files, pinned dependencies, and development server work together.

That is the workspace foundation complete. You now have a local Next.js page that is ready to become the full portfolio.

Can’t see the workspace-ready page?

  • Check that the PowerShell window running the server remains open.
  • Confirm that the terminal path ends with ai-systems-portfolio.
  • Compare app/page.tsx with the full-code reference below.

Share the browser behavior with the terminal output when you ask for help opening the local starter page.

✔️ Awesome, I've got everything!

Your starter page is running with the complete workspace file set. Keep the development server active for the next step.

ⓧ I'd like to double check the full code

.gitignore:

node_modules/
.next/
out/
.vercel/
*.log
.DS_Store

app/globals.css:

@import "tailwindcss";

body { margin: 0; }

app/layout.tsx:

import "./globals.css";

export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
  return <html lang="en"><body>{children}</body></html>;
}

app/page.tsx:

export default function Home() {
  return <main>AI Systems Playground: workspace ready.</main>;
}

package.json:

{"name":"ai-systems-portfolio","version":"1.0.0","private":true,"scripts":{"dev":"next dev","build":"next build","start":"next start"},"dependencies":{"motion":"14.6.0","next":"16.4.2","react":"19.3.1","react-dom":"19.3.1"},"devDependencies":{"@tailwindcss/postcss":"4.3.3","@types/node":"26.6.5","@types/react":"19.3.0","@types/react-dom":"19.3.0","tailwindcss":"4.3.3","typescript":"7.0.2"}}

postcss.config.mjs:

const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;

tsconfig.json:

{"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"}]},"include":["next-env.d.ts","**/*.ts","**/*.tsx",".next/types/**/*.ts"],"exclude":["node_modules"]}

Your version-pinned workspace is live. Next, you will turn this starter message into the static AI systems portfolio.

Build the Static Portfolio

Your local Next.js starter proves the workspace can render a page. It still needs the content hierarchy that presents you as an Applied AI builder.

A strong portfolio needs readable evidence before it needs visual effects. In this step, you will use Claude Code to plan a static page before building its responsive layout with Tailwind CSS.

In this step, get ready to:
  • Ask Claude Code to inspect the starter files before proposing a page structure.
  • Build the static hero, system-status visual, project cards, capability map, contact panel, and footer.
  • Test the responsive layout before experiencing its deliberate interaction limitation.
Plan the page before editing

An inspect-first workflow keeps the existing project structure visible. It also gives you a chance to review the proposed hierarchy before Claude Code changes any files.

  • Return to the Claude Code session from the workspace step.
  • Give Claude Code a planning-only task by pasting this prompt:
Inspect app/page.tsx, app/globals.css, and app/layout.tsx before changing anything. In no more than five bullets, propose how to turn the starter into a single-page “AI systems lab meets editorial portfolio.” Preserve the existing package versions. The required sections are navigation, hero with a CSS-only AI system-status visual, selected projects, capability map, and contact. Use semantic HTML, strong focus states, responsive Tailwind classes, and no external images or icon packages. Do not edit yet. Wait for my approval.

Why inspect before editing?

The prompt separates planning from implementation. Claude Code must explain its intended structure while leaving your starter files untouched.

The constraints also protect the pinned dependencies and keep the visual self-contained. You stay in control of the first major edit.

  • Submit the prompt by pressing Enter.
  • Count the proposal to confirm it contains no more than five bullets.
  • Check that the proposal covers navigation, the hero, selected projects, the capability map, and contact.
  • Confirm that Claude Code is waiting for approval without changing a file.

You now have a short implementation plan that accounts for every required content area. Your starter page remains unchanged.

Did Claude Code edit the files?

Ask Claude Code to restore the three inspected files to their pre-plan state before continuing. Repeat the planning prompt after the files match the starter versions.

Use this if you need help identifying an unexpected change: Help me compare Claude Code's edits with my original starter files.

Build the static portfolio

The approved plan now becomes a complete static page. Semantic sections provide the structure while CSS creates the editorial visual system and the system-status display.

  • Approve the proposed plan in Claude Code.
  • Give Claude Code the implementation constraints by pasting this prompt:
Implement the plan now. Create a polished but fully static version first. Use regular HTML elements, not Motion components, and do not add “use client” yet. Use the sample project data and wording from the agreed design, keep the system visual CSS-only, and make the page responsive from mobile through desktop. Use Tailwind for layout and app/globals.css for the background grid, signal visual, color tokens, selection color, and focus styling. Update app/layout.tsx with the title “AI Systems Playground” and a short description. Do not install packages. When finished, run the production build, fix only errors caused by your changes, and list the exact visual checks I should make in the browser.

What does this prompt control?

  • The static-only constraint keeps the page focused on content, layout, and responsive behavior.
  • The dependency constraint prevents Claude Code from changing the pinned workspace.
  • The production-build check catches rendering or type problems caused by the new files.
  • The final report gives you concrete browser checks instead of a vague completion message.

The production build can take a little time while Next.js prepares the application. A short pause in the terminal is expected during this check.

  • Submit the implementation prompt by pressing Enter.
  • Approve any Claude Code request to edit the three project files.
  • Wait for Claude Code to finish its production-build check.
  • Review the reported file summary to confirm that app/layout.tsx, app/page.tsx, and app/globals.css were updated.
  • Confirm that Claude Code reports a successful production build.
  • Switch back to the browser at http://localhost:3000.
  • Refresh the page.

You should see a dark portfolio hero beside a circular AI system-status display. The starter message has been replaced by the full editorial layout.

That is the first full-page milestone. Your workspace now communicates a professional AI systems position through visible content and structure.

Still seeing the workspace message?

  • Confirm that Claude Code edited app/page.tsx inside the ai-systems-portfolio folder.
  • Check that the development server from the workspace step is still running.
  • Refresh the browser after Claude Code finishes saving the files.

Use this if the page still shows the starter content: Help me find why my updated Next.js page is not appearing.

✔️ Awesome, I've got everything!

Your three generated files are saved. Continue to the responsive and interaction checks below.

ⓧ I'd like to double check the full code

  • Compare app/layout.tsx with this complete static version:
import "./globals.css";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <head>
        <title>AI Systems Playground</title>
        <meta
          name="description"
          content="Applied AI and AI Engineering portfolio"
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

What should this file contain?

The root layout loads the global stylesheet and identifies the page language. Its title and description define the portfolio metadata.

  • Compare app/page.tsx with this complete static version:
const projects = [
  {
    index: "01",
    title: "Evidence-first RAG assistant",
    problem: "Teams could find documents, but they could not trust an answer without seeing its source.",
    approach: "Designed retrieval, answer grounding, and citation checks as one observable workflow.",
    stack: ["RAG", "Evaluation", "Observability"],
    result: "Every answer exposes the evidence used to produce it.",
    accent: "cyan",
  },
  {
    index: "02",
    title: "Support triage agent",
    problem: "Incoming requests arrived faster than a small operations team could classify them.",
    approach: "Combined structured outputs with human review for ambiguous or high-risk cases.",
    stack: ["Agents", "Guardrails", "Human review"],
    result: "Routine requests follow a visible path while uncertain cases escalate.",
    accent: "violet",
  },
  {
    index: "03",
    title: "Prompt evaluation harness",
    problem: "Prompt changes were judged from a few memorable examples instead of repeatable tests.",
    approach: "Turned representative cases and scoring criteria into a lightweight regression loop.",
    stack: ["Evals", "Test design", "Iteration"],
    result: "Changes can be compared against the same acceptance criteria.",
    accent: "lime",
  },
];

const capabilities = [
  "AI product engineering",
  "Retrieval-augmented generation",
  "Agent workflows",
  "Evaluation design",
  "Human-in-the-loop systems",
  "Production observability",
];

export default function Home() {
  return (
    <div className="site-shell">
      <div className="grid-overlay" aria-hidden="true" />

      <header className="relative z-20 mx-auto flex w-full max-w-6xl items-center justify-between px-5 py-6 md:px-8">
        <a className="brand-mark focus-ring" href="#top" aria-label="Back to top">
          AS<span>/</span>01
        </a>
        <nav aria-label="Primary navigation">
          <ul className="flex items-center gap-5 text-sm text-slate-300 md:gap-8">
            <li><a className="nav-link focus-ring" href="#work">Work</a></li>
            <li><a className="nav-link focus-ring" href="#capabilities">Capabilities</a></li>
            <li><a className="nav-link focus-ring" href="#contact">Contact</a></li>
          </ul>
        </nav>
      </header>

      <main id="top" className="relative z-10">
        <section className="mx-auto grid min-h-[78vh] w-full max-w-6xl items-center gap-12 px-5 py-16 md:px-8 lg:grid-cols-[1.1fr_0.9fr] lg:py-24">
          <div>
            <p className="eyebrow">Applied AI / AI Engineering</p>
            <h1 className="mt-5 max-w-3xl text-5xl font-semibold leading-[0.94] tracking-[-0.06em] text-white sm:text-6xl lg:text-8xl">
              I turn AI ideas into <span className="text-gradient">systems people can trust.</span>
            </h1>
            <p className="mt-7 max-w-xl text-base leading-7 text-slate-300 sm:text-lg">
              I design useful AI products around evidence, evaluation, and human judgment, then make the system understandable enough to improve.
            </p>
            <div className="mt-9 flex flex-wrap gap-3">
              <a className="primary-button focus-ring" href="#work">
                View selected work <span aria-hidden="true">↓</span>
              </a>
              <a className="secondary-button focus-ring" href="#contact">Start a conversation</a>
            </div>
          </div>

          <div
            className="signal-card"
            role="img"
            aria-label="Decorative AI system status visualization"
          >
            <div className="flex items-center justify-between text-xs uppercase tracking-[0.22em] text-slate-400">
              <span>System status</span>
              <span className="status-live"><i /> operational</span>
            </div>
            <div className="signal-stage" aria-hidden="true">
              <div className="signal-core">AI</div>
              <div className="signal-orbit orbit-one"><span /></div>
              <div className="signal-orbit orbit-two"><span /></div>
              <div className="signal-line line-one" />
              <div className="signal-line line-two" />
            </div>
            <div className="grid grid-cols-3 gap-2 border-t border-white/10 pt-4 text-center">
              {[
                ["Grounding", "active"],
                ["Evaluation", "ready"],
                ["Review", "human"],
              ].map(([label, value]) => (
                <div key={label}>
                  <p className="text-[10px] uppercase tracking-wider text-slate-500">{label}</p>
                  <p className="mt-1 text-sm text-slate-200">{value}</p>
                </div>
              ))}
            </div>
          </div>
        </section>

        <section
          id="work"
          className="mx-auto w-full max-w-6xl scroll-mt-8 px-5 py-20 md:px-8"
        >
          <div className="section-heading">
            <p className="eyebrow">Selected systems</p>
            <h2>Proof over promises.</h2>
            <p>Each case starts with a real constraint and ends with an observable result.</p>
          </div>

          <div className="mt-10 grid gap-4 lg:grid-cols-3">
            {projects.map((project) => (
              <article
                key={project.title}
                className={`project-card accent-${project.accent}`}
              >
                <div className="flex items-center justify-between">
                  <span className="project-index">{project.index}</span>
                  <span className="project-dot" aria-hidden="true" />
                </div>
                <h3>{project.title}</h3>
                <dl className="mt-6 space-y-4">
                  <div><dt>Problem</dt><dd>{project.problem}</dd></div>
                  <div><dt>Approach</dt><dd>{project.approach}</dd></div>
                  <div><dt>Result</dt><dd>{project.result}</dd></div>
                </dl>
                <ul className="mt-6 flex flex-wrap gap-2" aria-label="Technologies and concepts">
                  {project.stack.map((item) => <li key={item}>{item}</li>)}
                </ul>
                <a className="card-link focus-ring" href="#contact">Discuss this build <span aria-hidden="true">↗</span></a>
              </article>
            ))}
          </div>
        </section>

        <section
          id="capabilities"
          className="mx-auto grid w-full max-w-6xl scroll-mt-8 gap-10 px-5 py-20 md:px-8 lg:grid-cols-[0.8fr_1.2fr]"
        >
          <div className="section-heading">
            <p className="eyebrow">Capability map</p>
            <h2>Built around the whole system.</h2>
            <p>Models matter, but reliable outcomes also depend on product decisions, evaluation, and operations.</p>
          </div>
          <div className="capability-grid">
            {capabilities.map((capability, index) => (
              <div key={capability} className="capability-item">
                <span>0{index + 1}</span>
                <p>{capability}</p>
              </div>
            ))}
          </div>
        </section>

        <section
          id="contact"
          className="mx-auto w-full max-w-6xl scroll-mt-8 px-5 py-24 md:px-8"
        >
          <div className="contact-panel">
            <p className="eyebrow">Open channel</p>
            <h2>Have an AI system that needs to become real?</h2>
            <p>Let’s talk about the constraint, the evidence, and the smallest useful version worth shipping.</p>
            <a
              className="primary-button focus-ring mt-8"
              href="mailto:your-email@example.com"
            >
              your-email@example.com <span aria-hidden="true">↗</span>
            </a>
          </div>
        </section>
      </main>

      <footer className="relative z-10 mx-auto flex w-full max-w-6xl flex-col gap-2 border-t border-white/10 px-5 py-8 text-sm text-slate-500 md:flex-row md:items-center md:justify-between md:px-8">
        <p>AI Systems Playground</p>
        <p>Built with Next.js, Tailwind CSS, and Motion.</p>
      </footer>
    </div>
  );
}

What should this file do?

  • The two data arrays hold three sample projects and six AI systems capabilities.
  • The semantic page structure connects navigation, evidence, capabilities, contact, and footer content.
  • Regular HTML elements keep the portfolio static during this step.
  • The file contains no client directive or Motion import.
  • Compare app/globals.css with this complete stylesheet:
@import "tailwindcss";

:root {
  --background: #07080c;
  --panel: #10131b;
  --cyan: #47e8ff;
  --violet: #a78bfa;
  --lime: #b9f267;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--background);
  color: #f8fafc;
  font-family: Arial, Helvetica, sans-serif;
}

::selection {
  background: var(--cyan);
  color: #07080c;
}

.site-shell {
  min-height: 100vh;
  overflow: hidden;
  background:
    radial-gradient(circle at 12% 8%, rgba(71, 232, 255, 0.1), transparent 26rem),
    radial-gradient(circle at 88% 22%, rgba(167, 139, 250, 0.1), transparent 24rem),
    #07080c;
}

.grid-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0.12;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.12) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: linear-gradient(to bottom, black, transparent 88%);
}

.focus-ring:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 4px;
}

.brand-mark {
  color: white;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.2em;
}

.brand-mark span,
.text-gradient {
  color: var(--cyan);
}

.text-gradient {
  background: linear-gradient(100deg, var(--cyan), var(--violet) 52%, var(--lime));
  background-clip: text;
  color: transparent;
}

.nav-link {
  transition: color 160ms ease;
}

.nav-link:hover {
  color: white;
}

.eyebrow {
  color: var(--cyan);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.primary-button,
.secondary-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  min-height: 3rem;
  border-radius: 999px;
  padding: 0.75rem 1.2rem;
  font-size: 0.9rem;
  font-weight: 800;
}

.primary-button {
  background: var(--cyan);
  color: #071014;
}

.secondary-button {
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: white;
}

.signal-card,
.project-card,
.contact-panel {
  border: 1px solid rgba(255, 255, 255, 0.11);
  background: linear-gradient(145deg, rgba(18, 22, 33, 0.94), rgba(9, 11, 17, 0.94));
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.28);
}

.signal-card {
  min-height: 30rem;
  border-radius: 2rem;
  padding: 1.25rem;
}

.status-live {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--lime);
}

.status-live i {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 1rem currentColor;
}

.signal-stage {
  position: relative;
  display: grid;
  min-height: 22rem;
  place-items: center;
}

.signal-core {
  position: relative;
  z-index: 3;
  display: grid;
  width: 6.5rem;
  height: 6.5rem;
  place-items: center;
  border: 1px solid rgba(71, 232, 255, 0.5);
  border-radius: 50%;
  background: rgba(71, 232, 255, 0.08);
  color: var(--cyan);
  font-size: 1.5rem;
  font-weight: 900;
  box-shadow: 0 0 4rem rgba(71, 232, 255, 0.14);
}

.signal-orbit {
  position: absolute;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 50%;
  animation: orbit-spin 12s linear infinite;
}

.signal-orbit span {
  position: absolute;
  top: 50%;
  right: -0.32rem;
  width: 0.62rem;
  height: 0.62rem;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 1rem currentColor;
}

.orbit-one {
  width: 13rem;
  height: 13rem;
}

.orbit-two {
  width: 19rem;
  height: 19rem;
  animation-direction: reverse;
  animation-duration: 18s;
}

.orbit-two span {
  right: auto;
  left: -0.32rem;
  background: var(--violet);
}

.signal-line {
  position: absolute;
  width: 75%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(71, 232, 255, 0.4), transparent);
}

.line-one { transform: rotate(32deg); }
.line-two { transform: rotate(-32deg); }

@keyframes orbit-spin {
  to { transform: rotate(360deg); }
}

.section-heading h2,
.contact-panel h2 {
  margin-top: 0.85rem;
  max-width: 14ch;
  color: white;
  font-size: clamp(2.1rem, 5vw, 4.2rem);
  font-weight: 650;
  line-height: 1;
  letter-spacing: -0.045em;
}

.section-heading > p:last-child,
.contact-panel > p:nth-of-type(2) {
  margin-top: 1.2rem;
  max-width: 38rem;
  color: #94a3b8;
  line-height: 1.75;
}

.project-card {
  position: relative;
  display: flex;
  min-height: 34rem;
  flex-direction: column;
  border-radius: 1.5rem;
  padding: 1.5rem;
}

.project-card h3 {
  margin-top: 2rem;
  color: white;
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.035em;
}

.project-card dt {
  color: #64748b;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.project-card dd {
  margin-top: 0.35rem;
  color: #cbd5e1;
  font-size: 0.9rem;
  line-height: 1.55;
}

.project-card li {
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  padding: 0.35rem 0.6rem;
  color: #cbd5e1;
  font-size: 0.72rem;
}

.project-index {
  color: #64748b;
  font-family: monospace;
}

.project-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 1rem currentColor;
}

.accent-violet .project-dot { background: var(--violet); }
.accent-lime .project-dot { background: var(--lime); }

.card-link {
  margin-top: auto;
  padding-top: 2rem;
  color: white;
  font-size: 0.85rem;
  font-weight: 800;
}

.capability-grid {
  display: grid;
  gap: 0.75rem;
}

.capability-item {
  display: grid;
  grid-template-columns: 3rem 1fr;
  align-items: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.11);
  padding: 1rem 0;
}

.capability-item span {
  color: var(--cyan);
  font-family: monospace;
  font-size: 0.72rem;
}

.capability-item p {
  color: #e2e8f0;
  font-size: clamp(1.1rem, 3vw, 1.8rem);
  letter-spacing: -0.025em;
}

.contact-panel {
  border-radius: 2rem;
  padding: clamp(1.5rem, 5vw, 4rem);
}

@media (max-width: 520px) {
  .signal-card { min-height: 25rem; }
  .signal-stage { min-height: 17rem; transform: scale(0.78); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .signal-orbit { animation: none; }
}

What should this stylesheet control?

The stylesheet defines the dark editorial palette, background grid, focus treatment, buttons, cards, capability rows, and contact panel. It also draws the system-status visual without external image assets.

The mobile rule keeps the signal display inside a narrow screen. The reduced-motion rule disables smooth scrolling and the orbit animation when the visitor requests less motion.

Test the static experience

The page now needs to prove that its hierarchy survives different screen widths. Start with readability before testing how the interface responds to input.

  • Switch back to the portfolio in your browser.
  • Refresh http://localhost:3000.
  • Widen the browser window to a desktop-sized view.
  • Scroll from the hero to the contact section.

You should see the navigation, hero, system-status visual, three project cards, capability map, contact panel, and footer. Each area should remain readable with clear spacing.

  • Narrow the browser window to a phone-sized view.
  • Scroll through the full page again.

The hero should stack into one column. The project cards and capability rows should remain readable without horizontal clipping.

Before you test the interactions, do you think the sections and cards will respond to your pointer?

  • Hover over the first project card.
  • Press the first project card.
  • Hover over the View selected work call to action.
  • Press the View selected work call to action.

The link scrolls to the selected-work section. The sections already occupy their final positions while the cards provide no movement when hovered or pressed.

That flat response is the intended shortfall. You have proved that the content and layout work before introducing a richer interaction system.

Your static portfolio is complete and the production build succeeds. Next, you will add purposeful reveals and tactile feedback while preserving the accessible foundation.

Add Accessible Motion

Your static portfolio already presents the content clearly. The next goal is to make its reading order and interactive elements feel responsive.

The page currently has no sense of progression or tactile feedback. In this step, you will use Motion to add restrained animation while respecting each visitor's motion preferences.

In this step, get ready to:
  • Add accessible entrance animations and scroll reveals.
  • Give project cards and primary links restrained interaction feedback.
  • Verify the production build and reduced-motion behavior.
Add a restrained motion layer

Motion should explain hierarchy and reinforce interaction. Small opacity shifts and short transforms provide that feedback without competing with your project evidence.

Why Motion and CSS share the work

Motion connects animation to React components and viewport state. This makes it suitable for entrances and interaction feedback.

CSS keeps control of the decorative orbit. This division keeps JavaScript motion focused on meaningful interface behavior.

Claude Code may request permission to edit app/page.tsx or run the production build. Those requests are expected for this task.

  • Return to the Claude Code session for your ai-systems-portfolio folder.
  • Give Claude Code the constrained animation brief by pasting this prompt:
Inspect the current page and explain which elements should move and which should remain static. Then implement a restrained Motion pass without changing the content hierarchy or installing packages. Add “use client” as the first statement in app/page.tsx and import only motion and MotionConfig from “motion/react”. Wrap the page in MotionConfig with reducedMotion set to “user”. Add a short hero entrance, one-time scroll reveals for major sections, and subtle project-card hover and press feedback. Keep the decorative CSS orbit animation, but ensure the existing prefers-reduced-motion CSS disables it. Prefer opacity and small y or scale changes. Do not animate every element and do not use infinite JavaScript animation. Run the production build and fix any TypeScript or build errors before reporting back.

What does this prompt change?

  • The "use client" directive turns app/page.tsx into an interactive client entry point.
  • The MotionConfig wrapper applies the visitor's reduced-motion preference to every Motion component inside the page.
  • The viewport settings reveal each major section once. This supports the page's reading order without replaying effects during every scroll.
  • The hover and press targets give cards and primary links tactile feedback.
  • Review Claude Code's explanation of the moving elements.
  • Approve the edit to app/page.tsx.
  • Approve the production build command if Claude Code requests permission.
  • Confirm Claude Code reports a successful production build.

Before you refresh, which part of the hero do you expect to move first?

  • Switch back to the browser running http://localhost:3000.
  • Refresh the portfolio.

You will see the hero text and system-status card enter with small vertical movements. The CSS orbit continues rotating inside the signal visual.

That first motion pass is working. Your portfolio now communicates arrival without delaying access to its content.

✔️ Awesome, I've got everything!

Great. Confirm that Claude Code saved app/page.tsx before you review the motion system.

ⓧ I'd like to double check the full code

"use client";

import { MotionConfig, motion } from "motion/react";

const projects = [
  {
    index: "01",
    title: "Evidence-first RAG assistant",
    problem: "Teams could find documents, but they could not trust an answer without seeing its source.",
    approach: "Designed retrieval, answer grounding, and citation checks as one observable workflow.",
    stack: ["RAG", "Evaluation", "Observability"],
    result: "Every answer exposes the evidence used to produce it.",
    accent: "cyan",
  },
  {
    index: "02",
    title: "Support triage agent",
    problem: "Incoming requests arrived faster than a small operations team could classify them.",
    approach: "Combined structured outputs with human review for ambiguous or high-risk cases.",
    stack: ["Agents", "Guardrails", "Human review"],
    result: "Routine requests follow a visible path while uncertain cases escalate.",
    accent: "violet",
  },
  {
    index: "03",
    title: "Prompt evaluation harness",
    problem: "Prompt changes were judged from a few memorable examples instead of repeatable tests.",
    approach: "Turned representative cases and scoring criteria into a lightweight regression loop.",
    stack: ["Evals", "Test design", "Iteration"],
    result: "Changes can be compared against the same acceptance criteria.",
    accent: "lime",
  },
];

const capabilities = [
  "AI product engineering",
  "Retrieval-augmented generation",
  "Agent workflows",
  "Evaluation design",
  "Human-in-the-loop systems",
  "Production observability",
];

export default function Home() {
  return (
    <MotionConfig reducedMotion="user">
      <div className="site-shell">
        <div className="grid-overlay" aria-hidden="true" />

        <header className="relative z-20 mx-auto flex w-full max-w-6xl items-center justify-between px-5 py-6 md:px-8">
          <a className="brand-mark focus-ring" href="#top" aria-label="Back to top">
            AS<span>/</span>01
          </a>
          <nav aria-label="Primary navigation">
            <ul className="flex items-center gap-5 text-sm text-slate-300 md:gap-8">
              <li><a className="nav-link focus-ring" href="#work">Work</a></li>
              <li><a className="nav-link focus-ring" href="#capabilities">Capabilities</a></li>
              <li><a className="nav-link focus-ring" href="#contact">Contact</a></li>
            </ul>
          </nav>
        </header>

        <main id="top" className="relative z-10">
          <section className="mx-auto grid min-h-[78vh] w-full max-w-6xl items-center gap-12 px-5 py-16 md:px-8 lg:grid-cols-[1.1fr_0.9fr] lg:py-24">
            <motion.div
              initial={{ opacity: 0, y: 24 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.6 }}
            >
              <p className="eyebrow">Applied AI / AI Engineering</p>
              <h1 className="mt-5 max-w-3xl text-5xl font-semibold leading-[0.94] tracking-[-0.06em] text-white sm:text-6xl lg:text-8xl">
                I turn AI ideas into <span className="text-gradient">systems people can trust.</span>
              </h1>
              <p className="mt-7 max-w-xl text-base leading-7 text-slate-300 sm:text-lg">
                I design useful AI products around evidence, evaluation, and human judgment, then make the system understandable enough to improve.
              </p>
              <div className="mt-9 flex flex-wrap gap-3">
                <motion.a
                  className="primary-button focus-ring"
                  href="#work"
                  whileHover={{ y: -3 }}
                  whileTap={{ scale: 0.98 }}
                >
                  View selected work <span aria-hidden="true">↓</span>
                </motion.a>
                <a className="secondary-button focus-ring" href="#contact">Start a conversation</a>
              </div>
            </motion.div>

            <motion.div
              className="signal-card"
              initial={{ opacity: 0, y: 18 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.7 }}
              role="img"
              aria-label="Decorative AI system status visualization"
            >
              <div className="flex items-center justify-between text-xs uppercase tracking-[0.22em] text-slate-400">
                <span>System status</span>
                <span className="status-live"><i /> operational</span>
              </div>
              <div className="signal-stage" aria-hidden="true">
                <div className="signal-core">AI</div>
                <div className="signal-orbit orbit-one"><span /></div>
                <div className="signal-orbit orbit-two"><span /></div>
                <div className="signal-line line-one" />
                <div className="signal-line line-two" />
              </div>
              <div className="grid grid-cols-3 gap-2 border-t border-white/10 pt-4 text-center">
                {[
                  ["Grounding", "active"],
                  ["Evaluation", "ready"],
                  ["Review", "human"],
                ].map(([label, value]) => (
                  <div key={label}>
                    <p className="text-[10px] uppercase tracking-wider text-slate-500">{label}</p>
                    <p className="mt-1 text-sm text-slate-200">{value}</p>
                  </div>
                ))}
              </div>
            </motion.div>
          </section>

          <motion.section
            id="work"
            className="mx-auto w-full max-w-6xl scroll-mt-8 px-5 py-20 md:px-8"
            initial={{ opacity: 0, y: 26 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true, amount: 0.15 }}
            transition={{ duration: 0.55 }}
          >
            <div className="section-heading">
              <p className="eyebrow">Selected systems</p>
              <h2>Proof over promises.</h2>
              <p>Each case starts with a real constraint and ends with an observable result.</p>
            </div>

            <div className="mt-10 grid gap-4 lg:grid-cols-3">
              {projects.map((project) => (
                <motion.article
                  key={project.title}
                  className={`project-card accent-${project.accent}`}
                  whileHover={{ y: -7 }}
                  whileTap={{ scale: 0.99 }}
                  transition={{ duration: 0.2 }}
                >
                  <div className="flex items-center justify-between">
                    <span className="project-index">{project.index}</span>
                    <span className="project-dot" aria-hidden="true" />
                  </div>
                  <h3>{project.title}</h3>
                  <dl className="mt-6 space-y-4">
                    <div><dt>Problem</dt><dd>{project.problem}</dd></div>
                    <div><dt>Approach</dt><dd>{project.approach}</dd></div>
                    <div><dt>Result</dt><dd>{project.result}</dd></div>
                  </dl>
                  <ul className="mt-6 flex flex-wrap gap-2" aria-label="Technologies and concepts">
                    {project.stack.map((item) => <li key={item}>{item}</li>)}
                  </ul>
                  <a className="card-link focus-ring" href="#contact">Discuss this build <span aria-hidden="true">↗</span></a>
                </motion.article>
              ))}
            </div>
          </motion.section>

          <motion.section
            id="capabilities"
            className="mx-auto grid w-full max-w-6xl scroll-mt-8 gap-10 px-5 py-20 md:px-8 lg:grid-cols-[0.8fr_1.2fr]"
            initial={{ opacity: 0, y: 26 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true, amount: 0.2 }}
            transition={{ duration: 0.55 }}
          >
            <div className="section-heading">
              <p className="eyebrow">Capability map</p>
              <h2>Built around the whole system.</h2>
              <p>Models matter, but reliable outcomes also depend on product decisions, evaluation, and operations.</p>
            </div>
            <div className="capability-grid">
              {capabilities.map((capability, index) => (
                <div key={capability} className="capability-item">
                  <span>0{index + 1}</span>
                  <p>{capability}</p>
                </div>
              ))}
            </div>
          </motion.section>

          <motion.section
            id="contact"
            className="mx-auto w-full max-w-6xl scroll-mt-8 px-5 py-24 md:px-8"
            initial={{ opacity: 0, y: 26 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true, amount: 0.3 }}
            transition={{ duration: 0.55 }}
          >
            <div className="contact-panel">
              <p className="eyebrow">Open channel</p>
              <h2>Have an AI system that needs to become real?</h2>
              <p>Let’s talk about the constraint, the evidence, and the smallest useful version worth shipping.</p>
              <motion.a
                className="primary-button focus-ring mt-8"
                href="mailto:your-email@example.com"
                whileHover={{ y: -3 }}
                whileTap={{ scale: 0.98 }}
              >
                your-email@example.com <span aria-hidden="true">↗</span>
              </motion.a>
            </div>
          </motion.section>
        </main>

        <footer className="relative z-10 mx-auto flex w-full max-w-6xl flex-col gap-2 border-t border-white/10 px-5 py-8 text-sm text-slate-500 md:flex-row md:items-center md:justify-between md:px-8">
          <p>AI Systems Playground</p>
          <p>Built with Next.js, Tailwind CSS, and Motion.</p>
        </footer>
      </div>
    </MotionConfig>
  );
}

What should you compare?

Check the client directive and Motion import first. Next, compare the hero entrances and the three section reveals.

The project data and visible wording should remain unchanged. Motion only changes how the existing interface arrives and responds.

Review motion as a design system

Each effect now has a specific job. The hero signals arrival, the section reveals establish reading order, and the interaction feedback shows which elements respond to visitors.

  • Refresh the portfolio to replay the hero entrance.
  • Scroll to the Selected systems section.
  • Continue to the Capability map section.
  • Continue to the Open channel contact section.
  • Scroll back through the same sections a second time.

Each content group reveals during its first viewport entry. The sections remain visible without replaying the entrance during the second pass.

  • Hover over one project card.
  • Press and hold one project card with the pointer.
  • Hover over the View selected work link.
  • Press and hold the View selected work link with the pointer.

The card lifts slightly during hover and compresses subtly during a press. The primary link uses a smaller lift and press scale.

  • Ask Claude Code to reduce any effect that competes with the project evidence.

Keep motion purposeful

Use movement to show arrival, reading order, or interactivity. A restrained system makes the portfolio feel responsive while keeping attention on your case studies.

Validate the production experience

The development server gives fast visual feedback. A production build confirms that the client boundary and Motion components also pass the project's final compilation checks.

Before you run the build, do you expect the new client component to compile successfully?

  • Return to PowerShell in the ai-systems-portfolio folder.
  • Validate the production version by running:
npm run build

What does this command verify?

The production build compiles the Next.js application and checks the TypeScript changes. A successful result confirms that the Motion imports and component props are valid.

You should see the production build complete successfully. That confirms the animated page is ready for release checks.

Build not completing?

  • Check that "use client" is the first statement in app/page.tsx.
  • Check that the import source is exactly motion/react.
  • Compare every opening Motion component with its matching closing component.

Help me diagnose my production build.

The last check confirms that motion remains an enhancement. The layout, links, and content must stay usable across input methods and visitor preferences.

  • Resize the browser to a narrow mobile-sized window.

You should see the hero, system-status visual, project cards, capability map, and contact panel remain readable without horizontal clipping.

  • Resize the browser to a wide desktop-sized window.
  • Press Tab repeatedly to move through the page links.

You should see a visible focus outline on each focused link. Every focused link remains readable against the dark background.

Before you test reduced motion, do you expect the content to disappear when large transforms stop?

  • Press the Windows key to open search.
  • Type Animation effects into the search field.
  • Press Enter to open the matching setting.
  • Set Animation effects to Off.
  • Return to the portfolio in your browser.
  • Refresh the page.

The large vertical and scale transforms stop while opacity changes may remain. All portfolio content stays visible, and the decorative CSS orbit stops rotating.

  • Switch back to the Windows animation setting.
  • Set Animation effects to On after completing the check.

Your portfolio now uses motion to guide attention while preserving access for visitors who reduce animation. Next up, you will prepare this build for GitHub and publish it through Vercel.

Publish the Portfolio

Your portfolio now has accessible Motion, a responsive layout, and a successful production build. The final goal is to give it a stable public URL.

A local portfolio cannot support your job search when other people cannot open it. Its source also needs a dependable home for future updates.

Claude Code checks the release before publication. Git and GitHub preserve the source history.

Vercel connects that repository to a public production deployment. This creates a portfolio link you can share without running your local development server.

In this step, get ready to:
  • Run an AI-assisted release check with a successful production build.
  • Commit the portfolio source to a personal GitHub repository.
  • Deploy the repository to a public Vercel production URL.
Run the release check

A release check catches confirmed build errors before they reach visitors. It also identifies visible sample content that you may want to personalize.

  • Return to the Claude Code session from earlier.
  • Give Claude Code the release check by submitting this prompt:
Prepare this portfolio for its first public release. Inspect the current files, run npm run build, and fix only confirmed build errors. Search the visible copy for accidental placeholders, except the intentional email placeholder your-email@example.com, and tell me exactly where I must personalize content. Confirm that node_modules, .next, and .vercel are excluded by .gitignore. Do not add dependencies, analytics, environment variables, or external services. End with a concise release checklist and the files that should be committed.

What does this prompt check?

  • The build check proves the production version still compiles after the motion changes.
  • The placeholder search separates intentional sample content from accidental unfinished copy.
  • The exclusion check confirms generated files stay outside the repository.
  • The final checklist defines the source files that belong in the first commit.

The production build can take a little while as the project compiles. A quiet pause during this process is normal.

  • Wait for Claude Code to finish the release check.
  • Review every location it identifies for personalization.
  • Confirm the release checklist includes package.json, package-lock.json, .gitignore, postcss.config.mjs, tsconfig.json, app/layout.tsx, app/page.tsx, and app/globals.css.
  • Confirm the production build completes successfully.

Build still failing?

  • Compare the first reported file with the release checklist.
  • Ask Claude Code to explain the confirmed build error before approving another edit.
  • Reject any proposed dependency upgrade because the project already uses pinned versions.

Help me resolve the remaining production build failure without changing the pinned dependencies.

That clears the release gate. Your local portfolio now has a verified production build and a defined set of files to publish.

Push the source to GitHub

A repository gives your portfolio a versioned source history. Vercel also uses this repository as the source for each deployment.

  • Create a new GitHub repository named ai-systems-portfolio under your personal account.
  • Select your personal account as the repository owner.
  • Leave the repository initialization options empty.
  • Select Create repository.

Why leave the repository empty?

Your local folder already contains the complete project. An empty remote repository avoids an extra starter commit that could create merge conflicts.

The first push can trigger GitHub authentication. Its secure sign-in flow keeps your credentials outside the project files.

  • Locate the HTTPS instructions for pushing an existing local repository on the Quick Setup page.
  • Switch back to the PowerShell tab in the local ai-systems-portfolio folder.
  • Follow every per-repository command displayed by GitHub in its listed order.
  • Complete the GitHub sign-in flow if authentication is requested.
  • Wait for the generated commands to commit the source and finish the push.
  • Refresh the GitHub repository page.

You should see the portfolio source files plus package-lock.json in the repository. You should not see node_modules, .next, or .vercel.

Source missing from GitHub?

  • Confirm the commands came from the ai-systems-portfolio repository's Quick Setup page.
  • Confirm PowerShell still points to the local ai-systems-portfolio folder.
  • Repeat GitHub's secure authentication flow if the push was denied.

Help me diagnose why my portfolio source did not appear in my GitHub repository.

Your source now has a dependable home. The repository contains the files Vercel needs without uploading generated build folders.

Deploy with Vercel

A Git-connected deployment rebuilds the portfolio from the committed source. The resulting production URL works independently from your local computer.

Publishing can feel like a billing decision. This project uses Vercel Hobby at $0/mo. for personal, non-commercial use.

Vercel may request permission to access the personal GitHub repository. Grant access only to the account and repository used for this project.

  • Sign in to the Vercel dashboard with the account connected to your personal GitHub account.
  • Select New Project.
  • Choose the personal GitHub repository named ai-systems-portfolio.
  • Grant repository access if Vercel requests permission.
  • Confirm the detected framework is Next.js.
  • Confirm the root directory points to the repository root.
  • Select Deploy.

Expect the deployment to take a few minutes while Vercel installs the packages and creates the production build.

  • Wait for the deployment to finish.
  • Open the generated production URL.
  • Record the generated production URL here: your Vercel production URL.

The hosted page now runs separately from the development server. The final check proves visitors receive the production deployment.

  • Stop the local development server by closing its PowerShell tab.

Before you test the deployment, do you think every interactive feature will still work with the local server stopped?

  • Open a private browser window.
  • Paste your Vercel production URL into the browser address bar.
  • Scroll from the hero to the contact section.
  • Hover over a project card.
  • Press a project card to test its feedback.
  • Select the your-email@example.com contact link.

You'll see the hero enter when the production page loads.

Each major content group reveals once as you scroll.

The project card lifts on hover and responds when pressed.

Selecting the contact link opens an email composer or asks your browser to choose a mail application.

Production page not working?

  • Confirm the Vercel project is connected to the personal ai-systems-portfolio repository.
  • Confirm the Vercel root directory points to the repository root.
  • Check that the latest commit is visible on the GitHub repository page.
  • Check whether Windows Animation effects are off if large transforms appear reduced.

Help me troubleshoot my Vercel deployment using the failed check and the deployment details I can see.

You made it. Your animated AI systems portfolio is now available through a public production URL without relying on your local development server.

Secret mission

Replace a Sample with Your Real AI Project

Sample cards prove that your portfolio layout works. Replace one with a real AI case study that shows the problem you tackled, your contribution, the technologies you used, and a result you can defend.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep your portfolio available, pause publishing, or remove every project resource. Your personal Vercel Hobby deployment remains $0 within the plan limits.

Resources you used:

  • Local project: The Next.js project in your ai-systems-portfolio folder. This includes the generated dependency lockfile.
  • Source repository: The personal GitHub repository named ai-systems-portfolio.
  • Public deployment: The Vercel Hobby project named ai-systems-portfolio.

Keep everything running

No action is needed. Choose this option if you want your portfolio to remain available at its public URL.

  • Keep the ai-systems-portfolio folder available for future edits.
  • Keep the personal GitHub repository as the source of your published portfolio.
  • Keep the Vercel project connected so future pushes can publish updates.

Pause - I'll come back to this later

Pause new publishing activity while keeping the current portfolio online. The deployed site does not need a local development server to remain available.

  • Stop pushing changes to the ai-systems-portfolio repository.
  • Keep the Vercel project connected to the repository.
  • Keep the local ai-systems-portfolio folder for your next editing session.
  • Return to the public portfolio URL whenever you want to confirm the current deployment is still online.

Delete - I don't want to use this again

Remove the deployment first. Delete the repository next. Finish by removing the local project folder.

Delete the Vercel project:

  • Return to your Vercel dashboard.
  • Select the ai-systems-portfolio project.
  • Go to the project settings.
  • Use the project deletion control to remove the project.
  • Complete the confirmation prompt shown by Vercel.
  • Return to your Vercel project list. You should no longer see ai-systems-portfolio.

Delete the GitHub repository:

  • Return to your personal ai-systems-portfolio repository on GitHub.
  • Go to the repository settings.
  • Use the repository deletion control to remove the repository.
  • Complete the confirmation prompt shown by GitHub.
  • Return to your personal repository list. You should no longer see ai-systems-portfolio.

Delete the local project:

  • Use Windows search to open the file browser.
  • Use the file browser search field to find the ai-systems-portfolio folder.
  • Select the matching ai-systems-portfolio folder.
  • Delete the selected folder.
  • Return to its parent folder. You should no longer see ai-systems-portfolio.

Nice Work!

Nice Work!

You did it. Your AI systems portfolio now reaches visitors through a public Vercel URL backed by a successful production build in your personal GitHub repository.

What you learned:

  • Built a responsive single-page portfolio with React components styled through Tailwind CSS. Its editorial layout stays readable at narrow widths. It stays readable at wide widths too.
  • Added declarative interaction with Motion. Hero entrances communicate arrival. Section reveals guide reading order. Card feedback signals interactivity. Reduced-motion behavior keeps the content usable.
  • Used Claude Code through a controlled workflow from inspection to release. Verified the production build before publishing. Connected your GitHub repository to Vercel for Git-based deployment.
  • Secret Mission: Replaced one sample card with an evidence-based case study from your real work. The card states a genuine problem. It names your contribution. It lists technologies you used. It closes with a defensible result.

Ready to quiz yourself?