[
  {
    "path": ".dockerignore",
    "content": "node_modules\n.next\n.git\n.gitignore\n.claude\n.idea\n.vscode\n*.md\nDockerfile\ndocker-compose.yaml\n.env*\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n\n# ide\n.idea"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:22-slim AS base\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_HOME:$PATH\"\nRUN corepack enable && corepack prepare pnpm@latest-10 --activate\n\nFROM base AS deps\nWORKDIR /app\nCOPY package.json pnpm-lock.yaml ./\nRUN --mount=type=cache,id=pnpm,target=/pnpm/store \\\n    pnpm install --frozen-lockfile\n\nFROM base AS build\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nENV NEXT_TELEMETRY_DISABLED=1\nENV NODE_ENV=production\nRUN pnpm build\n\nFROM node:22-slim AS runner\nWORKDIR /app\nENV NODE_ENV=production\nENV NEXT_TELEMETRY_DISABLED=1\nENV HOSTNAME=\"0.0.0.0\"\nENV PORT=3000\n\nRUN addgroup --system --gid 1001 nodejs && \\\n    adduser --system --uid 1001 nextjs\n\nCOPY --from=build /app/public ./public\nCOPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Bartosz Jarocki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![cv](https://github.com/BartoszJarocki/cv/assets/1017620/79bdb9fc-0b20-4d2c-aafe-0526ad4a71d2)\n\n<h1>minimalist cv <a href=\"https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FBartoszJarocki%2Fcv\"><img src=\"https://vercel.com/button\" alt=\"Deploy with Vercel\" height=\"24\" align=\"right\"></a></h1>\n\n[![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org/)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue?logo=typescript)](https://www.typescriptlang.org/)\n[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4-38B2AC?logo=tailwind-css)](https://tailwindcss.com/)\n\nsimple web app that renders a minimalist CV with print-friendly layout.\n\n## getting started\n\n```bash\ngit clone https://github.com/BartoszJarocki/cv.git\ncd cv\npnpm install\npnpm dev\n# open http://localhost:3000\n# edit src/data/resume-data.ts to customize\n```\n\n## scripts\n\n```bash\npnpm dev          # start development server\npnpm build        # build for production\npnpm start        # start production server\npnpm lint         # run biome linting checks\npnpm lint:fix     # run biome linting with auto-fix\npnpm format       # check code formatting with biome\npnpm format:fix   # format code with biome\npnpm check        # run both linting and formatting checks\npnpm check:fix    # run both linting and formatting with auto-fix\n```\n\n## project structure\n\n```\nsrc/\n├── app/                # next.js app router\n│   ├── components/     # page-level components\n│   │   ├── education.tsx\n│   │   ├── header.tsx\n│   │   ├── projects.tsx\n│   │   ├── skills.tsx\n│   │   ├── summary.tsx\n│   │   └── work-experience.tsx\n│   ├── layout.tsx      # root layout with metadata\n│   └── page.tsx        # main resume page\n├── components/         # shared components\n│   ├── icons/          # social icon components\n│   └── ui/             # shadcn/ui components\n├── data/               # resume data configuration\n│   └── resume-data.ts\n└── lib/                # utilities and types\n    ├── structured-data.ts\n    ├── types.ts\n    └── utils.ts\n```\n\n## customization\n\nall resume content lives in a single file:\n\n```typescript\n// src/data/resume-data.ts\nexport const RESUME_DATA = {\n  name: \"Your Name\",\n  initials: \"YN\",\n  location: \"Your City, Country\",\n  about: \"Brief description\",\n  summary: \"Professional summary\",\n  // ... more fields\n}\n```\n\nstyling uses tailwind css — customize colors in `tailwind.config.js` and global styles in `src/app/globals.css`.\n\n## docker\n\n```bash\ndocker compose build     # build the container\ndocker compose up -d     # run the container\ndocker compose down      # stop the container\n```\n\n## license\n\nMIT\n"
  },
  {
    "path": "biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.0.6/schema.json\",\n  \"vcs\": {\n    \"enabled\": false,\n    \"clientKind\": \"git\",\n    \"useIgnoreFile\": false\n  },\n  \"files\": {\n    \"ignoreUnknown\": false,\n    \"includes\": [\"src/**/*\"]\n  },\n  \"formatter\": {\n    \"enabled\": true,\n    \"formatWithErrors\": false,\n    \"indentStyle\": \"space\",\n    \"indentWidth\": 2,\n    \"lineEnding\": \"lf\",\n    \"lineWidth\": 80,\n    \"attributePosition\": \"auto\"\n  },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": {\n      \"recommended\": true,\n      \"a11y\": {\n        \"recommended\": true\n      },\n      \"complexity\": {\n        \"recommended\": true,\n        \"noExtraBooleanCast\": \"error\",\n        \"noUselessCatch\": \"error\",\n        \"noUselessTypeConstraint\": \"error\"\n      },\n      \"correctness\": {\n        \"recommended\": true,\n        \"noChildrenProp\": \"error\",\n        \"noConstAssign\": \"error\",\n        \"noConstantCondition\": \"error\",\n        \"noEmptyCharacterClassInRegex\": \"error\",\n        \"noEmptyPattern\": \"error\",\n        \"noGlobalObjectCalls\": \"error\",\n        \"noInvalidConstructorSuper\": \"error\",\n        \"noSetterReturn\": \"error\",\n        \"noSwitchDeclarations\": \"error\",\n        \"noUndeclaredVariables\": \"error\",\n        \"noUnreachable\": \"error\",\n        \"noUnreachableSuper\": \"error\",\n        \"useIsNan\": \"error\",\n        \"useValidForDirection\": \"error\",\n        \"useYield\": \"error\"\n      },\n      \"security\": {\n        \"recommended\": true,\n        \"noDangerouslySetInnerHtml\": \"warn\"\n      },\n      \"style\": {\n        \"recommended\": true,\n        \"noImplicitBoolean\": \"error\",\n        \"noInferrableTypes\": \"error\",\n        \"noNamespace\": \"error\",\n        \"noNegationElse\": \"off\",\n        \"noNonNullAssertion\": \"warn\",\n        \"noParameterAssign\": \"error\",\n        \"noRestrictedGlobals\": \"error\",\n        \"noUselessElse\": \"error\",\n        \"useAsConstAssertion\": \"error\",\n        \"useBlockStatements\": \"off\",\n        \"useCollapsedElseIf\": \"error\",\n        \"useConst\": \"error\",\n        \"useDefaultParameterLast\": \"error\",\n        \"useEnumInitializers\": \"error\",\n        \"useExponentiationOperator\": \"error\",\n        \"useFilenamingConvention\": {\n          \"level\": \"error\",\n          \"options\": {\n            \"filenameCases\": [\"kebab-case\", \"PascalCase\"]\n          }\n        },\n        \"useForOf\": \"error\",\n        \"useFragmentSyntax\": \"error\",\n        \"useImportType\": \"error\",\n        \"useNodejsImportProtocol\": \"error\",\n        \"useNumberNamespace\": \"error\",\n        \"useSelfClosingElements\": \"error\",\n        \"useShorthandAssign\": \"error\",\n        \"useSingleVarDeclarator\": \"error\",\n        \"useTemplate\": \"error\"\n      },\n      \"suspicious\": {\n        \"recommended\": true,\n        \"noApproximativeNumericConstant\": \"error\",\n        \"noArrayIndexKey\": \"warn\",\n        \"noAssignInExpressions\": \"error\",\n        \"noAsyncPromiseExecutor\": \"error\",\n        \"noCatchAssign\": \"error\",\n        \"noClassAssign\": \"error\",\n        \"noCompareNegZero\": \"error\",\n        \"noControlCharactersInRegex\": \"error\",\n        \"noDebugger\": \"error\",\n        \"noDoubleEquals\": \"error\",\n        \"noDuplicateCase\": \"error\",\n        \"noDuplicateClassMembers\": \"error\",\n        \"noDuplicateObjectKeys\": \"error\",\n        \"noDuplicateParameters\": \"error\",\n        \"noEmptyBlockStatements\": \"error\",\n        \"noFallthroughSwitchClause\": \"error\",\n        \"noFunctionAssign\": \"error\",\n        \"noGlobalAssign\": \"error\",\n        \"noImportAssign\": \"error\",\n        \"noMisleadingCharacterClass\": \"error\",\n        \"noPrototypeBuiltins\": \"error\",\n        \"noRedeclare\": \"error\",\n        \"noShadowRestrictedNames\": \"error\",\n        \"noUnsafeNegation\": \"error\",\n        \"useGetterReturn\": \"error\"\n      }\n    }\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"jsxQuoteStyle\": \"double\",\n      \"quoteProperties\": \"asNeeded\",\n      \"trailingCommas\": \"es5\",\n      \"semicolons\": \"always\",\n      \"arrowParentheses\": \"always\",\n      \"bracketSpacing\": true,\n      \"bracketSameLine\": false,\n      \"quoteStyle\": \"double\",\n      \"attributePosition\": \"auto\"\n    }\n  },\n  \"json\": {\n    \"formatter\": {\n      \"enabled\": true,\n      \"indentStyle\": \"space\",\n      \"indentWidth\": 2,\n      \"lineEnding\": \"lf\",\n      \"lineWidth\": 80,\n      \"trailingCommas\": \"none\"\n    }\n  }\n}"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.js\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"gray\",\n    \"cssVariables\": true,\n    \"prefix\": \"\"\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}"
  },
  {
    "path": "docker-compose.yaml",
    "content": "services:\n  app:\n    build: .\n    ports:\n      - '3000:3000'\n    environment:\n      - NODE_ENV=production\n    restart: unless-stopped\n    healthcheck:\n      test: ['CMD-SHELL', 'curl -f http://localhost:3000 || exit 1']\n      interval: 30s\n      timeout: 10s\n      retries: 3\n      start_period: 10s\n    deploy:\n      resources:\n        limits:\n          cpus: '1'\n          memory: 512M\n"
  },
  {
    "path": "next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  output: 'standalone',\n\n  // Enable React strict mode for better development experience\n  reactStrictMode: true,\n\n  // Optimize images\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'avatars.githubusercontent.com',\n      },\n    ],\n    formats: ['image/avif', 'image/webp'],\n    minimumCacheTTL: 60,\n  },\n\n  // Compress output\n  compress: true,\n\n  // Headers for security and performance\n  async headers() {\n    return [\n      {\n        source: '/:path*',\n        headers: [\n          {\n            key: 'X-DNS-Prefetch-Control',\n            value: 'on'\n          },\n          {\n            key: 'X-Content-Type-Options',\n            value: 'nosniff'\n          },\n          {\n            key: 'X-Frame-Options',\n            value: 'SAMEORIGIN'\n          },\n          {\n            key: 'X-XSS-Protection',\n            value: '1; mode=block'\n          },\n          {\n            key: 'Referrer-Policy',\n            value: 'origin-when-cross-origin'\n          },\n          {\n            key: 'Permissions-Policy',\n            value: 'camera=(), microphone=(), geolocation=()'\n          }\n        ]\n      },\n      {\n        source: '/(.*).svg',\n        headers: [\n          {\n            key: 'Cache-Control',\n            value: 'public, max-age=31536000, immutable'\n          }\n        ]\n      },\n      {\n        source: '/(.*).png',\n        headers: [\n          {\n            key: 'Cache-Control',\n            value: 'public, max-age=31536000, immutable'\n          }\n        ]\n      }\n    ];\n  },\n\n\n  // Reduce bundle size by excluding source maps in production\n  productionBrowserSourceMaps: false,\n\n  // PoweredByHeader removes the X-Powered-By header\n  poweredByHeader: false,\n}\n\nmodule.exports = nextConfig"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"web-cv\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"biome lint ./src\",\n    \"lint:fix\": \"biome lint --write ./src\",\n    \"format\": \"biome format ./src\",\n    \"format:fix\": \"biome format --write ./src\",\n    \"check\": \"biome check ./src\",\n    \"check:fix\": \"biome check --write ./src\"\n  },\n  \"dependencies\": {\n    \"@radix-ui/react-dialog\": \"^1.1.6\",\n    \"@radix-ui/react-slot\": \"^1.1.2\",\n    \"@vercel/analytics\": \"^1.5.0\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.0.0\",\n    \"cmdk\": \"^1.0.0\",\n    \"geist\": \"^1.7.0\",\n    \"lucide-react\": \"^0.474.0\",\n    \"next\": \"^16.1.0\",\n    \"react\": \"^19\",\n    \"react-dom\": \"^19\",\n    \"tailwind-merge\": \"^2.2.0\",\n    \"tailwindcss-animate\": \"^1.0.7\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"2.0.6\",\n    \"@types/node\": \"^22\",\n    \"@types/react\": \"^19\",\n    \"@types/react-dom\": \"^19\",\n    \"autoprefixer\": \"^10.0.1\",\n    \"postcss\": \"^8\",\n    \"tailwindcss\": \"^3.4.0\",\n    \"typescript\": \"^5\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "public/robots.txt",
    "content": "# Robots.txt for https://cv.jarocki.me\n\nUser-agent: *\nAllow: /\nDisallow: /api/\nDisallow: /_next/\nDisallow: /graphql\n\n# Sitemap location\nSitemap: https://cv.jarocki.me/sitemap.xml\n\n# Crawl-delay for responsible crawling\nCrawl-delay: 1"
  },
  {
    "path": "src/app/components/education.tsx",
    "content": "import { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Section } from \"@/components/ui/section\";\nimport type { RESUME_DATA } from \"@/data/resume-data\";\n\ntype Education = (typeof RESUME_DATA)[\"education\"][number];\n\ninterface EducationPeriodProps {\n  start: Education[\"start\"];\n  end: Education[\"end\"];\n}\n\n/**\n * Displays the education period in a consistent format\n */\nfunction EducationPeriod({ start, end }: EducationPeriodProps) {\n  return (\n    <div\n      className=\"text-sm tabular-nums text-gray-500\"\n      title={`Period: ${start} to ${end}`}\n    >\n      {start} - {end}\n    </div>\n  );\n}\n\ninterface EducationItemProps {\n  education: Education;\n}\n\n/**\n * Individual education card component\n */\nfunction EducationItem({ education }: EducationItemProps) {\n  const { school, start, end, degree } = education;\n  const schoolId = `education-${school.toLowerCase().replace(/\\s+/g, \"-\")}`;\n\n  return (\n    <Card className=\"border-none\">\n      <CardHeader>\n        <div className=\"flex items-center justify-between gap-x-2 text-base\">\n          <h3 className=\"font-semibold leading-none\" id={schoolId}>\n            {school}\n          </h3>\n          <EducationPeriod start={start} end={end} />\n        </div>\n      </CardHeader>\n      <CardContent\n        className=\"mt-2 text-foreground/80 print:text-[12px]\"\n        aria-labelledby={schoolId}\n      >\n        {degree}\n      </CardContent>\n    </Card>\n  );\n}\n\ninterface EducationListProps {\n  education: readonly Education[];\n}\n\n/**\n * Main education section component\n * Renders a list of education experiences\n */\nexport function Education({ education }: EducationListProps) {\n  return (\n    <Section>\n      <h2 className=\"text-xl font-bold\" id=\"education-section\">\n        Education\n      </h2>\n      <div\n        className=\"space-y-4\"\n        role=\"feed\"\n        aria-labelledby=\"education-section\"\n      >\n        {education.map((item) => (\n          <article key={item.school}>\n            <EducationItem education={item} />\n          </article>\n        ))}\n      </div>\n    </Section>\n  );\n}\n"
  },
  {
    "path": "src/app/components/header.tsx",
    "content": "import { GlobeIcon, MailIcon, PhoneIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { Avatar } from \"@/components/avatar\";\nimport { GitHubIcon, LinkedInIcon } from \"@/components/icons\";\nimport { XIcon } from \"@/components/icons/x-icon\";\nimport { Button } from \"@/components/ui/button\";\nimport { RESUME_DATA } from \"@/data/resume-data\";\nimport type { IconType } from \"@/lib/types\";\n\n// Type-safe icon mapping\nconst ICON_MAP: Record<\n  IconType,\n  React.ComponentType<React.SVGProps<SVGSVGElement>>\n> = {\n  github: GitHubIcon,\n  linkedin: LinkedInIcon,\n  x: XIcon,\n  globe: GlobeIcon,\n  mail: MailIcon,\n  phone: PhoneIcon,\n} as const;\n\ninterface LocationLinkProps {\n  location: typeof RESUME_DATA.location;\n  locationLink: typeof RESUME_DATA.locationLink;\n}\n\nfunction LocationLink({ location, locationLink }: LocationLinkProps) {\n  return (\n    <p className=\"max-w-md items-center text-pretty font-mono text-xs text-foreground\">\n      <a\n        className=\"inline-flex gap-x-1.5 align-baseline leading-none hover:underline\"\n        href={locationLink}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        aria-label={`Location: ${location}`}\n      >\n        <GlobeIcon className=\"size-3\" aria-hidden=\"true\" />\n        {location}\n      </a>\n    </p>\n  );\n}\n\ninterface SocialButtonProps {\n  href: string;\n  iconType: IconType;\n  label: string;\n}\n\nfunction SocialButton({ href, iconType, label }: SocialButtonProps) {\n  const IconComponent = ICON_MAP[iconType];\n\n  return (\n    <Button className=\"size-8\" variant=\"outline\" size=\"icon\" asChild={true}>\n      <a\n        href={href}\n        aria-label={label}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n      >\n        <IconComponent className=\"size-4\" aria-hidden=\"true\" />\n      </a>\n    </Button>\n  );\n}\n\ninterface ContactButtonsProps {\n  contact: typeof RESUME_DATA.contact;\n  personalWebsiteUrl?: string;\n}\n\nfunction ContactButtons({ contact, personalWebsiteUrl }: ContactButtonsProps) {\n  return (\n    <ul\n      className=\"flex list-none gap-x-1 pt-1 font-mono text-sm text-foreground/80 print:hidden\"\n      aria-label=\"Contact links\"\n    >\n      {personalWebsiteUrl && (\n        <li>\n          <SocialButton\n            href={personalWebsiteUrl}\n            iconType=\"globe\"\n            label=\"Personal website\"\n          />\n        </li>\n      )}\n      {contact.email && (\n        <li>\n          <SocialButton\n            href={`mailto:${contact.email}`}\n            iconType=\"mail\"\n            label=\"Email\"\n          />\n        </li>\n      )}\n      {contact.tel && (\n        <li>\n          <SocialButton\n            href={`tel:${contact.tel}`}\n            iconType=\"phone\"\n            label=\"Phone\"\n          />\n        </li>\n      )}\n      {contact.social.map((social) => (\n        <li key={social.name}>\n          <SocialButton\n            href={social.url}\n            iconType={social.icon}\n            label={social.name}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\ninterface PrintContactProps {\n  contact: typeof RESUME_DATA.contact;\n  personalWebsiteUrl?: string;\n}\n\nfunction PrintContact({ contact, personalWebsiteUrl }: PrintContactProps) {\n  return (\n    <div className=\"hidden gap-x-2 font-mono text-sm text-foreground/80 print:flex print:text-[12px]\">\n      {personalWebsiteUrl && (\n        <>\n          <a\n            className=\"underline hover:text-foreground/70\"\n            href={personalWebsiteUrl}\n          >\n            {new URL(personalWebsiteUrl).hostname}\n          </a>\n          <span aria-hidden=\"true\">/</span>\n        </>\n      )}\n      {contact.email && (\n        <>\n          <a\n            className=\"underline hover:text-foreground/70\"\n            href={`mailto:${contact.email}`}\n          >\n            {contact.email}\n          </a>\n          <span aria-hidden=\"true\">/</span>\n        </>\n      )}\n      {contact.tel && (\n        <a\n          className=\"underline hover:text-foreground/70\"\n          href={`tel:${contact.tel}`}\n        >\n          {contact.tel}\n        </a>\n      )}\n    </div>\n  );\n}\n\n/**\n * Header component displaying personal information and contact details\n */\nexport function Header() {\n  return (\n    <header className=\"flex items-center justify-between\">\n      <div className=\"flex-1 space-y-1.5\">\n        <h1 className=\"text-3xl font-bold tracking-tight\" id=\"resume-name\">\n          {RESUME_DATA.name}\n        </h1>\n        <p className=\"max-w-md text-pretty font-mono text-sm text-foreground/80 print:text-[12px]\">\n          {RESUME_DATA.about}\n        </p>\n\n        <LocationLink\n          location={RESUME_DATA.location}\n          locationLink={RESUME_DATA.locationLink}\n        />\n\n        <ContactButtons\n          contact={RESUME_DATA.contact}\n          personalWebsiteUrl={RESUME_DATA.personalWebsiteUrl}\n        />\n\n        <PrintContact\n          contact={RESUME_DATA.contact}\n          personalWebsiteUrl={RESUME_DATA.personalWebsiteUrl}\n        />\n      </div>\n\n      <Avatar\n        className=\"size-28 ring-1 ring-muted\"\n        src={RESUME_DATA.avatarUrl}\n        alt={`${RESUME_DATA.name}'s profile picture`}\n        fallback={RESUME_DATA.initials}\n      />\n    </header>\n  );\n}\n"
  },
  {
    "path": "src/app/components/projects.tsx",
    "content": "import { Badge } from \"@/components/ui/badge\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\";\nimport { Section } from \"@/components/ui/section\";\nimport type { RESUME_DATA } from \"@/data/resume-data\";\n\ntype ProjectTags = readonly string[];\n\ninterface ProjectLinkProps {\n  title: string;\n  link?: string;\n}\n\n/**\n * Renders project title with optional link and status indicator\n */\nfunction ProjectLink({ title, link }: ProjectLinkProps) {\n  if (!link) {\n    return <span>{title}</span>;\n  }\n\n  return (\n    <>\n      <a\n        href={link}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className=\"inline-flex items-center gap-1 hover:underline\"\n        aria-label={`${title} project (opens in new tab)`}\n      >\n        {title}\n        <span\n          className=\"size-1 rounded-full bg-green-500\"\n          title=\"Active project indicator\"\n          aria-hidden=\"true\"\n        />\n      </a>\n      <div\n        className=\"hidden font-mono text-xs underline print:visible\"\n        aria-hidden=\"true\"\n      >\n        {link.replace(\"https://\", \"\").replace(\"www.\", \"\").replace(\"/\", \"\")}\n      </div>\n    </>\n  );\n}\n\ninterface ProjectTagsProps {\n  tags: ProjectTags;\n}\n\n/**\n * Renders a list of technology tags used in the project\n */\nfunction ProjectTags({ tags }: ProjectTagsProps) {\n  if (tags.length === 0) return null;\n\n  return (\n    <ul\n      className=\"mt-2 flex list-none flex-wrap gap-1 p-0\"\n      aria-label=\"Technologies used\"\n    >\n      {tags.map((tag) => (\n        <li key={tag}>\n          <Badge\n            className=\"px-1 py-0 text-[10px] print:px-1 print:py-0.5 print:text-[8px] print:leading-tight\"\n            variant=\"secondary\"\n          >\n            {tag}\n          </Badge>\n        </li>\n      ))}\n    </ul>\n  );\n}\n\ninterface ProjectCardProps {\n  title: string;\n  description: string;\n  tags: ProjectTags;\n  link?: string;\n}\n\n/**\n * Card component displaying project information\n */\nfunction ProjectCard({ title, description, tags, link }: ProjectCardProps) {\n  return (\n    <Card className=\"flex h-full flex-col overflow-hidden border p-3\">\n      <CardHeader>\n        <div className=\"space-y-1\">\n          <CardTitle className=\"text-base\">\n            <ProjectLink title={title} link={link} />\n          </CardTitle>\n          <CardDescription\n            className=\"text-pretty font-mono text-xs print:text-[10px]\"\n            aria-label=\"Project description\"\n          >\n            {description}\n          </CardDescription>\n        </div>\n      </CardHeader>\n      <CardContent className=\"mt-auto flex\">\n        <ProjectTags tags={tags} />\n      </CardContent>\n    </Card>\n  );\n}\n\ninterface ProjectsProps {\n  projects: (typeof RESUME_DATA)[\"projects\"];\n}\n\n/**\n * Section component displaying all side projects\n */\nexport function Projects({ projects }: ProjectsProps) {\n  return (\n    <Section className=\"scroll-mb-16 print:space-y-4\">\n      <h2 className=\"text-xl font-bold\" id=\"side-projects\">\n        Side projects\n      </h2>\n      <div\n        className=\"-mx-3 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3 print:grid-cols-3 print:gap-2\"\n        role=\"feed\"\n        aria-labelledby=\"side-projects\"\n      >\n        {projects.map((project) => (\n          <article\n            key={project.title}\n            className=\"h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-sm print:hover:translate-y-0 print:hover:shadow-none\"\n          >\n            <ProjectCard\n              title={project.title}\n              description={project.description}\n              tags={project.techStack}\n              link={project.link?.href}\n            />\n          </article>\n        ))}\n      </div>\n    </Section>\n  );\n}\n"
  },
  {
    "path": "src/app/components/skills.tsx",
    "content": "import { Badge } from \"@/components/ui/badge\";\nimport { Section } from \"@/components/ui/section\";\nimport { cn } from \"@/lib/utils\";\n\ntype Skills = readonly string[];\n\ninterface SkillsListProps {\n  skills: Skills;\n  className?: string;\n}\n\n/**\n * Renders a list of skills as badges\n */\nfunction SkillsList({ skills, className }: SkillsListProps) {\n  return (\n    <ul\n      className={cn(\"flex list-none flex-wrap gap-1 p-0\", className)}\n      aria-label=\"List of skills\"\n    >\n      {skills.map((skill) => (\n        <li key={skill}>\n          <Badge className=\"print:text-[10px]\" aria-label={`Skill: ${skill}`}>\n            {skill}\n          </Badge>\n        </li>\n      ))}\n    </ul>\n  );\n}\n\ninterface SkillsProps {\n  skills: Skills;\n  className?: string;\n}\n\n/**\n * Skills section component\n * Displays a list of professional skills as badges\n */\nexport function Skills({ skills, className }: SkillsProps) {\n  return (\n    <Section className={className}>\n      <h2 className=\"text-xl font-bold\" id=\"skills-section\">\n        Skills\n      </h2>\n      <SkillsList skills={skills} />\n    </Section>\n  );\n}\n"
  },
  {
    "path": "src/app/components/summary.tsx",
    "content": "import { Section } from \"../../components/ui/section\";\n\ninterface AboutProps {\n  summary: string;\n  className?: string;\n}\n\n/**\n * Summary section component\n * Displays a summary of professional experience and goals\n */\nexport function Summary({ summary, className }: AboutProps) {\n  return (\n    <Section className={className}>\n      <h2 className=\"text-xl font-bold\" id=\"about-section\">\n        About\n      </h2>\n      <div className=\"text-pretty font-mono text-sm text-foreground/80 print:text-[12px]\">\n        {summary}\n      </div>\n    </Section>\n  );\n}\n"
  },
  {
    "path": "src/app/components/work-experience.tsx",
    "content": "import { Badge } from \"@/components/ui/badge\";\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Section } from \"@/components/ui/section\";\nimport type { RESUME_DATA } from \"@/data/resume-data\";\nimport { cn } from \"@/lib/utils\";\n\ntype WorkExperience = (typeof RESUME_DATA)[\"work\"][number];\ntype WorkBadges = readonly string[];\n\ninterface BadgeListProps {\n  className?: string;\n  badges: WorkBadges;\n}\n\n/**\n * Renders a list of badges for work experience\n * Handles both mobile and desktop layouts through className prop\n */\nfunction BadgeList({ className, badges }: BadgeListProps) {\n  if (badges.length === 0) return null;\n\n  return (\n    <ul\n      className={cn(\"inline-flex list-none gap-x-1 p-0\", className)}\n      aria-label=\"Technologies used\"\n    >\n      {badges.map((badge) => (\n        <li key={badge}>\n          <Badge\n            variant=\"secondary\"\n            className=\"align-middle text-xs print:px-1 print:py-0.5 print:text-[8px] print:leading-tight\"\n          >\n            {badge}\n          </Badge>\n        </li>\n      ))}\n    </ul>\n  );\n}\n\ninterface WorkPeriodProps {\n  start: WorkExperience[\"start\"];\n  end?: WorkExperience[\"end\"];\n}\n\n/**\n * Displays the work period in a consistent format\n */\nfunction WorkPeriod({ start, end }: WorkPeriodProps) {\n  return (\n    <div\n      className=\"text-sm tabular-nums text-gray-500\"\n      title={`Employment period: ${start} to ${end ?? \"Present\"}`}\n    >\n      {start} - {end ?? \"Present\"}\n    </div>\n  );\n}\n\ninterface CompanyLinkProps {\n  company: WorkExperience[\"company\"];\n  link: WorkExperience[\"link\"];\n}\n\n/**\n * Renders company name with optional link\n */\nfunction CompanyLink({ company, link }: CompanyLinkProps) {\n  return (\n    <a\n      className=\"hover:underline\"\n      href={link}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      aria-label={`${company} company website`}\n    >\n      {company}\n    </a>\n  );\n}\n\ninterface WorkExperienceItemProps {\n  work: WorkExperience;\n}\n\n/**\n * Individual work experience card component\n * Handles responsive layout for badges (mobile/desktop)\n */\nfunction WorkExperienceItem({ work }: WorkExperienceItemProps) {\n  const { company, link, badges, title, start, end, description, highlights } =\n    work;\n\n  return (\n    <Card className=\"border-none py-1 print:py-0\">\n      <CardHeader className=\"print:space-y-1\">\n        <div className=\"flex items-center justify-between gap-x-2 text-base\">\n          <h3 className=\"inline-flex items-center justify-center gap-x-1 font-semibold leading-none print:text-sm\">\n            <CompanyLink company={company} link={link} />\n            <BadgeList\n              className=\"hidden gap-x-1 sm:inline-flex\"\n              badges={badges}\n            />\n          </h3>\n          <WorkPeriod start={start} end={end} />\n        </div>\n\n        <h4 className=\"font-mono text-sm font-semibold leading-none print:text-[12px]\">\n          {title}\n        </h4>\n      </CardHeader>\n\n      <CardContent>\n        <div className=\"mt-2 text-xs text-foreground/80 print:mt-1 print:text-[10px] text-pretty\">\n          {description}\n          {highlights && highlights.length > 0 && (\n            <ul className=\"list-inside list-disc\">\n              {highlights.map((highlight) => (\n                <li key={highlight}>{highlight}</li>\n              ))}\n            </ul>\n          )}\n        </div>\n        <div className=\"mt-2\">\n          <BadgeList\n            className=\"-mx-2 flex-wrap gap-1 sm:hidden\"\n            badges={badges}\n          />\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n\ninterface WorkExperienceProps {\n  work: (typeof RESUME_DATA)[\"work\"];\n}\n\n/**\n * Main work experience section component\n * Renders a list of work experiences in chronological order\n */\nexport function WorkExperience({ work }: WorkExperienceProps) {\n  return (\n    <Section>\n      <h2 className=\"text-xl font-bold\" id=\"work-experience\">\n        Work Experience\n      </h2>\n      <div\n        className=\"space-y-4 print:space-y-0\"\n        role=\"feed\"\n        aria-labelledby=\"work-experience\"\n      >\n        {work.map((item) => (\n          <article key={`${item.company}-${item.start}`}>\n            <WorkExperienceItem work={item} />\n          </article>\n        ))}\n      </div>\n    </Section>\n  );\n}\n"
  },
  {
    "path": "src/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 224 71.4% 4.1%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 224 71.4% 4.1%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 224 71.4% 4.1%;\n\n    --primary: 220.9 39.3% 11%;\n    --primary-foreground: 210 20% 98%;\n\n    --secondary: 220 14.3% 95.9%;\n    --secondary-foreground: 220.9 39.3% 11%;\n\n    --muted: 220 14.3% 95.9%;\n    --muted-foreground: 220 8.9% 46.1%;\n\n    --accent: 220 14.3% 95.9%;\n    --accent-foreground: 220.9 39.3% 11%;\n\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 20% 98%;\n\n    --border: 220 13% 91%;\n    --input: 220 13% 91%;\n    --ring: 224 71.4% 4.1%;\n\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 224 71.4% 4.1%;\n    --foreground: 210 20% 98%;\n\n    --card: 224 71.4% 4.1%;\n    --card-foreground: 210 20% 98%;\n\n    --popover: 224 71.4% 4.1%;\n    --popover-foreground: 210 20% 98%;\n\n    --primary: 210 20% 98%;\n    --primary-foreground: 220.9 39.3% 11%;\n\n    --secondary: 215 27.9% 16.9%;\n    --secondary-foreground: 210 20% 98%;\n\n    --muted: 215 27.9% 16.9%;\n    --muted-foreground: 217.9 10.6% 64.9%;\n\n    --accent: 215 27.9% 16.9%;\n    --accent-foreground: 210 20% 98%;\n\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 20% 98%;\n\n    --border: 215 27.9% 16.9%;\n    --input: 215 27.9% 16.9%;\n    --ring: 216 12.2% 83.9%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground font-sans;\n  }\n}\n\n.print-force-new-page {\n  page-break-before: always;\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n    transform: translateY(4px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n.animate-fade-in {\n  animation: fadeIn 400ms ease-out both;\n}\n\n@media print {\n  .animate-fade-in {\n    animation: none !important;\n    opacity: 1 !important;\n    transform: none !important;\n  }\n}\n"
  },
  {
    "path": "src/app/layout.tsx",
    "content": "import { Analytics } from \"@vercel/analytics/react\";\nimport { GeistMono } from \"geist/font/mono\";\nimport { GeistSans } from \"geist/font/sans\";\nimport type { Metadata, Viewport } from \"next\";\n\nimport \"./globals.css\";\nimport type React from \"react\";\nimport { ErrorBoundary } from \"@/components/error-boundary\";\nimport { RESUME_DATA } from \"@/data/resume-data\";\n\nexport const metadata: Metadata = {\n  metadataBase: new URL(\"https://cv.jarocki.me\"),\n  title: {\n    default: `${RESUME_DATA.name} - ${RESUME_DATA.about}`,\n    template: `%s | ${RESUME_DATA.name}`,\n  },\n  description: RESUME_DATA.about,\n  keywords: [\n    \"resume\",\n    \"cv\",\n    \"portfolio\",\n    RESUME_DATA.name,\n    \"software engineer\",\n    \"full stack developer\",\n    \"react\",\n    \"next.js\",\n    \"typescript\",\n  ],\n  authors: [{ name: RESUME_DATA.name }],\n  creator: RESUME_DATA.name,\n  publisher: RESUME_DATA.name,\n  formatDetection: {\n    email: false,\n    address: false,\n    telephone: false,\n  },\n  openGraph: {\n    type: \"website\",\n    locale: \"en_US\",\n    url: RESUME_DATA.personalWebsiteUrl,\n    siteName: `${RESUME_DATA.name}'s CV`,\n    title: `${RESUME_DATA.name} - ${RESUME_DATA.about}`,\n    description: RESUME_DATA.about,\n  },\n  robots: {\n    index: true,\n    follow: true,\n    googleBot: {\n      index: true,\n      follow: true,\n      \"max-video-preview\": -1,\n      \"max-image-preview\": \"large\",\n      \"max-snippet\": -1,\n    },\n  },\n  twitter: {\n    card: \"summary_large_image\",\n    title: `${RESUME_DATA.name} - ${RESUME_DATA.about}`,\n    description: RESUME_DATA.about,\n    creator: \"@BartoszJarocki\",\n  },\n  alternates: {\n    canonical: RESUME_DATA.personalWebsiteUrl,\n  },\n};\n\nexport const viewport: Viewport = {\n  themeColor: [\n    { media: \"(prefers-color-scheme: light)\", color: \"white\" },\n    { media: \"(prefers-color-scheme: dark)\", color: \"black\" },\n  ],\n  width: \"device-width\",\n  initialScale: 1,\n  maximumScale: 5,\n};\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\" className={`${GeistSans.variable} ${GeistMono.variable}`}>\n      <body>\n        <ErrorBoundary>{children}</ErrorBoundary>\n        <Analytics />\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "src/app/loading.tsx",
    "content": "export default function Loading() {\n  return (\n    <div className=\"container relative mx-auto scroll-my-12 overflow-auto p-4 md:p-16 print:p-11\">\n      <div className=\"mx-auto w-full max-w-2xl space-y-8 bg-white print:space-y-4\">\n        {/* Header skeleton */}\n        <div className=\"flex items-center justify-between\">\n          <div className=\"flex-1 space-y-3\">\n            <div className=\"h-8 w-48 animate-pulse rounded bg-gray-200\" />\n            <div className=\"h-4 w-64 animate-pulse rounded bg-gray-200\" />\n            <div className=\"h-4 w-32 animate-pulse rounded bg-gray-200\" />\n          </div>\n          <div className=\"size-28 animate-pulse rounded-xl bg-gray-200\" />\n        </div>\n\n        {/* Content sections skeleton */}\n        <div className=\"space-y-8\">\n          {[...Array(4)].map((_, i) => (\n            <div\n              // biome-ignore lint/suspicious/noArrayIndexKey: Static skeleton content doesn't reorder\n              key={`loading-section-${i}`}\n              className=\"space-y-4\"\n            >\n              <div className=\"h-6 w-32 animate-pulse rounded bg-gray-200\" />\n              <div className=\"space-y-3\">\n                <div className=\"h-4 w-full animate-pulse rounded bg-gray-200\" />\n                <div className=\"h-4 w-3/4 animate-pulse rounded bg-gray-200\" />\n                <div className=\"h-4 w-1/2 animate-pulse rounded bg-gray-200\" />\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/app/opengraph-image.tsx",
    "content": "import { ImageResponse } from \"next/og\";\nimport { RESUME_DATA } from \"../data/resume-data\";\n\nexport const runtime = \"edge\";\n\nexport const alt = \"Minimalist Resume\";\nexport const size = {\n  width: 1200,\n  height: 630,\n};\n\nexport const contentType = \"image/png\";\n\nexport default async function Image() {\n  return new ImageResponse(\n    <div\n      style={{\n        background: \"white\",\n        width: \"100%\",\n        height: \"100%\",\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        fontFamily: '\"Inter\"',\n      }}\n    >\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          textAlign: \"center\",\n        }}\n      >\n        {/* biome-ignore lint/performance/noImgElement: ImageResponse context requires img element */}\n        <img\n          src={RESUME_DATA.avatarUrl}\n          alt={RESUME_DATA.name}\n          style={{\n            width: \"150px\",\n            height: \"150px\",\n            borderRadius: \"10%\",\n            marginBottom: \"2rem\",\n          }}\n        />\n        <div\n          style={{\n            fontSize: \"3rem\",\n            fontWeight: \"bold\",\n            color: \"#333\",\n            marginBottom: \"1rem\",\n          }}\n        >\n          {RESUME_DATA.name}\n        </div>\n        <div\n          style={{\n            fontSize: \"1.5rem\",\n            color: \"#666\",\n            maxWidth: \"600px\",\n            lineHeight: \"1.4\",\n          }}\n        >\n          {RESUME_DATA.about}\n        </div>\n        <div\n          style={{\n            display: \"flex\",\n            marginTop: \"2rem\",\n            gap: \"1rem\",\n          }}\n        >\n          {RESUME_DATA.personalWebsiteUrl && (\n            <div style={{ fontSize: \"1rem\", color: \"#666\" }}>\n              {RESUME_DATA.personalWebsiteUrl}\n            </div>\n          )}\n        </div>\n      </div>\n    </div>,\n    {\n      ...size,\n    }\n  );\n}\n"
  },
  {
    "path": "src/app/page.tsx",
    "content": "import type { Metadata } from \"next\";\nimport { CommandMenu } from \"@/components/command-menu\";\nimport { RESUME_DATA } from \"@/data/resume-data\";\nimport { generateResumeStructuredData } from \"@/lib/structured-data\";\nimport { Education } from \"./components/education\";\nimport { Header } from \"./components/header\";\nimport { Projects } from \"./components/projects\";\nimport { Skills } from \"./components/skills\";\nimport { Summary } from \"./components/summary\";\nimport { WorkExperience } from \"./components/work-experience\";\n\nexport const metadata: Metadata = {\n  title: `${RESUME_DATA.name} - Resume`,\n  description: RESUME_DATA.about,\n  openGraph: {\n    title: `${RESUME_DATA.name} - Resume`,\n    description: RESUME_DATA.about,\n    type: \"profile\",\n    locale: \"en_US\",\n    images: [\n      {\n        url: \"https://cv.jarocki.me/opengraph-image\",\n        width: 1200,\n        height: 630,\n        alt: `${RESUME_DATA.name}'s profile picture`,\n      },\n    ],\n  },\n  twitter: {\n    card: \"summary_large_image\",\n    title: `${RESUME_DATA.name} - Resume`,\n    description: RESUME_DATA.about,\n    images: [\"https://cv.jarocki.me/opengraph-image\"],\n  },\n};\n\n/**\n * Transform social links for command menu\n */\nfunction getCommandMenuLinks() {\n  const links = [];\n\n  if (RESUME_DATA.personalWebsiteUrl) {\n    links.push({\n      url: RESUME_DATA.personalWebsiteUrl,\n      title: \"Personal Website\",\n    });\n  }\n\n  return [\n    ...links,\n    ...RESUME_DATA.contact.social.map((socialMediaLink) => ({\n      url: socialMediaLink.url,\n      title: socialMediaLink.name,\n    })),\n  ];\n}\n\nexport default function ResumePage() {\n  const structuredData = generateResumeStructuredData();\n\n  return (\n    <>\n      <script\n        type=\"application/ld+json\"\n        // biome-ignore lint/security/noDangerouslySetInnerHtml: Safe for JSON-LD structured data\n        dangerouslySetInnerHTML={{\n          __html: JSON.stringify(structuredData),\n        }}\n      />\n      <main\n        className=\"container relative mx-auto scroll-my-12 overflow-auto p-4 print:p-11 md:p-16\"\n        id=\"main-content\"\n      >\n        <div className=\"sr-only\">\n          <h1>{RESUME_DATA.name}&apos;s Resume</h1>\n        </div>\n\n        <section\n          className=\"mx-auto w-full max-w-2xl space-y-8 bg-white print:space-y-4 dark:bg-background\"\n          aria-label=\"Resume Content\"\n        >\n          <div className=\"animate-fade-in\" style={{ animationDelay: \"0ms\" }}>\n            <Header />\n          </div>\n\n          <div className=\"space-y-8 print:space-y-4\">\n            <div className=\"animate-fade-in\" style={{ animationDelay: \"75ms\" }}>\n              <Summary summary={RESUME_DATA.summary} />\n            </div>\n            <div\n              className=\"animate-fade-in\"\n              style={{ animationDelay: \"150ms\" }}\n            >\n              <WorkExperience work={RESUME_DATA.work} />\n            </div>\n            <div\n              className=\"animate-fade-in\"\n              style={{ animationDelay: \"225ms\" }}\n            >\n              <Education education={RESUME_DATA.education} />\n            </div>\n            <div\n              className=\"animate-fade-in\"\n              style={{ animationDelay: \"300ms\" }}\n            >\n              <Skills skills={RESUME_DATA.skills} />\n            </div>\n            <div\n              className=\"animate-fade-in\"\n              style={{ animationDelay: \"375ms\" }}\n            >\n              <Projects projects={RESUME_DATA.projects} />\n            </div>\n          </div>\n        </section>\n\n        <nav className=\"print:hidden\" aria-label=\"Quick navigation\">\n          <CommandMenu links={getCommandMenuLinks()} />\n        </nav>\n      </main>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/app/sitemap.ts",
    "content": "import type { MetadataRoute } from \"next\";\n\nexport default function sitemap(): MetadataRoute.Sitemap {\n  const baseUrl = \"https://cv.jarocki.me\";\n\n  return [\n    {\n      url: baseUrl,\n      lastModified: new Date(),\n      changeFrequency: \"monthly\",\n      priority: 1,\n    },\n  ];\n}\n"
  },
  {
    "path": "src/components/avatar.tsx",
    "content": "\"use client\";\n\nimport Image from \"next/image\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface OptimizedAvatarProps {\n  src: string;\n  alt: string;\n  fallback: string;\n  className?: string;\n}\n\nexport function Avatar({\n  src,\n  alt,\n  fallback,\n  className,\n}: OptimizedAvatarProps) {\n  const [error, setError] = React.useState(false);\n\n  return (\n    <div\n      className={cn(\n        \"relative flex shrink-0 overflow-hidden rounded-xl bg-muted\",\n        className\n      )}\n    >\n      {!error && src ? (\n        <Image\n          src={src}\n          alt={alt}\n          width={112}\n          height={112}\n          className=\"aspect-square h-full w-full object-cover\"\n          onError={() => setError(true)}\n          priority={true}\n        />\n      ) : (\n        <div className=\"flex h-full w-full items-center justify-center text-lg font-semibold\">\n          {fallback}\n        </div>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/command-menu.tsx",
    "content": "\"use client\";\n\nimport { CommandIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n} from \"@/components/ui/command\";\nimport { Button } from \"./ui/button\";\n\ninterface Props {\n  links: { url: string; title: string }[];\n}\n\nexport const CommandMenu = ({ links }: Props) => {\n  const [open, setOpen] = React.useState(false);\n  const [isMac, setIsMac] = React.useState(false);\n\n  React.useEffect(() => {\n    setIsMac(window.navigator.userAgent.includes(\"Mac\"));\n\n    const down = (e: KeyboardEvent) => {\n      if (e.key === \"j\" && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        setOpen((open) => !open);\n      }\n    };\n\n    document.addEventListener(\"keydown\", down);\n    return () => document.removeEventListener(\"keydown\", down);\n  }, []);\n\n  return (\n    <>\n      <p className=\"fixed bottom-0 left-0 right-0 hidden bg-gradient-to-t from-[hsl(var(--background))] to-transparent p-1 pt-6 text-center text-sm text-muted-foreground xl:block print:hidden\">\n        Press{\" \"}\n        <kbd className=\"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100\">\n          <span className=\"text-xs\">{isMac ? \"⌘\" : \"Ctrl\"}</span>+J\n        </kbd>{\" \"}\n        to open the command menu\n      </p>\n      <Button\n        onClick={() => setOpen((open) => !open)}\n        variant=\"outline\"\n        size=\"icon\"\n        className=\"fixed bottom-4 right-4 flex rounded-full shadow-2xl xl:hidden print:hidden\"\n      >\n        <CommandIcon className=\"my-6 size-6\" />\n      </Button>\n      <CommandDialog open={open} onOpenChange={setOpen}>\n        <CommandInput placeholder=\"Type a command or search...\" />\n        <CommandList>\n          <CommandEmpty>No results found.</CommandEmpty>\n          <CommandGroup heading=\"Actions\">\n            <CommandItem\n              onSelect={() => {\n                setOpen(false);\n                window.print();\n              }}\n            >\n              <span>Print</span>\n            </CommandItem>\n          </CommandGroup>\n          <CommandGroup heading=\"Links\">\n            {links.map(({ url, title }) => (\n              <CommandItem\n                key={url}\n                onSelect={() => {\n                  setOpen(false);\n                  window.open(url, \"_blank\");\n                }}\n              >\n                <span>{title}</span>\n              </CommandItem>\n            ))}\n          </CommandGroup>\n          <CommandSeparator />\n        </CommandList>\n      </CommandDialog>\n    </>\n  );\n};\n"
  },
  {
    "path": "src/components/error-boundary.tsx",
    "content": "\"use client\";\n\nimport { Component, type ErrorInfo, type ReactNode } from \"react\";\n\ninterface Props {\n  children: ReactNode;\n  fallback?: ReactNode;\n}\n\ninterface State {\n  hasError: boolean;\n  error?: Error;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n  public state: State = {\n    hasError: false,\n  };\n\n  public static getDerivedStateFromError(error: Error): State {\n    return { hasError: true, error };\n  }\n\n  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n    console.error(\"Uncaught error:\", error, errorInfo);\n  }\n\n  public render() {\n    if (this.state.hasError) {\n      return (\n        this.props.fallback || (\n          <div className=\"flex min-h-screen flex-col items-center justify-center p-4\">\n            <div className=\"max-w-md text-center\">\n              <h2 className=\"mb-4 text-2xl font-bold\">Something went wrong</h2>\n              <p className=\"mb-4 text-muted-foreground\">\n                We apologize for the inconvenience. Please try refreshing the\n                page.\n              </p>\n              <button\n                type=\"button\"\n                onClick={() => this.setState({ hasError: false })}\n                className=\"rounded-md bg-primary px-4 py-2 text-primary-foreground hover:bg-primary/90\"\n              >\n                Try again\n              </button>\n            </div>\n          </div>\n        )\n      );\n    }\n\n    return this.props.children;\n  }\n}\n"
  },
  {
    "path": "src/components/icons/github-icon.tsx",
    "content": "export const GitHubIcon = (props: React.SVGProps<SVGSVGElement>) => {\n  return (\n    <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" {...props}>\n      <title>GitHub</title>\n      <path\n        fill=\"currentColor\"\n        d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\n      />\n    </svg>\n  );\n};\n"
  },
  {
    "path": "src/components/icons/index.ts",
    "content": "import { GitHubIcon } from \"./github-icon\";\nimport { LinkedInIcon } from \"./linkedin-icon\";\n\nexport { GitHubIcon, LinkedInIcon };\n"
  },
  {
    "path": "src/components/icons/linkedin-icon.tsx",
    "content": "export const LinkedInIcon = (props: React.SVGProps<SVGSVGElement>) => {\n  return (\n    <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" {...props}>\n      <title>LinkedIn</title>\n      <path\n        fill=\"currentColor\"\n        d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\"\n      />\n    </svg>\n  );\n};\n"
  },
  {
    "path": "src/components/icons/x-icon.tsx",
    "content": "export const XIcon = (props: React.SVGProps<SVGSVGElement>) => {\n  return (\n    <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" {...props}>\n      <title>X</title>\n      <path\n        fill=\"currentColor\"\n        d=\"M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z\"\n      />\n    </svg>\n  );\n};\n"
  },
  {
    "path": "src/components/ui/badge.tsx",
    "content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst badgeVariants = cva(\n  \"inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-semibold font-mono transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-nowrap\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border-transparent bg-primary/80 text-primary-foreground hover:bg-primary/60\",\n        secondary:\n          \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/60\",\n        destructive:\n          \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n        outline: \"text-foreground\",\n      },\n    },\n\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n);\n\nexport interface BadgeProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n  return (\n    <div className={cn(badgeVariants({ variant }), className)} {...props} />\n  );\n}\n\nexport { Badge, badgeVariants };\n"
  },
  {
    "path": "src/components/ui/button.tsx",
    "content": "\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\";\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    );\n  }\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n"
  },
  {
    "path": "src/components/ui/card.tsx",
    "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      \"rounded-lg border border-muted bg-card text-card-foreground\",\n      className\n    )}\n    {...props}\n  />\n));\nCard.displayName = \"Card\";\n\nconst CardHeader = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5\", className)}\n    {...props}\n  />\n));\nCardHeader.displayName = \"CardHeader\";\n\nconst CardTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h3\n    ref={ref}\n    className={cn(\n      \"text-2xl font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n));\nCardTitle.displayName = \"CardTitle\";\n\nconst CardDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p\n    ref={ref}\n    className={cn(\"text-sm text-foreground\", className)}\n    {...props}\n  />\n));\nCardDescription.displayName = \"CardDescription\";\n\nconst CardContent = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      \"text-pretty font-mono text-sm text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n));\nCardContent.displayName = \"CardContent\";\n\nconst CardFooter = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"flex items-center\", className)} {...props} />\n));\nCardFooter.displayName = \"CardFooter\";\n\nexport {\n  Card,\n  CardHeader,\n  CardFooter,\n  CardTitle,\n  CardDescription,\n  CardContent,\n};\n"
  },
  {
    "path": "src/components/ui/command.tsx",
    "content": "\"use client\";\n\nimport type { DialogProps } from \"@radix-ui/react-dialog\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Search } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Dialog, DialogContent } from \"@/components/ui/dialog\";\nimport { cn } from \"@/lib/utils\";\n\nconst Command = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive\n    ref={ref}\n    className={cn(\n      \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n      className\n    )}\n    {...props}\n  />\n));\n\nCommand.displayName = CommandPrimitive.displayName;\n\ninterface CommandDialogProps extends DialogProps {}\n\nconst CommandDialog = ({ children, ...props }: CommandDialogProps) => {\n  return (\n    <Dialog {...props}>\n      <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n        <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n          {children}\n        </Command>\n      </DialogContent>\n    </Dialog>\n  );\n};\n\nconst CommandInput = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Input>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n  <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n    <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n    <CommandPrimitive.Input\n      ref={ref}\n      className={cn(\n        \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    />\n  </div>\n));\n\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.List\n    ref={ref}\n    className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n    {...props}\n  />\n));\n\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Empty>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n  <CommandPrimitive.Empty\n    ref={ref}\n    className=\"py-6 text-center text-sm\"\n    {...props}\n  />\n));\n\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Group>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Group\n    ref={ref}\n    className={cn(\n      \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n));\n\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 h-px bg-border\", className)}\n    {...props}\n  />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}\n  />\n));\n\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nconst CommandShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\n        \"ml-auto text-xs tracking-widest text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  );\n};\nCommandShortcut.displayName = \"CommandShortcut\";\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n};\n"
  },
  {
    "path": "src/components/ui/dialog.tsx",
    "content": "\"use client\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Dialog = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      \"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in  data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 print:hidden\",\n      className\n    )}\n    {...props}\n  />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n  <DialogPortal>\n    <DialogOverlay />\n    <DialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] print:hidden sm:rounded-lg\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </DialogPrimitive.Close>\n    </DialogPrimitive.Content>\n  </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-1.5 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\nconst DialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\nconst DialogTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn(\n      \"text-lg font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n  Dialog,\n  DialogPortal,\n  DialogOverlay,\n  DialogClose,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n};\n"
  },
  {
    "path": "src/components/ui/section.tsx",
    "content": "import type React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SectionProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport function Section({ className, ...props }: SectionProps) {\n  return (\n    <section\n      className={cn(\"flex min-h-0 flex-col gap-y-3 print:gap-y-1\", className)}\n      {...props}\n    />\n  );\n}\n"
  },
  {
    "path": "src/data/resume-data.ts",
    "content": "import type { ResumeData } from \"@/lib/types\";\n\nexport const RESUME_DATA: ResumeData = {\n  name: \"Bartosz Jarocki\",\n  initials: \"BJ\",\n  location: \"Wrocław, Poland, CET\",\n  locationLink: \"https://www.google.com/maps/place/Wrocław\",\n  about: \"Full Stack Engineer building products from the ground up.\",\n  summary:\n    \"Full Stack Engineer with 15+ years of experience and 10+ years working remotely. Building high-performance web applications, leading distributed teams, and creating open source tools used by thousands of developers.\",\n  avatarUrl: \"https://avatars.githubusercontent.com/u/1017620?v=4\",\n  personalWebsiteUrl: \"https://jarocki.me\",\n  contact: {\n    email: \"bartosz.jarocki@hey.com\",\n    tel: \"+48530213401\",\n    social: [\n      {\n        name: \"GitHub\",\n        url: \"https://github.com/BartoszJarocki\",\n        icon: \"github\",\n      },\n      {\n        name: \"LinkedIn\",\n        url: \"https://www.linkedin.com/in/bjarocki/\",\n        icon: \"linkedin\",\n      },\n      {\n        name: \"X\",\n        url: \"https://x.com/BartoszJarocki\",\n        icon: \"x\",\n      },\n    ],\n  },\n  education: [\n    {\n      school: \"Wrocław University of Technology\",\n      degree: \"Bachelor's Degree in Control systems engineering and Robotics\",\n      start: \"2007\",\n      end: \"2010\",\n    },\n  ],\n  work: [\n    {\n      company: \"Motion\",\n      link: \"https://motionapp.com/\",\n      badges: [\"Remote\", \"AI\", \"React\", \"Next.js\", \"TypeScript\", \"AdonisJS\"],\n      title: \"Senior Software Engineer\",\n      start: \"2025\",\n      end: null,\n      description:\n        \"Building an internal AI agents platform that enables marketing teams to create and manage AI-powered workflows.\",\n    },\n    {\n      company: \"Film.io\",\n      link: \"https://film.io\",\n      badges: [\"Remote\", \"React\", \"Next.js\", \"TypeScript\", \"Node.js\"],\n      title: \"Software Architect\",\n      start: \"2024\",\n      end: \"2025\",\n      description:\n        \"Led technical architecture of a blockchain-based film funding platform.\",\n      highlights: [\n        \"Architected migration from CRA to Next.js for improved performance, SEO, and DX\",\n        \"Established release process enabling faster deployments and reliable rollbacks\",\n        \"Implemented system-wide monitoring and security improvements\",\n      ],\n    },\n    {\n      company: \"Parabol\",\n      link: \"https://parabol.co\",\n      badges: [\n        \"Remote\",\n        \"React\",\n        \"TypeScript\",\n        \"Node.js\",\n        \"GraphQL\",\n        \"Tailwind CSS\",\n      ],\n      title: \"Senior Full Stack Developer\",\n      start: \"2021\",\n      end: \"2024\",\n      description:\n        \"Led a product squad building an enterprise agile meeting platform.\",\n      highlights: [\n        \"Built design system with Tailwind CSS, improving development speed and time to market\",\n        \"Implemented engineering practices: PR automation, code review guidelines, and workflows\",\n        \"Open source contributions to Relay DevTools and React i18n tooling\",\n      ],\n    },\n    {\n      company: \"Clevertech\",\n      link: \"https://clevertech.biz\",\n      badges: [\"Remote\", \"React\", \"TypeScript\", \"Node.js\", \"Android\", \"Kotlin\"],\n      title: \"Lead Android Developer → Full Stack Developer\",\n      start: \"2015\",\n      end: \"2021\",\n      description:\n        \"Transitioned from mobile to full-stack development while leading distributed teams across multiple client projects.\",\n      highlights: [\n        \"Led frontend team at Evercast, building real-time platform supporting 30+ users per room with HD streaming and collaboration tools\",\n        \"Developed offline-first Android app for DKMS, improving donor registration process\",\n        \"Led development teams across multiple successful client projects\",\n      ],\n    },\n    {\n      company: \"Jojo Mobile\",\n      link: \"https://bsgroup.eu/\",\n      badges: [\"On Site\", \"Android\", \"Java\", \"Kotlin\"],\n      title: \"Android Developer → Lead Android Developer\",\n      start: \"2012\",\n      end: \"2015\",\n      description:\n        \"First Android developer, grew and led a team of 15+ engineers. Established mobile engineering culture and delivery processes.\",\n      highlights: [\n        \"Developed apps for major Polish companies including LOT, Polskie Radio, and Agora\",\n        \"Built and mentored high-performing mobile development team\",\n      ],\n    },\n    {\n      company: \"Nokia Siemens Networks\",\n      link: \"https://www.nokia.com\",\n      badges: [\"On Site\", \"C/C++\", \"LTE\", \"Agile\"],\n      title: \"C/C++ Developer\",\n      start: \"2010\",\n      end: \"2012\",\n      description:\n        \"Developed software for LTE base stations at enterprise scale. Built strong foundations in software architecture, testing practices, and cross-team collaboration.\",\n    },\n  ],\n  skills: [\n    \"React/Next.js\",\n    \"TypeScript\",\n    \"Node.js\",\n    \"AI/LLMs\",\n    \"Tailwind CSS\",\n    \"Design Systems\",\n    \"WebRTC\",\n    \"WebSockets\",\n    \"GraphQL\",\n    \"System Architecture\",\n    \"Remote Team Leadership\",\n  ],\n  projects: [\n    {\n      title: \"Monito\",\n      techStack: [\"TypeScript\", \"Next.js\", \"AI\", \"Browser Extension\"],\n      description: \"Autonomous QA AI agent for web applications\",\n      link: {\n        label: \"monito.dev\",\n        href: \"https://monito.dev/\",\n      },\n    },\n    {\n      title: \"43frames\",\n      techStack: [\"TypeScript\", \"Next.js\", \"AI\"],\n      description: \"AI-powered image and video generation studio\",\n      link: {\n        label: \"43frames.com\",\n        href: \"https://43frames.com/\",\n      },\n    },\n    {\n      title: \"Minimalist CV\",\n      techStack: [\"TypeScript\", \"Next.js\", \"Tailwind CSS\"],\n      description:\n        \"Open source, print-friendly CV template. 9,600+ stars on GitHub\",\n      link: {\n        label: \"Minimalist CV\",\n        href: \"https://github.com/BartoszJarocki/cv\",\n      },\n    },\n  ],\n} as const;\n"
  },
  {
    "path": "src/lib/structured-data.ts",
    "content": "import { RESUME_DATA } from \"@/data/resume-data\";\n\nexport function generatePersonStructuredData() {\n  return {\n    \"@context\": \"https://schema.org\",\n    \"@type\": \"Person\",\n    name: RESUME_DATA.name,\n    alternateName: RESUME_DATA.initials,\n    description: RESUME_DATA.about,\n    url: RESUME_DATA.personalWebsiteUrl,\n    image: RESUME_DATA.avatarUrl,\n    sameAs: RESUME_DATA.contact.social.map((social) => social.url),\n    address: {\n      \"@type\": \"Place\",\n      name: RESUME_DATA.location,\n    },\n    contactPoint: {\n      \"@type\": \"ContactPoint\",\n      email: RESUME_DATA.contact.email,\n      telephone: RESUME_DATA.contact.tel,\n      contactType: \"personal\",\n    },\n    jobTitle: \"Full Stack Engineer\",\n    worksFor:\n      RESUME_DATA.work.length > 0\n        ? {\n            \"@type\": \"Organization\",\n            name: RESUME_DATA.work[0].company,\n            url: RESUME_DATA.work[0].link,\n          }\n        : undefined,\n    alumniOf: RESUME_DATA.education.map((edu) => ({\n      \"@type\": \"EducationalOrganization\",\n      name: edu.school,\n    })),\n    hasOccupation: RESUME_DATA.work.map((job) => ({\n      \"@type\": \"Occupation\",\n      name: job.title,\n      occupationLocation: {\n        \"@type\": \"Place\",\n        name: RESUME_DATA.location,\n      },\n      occupationalCategory: \"Software Engineering\",\n      estimatedSalary: {\n        \"@type\": \"MonetaryAmountDistribution\",\n        name: \"Professional software engineer\",\n      },\n    })),\n    knowsAbout: RESUME_DATA.skills,\n  };\n}\n\nexport function generateWebPageStructuredData() {\n  return {\n    \"@context\": \"https://schema.org\",\n    \"@type\": \"WebPage\",\n    name: `${RESUME_DATA.name} - Resume`,\n    description: RESUME_DATA.about,\n    url: \"https://cv.jarocki.me\",\n    inLanguage: \"en-US\",\n    isPartOf: {\n      \"@type\": \"WebSite\",\n      name: `${RESUME_DATA.name}'s Professional Resume`,\n      url: \"https://cv.jarocki.me\",\n    },\n    about: {\n      \"@type\": \"Person\",\n      name: RESUME_DATA.name,\n    },\n    mainEntity: generatePersonStructuredData(),\n  };\n}\n\nexport function generateResumeStructuredData() {\n  const person = generatePersonStructuredData();\n\n  return {\n    \"@context\": \"https://schema.org\",\n    \"@type\": \"ProfilePage\",\n    dateCreated: new Date().toISOString(),\n    dateModified: new Date().toISOString(),\n    mainEntity: person,\n    about: person,\n    name: `${RESUME_DATA.name} - Professional Resume`,\n    description: `Professional resume and portfolio of ${RESUME_DATA.name}, ${RESUME_DATA.about}`,\n    url: \"https://cv.jarocki.me\",\n  };\n}\n"
  },
  {
    "path": "src/lib/types.ts",
    "content": "import type { StaticImageData } from \"next/image\";\n\nexport type ResumeIcon =\n  | React.ComponentType<React.SVGProps<SVGSVGElement>>\n  | StaticImageData;\n\nexport type IconType = \"github\" | \"linkedin\" | \"x\" | \"globe\" | \"mail\" | \"phone\";\n\nexport interface ResumeData {\n  name: string;\n  initials: string;\n  location: string;\n  locationLink: string;\n  about: string;\n  summary: string;\n  avatarUrl: string;\n  personalWebsiteUrl: string;\n  contact: {\n    email: string;\n    tel: string;\n    social: Array<{\n      name: string;\n      url: string;\n      icon: IconType;\n    }>;\n  };\n  education: Array<{\n    school: string;\n    degree: string;\n    start: string;\n    end: string;\n  }>;\n  work: Array<{\n    company: string;\n    link: string;\n    badges: string[];\n    title: string;\n    start: string;\n    end: string | null;\n    description: string;\n    highlights?: readonly string[];\n  }>;\n  skills: string[];\n  projects: Array<{\n    title: string;\n    techStack: string[];\n    description: string;\n    link?: {\n      label: string;\n      href: string;\n    };\n  }>;\n}\n"
  },
  {
    "path": "src/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: [\"class\"],\n  content: [\n    './pages/**/*.{ts,tsx}',\n    './components/**/*.{ts,tsx}',\n    './app/**/*.{ts,tsx}',\n    './src/**/*.{ts,tsx}',\n  ],\n  prefix: \"\",\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      fontFamily: {\n        sans: [\"var(--font-geist-sans)\", \"system-ui\", \"sans-serif\"],\n        mono: [\"var(--font-geist-mono)\", \"ui-monospace\", \"monospace\"],\n      },\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2021\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\n        \"./src/*\"\n      ]\n    }\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\",\n    \"src/images/logos/*.*\",\n    \".next/dev/types/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  }
]