forked from jleibl/jleibl.net
refactor: update dependencies and enhance animations
This commit is contained in:
64
bun.lock
64
bun.lock
@ -163,15 +163,15 @@
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.25.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.25.0", "@typescript-eslint/type-utils": "8.25.0", "@typescript-eslint/utils": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.25.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.25.0", "@typescript-eslint/types": "8.25.0", "@typescript-eslint/typescript-estree": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg=="],
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.25.0", "", { "dependencies": { "@typescript-eslint/types": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0" } }, "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.25.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.25.0", "@typescript-eslint/utils": "8.25.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.25.0", "", {}, "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.25.0", "", { "dependencies": { "@typescript-eslint/types": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" } }, "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.25.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.25.0", "@typescript-eslint/types": "8.25.0", "@typescript-eslint/typescript-estree": "8.25.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA=="],
|
||||
|
||||
@ -201,6 +201,8 @@
|
||||
|
||||
"array-includes": ["array-includes@3.1.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ=="],
|
||||
|
||||
"array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
|
||||
|
||||
"array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
|
||||
|
||||
"array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ=="],
|
||||
@ -289,6 +291,8 @@
|
||||
|
||||
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
|
||||
|
||||
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
|
||||
|
||||
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
@ -403,6 +407,8 @@
|
||||
|
||||
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
||||
|
||||
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
@ -597,6 +603,8 @@
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@ -689,6 +697,8 @@
|
||||
|
||||
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
|
||||
|
||||
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"stable-hash": ["stable-hash@0.0.4", "", {}, "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g=="],
|
||||
@ -793,10 +803,28 @@
|
||||
|
||||
"@next/eslint-plugin-next/fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
"@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="],
|
||||
|
||||
"@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.25.0", "", {}, "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw=="],
|
||||
|
||||
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.25.0", "", { "dependencies": { "@typescript-eslint/types": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="],
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.25.0", "", {}, "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw=="],
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.25.0", "", { "dependencies": { "@typescript-eslint/types": "8.25.0", "@typescript-eslint/visitor-keys": "8.25.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.25.0", "", {}, "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw=="],
|
||||
|
||||
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
@ -805,6 +833,8 @@
|
||||
|
||||
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
|
||||
"eslint-plugin-react/doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
|
||||
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
@ -817,6 +847,8 @@
|
||||
|
||||
"sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
@ -827,20 +859,44 @@
|
||||
|
||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.25.0", "", {}, "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw=="],
|
||||
|
||||
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
}
|
||||
}
|
||||
|
14
package.json
14
package.json
@ -13,20 +13,20 @@
|
||||
"@fontsource/instrument-sans": "^5.1.1",
|
||||
"@studio-freight/lenis": "^1.0.42",
|
||||
"framer-motion": "^12.4.7",
|
||||
"next": "14.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"next": "15.1.7",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-intersection-observer": "^9.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.1.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.1.7",
|
||||
"@eslint/eslintrc": "^3"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useEffect, createContext, useContext, useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import Lenis from '@studio-freight/lenis';
|
||||
import Head from 'next/head';
|
||||
import Image from "next/image";
|
||||
import React from 'react';
|
||||
|
||||
import '@fontsource/dm-sans/400.css';
|
||||
import '@fontsource/dm-sans/500.css';
|
||||
@ -67,7 +68,12 @@ const FadeIn = ({ children, className = "", delay = 0 }: { children: React.React
|
||||
ref={ref}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={inView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
||||
transition={{ duration: 0.8, delay, ease: [0.22, 1, 0.36, 1] }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 20,
|
||||
stiffness: 100,
|
||||
delay,
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
@ -93,36 +99,225 @@ const GermanyText = () => {
|
||||
opacity: 0.6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
whileHover={{ opacity: 0.8 }}
|
||||
whileHover={{
|
||||
opacity: 0.8,
|
||||
transition: { duration: 0.2 }
|
||||
}}
|
||||
>
|
||||
<motion.button
|
||||
aria-label="Switch to dark theme"
|
||||
aria-pressed={theme === 'black'}
|
||||
style={{ flex: 1, backgroundColor: "rgba(0, 0, 0)", borderLeft: "2px solid rgba(255, 255, 255, 0.1)", borderTop: "2px solid rgba(255, 255, 255, 0.1)", borderBottom: "2px solid rgba(255, 255, 255, 0.1)" }}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
style={{ flex: 1, backgroundColor: "rgba(0, 0, 0, 0.5)", borderLeft: "2px solid rgba(255, 255, 255, 0.1)", borderTop: "2px solid rgba(255, 255, 255, 0.1)", borderBottom: "2px solid rgba(255, 255, 255, 0.1)" }}
|
||||
onClick={() => setTheme('black')}
|
||||
className={theme === 'black' ? 'ring-1 ring-white/20' : ''}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
/>
|
||||
<motion.button
|
||||
aria-label="Switch to red theme"
|
||||
aria-pressed={theme === 'red'}
|
||||
style={{ flex: 1, backgroundColor: "rgba(255, 0, 0)" }}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
onClick={() => setTheme('red')}
|
||||
className={theme === 'red' ? 'ring-1 ring-white/20' : ''}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
/>
|
||||
<motion.button
|
||||
aria-label="Switch to gold theme"
|
||||
aria-pressed={theme === 'gold'}
|
||||
style={{ flex: 1, backgroundColor: "rgba(255, 204, 0)" }}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
onClick={() => setTheme('gold')}
|
||||
className={theme === 'gold' ? 'ring-1 ring-white/20' : ''}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
/>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
const MobileMenu = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const menuRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleEscKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleEscKey);
|
||||
return () => document.removeEventListener('keydown', handleEscKey);
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="p-2 theme-accent rounded-lg theme-border"
|
||||
aria-label="Open menu"
|
||||
aria-expanded={isOpen}
|
||||
whileHover={{ scale: 1.03 }}
|
||||
whileTap={{ scale: 0.97 }}
|
||||
>
|
||||
<svg className="w-5 h-5 theme-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</motion.button>
|
||||
|
||||
<AnimatePresence mode="wait">
|
||||
{isOpen && (
|
||||
<>
|
||||
{/* Overlay - increased opacity */}
|
||||
<motion.div
|
||||
className="fixed inset-0 bg-black z-[90]"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.95 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 300,
|
||||
damping: 30
|
||||
}}
|
||||
onClick={() => setIsOpen(false)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
{/* Menu container - full height */}
|
||||
<motion.div
|
||||
className="fixed top-0 bottom-0 right-0 w-full sm:w-80 z-[100] h-[100dvh]"
|
||||
ref={menuRef}
|
||||
initial={{ x: "100%" }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: "100%" }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 30,
|
||||
stiffness: 300,
|
||||
mass: 1
|
||||
}}
|
||||
>
|
||||
{/* Glass background and content wrapper with explicit backdrop filter */}
|
||||
<div className="nav-glass w-full h-full flex flex-col shadow-2xl"
|
||||
style={{
|
||||
backdropFilter: 'blur(16px)',
|
||||
WebkitBackdropFilter: 'blur(16px)'
|
||||
}}>
|
||||
{/* Close button */}
|
||||
<motion.button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="absolute top-4 right-4 p-2 theme-accent rounded-lg theme-border z-[110]"
|
||||
aria-label="Close menu"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 300,
|
||||
damping: 20,
|
||||
delay: 0.2
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<svg className="w-5 h-5 theme-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</motion.button>
|
||||
|
||||
{/* Content wrapper with padding and scrolling */}
|
||||
<div className="w-full h-full p-6 pt-16 flex flex-col overflow-y-auto">
|
||||
{/* Navigation links */}
|
||||
<nav className="mb-8">
|
||||
<motion.div
|
||||
className="flex flex-col space-y-2"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 100,
|
||||
damping: 20,
|
||||
delay: 0.1
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{ href: "#work", label: "Work" },
|
||||
{ href: "#about", label: "About" }
|
||||
].map((item, index) => (
|
||||
<motion.a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="p-4 text-xl theme-primary rounded-lg theme-accent flex items-center hover:theme-bg-05 transition-colors"
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 200,
|
||||
damping: 20,
|
||||
delay: 0.2 + index * 0.1
|
||||
}}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
<svg className="ml-auto w-5 h-5 theme-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</motion.a>
|
||||
))}
|
||||
</motion.div>
|
||||
</nav>
|
||||
|
||||
{/* Footer content */}
|
||||
<motion.div
|
||||
className="mt-auto"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 100,
|
||||
damping: 20,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<div className="pt-4 border-t theme-border">
|
||||
<motion.a
|
||||
href="mailto:jleibl@proton.me"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="flex items-center justify-center gap-3 w-full py-4 px-6 theme-primary border theme-border rounded-lg hover:theme-bg-05 transition-colors"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span>Contact Me</span>
|
||||
</motion.a>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 flex justify-center pb-6">
|
||||
<GermanyText />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
useSmoothScroll();
|
||||
|
||||
@ -149,6 +344,98 @@ export default function Home() {
|
||||
.red .theme-border { border-color: rgba(255, 0, 0, 0.1); }
|
||||
.gold .theme-border { border-color: rgba(255, 204, 0, 0.1); }
|
||||
|
||||
.black .theme-text-40 { color: rgba(255, 255, 255, 0.4); }
|
||||
.red .theme-text-40 { color: rgba(255, 0, 0, 0.4); }
|
||||
.gold .theme-text-40 { color: rgba(255, 204, 0, 0.4); }
|
||||
|
||||
.black .theme-text-70 { color: rgba(255, 255, 255, 0.7); }
|
||||
.red .theme-text-70 { color: rgba(255, 0, 0, 0.7); }
|
||||
.gold .theme-text-70 { color: rgba(255, 204, 0, 0.7); }
|
||||
|
||||
.black .theme-text-90 { color: rgba(255, 255, 255, 0.9); }
|
||||
.red .theme-text-90 { color: rgba(255, 0, 0, 0.9); }
|
||||
.gold .theme-text-90 { color: rgba(255, 204, 0, 0.9); }
|
||||
|
||||
.black .theme-bg-05 { background-color: rgba(255, 255, 255, 0.05); }
|
||||
.red .theme-bg-05 { background-color: rgba(255, 0, 0, 0.05); }
|
||||
.gold .theme-bg-05 { background-color: rgba(255, 204, 0, 0.05); }
|
||||
|
||||
.black .nav-glass {
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.red .nav-glass {
|
||||
background: rgba(26, 0, 0, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(26, 0, 0, 0.1);
|
||||
}
|
||||
.gold .nav-glass {
|
||||
background: rgba(26, 20, 0, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(26, 20, 0, 0.1);
|
||||
}
|
||||
|
||||
.black .nav-item-active {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-bottom: 2px solid rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.red .nav-item-active {
|
||||
background: rgba(255, 0, 0, 0.05);
|
||||
border-bottom: 2px solid rgba(255, 0, 0, 0.8);
|
||||
}
|
||||
.gold .nav-item-active {
|
||||
background: rgba(255, 204, 0, 0.05);
|
||||
border-bottom: 2px solid rgba(255, 204, 0, 0.8);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.black .nav-item::after {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.red .nav-item::after {
|
||||
background-color: rgba(255, 0, 0, 0.8);
|
||||
}
|
||||
.gold .nav-item::after {
|
||||
background-color: rgba(255, 204, 0, 0.8);
|
||||
}
|
||||
|
||||
.nav-item:hover::after {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.black .theme-bg-gradient { background: linear-gradient(to bottom, rgba(255, 255, 255, 0.02), transparent); }
|
||||
.red .theme-bg-gradient { background: linear-gradient(to bottom, rgba(255, 0, 0, 0.02), transparent); }
|
||||
.gold .theme-bg-gradient { background: linear-gradient(to bottom, rgba(255, 204, 0, 0.02), transparent); }
|
||||
|
||||
.black .theme-button-primary { background-color: rgba(255, 255, 255, 0.9); color: #0A0A0A; }
|
||||
.red .theme-button-primary { background-color: rgba(255, 0, 0, 0.9); color: #1A0000; }
|
||||
.gold .theme-button-primary { background-color: rgba(255, 204, 0, 0.9); color: #1A1400; }
|
||||
|
||||
.black .theme-button-primary:hover { background-color: rgba(255, 255, 255, 0.8); }
|
||||
.red .theme-button-primary:hover { background-color: rgba(255, 0, 0, 0.8); }
|
||||
.gold .theme-button-primary:hover { background-color: rgba(255, 204, 0, 0.8); }
|
||||
|
||||
.black .theme-button-secondary { border-color: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.9); }
|
||||
.red .theme-button-secondary { border-color: rgba(255, 0, 0, 0.1); color: rgba(255, 0, 0, 0.9); }
|
||||
.gold .theme-button-secondary { border-color: rgba(255, 204, 0, 0.1); color: rgba(255, 204, 0, 0.9); }
|
||||
|
||||
.black .theme-button-secondary:hover { background-color: rgba(255, 255, 255, 0.05); }
|
||||
.red .theme-button-secondary:hover { background-color: rgba(255, 0, 0, 0.05); }
|
||||
.gold .theme-button-secondary:hover { background-color: rgba(255, 204, 0, 0.05); }
|
||||
|
||||
.theme-transition {
|
||||
transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
@ -191,41 +478,91 @@ export default function Home() {
|
||||
<link rel="canonical" href="https://jleibl.net/" />
|
||||
</Head>
|
||||
|
||||
<header className="fixed top-0 left-0 right-0 z-40 backdrop-blur-md bg-black/20 border-b theme-border theme-transition" role="banner">
|
||||
<nav className="max-w-screen-xl mx-auto px-4 sm:px-8 py-3 sm:py-4 flex flex-wrap justify-between items-center font-['DM_Sans']" role="navigation" aria-label="Main navigation">
|
||||
<Link href="/" className="text-xl sm:text-xl font-medium tracking-tight theme-primary theme-transition" aria-label="Home">
|
||||
JL
|
||||
</Link>
|
||||
<div className="flex items-center gap-4 sm:gap-12 text-sm sm:text-base tracking-wide">
|
||||
<a href="#work" className="theme-secondary hover:theme-primary theme-transition" aria-label="View my work">
|
||||
Work
|
||||
</a>
|
||||
<a href="#about" className="theme-secondary hover:theme-primary theme-transition" aria-label="About me">
|
||||
About
|
||||
</a>
|
||||
<a
|
||||
href="mailto:jleibl@proton.me"
|
||||
className="px-4 sm:px-6 py-2 sm:py-2.5 text-sm sm:text-base theme-accent theme-border hover:bg-white/[0.06] theme-transition rounded-lg sm:rounded-none"
|
||||
aria-label="Contact me via email"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<motion.header
|
||||
className="fixed top-0 left-0 right-0 z-40 nav-glass backdrop-blur-lg theme-transition"
|
||||
role="banner"
|
||||
initial={{ y: -100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 25,
|
||||
stiffness: 100,
|
||||
mass: 1,
|
||||
delay: 0.2
|
||||
}}
|
||||
>
|
||||
<div className="max-w-screen-xl mx-auto">
|
||||
<nav className="py-5 px-6 sm:px-8 flex justify-between items-center font-['DM_Sans']" role="navigation" aria-label="Main navigation">
|
||||
<div className="flex items-center">
|
||||
<Link href="/" className="flex items-center gap-2 group" aria-label="Home">
|
||||
<div className="relative w-10 h-10 rounded-full theme-accent flex items-center justify-center border theme-border overflow-hidden group-hover:border-opacity-80 transition-all duration-300">
|
||||
<span className="text-lg font-bold tracking-tight theme-primary theme-transition">JL</span>
|
||||
<div className="absolute inset-0 theme-bg-05 opacity-0 group-hover:opacity-100 transition-opacity duration-500"/>
|
||||
</div>
|
||||
<span className="text-lg font-medium tracking-tight theme-primary theme-transition hidden sm:block">
|
||||
Jan-Marlon Leibl
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex items-center">
|
||||
<div className="flex bg-black/10 backdrop-blur-md rounded-full overflow-hidden theme-border border divide-x divide-white/5">
|
||||
{["Work", "About"].map((item) => (
|
||||
<a
|
||||
key={item}
|
||||
href={`#${item.toLowerCase()}`}
|
||||
className="nav-item px-6 py-2 theme-secondary hover:theme-primary theme-transition"
|
||||
aria-label={`View my ${item.toLowerCase()}`}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="ml-6">
|
||||
<motion.a
|
||||
href="mailto:jleibl@proton.me"
|
||||
className="flex items-center gap-2 px-6 py-2 theme-bg-05 theme-primary border theme-border rounded-full hover:bg-opacity-100 transition-all duration-300 group"
|
||||
aria-label="Contact me via email"
|
||||
whileHover={{ scale: 1.03 }}
|
||||
whileTap={{ scale: 0.97 }}
|
||||
>
|
||||
<svg className="w-4 h-4 theme-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span className="text-sm">Contact</span>
|
||||
<div className="w-0 overflow-hidden group-hover:w-4 transition-all duration-500">
|
||||
<svg className="w-4 h-4 theme-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
</motion.a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex md:hidden">
|
||||
<MobileMenu />
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="h-px w-full theme-border opacity-50"></div>
|
||||
</motion.header>
|
||||
|
||||
<section className="min-h-[100dvh] flex items-center px-4 sm:px-8 relative pt-16 sm:pt-0">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-white/[0.02] via-white/[0.01] to-transparent"></div>
|
||||
<section className="min-h-[100dvh] flex items-center px-4 sm:px-8 relative pt-24 sm:pt-8">
|
||||
<div className="absolute inset-0 theme-bg-gradient"></div>
|
||||
<div className="max-w-screen-xl w-full relative pt-8 sm:pt-24">
|
||||
<FadeIn className="space-y-8 sm:space-y-16">
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<div className="inline-flex items-center gap-2.5 px-4 py-2 rounded-full theme-accent theme-border hover:theme-border theme-transition">
|
||||
<div className="inline-flex items-center gap-2.5 px-4 py-2 rounded-full theme-accent theme-border theme-transition">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></span>
|
||||
<span className="theme-secondary uppercase tracking-[0.2em] text-xs sm:text-sm font-['Instrument_Sans']">Available for Work</span>
|
||||
</div>
|
||||
<div className="space-y-2 sm:space-y-3">
|
||||
<h2 className="font-['DM_Sans'] text-xl sm:text-3xl theme-secondary tracking-wide font-medium theme-transition">Jan-Marlon Leibl</h2>
|
||||
<h2 className="font-['DM_Sans'] text-xl sm:text-3xl theme-secondary tracking-wide font-medium theme-transition">
|
||||
Jan-Marlon Leibl
|
||||
</h2>
|
||||
<div>
|
||||
<h1 className="font-['DM_Sans'] text-4xl sm:text-6xl md:text-7xl lg:text-8xl font-bold tracking-tight leading-[1.1] sm:leading-[0.95] max-w-4xl theme-primary theme-transition">
|
||||
Software Developer
|
||||
@ -239,9 +576,11 @@ export default function Home() {
|
||||
Passionate about creating digital experiences, with a focus on PHP and modern web technologies.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||
<a
|
||||
<motion.a
|
||||
href="#work"
|
||||
className="w-full sm:w-auto text-center inline-flex items-center justify-center gap-2 px-6 sm:px-8 py-3 sm:py-4 bg-white text-black rounded-lg sm:rounded-full hover:bg-white/90 transition-colors text-sm sm:text-base tracking-wide font-medium group"
|
||||
className="w-full sm:w-auto text-center inline-flex items-center justify-center gap-2 px-6 sm:px-8 py-3 sm:py-4 theme-button-primary rounded-lg sm:rounded-full transition-colors text-sm sm:text-base tracking-wide font-medium group"
|
||||
whileHover={{ scale: 1.03 }}
|
||||
whileTap={{ scale: 0.97 }}
|
||||
>
|
||||
View My Work
|
||||
<svg
|
||||
@ -253,10 +592,12 @@ export default function Home() {
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="mailto:jleibl@proton.me"
|
||||
className="w-full sm:w-auto text-center inline-flex items-center justify-center gap-2 px-6 sm:px-8 py-3 sm:py-4 border border-white/10 rounded-lg sm:rounded-full hover:bg-white/[0.05] transition-colors text-sm sm:text-base tracking-wide group"
|
||||
className="w-full sm:w-auto text-center inline-flex items-center justify-center gap-2 px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full theme-button-secondary hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide group"
|
||||
whileHover={{ scale: 1.03 }}
|
||||
whileTap={{ scale: 0.97 }}
|
||||
>
|
||||
Get in Touch
|
||||
<svg
|
||||
@ -268,41 +609,51 @@ export default function Home() {
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
</motion.a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-8 font-['Instrument_Sans'] text-sm sm:text-base tracking-wide border-t border-white/10 pt-6 sm:pt-8">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-8 font-['Instrument_Sans'] text-sm sm:text-base tracking-wide border-t theme-border pt-6 sm:pt-8">
|
||||
<div className="space-y-1.5 sm:space-y-2.5 group">
|
||||
<span className="text-white/40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Email</span>
|
||||
<span className="theme-text-40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Email</span>
|
||||
<a
|
||||
href="mailto:jleibl@proton.me"
|
||||
className="hover:text-white/90 transition-colors flex items-center gap-2 sm:gap-2.5 group-hover:gap-3 duration-300"
|
||||
className="theme-text-90 hover:theme-primary transition-colors flex items-center gap-2 sm:gap-2.5 group-hover:gap-3 duration-300"
|
||||
>
|
||||
jleibl@proton.me
|
||||
<svg className="w-4 h-4 sm:w-5 sm:h-5 opacity-0 group-hover:opacity-100 transition-all -translate-x-4 group-hover:translate-x-0 duration-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<svg
|
||||
className="w-4 h-4 sm:w-5 sm:h-5 opacity-0 group-hover:opacity-100 transition-all -translate-x-4 group-hover:translate-x-0 duration-300"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div className="space-y-1.5 sm:space-y-2.5">
|
||||
<span className="text-white/40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Role</span>
|
||||
<span className="text-white/90">Fullstack Developer</span>
|
||||
<span className="theme-text-40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Role</span>
|
||||
<span className="theme-text-90">
|
||||
Fullstack Developer
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-1.5 sm:space-y-2.5">
|
||||
<span className="text-white/40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Experience</span>
|
||||
<span className="text-white/90">5+ Years</span>
|
||||
<span className="theme-text-40 block uppercase tracking-[0.2em] text-xs sm:text-sm">Experience</span>
|
||||
<span className="theme-text-90">
|
||||
5+ Years
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="about" className="py-20 sm:py-40 px-4 sm:px-8 bg-gradient-to-b from-white/[0.02] to-transparent">
|
||||
<section id="about" className="py-20 sm:py-40 px-4 sm:px-8 theme-bg-gradient">
|
||||
<div className="max-w-screen-xl mx-auto">
|
||||
<FadeIn>
|
||||
<div className="flex items-baseline gap-4 mb-12 sm:mb-24">
|
||||
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight">About</h2>
|
||||
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">About</h2>
|
||||
<div className="h-px flex-grow bg-white/10 relative top-[-4px]"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
@ -310,7 +661,7 @@ export default function Home() {
|
||||
<div className="grid md:grid-cols-[1fr,2fr] gap-12 sm:gap-24">
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<FadeIn>
|
||||
<div className="aspect-square bg-gradient-to-tr from-white/[0.05] to-white/[0.02] rounded-2xl overflow-hidden border border-white/[0.05] hover:border-white/10 transition-colors mx-auto md:mx-0 max-w-[280px] md:max-w-none">
|
||||
<div className="aspect-square bg-gradient-to-tr theme-bg-05 rounded-2xl overflow-hidden border theme-border hover:border-white/10 transition-colors mx-auto md:mx-0 max-w-[280px] md:max-w-none">
|
||||
<Image
|
||||
src="/profile-image.jpg"
|
||||
alt="Jan-Marlon Leibl - Fullstack Software Developer"
|
||||
@ -323,8 +674,8 @@ export default function Home() {
|
||||
</FadeIn>
|
||||
<FadeIn delay={0.1}>
|
||||
<div className="space-y-2 text-center md:text-left">
|
||||
<h3 className="font-['DM_Sans'] text-xl sm:text-2xl font-medium">Jan-Marlon Leibl</h3>
|
||||
<p className="font-['Instrument_Sans'] text-white/70 text-base sm:text-lg">Fullstack Developer</p>
|
||||
<h3 className="font-['DM_Sans'] text-xl sm:text-2xl font-medium theme-primary">Jan-Marlon Leibl</h3>
|
||||
<p className="font-['Instrument_Sans'] theme-text-70 text-base sm:text-lg">Fullstack Developer</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
@ -332,10 +683,10 @@ export default function Home() {
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="space-y-10 sm:space-y-16 font-['Instrument_Sans']">
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<p className="text-xl sm:text-2xl text-white/90 leading-relaxed">
|
||||
<p className="text-xl sm:text-2xl theme-text-90 leading-relaxed">
|
||||
Hello! I'm Jan-Marlon, but please call me Jan. I started my journey in programming at the age of 11 with C#, fascinated by a desktop application my friend created.
|
||||
</p>
|
||||
<p className="text-base sm:text-xl text-white/70 leading-relaxed">
|
||||
<p className="text-base sm:text-xl theme-text-70 leading-relaxed">
|
||||
Today, I specialize in PHP and TypeScript development, constantly pushing the boundaries of what's possible on the web. My journey has led me from creating simple applications to developing complex systems used by thousands.
|
||||
</p>
|
||||
</div>
|
||||
@ -343,16 +694,16 @@ export default function Home() {
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-10 sm:gap-16">
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<div className="flex items-baseline gap-4">
|
||||
<h3 className="text-sm sm:text-base text-white/40 uppercase tracking-[0.2em]">Technologies</h3>
|
||||
<div className="h-px flex-grow bg-white/10"></div>
|
||||
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Technologies</h3>
|
||||
<div className="h-px flex-grow theme-border"></div>
|
||||
</div>
|
||||
<ul className="space-y-4 sm:space-y-5">
|
||||
{['PHP', 'JavaScript', 'MySQL', 'React'].map((tech) => (
|
||||
<li
|
||||
key={tech}
|
||||
className="text-base sm:text-lg group flex items-center gap-3 hover:text-white/90 transition-colors cursor-default"
|
||||
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default"
|
||||
>
|
||||
<span className="w-2 h-2 rounded-full bg-white/40 group-hover:bg-white/90 transition-colors"></span>
|
||||
<span className="w-2 h-2 rounded-full theme-text-40 group-hover:theme-text-90 transition-colors"></span>
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
@ -361,8 +712,8 @@ export default function Home() {
|
||||
|
||||
<div className="space-y-6 sm:space-y-8">
|
||||
<div className="flex items-baseline gap-4">
|
||||
<h3 className="text-sm sm:text-base text-white/40 uppercase tracking-[0.2em]">Interests</h3>
|
||||
<div className="h-px flex-grow bg-white/10"></div>
|
||||
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Interests</h3>
|
||||
<div className="h-px flex-grow theme-border"></div>
|
||||
</div>
|
||||
<ul className="space-y-4 sm:space-y-5">
|
||||
{[
|
||||
@ -373,9 +724,9 @@ export default function Home() {
|
||||
].map((interest) => (
|
||||
<li
|
||||
key={interest}
|
||||
className="text-base sm:text-lg group flex items-center gap-3 hover:text-white/90 transition-colors cursor-default"
|
||||
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default"
|
||||
>
|
||||
<span className="w-2 h-2 rounded-full bg-white/40 group-hover:bg-white/90 transition-colors"></span>
|
||||
<span className="w-2 h-2 rounded-full theme-text-40 group-hover:theme-text-90 transition-colors"></span>
|
||||
{interest}
|
||||
</li>
|
||||
))}
|
||||
@ -384,22 +735,24 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="pt-6 sm:pt-8 flex flex-col sm:flex-row gap-3 sm:gap-6">
|
||||
<a
|
||||
<motion.a
|
||||
href="https://github.com/AtomicWasTaken"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border border-white/10 rounded-lg sm:rounded-full hover:bg-white/[0.05] transition-colors text-sm sm:text-base tracking-wide font-medium"
|
||||
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
>
|
||||
View GitHub
|
||||
</a>
|
||||
<a
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="https://www.linkedin.com/in/janmarlonleibl/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border border-white/10 rounded-lg sm:rounded-full hover:bg-white/[0.05] transition-colors text-sm sm:text-base tracking-wide font-medium"
|
||||
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
>
|
||||
Connect on LinkedIn
|
||||
</a>
|
||||
</motion.a>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
@ -408,14 +761,14 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
<section id="work" className="py-20 sm:py-40 px-4 sm:px-8 relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-white/[0.02] to-transparent pointer-events-none"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-white/[0.02] to-transparent pointer-events-none theme-bg-gradient"></div>
|
||||
<div className="max-w-screen-xl mx-auto relative">
|
||||
<FadeIn>
|
||||
<div className="flex flex-col gap-3 mb-12 sm:mb-24">
|
||||
<span className="text-white/40 uppercase tracking-[0.2em] text-sm sm:text-base font-['Instrument_Sans']">Portfolio</span>
|
||||
<span className="theme-text-40 uppercase tracking-[0.2em] text-sm sm:text-base font-['Instrument_Sans']">Portfolio</span>
|
||||
<div className="flex items-baseline gap-4">
|
||||
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight">Selected Work</h2>
|
||||
<div className="h-px flex-grow bg-white/10"></div>
|
||||
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">Selected Work</h2>
|
||||
<div className="h-px flex-grow theme-border"></div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
@ -450,33 +803,33 @@ export default function Home() {
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="group relative">
|
||||
<div className="absolute top-0 left-0 right-0 flex items-center gap-4">
|
||||
<div className="font-['Instrument_Sans'] text-sm sm:text-base text-white/40 uppercase tracking-[0.2em] py-2 pr-4">
|
||||
<div className="font-['Instrument_Sans'] text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em] py-2 pr-4">
|
||||
{project.year}
|
||||
</div>
|
||||
<div className="h-px flex-grow bg-white/10"></div>
|
||||
<div className="h-px flex-grow theme-border"></div>
|
||||
</div>
|
||||
|
||||
<div className="pt-12 sm:pt-16 grid grid-cols-1 lg:grid-cols-[1.5fr,1fr] gap-6 sm:gap-16">
|
||||
<div className="space-y-4 sm:space-y-8">
|
||||
<h3 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight text-white group-hover:text-white/90 transition-colors">
|
||||
<h3 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary group-hover:theme-text-90 transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
<p className="font-['Instrument_Sans'] text-base sm:text-xl text-white/70 leading-relaxed group-hover:text-white/80 transition-colors max-w-xl">
|
||||
<p className="font-['Instrument_Sans'] text-base sm:text-xl theme-text-70 leading-relaxed group-hover:theme-text-70 transition-colors max-w-xl">
|
||||
{project.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 sm:space-y-12">
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<h4 className="font-['Instrument_Sans'] text-sm sm:text-base text-white/40 uppercase tracking-[0.2em]">Technologies</h4>
|
||||
<h4 className="font-['Instrument_Sans'] text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Technologies</h4>
|
||||
<div className="flex flex-wrap items-center gap-3 sm:gap-4">
|
||||
{project.tags.map((tag, tagIndex) => (
|
||||
<span
|
||||
key={tagIndex}
|
||||
className="text-sm sm:text-base text-white/70 font-['Instrument_Sans'] tracking-wide py-1 sm:py-2 group-hover:text-white/80 transition-colors"
|
||||
className="text-sm sm:text-base theme-text-70 font-['Instrument_Sans'] tracking-wide py-1 sm:py-2 group-hover:theme-text-90 transition-colors"
|
||||
>
|
||||
{tag}{tagIndex !== project.tags.length - 1 && (
|
||||
<span className="mx-3 sm:mx-4 text-white/20 select-none">•</span>
|
||||
<span className="mx-3 sm:mx-4 theme-text-40 select-none">•</span>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
@ -485,7 +838,7 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute -inset-x-4 sm:-inset-x-8 -inset-y-4 sm:-inset-y-6 rounded-2xl sm:rounded-3xl border border-white/0 group-hover:border-white/5 transition-colors"></div>
|
||||
<div className="absolute -inset-x-4 sm:-inset-x-8 -inset-y-4 sm:-inset-y-6 rounded-2xl sm:rounded-3xl border border-white/0 group-hover:theme-border transition-colors"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
@ -493,13 +846,13 @@ export default function Home() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer role="contentinfo" className="mt-20 sm:mt-32 bg-gradient-to-b from-white/[0.02] via-white/[0.03] to-white/[0.02] border-t border-white/[0.05]">
|
||||
<footer role="contentinfo" className="mt-20 sm:mt-32 theme-bg-gradient border-t theme-border">
|
||||
<div className="max-w-screen-xl mx-auto px-4 sm:px-8 py-20 sm:py-32">
|
||||
<div className="grid gap-16 sm:gap-24">
|
||||
<FadeIn>
|
||||
<div className="text-center space-y-4 sm:space-y-5">
|
||||
<h2 className="font-['DM_Sans'] text-4xl sm:text-7xl font-semibold tracking-tight">Let's Connect</h2>
|
||||
<p className="font-['Instrument_Sans'] text-white/70 text-lg sm:text-2xl max-w-2xl mx-auto">
|
||||
<h2 className="font-['DM_Sans'] text-4xl sm:text-7xl font-semibold tracking-tight theme-primary">Let's Connect</h2>
|
||||
<p className="font-['Instrument_Sans'] theme-text-70 text-lg sm:text-2xl max-w-2xl mx-auto">
|
||||
Always interested in new opportunities and collaborations.
|
||||
</p>
|
||||
</div>
|
||||
@ -509,20 +862,19 @@ export default function Home() {
|
||||
<div className="flex flex-col items-center gap-8 sm:gap-12">
|
||||
<a
|
||||
href="mailto:jleibl@proton.me"
|
||||
className="group flex items-center gap-2 sm:gap-3 text-xl sm:text-4xl text-white/90 hover:text-white transition-colors"
|
||||
className="group flex items-center gap-2 sm:gap-3 text-xl sm:text-4xl theme-text-90 hover:theme-primary transition-colors"
|
||||
>
|
||||
jleibl@proton.me
|
||||
<svg className="w-5 h-5 sm:w-8 sm:h-8 opacity-0 group-hover:opacity-100 transition-all -translate-x-4 group-hover:translate-x-0 duration-300" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="pt-6 sm:pt-12 text-center">
|
||||
<p className="text-white/40 text-xs sm:text-sm tracking-[0.1em]">
|
||||
<p className="theme-text-40 text-xs sm:text-sm tracking-[0.1em]">
|
||||
© {new Date().getFullYear()} Jan-Marlon Leibl. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user