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/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/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/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=="],
|
"@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-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.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=="],
|
"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=="],
|
"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=="],
|
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||||
|
|
||||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"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-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=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"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=="],
|
"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=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
"stable-hash": ["stable-hash@0.0.4", "", {}, "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g=="],
|
"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=="],
|
"@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/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=="],
|
"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=="],
|
"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-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=="],
|
"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=="],
|
"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=="],
|
"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/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=="],
|
"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/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/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=="],
|
"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=="],
|
"@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/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=="],
|
"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-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/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-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",
|
"@fontsource/instrument-sans": "^5.1.1",
|
||||||
"@studio-freight/lenis": "^1.0.42",
|
"@studio-freight/lenis": "^1.0.42",
|
||||||
"framer-motion": "^12.4.7",
|
"framer-motion": "^12.4.7",
|
||||||
"next": "14.1.0",
|
"next": "15.1.7",
|
||||||
"react": "^18.2.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-intersection-observer": "^9.15.1"
|
"react-intersection-observer": "^9.15.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^19",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"eslint": "^8",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "14.1.0",
|
"eslint-config-next": "15.1.7",
|
||||||
"@eslint/eslintrc": "^3"
|
"@eslint/eslintrc": "^3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { useEffect, createContext, useContext, useState } from 'react';
|
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 { useInView } from 'react-intersection-observer';
|
||||||
import Lenis from '@studio-freight/lenis';
|
import Lenis from '@studio-freight/lenis';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import '@fontsource/dm-sans/400.css';
|
import '@fontsource/dm-sans/400.css';
|
||||||
import '@fontsource/dm-sans/500.css';
|
import '@fontsource/dm-sans/500.css';
|
||||||
@ -67,7 +68,12 @@ const FadeIn = ({ children, className = "", delay = 0 }: { children: React.React
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={inView ? { opacity: 1, y: 0 } : { 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}
|
className={className}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@ -93,36 +99,225 @@ const GermanyText = () => {
|
|||||||
opacity: 0.6,
|
opacity: 0.6,
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}}
|
}}
|
||||||
whileHover={{ opacity: 0.8 }}
|
whileHover={{
|
||||||
|
opacity: 0.8,
|
||||||
|
transition: { duration: 0.2 }
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<motion.button
|
<motion.button
|
||||||
aria-label="Switch to dark theme"
|
aria-label="Switch to dark theme"
|
||||||
aria-pressed={theme === 'black'}
|
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)" }}
|
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)" }}
|
||||||
whileHover={{ scale: 1.1 }}
|
|
||||||
onClick={() => setTheme('black')}
|
onClick={() => setTheme('black')}
|
||||||
className={theme === 'black' ? 'ring-1 ring-white/20' : ''}
|
className={theme === 'black' ? 'ring-1 ring-white/20' : ''}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
/>
|
/>
|
||||||
<motion.button
|
<motion.button
|
||||||
aria-label="Switch to red theme"
|
aria-label="Switch to red theme"
|
||||||
aria-pressed={theme === 'red'}
|
aria-pressed={theme === 'red'}
|
||||||
style={{ flex: 1, backgroundColor: "rgba(255, 0, 0)" }}
|
style={{ flex: 1, backgroundColor: "rgba(255, 0, 0)" }}
|
||||||
whileHover={{ scale: 1.1 }}
|
|
||||||
onClick={() => setTheme('red')}
|
onClick={() => setTheme('red')}
|
||||||
className={theme === 'red' ? 'ring-1 ring-white/20' : ''}
|
className={theme === 'red' ? 'ring-1 ring-white/20' : ''}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
/>
|
/>
|
||||||
<motion.button
|
<motion.button
|
||||||
aria-label="Switch to gold theme"
|
aria-label="Switch to gold theme"
|
||||||
aria-pressed={theme === 'gold'}
|
aria-pressed={theme === 'gold'}
|
||||||
style={{ flex: 1, backgroundColor: "rgba(255, 204, 0)" }}
|
style={{ flex: 1, backgroundColor: "rgba(255, 204, 0)" }}
|
||||||
whileHover={{ scale: 1.1 }}
|
|
||||||
onClick={() => setTheme('gold')}
|
onClick={() => setTheme('gold')}
|
||||||
className={theme === 'gold' ? 'ring-1 ring-white/20' : ''}
|
className={theme === 'gold' ? 'ring-1 ring-white/20' : ''}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</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() {
|
export default function Home() {
|
||||||
useSmoothScroll();
|
useSmoothScroll();
|
||||||
|
|
||||||
@ -149,6 +344,98 @@ export default function Home() {
|
|||||||
.red .theme-border { border-color: rgba(255, 0, 0, 0.1); }
|
.red .theme-border { border-color: rgba(255, 0, 0, 0.1); }
|
||||||
.gold .theme-border { border-color: rgba(255, 204, 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 {
|
.theme-transition {
|
||||||
transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
|
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/" />
|
<link rel="canonical" href="https://jleibl.net/" />
|
||||||
</Head>
|
</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">
|
<motion.header
|
||||||
<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">
|
className="fixed top-0 left-0 right-0 z-40 nav-glass backdrop-blur-lg theme-transition"
|
||||||
<Link href="/" className="text-xl sm:text-xl font-medium tracking-tight theme-primary theme-transition" aria-label="Home">
|
role="banner"
|
||||||
JL
|
initial={{ y: -100, opacity: 0 }}
|
||||||
</Link>
|
animate={{ y: 0, opacity: 1 }}
|
||||||
<div className="flex items-center gap-4 sm:gap-12 text-sm sm:text-base tracking-wide">
|
transition={{
|
||||||
<a href="#work" className="theme-secondary hover:theme-primary theme-transition" aria-label="View my work">
|
type: "spring",
|
||||||
Work
|
damping: 25,
|
||||||
</a>
|
stiffness: 100,
|
||||||
<a href="#about" className="theme-secondary hover:theme-primary theme-transition" aria-label="About me">
|
mass: 1,
|
||||||
About
|
delay: 0.2
|
||||||
</a>
|
}}
|
||||||
<a
|
>
|
||||||
href="mailto:jleibl@proton.me"
|
<div className="max-w-screen-xl mx-auto">
|
||||||
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"
|
<nav className="py-5 px-6 sm:px-8 flex justify-between items-center font-['DM_Sans']" role="navigation" aria-label="Main navigation">
|
||||||
aria-label="Contact me via email"
|
<div className="flex items-center">
|
||||||
>
|
<Link href="/" className="flex items-center gap-2 group" aria-label="Home">
|
||||||
Contact
|
<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">
|
||||||
</a>
|
<span className="text-lg font-bold tracking-tight theme-primary theme-transition">JL</span>
|
||||||
</div>
|
<div className="absolute inset-0 theme-bg-05 opacity-0 group-hover:opacity-100 transition-opacity duration-500"/>
|
||||||
</nav>
|
</div>
|
||||||
</header>
|
<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">
|
<section className="min-h-[100dvh] flex items-center px-4 sm:px-8 relative pt-24 sm:pt-8">
|
||||||
<div className="absolute inset-0 bg-gradient-to-b from-white/[0.02] via-white/[0.01] to-transparent"></div>
|
<div className="absolute inset-0 theme-bg-gradient"></div>
|
||||||
<div className="max-w-screen-xl w-full relative pt-8 sm:pt-24">
|
<div className="max-w-screen-xl w-full relative pt-8 sm:pt-24">
|
||||||
<FadeIn className="space-y-8 sm:space-y-16">
|
<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="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="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>
|
<span className="theme-secondary uppercase tracking-[0.2em] text-xs sm:text-sm font-['Instrument_Sans']">Available for Work</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 sm:space-y-3">
|
<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>
|
<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">
|
<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
|
Software Developer
|
||||||
@ -239,9 +576,11 @@ export default function Home() {
|
|||||||
Passionate about creating digital experiences, with a focus on PHP and modern web technologies.
|
Passionate about creating digital experiences, with a focus on PHP and modern web technologies.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||||
<a
|
<motion.a
|
||||||
href="#work"
|
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
|
View My Work
|
||||||
<svg
|
<svg
|
||||||
@ -253,10 +592,12 @@ export default function Home() {
|
|||||||
>
|
>
|
||||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</motion.a>
|
||||||
<a
|
<motion.a
|
||||||
href="mailto:jleibl@proton.me"
|
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
|
Get in Touch
|
||||||
<svg
|
<svg
|
||||||
@ -268,41 +609,51 @@ export default function Home() {
|
|||||||
>
|
>
|
||||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</motion.a>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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
|
<a
|
||||||
href="mailto:jleibl@proton.me"
|
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
|
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"/>
|
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 sm:space-y-2.5">
|
<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="theme-text-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-90">
|
||||||
|
Fullstack Developer
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 sm:space-y-2.5">
|
<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="theme-text-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-90">
|
||||||
|
5+ Years
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</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">
|
<div className="max-w-screen-xl mx-auto">
|
||||||
<FadeIn>
|
<FadeIn>
|
||||||
<div className="flex items-baseline gap-4 mb-12 sm:mb-24">
|
<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 className="h-px flex-grow bg-white/10 relative top-[-4px]"></div>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
@ -310,7 +661,7 @@ export default function Home() {
|
|||||||
<div className="grid md:grid-cols-[1fr,2fr] gap-12 sm:gap-24">
|
<div className="grid md:grid-cols-[1fr,2fr] gap-12 sm:gap-24">
|
||||||
<div className="space-y-6 sm:space-y-8">
|
<div className="space-y-6 sm:space-y-8">
|
||||||
<FadeIn>
|
<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
|
<Image
|
||||||
src="/profile-image.jpg"
|
src="/profile-image.jpg"
|
||||||
alt="Jan-Marlon Leibl - Fullstack Software Developer"
|
alt="Jan-Marlon Leibl - Fullstack Software Developer"
|
||||||
@ -323,8 +674,8 @@ export default function Home() {
|
|||||||
</FadeIn>
|
</FadeIn>
|
||||||
<FadeIn delay={0.1}>
|
<FadeIn delay={0.1}>
|
||||||
<div className="space-y-2 text-center md:text-left">
|
<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>
|
<h3 className="font-['DM_Sans'] text-xl sm:text-2xl font-medium theme-primary">Jan-Marlon Leibl</h3>
|
||||||
<p className="font-['Instrument_Sans'] text-white/70 text-base sm:text-lg">Fullstack Developer</p>
|
<p className="font-['Instrument_Sans'] theme-text-70 text-base sm:text-lg">Fullstack Developer</p>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
</div>
|
</div>
|
||||||
@ -332,10 +683,10 @@ export default function Home() {
|
|||||||
<FadeIn delay={0.2}>
|
<FadeIn delay={0.2}>
|
||||||
<div className="space-y-10 sm:space-y-16 font-['Instrument_Sans']">
|
<div className="space-y-10 sm:space-y-16 font-['Instrument_Sans']">
|
||||||
<div className="space-y-6 sm:space-y-8">
|
<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.
|
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>
|
||||||
<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.
|
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>
|
</p>
|
||||||
</div>
|
</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="grid grid-cols-1 sm:grid-cols-2 gap-10 sm:gap-16">
|
||||||
<div className="space-y-6 sm:space-y-8">
|
<div className="space-y-6 sm:space-y-8">
|
||||||
<div className="flex items-baseline gap-4">
|
<div className="flex items-baseline gap-4">
|
||||||
<h3 className="text-sm sm:text-base text-white/40 uppercase tracking-[0.2em]">Technologies</h3>
|
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Technologies</h3>
|
||||||
<div className="h-px flex-grow bg-white/10"></div>
|
<div className="h-px flex-grow theme-border"></div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-4 sm:space-y-5">
|
<ul className="space-y-4 sm:space-y-5">
|
||||||
{['PHP', 'JavaScript', 'MySQL', 'React'].map((tech) => (
|
{['PHP', 'JavaScript', 'MySQL', 'React'].map((tech) => (
|
||||||
<li
|
<li
|
||||||
key={tech}
|
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}
|
{tech}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@ -361,8 +712,8 @@ export default function Home() {
|
|||||||
|
|
||||||
<div className="space-y-6 sm:space-y-8">
|
<div className="space-y-6 sm:space-y-8">
|
||||||
<div className="flex items-baseline gap-4">
|
<div className="flex items-baseline gap-4">
|
||||||
<h3 className="text-sm sm:text-base text-white/40 uppercase tracking-[0.2em]">Interests</h3>
|
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Interests</h3>
|
||||||
<div className="h-px flex-grow bg-white/10"></div>
|
<div className="h-px flex-grow theme-border"></div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-4 sm:space-y-5">
|
<ul className="space-y-4 sm:space-y-5">
|
||||||
{[
|
{[
|
||||||
@ -373,9 +724,9 @@ export default function Home() {
|
|||||||
].map((interest) => (
|
].map((interest) => (
|
||||||
<li
|
<li
|
||||||
key={interest}
|
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}
|
{interest}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@ -384,22 +735,24 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-6 sm:pt-8 flex flex-col sm:flex-row gap-3 sm:gap-6">
|
<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"
|
href="https://github.com/AtomicWasTaken"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
View GitHub
|
||||||
</a>
|
</motion.a>
|
||||||
<a
|
<motion.a
|
||||||
href="https://www.linkedin.com/in/janmarlonleibl/"
|
href="https://www.linkedin.com/in/janmarlonleibl/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
Connect on LinkedIn
|
||||||
</a>
|
</motion.a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
@ -408,14 +761,14 @@ export default function Home() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="work" className="py-20 sm:py-40 px-4 sm:px-8 relative">
|
<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">
|
<div className="max-w-screen-xl mx-auto relative">
|
||||||
<FadeIn>
|
<FadeIn>
|
||||||
<div className="flex flex-col gap-3 mb-12 sm:mb-24">
|
<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">
|
<div className="flex items-baseline gap-4">
|
||||||
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight">Selected Work</h2>
|
<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 bg-white/10"></div>
|
<div className="h-px flex-grow theme-border"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
@ -450,33 +803,33 @@ export default function Home() {
|
|||||||
<FadeIn key={index} delay={index * 0.1}>
|
<FadeIn key={index} delay={index * 0.1}>
|
||||||
<div className="group relative">
|
<div className="group relative">
|
||||||
<div className="absolute top-0 left-0 right-0 flex items-center gap-4">
|
<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}
|
{project.year}
|
||||||
</div>
|
</div>
|
||||||
<div className="h-px flex-grow bg-white/10"></div>
|
<div className="h-px flex-grow theme-border"></div>
|
||||||
</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="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">
|
<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}
|
{project.title}
|
||||||
</h3>
|
</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}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-6 sm:space-y-12">
|
<div className="space-y-6 sm:space-y-12">
|
||||||
<div className="space-y-4 sm:space-y-6">
|
<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">
|
<div className="flex flex-wrap items-center gap-3 sm:gap-4">
|
||||||
{project.tags.map((tag, tagIndex) => (
|
{project.tags.map((tag, tagIndex) => (
|
||||||
<span
|
<span
|
||||||
key={tagIndex}
|
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 && (
|
{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>
|
</span>
|
||||||
))}
|
))}
|
||||||
@ -485,7 +838,7 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
))}
|
))}
|
||||||
@ -493,13 +846,13 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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="max-w-screen-xl mx-auto px-4 sm:px-8 py-20 sm:py-32">
|
||||||
<div className="grid gap-16 sm:gap-24">
|
<div className="grid gap-16 sm:gap-24">
|
||||||
<FadeIn>
|
<FadeIn>
|
||||||
<div className="text-center space-y-4 sm:space-y-5">
|
<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>
|
<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'] text-white/70 text-lg sm:text-2xl max-w-2xl mx-auto">
|
<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.
|
Always interested in new opportunities and collaborations.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -509,20 +862,19 @@ export default function Home() {
|
|||||||
<div className="flex flex-col items-center gap-8 sm:gap-12">
|
<div className="flex flex-col items-center gap-8 sm:gap-12">
|
||||||
<a
|
<a
|
||||||
href="mailto:jleibl@proton.me"
|
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
|
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">
|
<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"/>
|
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
|
|
||||||
<FadeIn delay={0.2}>
|
<FadeIn delay={0.2}>
|
||||||
<div className="pt-6 sm:pt-12 text-center">
|
<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.
|
© {new Date().getFullYear()} Jan-Marlon Leibl. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user