Compare commits
18 Commits
fa64c4a254
...
refinement
| Author | SHA1 | Date | |
|---|---|---|---|
| ba87c60360 | |||
| 4fea41af28 | |||
| eed16efb1c | |||
| 2e6fb7fa49 | |||
| bd671155a3 | |||
| 108b2a9c8a | |||
| 6992fd44b9 | |||
| bc8cae3ea0 | |||
| 55e280de72 | |||
| 169a6c213e | |||
| 418014fb04 | |||
| 295908b8cf | |||
| 32852be42b | |||
| f79ff6c434 | |||
| 6c0d137ba9 | |||
| 4f98210b3c | |||
| 5f8300612d | |||
| 67791ba6a3 |
308
package-lock.json
generated
308
package-lock.json
generated
@@ -22,12 +22,8 @@
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.10.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-syntax-highlighter": "^15.5.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -2046,9 +2042,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
|
||||
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -3748,15 +3744,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
|
||||
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/html-minifier-terser": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
|
||||
@@ -3857,12 +3844,6 @@
|
||||
"integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prismjs": {
|
||||
"version": "1.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
|
||||
"integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/q": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz",
|
||||
@@ -3899,16 +3880,6 @@
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-syntax-highlighter": {
|
||||
"version": "15.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz",
|
||||
"integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||
@@ -3981,12 +3952,6 @@
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
@@ -5639,36 +5604,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/character-entities": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
|
||||
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-entities-legacy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
|
||||
"integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-reference-invalid": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
|
||||
"integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/check-types": {
|
||||
"version": "11.2.3",
|
||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz",
|
||||
@@ -5916,16 +5851,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/comma-separated-tokens": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
||||
"integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
@@ -6613,19 +6538,6 @@
|
||||
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/decode-named-character-reference": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
|
||||
"integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
|
||||
@@ -8139,19 +8051,6 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fault": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
||||
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"format": "^0.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/faye-websocket": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
|
||||
@@ -8533,14 +8432,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/format": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -9042,36 +8933,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-parse-selector": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
|
||||
"integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hastscript": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
|
||||
"integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"comma-separated-tokens": "^2.0.0",
|
||||
"hast-util-parse-selector": "^4.0.0",
|
||||
"property-information": "^7.0.0",
|
||||
"space-separated-tokens": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
@@ -9081,21 +8942,6 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/highlightjs-vue": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz",
|
||||
"integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/hoopy": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
|
||||
@@ -9539,30 +9385,6 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
|
||||
"integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphanumerical": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
|
||||
"integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -9708,16 +9530,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-decimal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
|
||||
"integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
@@ -9805,16 +9617,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-hexadecimal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
|
||||
"integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-map": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
|
||||
@@ -11533,20 +11335,6 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/lowlight": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
|
||||
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fault": "^1.0.0",
|
||||
"highlight.js": "~10.7.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
@@ -12313,31 +12101,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-entities": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
|
||||
"integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"character-entities-legacy": "^3.0.0",
|
||||
"character-reference-invalid": "^2.0.0",
|
||||
"decode-named-character-reference": "^1.0.0",
|
||||
"is-alphanumerical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0",
|
||||
"is-hexadecimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-entities/node_modules/@types/unist": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
@@ -13938,15 +13701,6 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
@@ -13992,16 +13746,6 @@
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
|
||||
"integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -14439,26 +14183,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-syntax-highlighter": {
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz",
|
||||
"integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"highlight.js": "^10.4.1",
|
||||
"highlightjs-vue": "^1.0.0",
|
||||
"lowlight": "^1.17.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"refractor": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.20.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -14541,22 +14265,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/refractor": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz",
|
||||
"integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/prismjs": "^1.0.0",
|
||||
"hastscript": "^9.0.0",
|
||||
"parse-entities": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
@@ -15574,16 +15282,6 @@
|
||||
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/space-separated-tokens": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
|
||||
"integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/spdy": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.10.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
@@ -44,8 +43,5 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-syntax-highlighter": "^15.5.13"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
BIN
public/tgPic.jpg
BIN
public/tgPic.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 7.8 KiB |
709
src/App.css
Normal file
709
src/App.css
Normal file
@@ -0,0 +1,709 @@
|
||||
.background {
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.mainContentBlock {
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
max-height: 200vh;
|
||||
width: 66vw;
|
||||
min-width: auto;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.horizontalContentItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.verticalContentItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flexContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3%;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Home page specific styles */
|
||||
.home-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 80vh;
|
||||
/* Takes up most of the viewport */
|
||||
text-align: center;
|
||||
gap: 30px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.hero-profile-container {
|
||||
padding: 10px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
/* Subtle backing */
|
||||
border-radius: 50%;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.hero-profile-img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
/* Circle */
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
color: white;
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
font-size: clamp(40px, 6vw, 80px);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin: 0;
|
||||
letter-spacing: -0.5px;
|
||||
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hero-bio {
|
||||
font-size: clamp(16px, 1.5vw, 20px);
|
||||
/* font-family: 'Roboto', sans-serif; - inherent*/
|
||||
font-weight: 300;
|
||||
line-height: 1.6;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.hero-socials {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero-social-link {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 10px 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50px;
|
||||
/* Pill shape */
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.hero-social-link:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Legacy Home styles (keep if referenced elsewhere, otherwise minimal) */
|
||||
.hero-card {
|
||||
/* Kept for backward compat if needed, but redesigned home won't use it */
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.portrait-img {
|
||||
object-fit: cover;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: clamp(10px, 1.5vw, 100px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name-text {
|
||||
font-size: clamp(24px, 8vw, 120px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.skills-list {
|
||||
height: 5vh;
|
||||
color: white;
|
||||
font-family: roboto, sans-serif;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 75px;
|
||||
font-family: roboto, sans-serif;
|
||||
}
|
||||
|
||||
.contact-link {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
transition: color 0.3s ease;
|
||||
font-size: clamp(10px, 2vw, 1.2em);
|
||||
}
|
||||
|
||||
.contact-link:hover {
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.floating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
padding: 20px 0px;
|
||||
text-align: center;
|
||||
background: rgba(52, 87, 245, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: clamp(10px, 2vw, 40px);
|
||||
flex-wrap: nowrap;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: "roboto, sans-serif";
|
||||
font-size: clamp(10px, 1.5vw, 18px);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
padding-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.nav-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.nav-link.active::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
font-size: clamp(10px, 1.5vw, 60px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.contentCard {
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contentCard:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contentCard h2 {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.contentCard p {
|
||||
white-space: normal;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@keyframes slideInFromLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* About Page Styles */
|
||||
.about-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: auto;
|
||||
/* Changed from 80vh to auto to let flex parent handle it, or reduced */
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
/* Reduced from 40px */
|
||||
color: white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
font-size: clamp(32px, 5vw, 60px);
|
||||
margin-top: 0;
|
||||
/* Clear top margin */
|
||||
margin-bottom: 20px;
|
||||
/* Reduced from 40px */
|
||||
font-weight: 700;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.about-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
/* Reduced from 60px */
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.about-image-container {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.about-image {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
border-radius: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.about-image:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.about-text-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 30px 30px 30px 30px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.about-text {
|
||||
font-size: clamp(16px, 1.2vw, 18px);
|
||||
line-height: 1.8;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
white-space: pre-line;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Mobile portrait mode - width < height */
|
||||
@media (orientation: portrait) {
|
||||
.mainContentBlock {
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.about-content {
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.about-image {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.about-text-container {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.home-container {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.about-content {
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.home-container {
|
||||
min-height: 70vh;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Projects Page Styles */
|
||||
|
||||
.projects-container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.projects-title {
|
||||
font-size: clamp(32px, 6vw, 60px);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
color: white;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
/* Slightly lighter base */
|
||||
backdrop-filter: blur(16px);
|
||||
/* Increased blur */
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
/* Softer border */
|
||||
border-radius: 24px;
|
||||
/* More rounded */
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
/* Softer shadow */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transform: translateY(-8px) scale(1.02);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
||||
/* Deeper but soft shadow */
|
||||
border-color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.project-card::before {
|
||||
/* Shine effect */
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.1) 50%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
transform: skewX(-25deg);
|
||||
transition: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.project-card:hover::before {
|
||||
animation: shine 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
left: 200%;
|
||||
}
|
||||
}
|
||||
|
||||
.project-image-container {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.project-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.project-card:hover .project-image {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.project-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.project-description {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
flex-grow: 1;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.project-tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tech-chip {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.project-card:hover .tech-chip {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.project-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.project-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
transition: color 0.3s ease;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.project-link:hover {
|
||||
color: white;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Contact Page Styles */
|
||||
.contact-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.contact-background-placeholder {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.contact-main-block {
|
||||
min-width: 66vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-card {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
/* Reduced from 800px to be tighter */
|
||||
padding: 40px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(5px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-title {
|
||||
font-size: clamp(40px, 5vw, 60px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
font-size: 1.1rem;
|
||||
font-family: roboto, sans-serif;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contact-intro {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.contact-links-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contact-link-item {
|
||||
padding: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 15px;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contact-link-text {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.contact-label {
|
||||
opacity: 0.7;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.contact-value {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
|
||||
import './styles/index.css';
|
||||
import './App.css';
|
||||
import FloatingHeader from './components/floatingHeader';
|
||||
import ParticlesBackground from './components/ParticlesBackground';
|
||||
import Home from './pages/Home';
|
||||
@@ -8,8 +8,7 @@ import About from './pages/About';
|
||||
import WorkExperience from './pages/WorkExperience';
|
||||
import Projects from './pages/Projects';
|
||||
import Skills from './pages/Skills';
|
||||
import SidequestDetail from './pages/SidequestDetail';
|
||||
|
||||
import Contact from './pages/Contact';
|
||||
|
||||
function ScrollToTop() {
|
||||
const { pathname } = useLocation();
|
||||
@@ -42,8 +41,7 @@ function App() {
|
||||
<Route path="/work-experience" element={<WorkExperience />} />
|
||||
<Route path="/projects" element={<Projects />} />
|
||||
<Route path="/skills" element={<Skills />} />
|
||||
<Route path="/sidequest/:id" element={<SidequestDetail />} />
|
||||
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
</Routes>
|
||||
<div>
|
||||
<div style={{ height: "20px" }}></div>
|
||||
|
||||
@@ -25,12 +25,6 @@ export default function BioSection({ imageSrc, imageAlt, text, reversed = false
|
||||
return () => clearInterval(interval);
|
||||
}, [images.length, currentIndex]);
|
||||
|
||||
const handleImageClick = () => {
|
||||
if (images.length <= 1) return;
|
||||
setPrevIndex(currentIndex);
|
||||
setCurrentIndex((prev) => (prev + 1) % images.length);
|
||||
};
|
||||
|
||||
const slideVariants = {
|
||||
enter: {
|
||||
x: "-100%",
|
||||
@@ -58,7 +52,6 @@ export default function BioSection({ imageSrc, imageAlt, text, reversed = false
|
||||
initial={{ x: reversed ? 30 : -30, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.4, duration: 0.6 }}
|
||||
onClick={handleImageClick}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "250px",
|
||||
@@ -67,7 +60,6 @@ export default function BioSection({ imageSrc, imageAlt, text, reversed = false
|
||||
borderRadius: "20px",
|
||||
border: "3px solid rgba(255, 255, 255, 0.8)",
|
||||
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
|
||||
cursor: images.length > 1 ? "pointer" : "default",
|
||||
}}
|
||||
>
|
||||
<AnimatePresence initial={false}>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import '../styles/components/codeBlock.css';
|
||||
|
||||
interface CodeBlockProps {
|
||||
language?: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
const CodeBlock: React.FC<CodeBlockProps> = ({ language, code }) => (
|
||||
<div className="code-block-container">
|
||||
<div className="code-block-header">
|
||||
<div className="window-controls">
|
||||
<div className="control-dot red"></div>
|
||||
<div className="control-dot yellow"></div>
|
||||
<div className="control-dot green"></div>
|
||||
</div>
|
||||
<span className="language-label">
|
||||
{language || 'text'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<SyntaxHighlighter
|
||||
language={language || 'text'}
|
||||
style={vscDarkPlus}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: '0 0 12px 12px',
|
||||
fontSize: '14px',
|
||||
background: 'transparent',
|
||||
padding: '20px'
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CodeBlock;
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect, useRef } from 'react';
|
||||
|
||||
const ParticlesBackground: React.FC = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const prevWidth = useRef(window.innerWidth);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
@@ -70,16 +69,11 @@ const ParticlesBackground: React.FC = () => {
|
||||
const init = () => {
|
||||
if (!canvas) return;
|
||||
canvas.width = window.innerWidth;
|
||||
// Use logical height that works well with the 120vh style
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
// Calculate particle count based on screen area (resolution)
|
||||
// Formula: sqrt(width * height) / factor
|
||||
// Desktop (1920x1080) -> ~120 particles
|
||||
// Mobile (390x844) -> ~48 particles
|
||||
// Mobile Landscape (844x390) -> ~48 particles (Same as portrait!)
|
||||
const area = canvas.width * canvas.height;
|
||||
const particleCount = Math.floor(Math.sqrt(area) / 12);
|
||||
// Reduce particle count on mobile/portrait screens
|
||||
const isPortrait = canvas.height > canvas.width;
|
||||
const particleCount = isPortrait ? 90 : 180;
|
||||
|
||||
particles = [];
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
@@ -140,11 +134,6 @@ const ParticlesBackground: React.FC = () => {
|
||||
animate();
|
||||
|
||||
const handleResize = () => {
|
||||
// Ignore vertical-only resizes (addressing mobile browser bar toggle issue)
|
||||
if (window.innerWidth === prevWidth.current) {
|
||||
return;
|
||||
}
|
||||
prevWidth.current = window.innerWidth;
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -164,7 +153,7 @@ const ParticlesBackground: React.FC = () => {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '120vh', // Extend well below viewport to cover mobile browser bar retraction
|
||||
height: '100%',
|
||||
zIndex: -1, // Behind everything
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { motion, Variants } from "framer-motion";
|
||||
import "../styles/components/cards.css";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export type DetailSection =
|
||||
| { type: 'text'; title?: string; content: string }
|
||||
| { type: 'code'; title?: string; language?: string; code: string }
|
||||
| { type: 'image'; src: string; alt?: string; caption?: string; title?: string };
|
||||
|
||||
export interface Project {
|
||||
id: number;
|
||||
@@ -17,8 +10,6 @@ export interface Project {
|
||||
demo?: string;
|
||||
repo?: string;
|
||||
};
|
||||
hasDetails?: boolean;
|
||||
sections?: DetailSection[];
|
||||
}
|
||||
|
||||
interface ProjectCardProps {
|
||||
@@ -65,11 +56,6 @@ export default function ProjectCard({ project, variants }: ProjectCardProps) {
|
||||
GitHub <span>↗</span>
|
||||
</a>
|
||||
)}
|
||||
{project.hasDetails && (
|
||||
<Link to={`/sidequest/${project.id}`} className="project-link" style={{ background: "rgba(255, 255, 255, 0.15)", border: "1px solid rgba(255, 255, 255, 0.3)" }}>
|
||||
Read More <span>→</span>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import React from 'react';
|
||||
import CodeBlock from './CodeBlock';
|
||||
|
||||
interface RichTextRendererProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const RichTextRenderer: React.FC<RichTextRendererProps> = ({ content }) => {
|
||||
if (!content) return null;
|
||||
|
||||
const parts = content.split(/```/);
|
||||
|
||||
return (
|
||||
<div className="rich-text-content">
|
||||
{parts.map((part, index) => {
|
||||
if (index % 2 === 1) {
|
||||
// Code block
|
||||
const firstLineBreak = part.indexOf('\n');
|
||||
const language = part.slice(0, firstLineBreak).trim();
|
||||
const code = part.slice(firstLineBreak + 1).trim();
|
||||
|
||||
return <CodeBlock key={index} language={language} code={code} />;
|
||||
}
|
||||
|
||||
// Regular text (split by newlines for paragraph handling)
|
||||
return (
|
||||
<span key={index} style={{ whiteSpace: 'pre-wrap' }}>
|
||||
{part}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RichTextRenderer;
|
||||
@@ -1,82 +0,0 @@
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export interface SkillDetail {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SkillCategory {
|
||||
category: string;
|
||||
skills: SkillDetail[];
|
||||
}
|
||||
|
||||
interface SkillCardProps {
|
||||
category: SkillCategory;
|
||||
isExpanded: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
export default function SkillCard({ category, isExpanded, onToggle }: SkillCardProps) {
|
||||
return (
|
||||
<motion.div
|
||||
layout
|
||||
onClick={onToggle}
|
||||
style={{
|
||||
background: isExpanded ? "rgba(255, 255, 255, 0.12)" : "rgba(255, 255, 255, 0.07)",
|
||||
backdropFilter: "blur(10px)",
|
||||
border: isExpanded ? "1px solid rgba(255, 255, 255, 0.3)" : "1px solid rgba(255, 255, 255, 0.15)",
|
||||
borderRadius: "12px",
|
||||
padding: "24px",
|
||||
cursor: "pointer",
|
||||
gridColumn: isExpanded ? "1 / -1" : "auto",
|
||||
zIndex: isExpanded ? 10 : 1
|
||||
}}
|
||||
whileHover={!isExpanded ? { scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.12)" } : {}}
|
||||
transition={{ duration: 0.3, type: "spring" }}
|
||||
>
|
||||
<motion.h3 layout="position" style={{ color: "white", marginTop: 0, marginBottom: "15px", fontSize: "1.2rem" }}>
|
||||
{category.category} {isExpanded ? <span style={{ fontSize: "0.8em", opacity: 0.7 }}>(Click to collapse)</span> : null}
|
||||
</motion.h3>
|
||||
|
||||
<motion.div layout="position" style={{ display: "flex", flexWrap: "wrap", gap: "10px", marginBottom: isExpanded ? "20px" : "0" }}>
|
||||
{category.skills.map((skill) => (
|
||||
<span
|
||||
key={skill.name}
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.15)",
|
||||
color: "rgba(255, 255, 255, 0.9)",
|
||||
padding: "6px 12px",
|
||||
borderRadius: "20px",
|
||||
fontSize: "0.85rem",
|
||||
fontWeight: 500,
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)"
|
||||
}}
|
||||
>
|
||||
{skill.name}
|
||||
</span>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{isExpanded && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
style={{ borderTop: "1px solid rgba(255,255,255,0.2)", paddingTop: "20px" }}
|
||||
>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: "15px" }}>
|
||||
{category.skills.map((skill) => (
|
||||
<div key={skill.name} style={{ display: "flex", flexDirection: "column", gap: "5px" }}>
|
||||
<strong style={{ color: "#fff", fontSize: "1rem" }}>{skill.name}</strong>
|
||||
<p style={{ margin: 0, color: "rgba(255,255,255,0.7)", fontSize: "0.9rem" }}>
|
||||
{skill.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import "../styles/components/cards.css";
|
||||
|
||||
interface ContentCardProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
|
||||
@@ -1,67 +1,35 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import "../styles/components/header.css";
|
||||
|
||||
export default function FloatingHeader() {
|
||||
const location = useLocation();
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const toggleMenu = () => {
|
||||
setIsMenuOpen(!isMenuOpen);
|
||||
};
|
||||
|
||||
// Close menu when route changes
|
||||
useEffect(() => {
|
||||
setIsMenuOpen(false);
|
||||
}, [location]);
|
||||
|
||||
const ROUTE_TITLES: { [key: string]: string } = {
|
||||
"/": "Sasha Bayda",
|
||||
"/skills": "Skills",
|
||||
"/work-experience": "Work Experience",
|
||||
"/projects": "Projects",
|
||||
"/about": "About Me"
|
||||
};
|
||||
|
||||
const currentTitle = ROUTE_TITLES[location.pathname] || "Sasha Bayda";
|
||||
|
||||
return (
|
||||
<header className="floating-header">
|
||||
<div className="header-content">
|
||||
<div className="mobile-page-title">
|
||||
{currentTitle}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className={`mobile-toggle ${isMenuOpen ? "open" : ""}`}
|
||||
onClick={toggleMenu}
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span className="hamburger-line"></span>
|
||||
<span className="hamburger-line"></span>
|
||||
<span className="hamburger-line"></span>
|
||||
</button>
|
||||
|
||||
<nav className={`header-nav ${isMenuOpen ? "is-open" : ""}`}>
|
||||
<nav className="header-nav">
|
||||
<Link
|
||||
to="/"
|
||||
className={`nav-link ${location.pathname === "/" ? "active" : ""}`}
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
<Link
|
||||
to="/skills"
|
||||
className={`nav-link ${location.pathname === "/skills" ? "active" : ""}`}
|
||||
>
|
||||
Skills
|
||||
</Link>
|
||||
<Link
|
||||
to="/work-experience"
|
||||
className={`nav-link ${location.pathname === "/work-experience" ? "active" : ""}`}
|
||||
>
|
||||
Work Experience
|
||||
</Link>
|
||||
<Link
|
||||
to="/skills"
|
||||
className={`nav-link ${location.pathname === "/skills" ? "active" : ""}`}
|
||||
>
|
||||
Skills
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className={`nav-link ${location.pathname === "/about" ? "active" : ""}`}
|
||||
>
|
||||
About Me
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects"
|
||||
className={`nav-link ${location.pathname === "/projects" ? "active" : ""}`}
|
||||
@@ -69,13 +37,12 @@ export default function FloatingHeader() {
|
||||
Projects and Sidequests
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className={`nav-link ${location.pathname === "/about" ? "active" : ""}`}
|
||||
to="/contact"
|
||||
className={`nav-link ${location.pathname === "/contact" ? "active" : ""}`}
|
||||
>
|
||||
Personal About Me
|
||||
Contact
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { motion } from "framer-motion";
|
||||
import VisitedMap from "../components/VisitedMap";
|
||||
import BioSection from "../components/BioSection";
|
||||
import "../styles/pages/about.css";
|
||||
|
||||
const ABOUT_TEXT = "Hi! I'm Sasha Bayda, a passionate developer focused on creating beautiful and functional web experiences. With a background in computer science and a keen eye for design, I strive to bridge the gap between technology and user-centric solutions. When I'm not coding, you can find me exploring the outdoors, experimenting with new recipes, or indulging in photography. Feel free to explore my projects and get in touch if you'd like to collaborate or learn more about my work!" + "\n\n" + "Thank you for visiting my digital resume site. I look forward to connecting with you!";
|
||||
|
||||
|
||||
75
src/pages/Contact.tsx
Normal file
75
src/pages/Contact.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export default function Contact() {
|
||||
return (
|
||||
<div className="contact-wrapper">
|
||||
|
||||
<div className="mainContentBlock contact-main-block">
|
||||
<motion.div
|
||||
className="contact-card"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<motion.h1
|
||||
className="contact-title"
|
||||
initial={{ y: -20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.2, duration: 0.6 }}
|
||||
>
|
||||
Contact Me
|
||||
</motion.h1>
|
||||
|
||||
<motion.div
|
||||
className="contact-content"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.4, duration: 0.6 }}
|
||||
>
|
||||
<div className="contact-intro">
|
||||
<p>
|
||||
I would love to hear from you! Feel free to reach out through any of these channels:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="contact-links-container">
|
||||
<motion.div
|
||||
className="contact-link-item"
|
||||
whileHover={{ scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.15)" }}
|
||||
>
|
||||
<p className="contact-link-text">
|
||||
<span className="contact-label">Email:</span>
|
||||
<a href="mailto:sasha.bayda@outlook.com" className="contact-value">
|
||||
sasha.bayda@outlook.com
|
||||
</a>
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="contact-link-item"
|
||||
whileHover={{ scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.15)" }}
|
||||
>
|
||||
<p className="contact-link-text">
|
||||
<span className="contact-label">Phone:</span>
|
||||
<span className="contact-value">(306) 921-7145</span>
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="contact-link-item"
|
||||
whileHover={{ scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.15)" }}
|
||||
>
|
||||
<p className="contact-link-text">
|
||||
<span className="contact-label">LinkedIn:</span>
|
||||
<a href="https://www.linkedin.com/in/sasha-bayda/" target="_blank" rel="noopener noreferrer" className="contact-value">
|
||||
linkedin.com/in/sasha-bayda
|
||||
</a>
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { motion } from "framer-motion";
|
||||
import TypingText from "../components/animatedTyping";
|
||||
import { useState } from "react";
|
||||
import "../styles/pages/home.css";
|
||||
|
||||
const welcomeText = `Hello! My name is Sasha Bayda and welcome to my digital resume site!
|
||||
|
||||
@@ -9,14 +7,6 @@ Here you will find some of my projects, skills, contact information and any info
|
||||
|
||||
Feel free to explore and learn more about me and if something isn't answered, don't hesitate to reach out via the contact page!`;
|
||||
|
||||
const HOVER_TEXTS: { [key: string]: string } = {
|
||||
"Email": "Drop me a line! I'm always open to new opportunities and interesting conversations.",
|
||||
"Phone": "Prefer a mix of voice? Give me a call or text.",
|
||||
"LinkedIn": "Let's connect professionally. View my full experience and network.",
|
||||
"GitHub": "Dive into my code. See what I'm building and how I solve problems.",
|
||||
"Portfolio": "See more of my work in action on my portfolio site."
|
||||
};
|
||||
|
||||
const CONTACT_LINKS = [
|
||||
{ label: "Email", url: "mailto:sasha.bayda@outlook.com" },
|
||||
{ label: "Phone", url: "tel:+13069217145" },
|
||||
@@ -26,8 +16,6 @@ const CONTACT_LINKS = [
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
const [displayedText, setDisplayedText] = useState(welcomeText);
|
||||
|
||||
return (
|
||||
<div className="mainContentBlock">
|
||||
<div className="home-container">
|
||||
@@ -60,7 +48,7 @@ export default function Home() {
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 2.0 }}
|
||||
>
|
||||
{displayedText}
|
||||
{welcomeText}
|
||||
</motion.p>
|
||||
|
||||
<div className="hero-socials">
|
||||
@@ -76,8 +64,6 @@ export default function Home() {
|
||||
delay: 2.5 + (index * 0.1),
|
||||
ease: "easeOut"
|
||||
}}
|
||||
onMouseEnter={() => setDisplayedText(HOVER_TEXTS[link.label] || welcomeText)}
|
||||
onMouseLeave={() => setDisplayedText(welcomeText)}
|
||||
>
|
||||
{link.label}
|
||||
</motion.a>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { motion, Variants } from "framer-motion";
|
||||
import FullPageImage from "../components/fullPageImage";
|
||||
import ProjectCard, { Project } from "../components/ProjectCard";
|
||||
import "../styles/pages/projects.css";
|
||||
|
||||
const FEATURED_PROJECTS: Project[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Digital Resume",
|
||||
description: "A fully responsive, glassmorphic portfolio site built to showcase my skills and experience. Features animated page transitions, typing effects, and a dynamic map component.",
|
||||
techStack: ["React", "TypeScript", "Framer Motion", "@react-google-maps/api", "nginx", "docker"],
|
||||
techStack: ["React", "TypeScript", "Framer Motion", "Vite"],
|
||||
image: "/digitCode.jpg",
|
||||
links: {
|
||||
repo: "https://github.com/Bayda77/resume-site",
|
||||
@@ -17,92 +16,26 @@ const FEATURED_PROJECTS: Project[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const SIDEQUESTS: Project[] = [
|
||||
const SIDEQUESTS: Project[] = [
|
||||
{
|
||||
id: 101,
|
||||
title: "Working with Gitea Workers",
|
||||
description: "Implementing a CI/CD pipeline using Gitea Actions to automate deployment to a Windows production server.",
|
||||
techStack: ["Gitea Actions", "Docker", "PowerShell", "YAML"],
|
||||
image: "/giteaAction.png",
|
||||
links: {},
|
||||
hasDetails: true,
|
||||
sections: [
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Overview',
|
||||
content: "Automating the deployment process using Gitea Actions. This workflow listens for pushes to the main branch, connects to a remote Windows server via SSH, updates the code, rebuilds the Docker container, and restarts the service."
|
||||
id: 101, // Different ID range for sidequests
|
||||
title: "Experimental Shader",
|
||||
description: "A WebGL shader experiment creating procedural textures and animations. Exploring noise functions and light interactions.",
|
||||
techStack: ["WebGL", "GLSL", "React Three Fiber"],
|
||||
image: "https://placehold.co/600x400/1a1a1a/cccccc?text=Shader+Experiment", // Placeholder image
|
||||
links: {
|
||||
repo: "https://github.com/Bayda77/sidequests", // Placeholder link
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
language: 'yaml',
|
||||
title: 'Gitea Runner Config',
|
||||
code: `# Gitea Actions Runner
|
||||
gitea-runner:
|
||||
container_name: gitea-runner
|
||||
image: gitea/act_runner:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- web
|
||||
environment:
|
||||
- GITEA_INSTANCE_URL=http://gitea:3000
|
||||
- GITEA_RUNNER_REGISTRATION_TOKEN=\${GITEA_RUNNER_TOKEN}
|
||||
- GITEA_RUNNER_NAME=docker-runner
|
||||
# Using catthehacker/ubuntu:act-latest which includes Docker CLI for container operations
|
||||
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04
|
||||
- CONFIG_FILE=/config.yaml
|
||||
volumes:
|
||||
- ./gitea/runner:/data
|
||||
- ./gitea/runner/config.yaml:/config.yaml:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
depends_on:
|
||||
- gitea`
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
language: 'yaml',
|
||||
title: 'Deploy Workflow',
|
||||
code: `name: Deploy to Production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy Application
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: \${{ secrets.REMOTE_HOST }}
|
||||
username: \${{ secrets.REMOTE_USER }}
|
||||
key: \${{ secrets.SSH_PRIVATE_KEY }}
|
||||
passphrase: \${{ secrets.SSH_PASSPHRASE }}
|
||||
script: |
|
||||
powershell -ExecutionPolicy Bypass -Command "Write-Host '=== Starting deployment ==='; if (Test-Path 'C:\\projects\\digital-resume-FE') { Set-Location 'C:\\projects\\digital-resume-FE'; git pull origin main } else { New-Item -ItemType Directory -Path 'C:\\projects' -Force; Set-Location 'C:\\projects'; git clone https://gitea.sashabayda.ca/Bayda77/digital-resume-FE.git }; Write-Host '=== Stopping container ==='; docker stop resume-frontend; docker rm resume-frontend; Write-Host '=== Building image ==='; Set-Location 'C:\\projects\\digital-resume-FE'; docker build -t resume-frontend:latest .; Write-Host '=== Running container ==='; docker run -d --name resume-frontend --network nginx_web --restart unless-stopped -p 3001:80 resume-frontend:latest; Write-Host '=== Verifying ==='; docker ps -a --filter name=resume-frontend"`
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Reasoning',
|
||||
content: "I wanted to automate the deployment process using Gitea Actions. Gitea is a self-hosted Git server and I wanted to take advantage of its built-in CI/CD pipeline to automate the deployment process. With it being self hosted this ment that I needed to self host my own runners. Which I used as a learning opportunity to learn more about deployment containers"
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Challenges',
|
||||
content: "Some of the main challenges was ensuring security to the host system and ensuring the deployment process was reliable and consistent. This includes stuff such as not exposing port 22 to the internet and keeping 'secrets' secure"
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
language: 'powershell',
|
||||
title: 'Deployment Script',
|
||||
code: `powershell -ExecutionPolicy Bypass -Command "Write-Host '=== Starting deployment ==='; if (Test-Path 'C:\\projects\\digital-resume-FE') { Set-Location 'C:\\projects\\digital-resume-FE'; git pull origin main } else { New-Item -ItemType Directory -Path 'C:\\projects' -Force; Set-Location 'C:\\projects'; git clone https://gitea.sashabayda.ca/Bayda77/digital-resume-FE.git }; Write-Host '=== Stopping container ==='; docker stop resume-frontend; docker rm resume-frontend; Write-Host '=== Building image ==='; Set-Location 'C:\\projects\\digital-resume-FE'; docker build -t resume-frontend:latest .; Write-Host '=== Running container ==='; docker run -d --name resume-frontend --network nginx_web --restart unless-stopped -p 3001:80 resume-frontend:latest; Write-Host '=== Verifying ==='; docker ps -a --filter name=resume-frontend"`
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Deployment Script Explanation',
|
||||
content: "The deployment script is a PowerShell script that is used to deploy the digital resume frontend to a Windows server. It first checks if the project directory exists, if it does it pulls the latest changes from the repository, if it doesn't it clones the repository. Then it stops the container, removes it, builds the image, and runs the container. Finally it verifies that the container is running and reports to gitea if it was successfull"
|
||||
},
|
||||
]
|
||||
id: 102,
|
||||
title: "CLI Tool",
|
||||
description: "A command-line utility for automating daily workflows and file management tasks.",
|
||||
techStack: ["Rust", "Clap"],
|
||||
image: "https://placehold.co/600x400/2a2a2a/dddddd?text=CLI+Tool", // Placeholder
|
||||
links: {
|
||||
repo: "https://github.com/Bayda77/cli-tools"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -132,7 +65,7 @@ const cardVariants: Variants = {
|
||||
export default function Projects() {
|
||||
return (
|
||||
<div style={{ position: "relative", width: "100%", minHeight: "100vh" }}>
|
||||
<div className="mainContentBlock" style={{ width: "100%", minWidth: "100vw", maxWidth: "100vw", paddingTop: "20px", paddingBottom: "100px", boxSizing: "border-box", display: "flex", justifyContent: "center", position: "relative", zIndex: 1 }}>
|
||||
<div className="mainContentBlock" style={{ width: "100%", minWidth: "100vw", maxWidth: "100vw", paddingTop: "20px", paddingBottom: "10px", boxSizing: "border-box", display: "flex", justifyContent: "center", position: "relative", zIndex: 1 }}>
|
||||
<div className="projects-container">
|
||||
{/* Featured Projects Section */}
|
||||
<motion.div
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import { motion } from "framer-motion";
|
||||
import { SIDEQUESTS } from "./Projects";
|
||||
import CodeBlock from "../components/CodeBlock";
|
||||
import RichTextRenderer from "../components/RichTextRenderer";
|
||||
import "../styles/pages/sidequestDetail.css";
|
||||
|
||||
export default function SidequestDetail() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const project = SIDEQUESTS.find(p => p.id === Number(id));
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
if (!project) {
|
||||
return (
|
||||
<div className="sidequest-error-container">
|
||||
<h2>Sidequest not found</h2>
|
||||
<Link to="/projects" className="sidequest-back-link-error">Back to Projects</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mainContentBlock sidequest-container">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="sidequest-content-wrapper"
|
||||
>
|
||||
<Link to="/projects" className="sidequest-back-link">
|
||||
← Back to Projects
|
||||
</Link>
|
||||
|
||||
<div className="sidequest-header">
|
||||
<h1 className="sidequest-title">{project.title}</h1>
|
||||
<div className="sidequest-tech-stack">
|
||||
{project.techStack.map(tech => (
|
||||
<span key={tech} className="sidequest-tech-tag">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sidequest-hero-image-container">
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="sidequest-hero-image"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="sidequest-details-container">
|
||||
{/* Dynamic Sections Rendering */}
|
||||
{project.sections?.map((section, index) => {
|
||||
switch (section.type) {
|
||||
case 'text':
|
||||
return (
|
||||
<section key={index} className="sidequest-section">
|
||||
{section.title && <h2 className="sidequest-section-title">{section.title}</h2>}
|
||||
<div className="sidequest-section-content">
|
||||
<RichTextRenderer content={section.content} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
case 'code':
|
||||
return (
|
||||
<section key={index} className="sidequest-section">
|
||||
{section.title && <h2 className="sidequest-section-title">{section.title}</h2>}
|
||||
<CodeBlock language={section.language} code={section.code} />
|
||||
</section>
|
||||
);
|
||||
case 'image':
|
||||
return (
|
||||
<section key={index} className="sidequest-section">
|
||||
{section.title && <h2 className="sidequest-section-title">{section.title}</h2>}
|
||||
<img src={section.src} alt={section.alt || ''} className="sidequest-section-image" />
|
||||
{section.caption && <p className="sidequest-section-caption">{section.caption}</p>}
|
||||
</section>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
|
||||
<div className="sidequest-footer-links">
|
||||
{project.links.demo && (
|
||||
<a href={project.links.demo} target="_blank" rel="noopener noreferrer" className="sidequest-footer-link">
|
||||
Live Demo ↗
|
||||
</a>
|
||||
)}
|
||||
{project.links.repo && (
|
||||
<a href={project.links.repo} target="_blank" rel="noopener noreferrer" className="sidequest-footer-link">
|
||||
View Code ↗
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sidequest-footer-spacer"></div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { motion, Variants, AnimatePresence } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import SkillCard, { SkillCategory } from "../components/SkillCard";
|
||||
import { motion, Variants } from "framer-motion";
|
||||
|
||||
interface SkillCategory {
|
||||
category: string;
|
||||
skills: string[];
|
||||
}
|
||||
|
||||
interface ProfessionalSkill {
|
||||
skill: string;
|
||||
@@ -10,69 +13,27 @@ interface ProfessionalSkill {
|
||||
const TECHNICAL_SKILLS: SkillCategory[] = [
|
||||
{
|
||||
category: "Languages",
|
||||
skills: [
|
||||
{ name: "JavaScript (ES6+)", description: "Core language for web interactivity, closures, and async programming." },
|
||||
{ name: "TypeScript", description: "Strict syntactical superset of JavaScript adding static typing." },
|
||||
{ name: "Python", description: "Versatile language used for backend scripting and data processing." },
|
||||
{ name: "Java", description: "Object-oriented language for robust enterprise backends." },
|
||||
{ name: "C", description: "Low-level system programming and memory management." },
|
||||
{ name: "C++", description: "High-performance application development with OOP features." },
|
||||
{ name: "Lua", description: "Lightweight scripting often used in embedded systems and game dev." },
|
||||
{ name: "HTML", description: "Standard markup language for document structure." },
|
||||
{ name: "CSS", description: "Style sheet language for presentation and layout." },
|
||||
{ name: "SQL", description: "Standard language for relational database management." }
|
||||
]
|
||||
skills: ["JavaScript (ES6+)", "TypeScript", "Python", "Java", "C", "C++", "Lua", "HTML", "CSS", "SQL"]
|
||||
},
|
||||
{
|
||||
category: "Frameworks/Libraries",
|
||||
skills: [
|
||||
{ name: "React", description: "Library for building component-based user interfaces." },
|
||||
{ name: "Express.js", description: "Minimalist web framework for Node.js servers." },
|
||||
{ name: "Django REST Framework", description: "Toolkit for building Web APIs with Python/Django." },
|
||||
{ name: "AWS Lambda", description: "Serverless compute service for running code without provisioning." }
|
||||
]
|
||||
skills: ["React", "Express.js", "Django REST Framework", "AWS Lambda"]
|
||||
},
|
||||
{
|
||||
category: "Databases",
|
||||
skills: [
|
||||
{ name: "PostgreSQL", description: "Advanced open-source relational database (SQL)." },
|
||||
{ name: "CouchDB", description: "Seamless multi-master sync capability database (No-SQL)." }
|
||||
]
|
||||
skills: ["PostgreSQL (SQL Based)", "CouchDB (No-SQL Based)"]
|
||||
},
|
||||
{
|
||||
category: "DevOps & Tools",
|
||||
skills: [
|
||||
{ name: "Docker", description: "Platform for developing, shipping, and running applications in containers." },
|
||||
{ name: "CircleCI", description: "Continuous integration and delivery platform." },
|
||||
{ name: "Git (CLI)", description: "Distributed version control system." },
|
||||
{ name: "Postman", description: "API platform for building and using APIs." },
|
||||
{ name: "AWS CDK", description: "Software development framework for defining cloud infrastructure in code." },
|
||||
{ name: "Linux CLI", description: "Command line interface for system interaction and scripting." }
|
||||
]
|
||||
skills: ["Docker", "CircleCI", "Git (CLI)", "Postman", "AWS CDK", "Linux CLI"]
|
||||
},
|
||||
{
|
||||
category: "AWS Technologies",
|
||||
skills: [
|
||||
{ name: "S3", description: "Scalable object storage service." },
|
||||
{ name: "Cloudfront", description: "Content Delivery Network (CDN) service." },
|
||||
{ name: "Route 53", description: "Scalable Domain Name System (DNS) web service." },
|
||||
{ name: "API Gateway", description: "Service to create, publish, maintain, monitor, and secure APIs." },
|
||||
{ name: "Lambda", description: "Serverless compute service." },
|
||||
{ name: "RDS", description: "Managed relational database service." },
|
||||
{ name: "VPC", description: "Logically isolated section of the AWS Cloud." },
|
||||
{ name: "ECS", description: "Fully managed container orchestration service." },
|
||||
{ name: "SQS", description: "Message queuing service for decoupling microservices." }
|
||||
]
|
||||
skills: ["S3", "Cloudfront", "Route 53", "API Gateway", "Lambda", "RDS", "VPC", "ECS", "SQS"]
|
||||
},
|
||||
{
|
||||
category: "Concepts",
|
||||
skills: [
|
||||
{ name: "RESTful APIs", description: "Architectural style for network-based software." },
|
||||
{ name: "Agile/Scrum", description: "Iterative approach to project software delivery." },
|
||||
{ name: "MVC Architecture", description: "Design pattern separating Model, View, and Controller." },
|
||||
{ name: "Cloud Deployment", description: "Process of deploying applications to cloud infrastructure." },
|
||||
{ name: "Responsive Design", description: "Design approach for rendering well on variety of devices." }
|
||||
]
|
||||
skills: ["RESTful APIs", "Agile/Scrum", "MVC Architecture", "Cloud Deployment", "Responsive Design"]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -124,100 +85,54 @@ const itemVariants: Variants = {
|
||||
};
|
||||
|
||||
export default function Skills() {
|
||||
const [expandedCategories, setExpandedCategories] = useState<string[]>([]);
|
||||
|
||||
const toggleCategory = (category: string) => {
|
||||
setExpandedCategories(prev =>
|
||||
prev.includes(category)
|
||||
? prev.filter(c => c !== category)
|
||||
: [...prev, category]
|
||||
);
|
||||
};
|
||||
|
||||
const allExpanded = expandedCategories.length === TECHNICAL_SKILLS.length;
|
||||
|
||||
const toggleAll = () => {
|
||||
if (allExpanded) {
|
||||
setExpandedCategories([]);
|
||||
} else {
|
||||
setExpandedCategories(TECHNICAL_SKILLS.map(c => c.category));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mainContentBlock" style={{ minWidth: "66vw", display: "flex", justifyContent: "center" }}>
|
||||
<div className="mainContentBlock" style={{ minWidth: "66vw", alignItems: "center" }}>
|
||||
<div style={{ height: "30px" }}></div>
|
||||
<motion.div
|
||||
className="skills-container"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
style={{ width: "100%", maxWidth: "1200px", padding: "0 40px", boxSizing: "border-box" }}
|
||||
style={{ width: "100%", maxWidth: "1000px", padding: "0 20px" }}
|
||||
>
|
||||
<motion.div variants={itemVariants} style={{ marginBottom: "50px" }}>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.2)",
|
||||
marginBottom: "25px",
|
||||
paddingBottom: "10px",
|
||||
marginTop: "15px" // Restore approximate default h2 top margin
|
||||
}}>
|
||||
<h2 style={{ color: "white", margin: 0 }}>
|
||||
<h2 style={{ color: "white", borderBottom: "1px solid rgba(255,255,255,0.2)", paddingBottom: "10px", marginBottom: "25px" }}>
|
||||
Technical Skills
|
||||
</h2>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||
<span style={{ color: "rgba(255,255,255,0.8)", fontSize: "0.9rem" }}>Expand All</span>
|
||||
<div
|
||||
onClick={toggleAll}
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "25px" }}>
|
||||
{TECHNICAL_SKILLS.map((category) => (
|
||||
<motion.div
|
||||
key={category.category}
|
||||
style={{
|
||||
width: "40px",
|
||||
height: "22px",
|
||||
background: allExpanded ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.1)",
|
||||
borderRadius: "11px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "2px",
|
||||
cursor: "pointer",
|
||||
boxSizing: "border-box",
|
||||
border: "1px solid rgba(255,255,255,0.2)",
|
||||
transition: "background 0.3s"
|
||||
background: "rgba(255, 255, 255, 0.07)",
|
||||
backdropFilter: "blur(10px)",
|
||||
border: "1px solid rgba(255, 255, 255, 0.15)",
|
||||
borderRadius: "12px",
|
||||
padding: "20px",
|
||||
}}
|
||||
whileHover={{ scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.12)" }}
|
||||
>
|
||||
<h3 style={{ color: "white", marginTop: 0, marginBottom: "15px", fontSize: "1.2rem" }}>{category.category}</h3>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: "8px" }}>
|
||||
{category.skills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.15)",
|
||||
color: "rgba(255, 255, 255, 0.9)",
|
||||
padding: "6px 12px",
|
||||
borderRadius: "20px",
|
||||
fontSize: "0.85rem",
|
||||
fontWeight: 500,
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)"
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
x: allExpanded ? 18 : 0,
|
||||
backgroundColor: allExpanded ? "#ffffff" : "rgba(255,255,255,0.5)"
|
||||
}}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
||||
style={{
|
||||
width: "16px",
|
||||
height: "16px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "white",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tech-skills-grid" style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))",
|
||||
gap: "30px",
|
||||
position: "relative" // For layout animations
|
||||
}}>
|
||||
<AnimatePresence>
|
||||
{TECHNICAL_SKILLS.map((category) => (
|
||||
<SkillCard
|
||||
key={category.category}
|
||||
category={category}
|
||||
isExpanded={expandedCategories.includes(category.category)}
|
||||
onToggle={() => toggleCategory(category.category)}
|
||||
/>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -225,7 +140,7 @@ export default function Skills() {
|
||||
<h2 style={{ color: "white", borderBottom: "1px solid rgba(255,255,255,0.2)", paddingBottom: "10px", marginBottom: "25px" }}>
|
||||
Professional Skills
|
||||
</h2>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: "25px" }}>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "20px" }}>
|
||||
{PROFESSIONAL_SKILLS.map((skill) => (
|
||||
<motion.div
|
||||
key={skill.skill}
|
||||
|
||||
@@ -10,7 +10,7 @@ const WORK_EXPERIENCES = [
|
||||
"Provide excellent customer service by explaining repair processes, timelines, and care instructions.",
|
||||
"Collaborate with team members to coordinate and work efficiently, delivering the best possible service."
|
||||
],
|
||||
image: "tgPic.jpg",
|
||||
image: "towerglass.jpg",
|
||||
highlighted: false
|
||||
},
|
||||
{
|
||||
@@ -70,7 +70,7 @@ const WORK_EXPERIENCES = [
|
||||
"Assisted in high-volume automotive glass installations, developing fundamental skills in tool handling and glass preparation.",
|
||||
"Performed quality control checks, ensuring rigorous safety standards were met for every client.",
|
||||
],
|
||||
image: "tgPic.jpg",
|
||||
image: "towerglass.jpg",
|
||||
highlighted: false
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* Base styles - Layout utilities and common elements */
|
||||
|
||||
.background {
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.mainContentBlock {
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
max-height: 200vh;
|
||||
width: 66vw;
|
||||
min-width: auto;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.horizontalContentItem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.verticalContentItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flexContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3%;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
font-size: clamp(10px, 1.5vw, 60px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 75px;
|
||||
font-family: roboto, sans-serif;
|
||||
}
|
||||
|
||||
.contact-link {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
transition: color 0.3s ease;
|
||||
font-size: clamp(10px, 2vw, 1.2em);
|
||||
}
|
||||
|
||||
.contact-link:hover {
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes slideInFromLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile portrait mode - width < height */
|
||||
@media (orientation: portrait) {
|
||||
.mainContentBlock {
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/* Content Card and Project Card Component Styles */
|
||||
|
||||
.contentCard {
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contentCard:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contentCard h2 {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.contentCard p {
|
||||
white-space: normal;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* Hero Card (Legacy) */
|
||||
.hero-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Project Card Styles */
|
||||
.project-card {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 24px;
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transform: translateY(-8px) scale(1.02);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.project-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.1) 50%,
|
||||
rgba(255, 255, 255, 0) 100%);
|
||||
transform: skewX(-25deg);
|
||||
transition: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.project-card:hover::before {
|
||||
animation: shine 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
left: 200%;
|
||||
}
|
||||
}
|
||||
|
||||
.project-image-container {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.project-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.project-card:hover .project-image {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.project-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.project-description {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
flex-grow: 1;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.project-tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tech-chip {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.project-card:hover .tech-chip {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.project-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.project-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
transition: color 0.3s ease;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.project-link:hover {
|
||||
color: white;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
.code-block-container {
|
||||
margin: 30px 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: #1e1e1e;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.code-block-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.window-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.control-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.control-dot.red {
|
||||
background: #ff5f56;
|
||||
}
|
||||
|
||||
.control-dot.yellow {
|
||||
background: #ffbd2e;
|
||||
}
|
||||
|
||||
.control-dot.green {
|
||||
background: #27c93f;
|
||||
}
|
||||
|
||||
.language-label {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-family: monospace;
|
||||
text-transform: uppercase;
|
||||
margin-top: 2px;
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/* Floating Header Component Styles */
|
||||
|
||||
.floating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
padding: 20px 0px;
|
||||
text-align: center;
|
||||
background: rgba(52, 87, 245, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: clamp(8px, 2vw, 32px);
|
||||
flex-wrap: nowrap;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: "roboto, sans-serif";
|
||||
font-size: clamp(14px, 2vw, 20px);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
padding-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.nav-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.nav-link.active::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mobile Navigation Styles */
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.mobile-page-title {
|
||||
display: none;
|
||||
/* Hidden on desktop */
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 30px;
|
||||
height: 21px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
z-index: 1001;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.hamburger-line {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 3px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.floating-header {
|
||||
padding: 15px 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mobile-page-title {
|
||||
display: block;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
z-index: 1001;
|
||||
/* Ensure visible above nav overlay if needed, though nav usually covers content */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
justify-content: space-between;
|
||||
/* Push title left, hamburger right */
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Animate Hamburger to X */
|
||||
.mobile-toggle.open .hamburger-line:nth-child(1) {
|
||||
transform: translateY(9px) rotate(45deg);
|
||||
}
|
||||
|
||||
.mobile-toggle.open .hamburger-line:nth-child(2) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.mobile-toggle.open .hamburger-line:nth-child(3) {
|
||||
transform: translateY(-9px) rotate(-45deg);
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100vh;
|
||||
background: rgba(10, 10, 30, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease-in-out;
|
||||
z-index: 999;
|
||||
/* Below toggle button */
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header-nav.is-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 24px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
/* Main CSS Entry Point - Shared/Base styles only */
|
||||
/* Component and page-specific styles are imported directly in their respective files */
|
||||
|
||||
@import './base.css';
|
||||
@@ -1,98 +0,0 @@
|
||||
/* About Page Styles */
|
||||
|
||||
.about-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: auto;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
font-size: clamp(32px, 5vw, 60px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.about-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.about-image-container {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.about-image {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
border-radius: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.about-image:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.about-text-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 30px 30px 30px 30px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.about-text {
|
||||
font-size: clamp(16px, 1.2vw, 18px);
|
||||
line-height: 1.8;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
white-space: pre-line;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Mobile/Responsive */
|
||||
@media (orientation: portrait) {
|
||||
.about-content {
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.about-image {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.about-text-container {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.about-content {
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/* Home Page Styles */
|
||||
|
||||
.home-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 80vh;
|
||||
text-align: center;
|
||||
gap: 30px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.hero-profile-container {
|
||||
padding: 10px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 50%;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.hero-profile-img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
color: white;
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
font-size: clamp(40px, 6vw, 80px);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin: 0;
|
||||
letter-spacing: -0.5px;
|
||||
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hero-bio {
|
||||
font-size: clamp(16px, 1.5vw, 20px);
|
||||
font-weight: 300;
|
||||
line-height: 1.6;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
max-width: 600px;
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
.hero-socials {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero-social-link {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 10px 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.hero-social-link:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Legacy Home styles */
|
||||
.portrait-img {
|
||||
object-fit: cover;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: clamp(10px, 1.5vw, 100px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name-text {
|
||||
font-size: clamp(24px, 8vw, 120px);
|
||||
font-family: roboto, sans-serif;
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.skills-list {
|
||||
height: 5vh;
|
||||
color: white;
|
||||
font-family: roboto, sans-serif;
|
||||
}
|
||||
|
||||
/* Mobile/Responsive */
|
||||
@media (orientation: portrait) {
|
||||
.home-container {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.home-container {
|
||||
min-height: 70vh;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/* Projects Page Styles */
|
||||
|
||||
.projects-container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.projects-title {
|
||||
font-size: clamp(32px, 6vw, 60px);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
color: white;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
.sidequest-error-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidequest-back-link-error {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.sidequest-container {
|
||||
min-width: 66vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.sidequest-content-wrapper {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.sidequest-back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
text-decoration: none;
|
||||
margin-bottom: 30px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sidequest-back-link:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidequest-header {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sidequest-title {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidequest-tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidequest-tech-tag {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 4px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.sidequest-hero-image-container {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 40px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.sidequest-hero-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.sidequest-details-container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.sidequest-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.sidequest-section-title {
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.sidequest-section-content {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.sidequest-section-image {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.sidequest-section-caption {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.9rem;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidequest-footer-links {
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.sidequest-footer-link {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.sidequest-footer-spacer {
|
||||
height: 100px;
|
||||
}
|
||||
Reference in New Issue
Block a user