layout, new work, analytics

This commit is contained in:
saiminh 2026-02-10 18:06:34 +01:00
parent b019401fb8
commit 7777235104
37 changed files with 1722 additions and 1314 deletions

3
.gitignore vendored
View file

@ -11,3 +11,6 @@ vite.config.ts.timestamp-*
# Local Netlify folder
.netlify
# IDE files
.idea

746
package-lock.json generated
View file

@ -21,6 +21,7 @@
"@sveltejs/adapter-node": "^5.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^5.1.1",
"@tailwindcss/vite": "^4.1.18",
"@types/node": "^20.5.1",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
@ -29,11 +30,11 @@
"eslint-plugin-svelte": "^3.0.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.0.0",
"sass": "^1.64.2",
"sharp": "^0.32.5",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.1.18",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vanilla-lazyload": "^17.8.4",
@ -1357,6 +1358,278 @@
"vite": "^6.0.0"
}
},
"node_modules/@tailwindcss/node": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
"integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"enhanced-resolve": "^5.18.3",
"jiti": "^2.6.1",
"lightningcss": "1.30.2",
"magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
"tailwindcss": "4.1.18"
}
},
"node_modules/@tailwindcss/oxide": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
"integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@tailwindcss/oxide-android-arm64": "4.1.18",
"@tailwindcss/oxide-darwin-arm64": "4.1.18",
"@tailwindcss/oxide-darwin-x64": "4.1.18",
"@tailwindcss/oxide-freebsd-x64": "4.1.18",
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
"@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
"@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
"@tailwindcss/oxide-linux-x64-musl": "4.1.18",
"@tailwindcss/oxide-wasm32-wasi": "4.1.18",
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
"@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
"integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
"integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
"integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
"integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
"integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
"integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
"integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
"integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
"integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
"integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
],
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@emnapi/wasi-threads": "^1.1.0",
"@napi-rs/wasm-runtime": "^1.1.0",
"@tybys/wasm-util": "^0.10.1",
"tslib": "^2.4.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
"integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
"integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz",
"integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tailwindcss/node": "4.1.18",
"@tailwindcss/oxide": "4.1.18",
"tailwindcss": "4.1.18"
},
"peerDependencies": {
"vite": "^5.2.0 || ^6 || ^7"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -1701,19 +1974,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -1878,15 +2138,6 @@
}
]
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -1972,42 +2223,19 @@
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 8.10.0"
"node": ">= 14.16.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/chownr": {
@ -2224,10 +2452,11 @@
}
},
"node_modules/detect-libc": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
@ -2272,6 +2501,20 @@
"once": "^1.4.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.3.0"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/esbuild": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
@ -2868,6 +3111,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@ -2931,12 +3181,6 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz",
"integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==",
"dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3001,18 +3245,6 @@
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"dev": true
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@ -3108,6 +3340,16 @@
"resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz",
"integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw=="
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/js-binary-schema-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
@ -3169,6 +3411,267 @@
"node": ">= 0.8.0"
}
},
"node_modules/lightningcss": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.30.2",
"lightningcss-darwin-arm64": "1.30.2",
"lightningcss-darwin-x64": "1.30.2",
"lightningcss-freebsd-x64": "1.30.2",
"lightningcss-linux-arm-gnueabihf": "1.30.2",
"lightningcss-linux-arm64-gnu": "1.30.2",
"lightningcss-linux-arm64-musl": "1.30.2",
"lightningcss-linux-x64-gnu": "1.30.2",
"lightningcss-linux-x64-musl": "1.30.2",
"lightningcss-win32-arm64-msvc": "1.30.2",
"lightningcss-win32-x64-msvc": "1.30.2"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
"integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
"integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
"integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
"integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
"integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
"integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
"integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
"integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
"integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
"integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
"integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@ -3378,15 +3881,6 @@
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
"dev": true
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -3894,15 +4388,17 @@
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"license": "MIT",
"engines": {
"node": ">=8.10.0"
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/resolve": {
@ -4060,23 +4556,6 @@
}
]
},
"node_modules/sass": {
"version": "1.64.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.64.2.tgz",
"integrity": "sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
@ -4367,36 +4846,6 @@
"typescript": ">=5.0.0"
}
},
"node_modules/svelte-check/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/svelte-check/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/svelte-cloudinary": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/svelte-cloudinary/-/svelte-cloudinary-1.3.2.tgz",
@ -4567,6 +5016,27 @@
"@types/estree": "^1.0.6"
}
},
"node_modules/tailwindcss": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/tar-fs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",

View file

@ -18,6 +18,7 @@
"@sveltejs/adapter-node": "^5.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^5.1.1",
"@tailwindcss/vite": "^4.1.18",
"@types/node": "^20.5.1",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
@ -26,11 +27,11 @@
"eslint-plugin-svelte": "^3.0.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.0.0",
"sass": "^1.64.2",
"sharp": "^0.32.5",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.1.18",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vanilla-lazyload": "^17.8.4",
@ -40,8 +41,8 @@
"dependencies": {
"gsap": "^3.13.0",
"mdsvex": "^0.11.0",
"pixi.js": "^8.15.0",
"pixi-filters": "^6.1.5",
"pixi.js": "^8.15.0",
"superjson": "^1.13.1",
"svelte-cloudinary": "^1.1.0"
}

View file

@ -0,0 +1,200 @@
<script lang="ts">
import { onMount } from 'svelte';
import { gsap } from 'gsap';
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';
import { CldImage } from 'svelte-cloudinary';
interface Props {
images: string[];
title: string;
}
let { images, title }: Props = $props();
gsap.registerPlugin(ScrollToPlugin);
let wrapperEl: HTMLDivElement;
onMount(() => {
let is_landscape = window.matchMedia('(orientation:landscape)').matches;
window.addEventListener('resize', () => {
is_landscape = window.matchMedia('(orientation:landscape)').matches;
});
setTimeout(() => {
document.querySelectorAll('.gallery img')?.forEach((image) => {
const img = image as HTMLImageElement;
if (!img.complete) {
img.classList.add('imageIsLoading');
img.onload = () => {
gsap.fromTo(
img,
{ autoAlpha: 0, scale: 1.2 },
{ autoAlpha: 1, scale: 1, duration: 1, ease: 'power4.out' }
);
};
}
});
}, 10);
const gallery = document.querySelector('.gallery') as HTMLElement;
let isAnimating = false;
const galleryClickHandler = (e: MouseEvent) => {
if (isAnimating) return;
const allImgs = gallery.querySelectorAll('figure');
const scrollX = gallery.scrollLeft;
const imgWidth = allImgs[0].offsetWidth || 0;
const imgCount = allImgs.length;
const galleryOuterWidth = wrapperEl?.offsetWidth || 0;
const galleryOverlap = imgWidth - (galleryOuterWidth % imgWidth);
const maxScrollX = imgWidth * (imgCount - 1);
const imgIndex =
scrollX < maxScrollX ? Math.round(scrollX / imgWidth) : imgCount - 1;
let newScrollPos;
if (imgIndex < imgCount - 2) {
newScrollPos = scrollX + imgWidth;
} else if (imgIndex === imgCount - 2) {
newScrollPos = is_landscape
? scrollX + galleryOverlap * 0.8
: scrollX + galleryOverlap * 0.85;
} else {
newScrollPos = 0;
}
gsap.to(gallery, {
scrollTo: { x: newScrollPos, autoKill: false },
duration: 0.75,
ease: 'power4.out',
onStart: () => {
isAnimating = true;
},
onComplete: () => {
isAnimating = false;
},
});
};
const hoverElement = document.createElement('div');
hoverElement.classList.add('hoverElement');
wrapperEl?.appendChild(hoverElement);
const galleryHoverHandler = (e: MouseEvent) => {
gsap.to(hoverElement, {
x: e.clientX,
y: e.clientY + 25,
rotateX: 10,
rotateZ: -5,
autoAlpha: 1,
duration: 0.3,
overwrite: true,
});
};
const galleryLeaveHandler = () => {
gsap.to(hoverElement, {
autoAlpha: 0,
duration: 0.3,
overwrite: true,
});
};
if (gallery) {
gallery.addEventListener('click', galleryClickHandler);
gallery.addEventListener('mousemove', galleryHoverHandler);
gallery.addEventListener('mouseleave', galleryLeaveHandler);
}
return () => {
gallery?.removeEventListener('click', galleryClickHandler);
gallery?.removeEventListener('mousemove', galleryHoverHandler);
gallery?.removeEventListener('mouseleave', galleryLeaveHandler);
};
});
</script>
<div
class="
gallery-wrapper
relative
w-screen overflow-hidden
pb-[1em] z-1
perspective-[250px] perspective-origin-[center_bottom]
md:py-[2em] md:pb-[2.5em]
md:mb-[-120px]
md:perspective-normal
"
bind:this={wrapperEl}
>
<div
class="
gallery
relative flex scroll-smooth gap-0
overflow-x-auto snap-x snap-mandatory
origin-center transform-[rotate3d(-2,0,1,-10deg)]
[scrollbar-width:none]
w-[115vw] -left-[4vw]
md:w-[110vw] md:-left-[3vw]
"
>
{#each images as image (image)}
<figure
class="
flex flex-col overflow-hidden justify-center snap-start
px-px flex-[1_0_85%] h-full aspect-140/84 w-auto m-0
md:flex-[1_0_66.666%]
[&_img]:w-full [&_img]:h-full [&_img]:object-top-left
[&_video]:w-full [&_video]:h-full [&_video]:object-top-left [&_video]:object-cover
"
>
{#if image.includes('/video/')}
<video src={image} width="1400" height="840" autoplay muted loop></video>
{:else}
<CldImage
src={image}
alt={title}
sizes="(min-width: 768px) 67vw, 90vw"
width={1400}
height={840}
placeholder="blur"
loading="eager"
objectFit="cover"
/>
{/if}
</figure>
{/each}
</div>
</div>
<style>
.gallery::-webkit-scrollbar {
display: none;
}
:global(.imageIsLoading) {
visibility: hidden;
opacity: 0;
}
:global(.hoverElement) {
position: fixed;
top: 0;
left: 0;
width: 50px;
height: 50px;
background-color: var(--color-bg);
z-index: 100;
opacity: 0;
visibility: hidden;
box-shadow: -4px 4px 8px rgba(0, 0, 0, 0.2);
pointer-events: none;
}
:global(.hoverElement)::before {
content: url('/moveright.svg');
font-size: 33px;
line-height: 50px;
text-align: center;
display: block;
height: 50px;
width: 50px;
}
@media (pointer: coarse) {
:global(.hoverElement) {
display: none;
}
}
</style>

View file

@ -70,14 +70,13 @@
</div>
</details>
<style lang="scss">
<style>
details {
--height: 0;
overflow: hidden;
padding: 0;
margin-bottom: 0;
transition: margin-bottom .3s ease-out;
margin-bottom: .1em;
transition: margin-bottom .3s ease-out;
}
summary {
background-color: rgba(0,0,0,0.1);
@ -88,37 +87,29 @@
line-height: 1.2;
font-size: 1em;
padding: 0.5em 0 0.5em 1.75em;
&::-webkit-details-marker {
display:none;
}
&:before, &:after {
content: '';
position: absolute;
left: .5em;
top: 1em;
width: 17px;
height: 2px;
background-color: var(--color-text);
transform-origin: 50% 50%;
}
&:before {
transform: rotate(90deg);
transition: all .2s ease-in-out;
}
}
:global(summary em) {
font-weight: 800;
summary::-webkit-details-marker { display: none; }
summary::before, summary::after {
content: '';
position: absolute;
left: .5em;
top: 1em;
width: 17px;
height: 2px;
background-color: var(--color-text);
transform-origin: 50% 50%;
}
// details[open] summary,
summary::before {
transform: rotate(90deg);
transition: all .2s ease-in-out;
}
:global(summary em) { font-weight: 800; }
:global(details.is-open summary) {
background-color: rgba(0,0,0,0.1);
&:before {
transition: all .2s ease-in-out;
transform: rotate(0deg);
}
}
:global(details.is-open summary)::before {
transition: all .2s ease-in-out;
transform: rotate(0deg);
}
.faq-content {
border-top: 1px solid var(--color-bg);
@ -129,16 +120,8 @@
font-size: 1em;
box-sizing: border-box;
}
:global(.faq-content p, .faq-content li){
font-size: 1em!important;
}
:global(.faq-content li){
margin-left: 2em;
}
:global(.faq-content > :first-child){
margin-top: 0;
}
:global(.faq-content > :last-child){
margin-bottom: 0;
}
:global(.faq-content p, .faq-content li) { font-size: 1em !important; }
:global(.faq-content li) { margin-left: 2em; }
:global(.faq-content > :first-child) { margin-top: 0; }
:global(.faq-content > :last-child) { margin-bottom: 0; }
</style>

View file

@ -10,9 +10,9 @@
<MainNav />
</header>
<style lang="scss">
<style>
@media screen and (max-width: 767px) {
.header-nav:before {
.header-nav::before {
content: '';
position: fixed;
left: 0;

View file

@ -72,6 +72,8 @@
<HomeIlluShape />
<HomeIlluShape />
<HomeIlluShape />
<HomeIlluShape />
<HomeIlluShape />
<!-- <HomeIlluShape />
<HomeIlluShape />
<HomeIlluShape />
@ -90,7 +92,7 @@
gap: clamp(1px, 0.3vw, 5px);
}
:global(.home-illu-shapes > *) {
flex-basis: calc(33.333% - clamp(1px, 0.3vw, 5px));
flex-basis: calc(25% - clamp(1px, 0.3vw, 5px));
opacity: 0;
}
</style>

View file

@ -3,66 +3,53 @@
<p>Loading...</p>
</div>
<style lang="scss">
.loader {
position: fixed;
bottom: 0;
right: 0;
transform: translate(-50%, -50%);
text-align: center;
opacity: 0;
transform: translateY(100%);
animation: fade-in .5s cubic-bezier(0.165, 0.84, 0.44, 1) .3s forwards;
display: flex;
align-items: center;
padding: 15px var(--spacing-outer);
z-index: 1000;
background-color: var(--color-bg);
& p {
<style>
.loader {
position: fixed;
bottom: 0;
right: 0;
transform: translate(-50%, -50%);
text-align: center;
opacity: 0;
transform: translateY(100%);
animation: fade-in .5s cubic-bezier(0.165, 0.84, 0.44, 1) .3s forwards;
display: flex;
align-items: center;
padding: 15px var(--spacing-outer);
z-index: 1000;
background-color: var(--color-bg);
}
.loader p {
font-size: .75em;
line-height: 1;
margin: 0;
display: inline-block;
}
}
.lds-circle {
display: flex;
align-items: center;
transform: translateZ(1px);
}
.lds-circle > div {
display: inline-block;
width: 24px;
height: 24px;
margin: 8px;
border-radius: 50%;
background: var(--color-text);
animation: lds-circle 6s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
@keyframes lds-circle {
0%, 100% {
animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
.lds-circle {
display: flex;
align-items: center;
transform: translateZ(1px);
}
0% {
transform: rotateY(0deg);
.lds-circle > div {
display: inline-block;
width: 24px;
height: 24px;
margin: 8px;
border-radius: 50%;
background: var(--color-text);
animation: lds-circle 6s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
50% {
transform: rotateY(1800deg);
animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
@keyframes lds-circle {
0%, 100% { animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5); }
0% { transform: rotateY(0deg); }
50% {
transform: rotateY(1800deg);
animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
}
100% { transform: rotateY(3600deg); }
}
100% {
transform: rotateY(3600deg);
@keyframes fade-in {
from { opacity: 0; transform: translateY(100%); }
to { opacity: 1; transform: translateY(0%); }
}
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0%);
}
}
</style>

View file

@ -1,35 +1,21 @@
<script lang="ts">
import { logotext } from "$lib/utils/stores";
// let $logotexttext = logotext.subscribe((value) => JSON.stringify(value));
</script>
<!-- <svg id="floter-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 287.4 83.5" width="290" height="85">
<path id="r" d="M259.9 82.4h-24.2l11.6-54.9h22l-2.7 12.9c4.5-8.9 11-14 18.1-14 1 0 2.1 0 2.7.2l-4.7 22.1c-1.4-.4-3.3-.9-6.4-.9-5.4 0-9.7 2.3-10.8 7.7l-5.6 26.9z"/>
<path id="e" d="M237.9 59.3h-35.7c.3 5.2 2.2 7.5 6.5 7.5 2.9 0 5.1-1.5 5.9-4.2h22.3c-4.3 13.2-14.5 20.9-30.4 20.9-16.3 0-26.5-9.7-26.5-25.2 0-17.8 13.4-32 32.3-32 16.3 0 26.5 9.7 26.5 25.2 0 2.7-.4 5.3-.9 7.8zM203.3 50h13.5c-.2-4-1.8-6.5-6-6.5-3.4 0-6.1 2.1-7.5 6.5z"/>
<path id="t" d="M171.5 65.9c1.9 0 4.3-.3 6.3-1L174 82.2c-2.5.8-7.1 1.3-11.1 1.3-14 0-23.2-5.1-19.6-22.3l3.5-16.7h-5.5l3.6-17h5.5l3.3-15.4H178l-3.3 15.4h11l-3.6 17h-11l-3.3 15.8c-.9 3.9.3 5.6 3.7 5.6z"/>
<polygon id="odash" points="137.1,22.5 94.7,22.4 98.2,5.7 140.6,5.9 "/>
<path id="o" d="M111.4 26.4c16.7 0 27.2 9.7 27.2 25.2 0 17.8-13.7 32-33.1 32-16.7 0-27.2-9.7-27.2-25.2 0-17.9 13.8-32 33.1-32zm-3.9 36.7c5.3 0 8.8-4.3 8.8-9.4 0-4.2-2.5-6.8-6.6-6.8-5.4 0-8.9 4.3-8.9 9.4 0 4.1 2.5 6.8 6.7 6.8z"/>
<polygon id="l" points="45.5,82.4 63,0 87.1,0 69.7,82.4"/>
<polygon id="F" points="17.5,0 59.7,0 55.5,19.8 36.9,19.8 34.1,33 50.5,33 46.3,52.7 29.9,52.7 23.6,82.4 0,82.4"/>
</svg> -->
<div id="floter-logo">{$logotext}</div>
<style>
/* #floter-logo {
fill: currentColor;
height: 100%;
width: auto;
display: block;
} */
#floter-logo {
text-decoration: none;
display: block;
font-size: 36px;
letter-spacing: -0.033em;
line-height: 36px;
<div id="floter-logo">
{$logotext}
</div>
@media screen and (min-width: 768px) {
font-size: 48px;
}
<style>
#floter-logo {
text-decoration: none;
display: block;
font-size: 36px;
letter-spacing: -0.033em;
line-height: 36px;
@media screen and (min-width: 768px) {
font-size: 48px;
}
</style>
}
</style>

View file

@ -48,7 +48,11 @@
<header>
<input aria-hidden="true" type="checkbox" id="menustate" />
<label for="menustate" aria-hidden="true">
<span class="open"></span>
<span class="open">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-12 h-12">
<path d="M3 4H21V6H3V4ZM9 11H21V13H9V11ZM3 18H21V20H3V18Z"></path>
</svg>
</span>
<span class="close">×</span>
</label>
<nav id="nav">
@ -61,7 +65,7 @@
</nav>
</header>
<style lang="scss">
<style>
header {
position: fixed;
bottom: 0;
@ -74,9 +78,7 @@
align-items: flex-end;
box-sizing: border-box;
}
label {
height: 36px;
}
label { height: 36px; }
.open, .close {
font-size: 4em;
line-height: 0.3;
@ -84,25 +86,11 @@
z-index: 2;
cursor: url('/pointer.svg'), auto;
}
.close {
top: 0.033em;
}
#menustate, #nav, .close {
/* Hide the checkbox, menu and close button by default */
display: none;
}
#menustate:checked ~ #nav ,
#menustate:checked ~ label .close {
/*
Show the menu and close button when the menu is open
(when the #menustate input field is checked)
*/
display: block;
}
#menustate:checked ~ label .open {
/* Hide the open button when the menu is open */
display: none;
}
.close { top: 0.033em; }
#menustate, #nav, .close { display: none; }
#menustate:checked ~ #nav,
#menustate:checked ~ label .close { display: block; }
#menustate:checked ~ label .open { display: none; }
#nav {
background-color: var(--color-bg);
position: fixed;
@ -118,31 +106,21 @@
font-size: 2.5em;
font-weight: 800;
font-style: italic;
// text-transform: lowercase;
text-decoration: none;
color: var(--color-text);
outline: none;
-webkit-tap-highlight-color: transparent;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transition: color .1s ease-out;
@media screen and (min-width: 768px) {
font-size: 4.5em;
}
&:first-child {
margin-top: 1em;
}
&:hover {
color: var(--color-highlight);
}
&:active {
color: var(--color-highlight);
}
&.current {
color: var(--color-highlight);
transition: none;
}
}
#nav a:first-child { margin-top: 1em; }
#nav a:hover, #nav a:active { color: var(--color-highlight); }
#nav a.current {
color: var(--color-highlight);
transition: none;
}
@media screen and (min-width: 768px) {
#nav a { font-size: 4.5em; }
}
</style>

View file

@ -1,9 +1,12 @@
:root {
@import "tailwindcss";
@layer base {
:root {
--spacing-outer: 5vw;
--spacing-nav: 5vw;
--color-bg: rgb(207, 63, 70);;
--color-text: rgb(255, 234, 217);
--color-highlight: rgb(29, 12, 18);
--color-bg: rgb(0, 0, 90);
--color-text: rgb(255, 240, 240);
--color-highlight: rgb(250, 125, 0);
--color-bg-variant-1: rgb(207, 63, 70);
--color-text-variant-1: rgb(255, 234, 217);
@ -22,7 +25,7 @@
--color-highlight-variant-4: rgb(0, 39, 67);
--aspect-ratio-heroes: 1.5;
--font-size-p: clamp(20px, 1.6vw, 1.6vw);
--font-size-p: clamp(20px, 1.6vw, 125px);
@media screen and (min-width: 768px) {
--spacing-outer: 5vw;
@ -48,6 +51,7 @@
a:hover, input:hover, button:hover {
cursor: url('/pointer.svg'), auto;
}
body {
cursor: url('/cursor.svg'), auto;
font-family: stratos, sans-serif;
@ -61,47 +65,59 @@ body {
overflow-x: hidden;
transition: background-color .5s ease-in-out, color .5s ease-in-out;
}
body * {
box-sizing: border-box;
}
h1, h2, h3, h4, h5 {
:where(h1, h2, h3, h4, h5) {
font-size: 2em;
line-height: 1.1;
font-weight: 400;
letter-spacing: -0.025em;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.25em;
}
ul, ol {
padding-left: 0;
}
ul {
list-style: '▪︎ ';
}
p, li {
font-weight: 400;
font-size: var(--font-size-p);
line-height: 1.3;
}
a {
color: inherit;
text-underline-offset: 0.25em;
text-decoration-thickness: 0.066em;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
&:hover {
text-underline-offset: 0.1em;
}
}
a:hover {
text-underline-offset: 0.1em;
}
.cta {
font-size: clamp(20px, 1.6vw, 1.6vw);
}
.button {
font-weight: 800;
font-style: italic;
@ -118,88 +134,99 @@ a {
position: relative;
z-index: 2;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
&:hover {
color: var(--color-bg);
@media screen and (min-width: 768px) {
letter-spacing: 0.01em;
}
}
.button:hover {
color: var(--color-bg);
}
&:before {
content: '';
display: block;
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
left: 0px;
bottom: 0;
border: 2px solid var(--color-text);
background-color: var(--color-bg);
box-sizing: border-box;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
&:hover:before {
background-color: var(--color-text);
width: calc(100% + 0.25em);
height: calc(100% + 10px);
left: -0.125em;
bottom: -5px;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
&.button--primary {
color: var(--color-text);
&:hover {
color: var(--color-highlight);
}
&:before {
background-color: var(--color-highlight);
border: 2px solid var(--color-highlight);
}
&:hover:before {
background-color: var(--color-text);
border: 2px solid var(--color-text);
}
}
&.button--xl {
font-size: clamp(24px, 3.2vw, 3.2vw);
padding: 0.5em;
letter-spacing: -0.02em;
margin: 0 auto;
text-align: center;
&:hover {
letter-spacing: 0;
}
@media screen and (min-width: 768px) {
.button:hover {
letter-spacing: 0.01em;
}
}
.button::before {
content: '';
display: block;
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
left: 0px;
bottom: 0;
border: 2px solid var(--color-text);
background-color: var(--color-bg);
box-sizing: border-box;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.button:hover::before {
background-color: var(--color-text);
width: calc(100% + 0.25em);
height: calc(100% + 10px);
left: -0.125em;
bottom: -5px;
transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.button.button--primary {
color: var(--color-text);
}
.button.button--primary:hover {
color: var(--color-highlight);
}
.button.button--primary::before {
background-color: var(--color-highlight);
border: 2px solid var(--color-highlight);
}
.button.button--primary:hover::before {
background-color: var(--color-text);
border: 2px solid var(--color-text);
}
.button.button--xl {
font-size: clamp(24px, 3.2vw, 3.2vw);
padding: 0.5em;
letter-spacing: -0.02em;
margin: 0 auto;
text-align: center;
}
.button.button--xl:hover {
letter-spacing: 0;
}
.infobox {
// border-top: 2px solid var(--color-text);
// border-bottom: 2px solid var(--color-text);
padding: 1.5em 0;
font-size: var(--font-size-p);
& li {
border-bottom: 1px solid;
padding: 0.5em 0;
}
& > :first-child {
margin-top: 0;
}
& > :last-child {
margin-bottom: 0;
}
}
.content{
.infobox li {
border-bottom: 1px solid;
padding: 0.5em 0;
}
.infobox > :first-child {
margin-top: 0;
}
.infobox > :last-child {
margin-bottom: 0;
}
.content {
position: absolute;
top: 0;
width: 100%;
}
.hide-on-mobile {
@media screen and (orientation: portrait) and (max-width: 767px) {
@media screen and (orientation: portrait) and (max-width: 767px) {
.hide-on-mobile {
display: none;
}
}
}

View file

@ -2,4 +2,4 @@ import { writable } from 'svelte/store';
export const workbulge = writable(0.25);
export const loading = writable(false);
export const logotext = writable('Flöter');
export const logotext = writable('FlöTeR');

View file

@ -1,5 +1,5 @@
<script lang="ts">
import '$lib/styles/global.scss';
import '$lib/styles/global.css';
import Header from '$lib/components/Header.svelte';
import Loader from '$lib/components/Loader.svelte';
import { page } from '$app/state';
@ -12,11 +12,10 @@
pathname: string;
}
// onMount(() => {
// // console.log('layout mounted');
// // Set initial random background color on first load
// setRandomBgColor();
// });
onMount(() => {
// console.log('layout mounted');
setTheme();
});
let { children }: { children: Snippet } = $props();
@ -31,35 +30,6 @@
}
}
function setRandomBgColor() {
const currentBgColor = getComputedStyle(document.documentElement)
.getPropertyValue('--color-bg');
const currentHighlightColor = getComputedStyle(document.documentElement)
.getPropertyValue('--color-highlight');
const currentTextColor = getComputedStyle(document.documentElement)
.getPropertyValue('--color-text');
let variantNumber: number;
let variantBgColor: string;
let variantHighlightColor: string;
let variantTextColor: string;
// Keep picking a random variant until it's different from the current color
do {
variantNumber = Math.floor(Math.random() * 4) + 1;
variantBgColor = getComputedStyle(document.documentElement)
.getPropertyValue(`--color-bg-variant-${variantNumber}`);
variantHighlightColor = getComputedStyle(document.documentElement)
.getPropertyValue(`--color-highlight-variant-${variantNumber}`);
variantTextColor = getComputedStyle(document.documentElement)
.getPropertyValue(`--color-text-variant-${variantNumber}`);
} while (variantBgColor.trim() === currentBgColor.trim());
document.documentElement.style.setProperty('--color-bg', variantBgColor);
document.documentElement.style.setProperty('--color-highlight', variantHighlightColor);
document.documentElement.style.setProperty('--color-text', variantTextColor);
}
if (browser) {
beforeNavigate((nav) => {
// token to avoid races between overlapping navigations
@ -68,9 +38,6 @@
clearTimer();
showLoader = false;
// Change background color on route change
// setRandomBgColor();
// only show if navigation takes >150ms
timer = setTimeout(() => {
if (token === navToken) showLoader = true;
@ -81,9 +48,23 @@
if (token !== navToken) return;
clearTimer();
showLoader = false;
setTheme();
});
});
}
function setTheme() {
if (page.data.theme) {
document.documentElement.style.setProperty('--color-bg', page.data.theme.bg);
document.documentElement.style.setProperty('--color-text', page.data.theme.text);
document.documentElement.style.setProperty('--color-highlight', page.data.theme.highlight);
} else {
document.documentElement.style.setProperty('--color-bg', 'var(--color-bg)');
document.documentElement.style.setProperty('--color-text', 'var(--color-text)');
document.documentElement.style.setProperty('--color-highlight', 'var(--color-highlight)');
}
}
</script>
<svelte:head>
@ -93,14 +74,13 @@
<meta name="twitter:card" content="summary_large_image">
<meta name="description" content={page.data.description ? page.data.description : 'Simon Flöter is a designer and creative developer'} />
<title>{page.data.title ? page.data.title : 'Simon Flöter, Designer and Creative Developer'}</title>
<script defer data-domain="floter.design" src="https://analytics.floter.design/js/script.js"></script>
</svelte:head>
<Header />
<!-- {#key data.pathname} -->
<div class="content">
{#if showLoader}
<Loader />
{/if}
{@render children()}
</div>
<!-- {/key} -->

View file

@ -22,6 +22,14 @@
gsap.registerPlugin( ScrollTrigger, ScrollToPlugin, SplitText );
const sections = document.querySelectorAll('section');
const highlight = document.querySelector('h1 em');
gsap.to(highlight, {
duration: .5,
color: 'var(--color-highlight)',
ease: 'power4.out',
delay: 2
})
sections.forEach( (section) => {
if ( section.classList.contains('splash')){
@ -93,210 +101,155 @@
</script>
<article class="scroller">
<section class="canvasized splash">
<h1 class="align-middle">I create digital experiences that <em>stand out</em> from the rest.</h1>
<article class="scroller text-[clamp(32px,4.5vw,4.5vw)]">
<section
class="
canvasized splash
snap-start box-border
px-(--spacing-outer) p-(--spacing-outer)
min-h-svh flex flex-col justify-center
max-w-full landscape:max-w-[75vw] landscape:mx-auto
cursor-[url('/pointer.svg'),auto]
"
>
<h1
class="
align-middle select-none
leading-[0.85] text-[14.75vw] tracking-[-0.05em]
text-(--color-text)
landscape:text-[9vw] landscape:px-4
landscape:-mt-[0.5em]
[&_em]:not-italic [&_em]:font-normal
opacity-0 invisible
"
>
I'm Simon Flöter. <br>
I create brands and websites that <em>stand out.</em>
</h1>
</section>
<section class="dev">
<figure class="dev-image">
<section
class="
dev snap-start box-border
px-(--spacing-outer) min-h-svh
flex flex-col justify-center
landscape:max-w-[75vw] landscape:mx-auto
"
>
<figure
class="
dev-image relative left-0 text-center block -z-2
-my-2 -mb-[0.5em]
landscape:-my-[1.5em] landscape:-mb-[0.5em]
"
>
<HomeIlluDev />
</figure>
<h2>Creative Development</h2>
<p>I create exquisitly tailored web experiences for discerning enterprises and their audiences.</p>
<div class="cta">
<a href="/service" class="button">Services <span class="hide-on-mobile"> I provide</span></a>
<a href="/contact" class="button button--primary">Contact me</a>
<h2
class="
select-none cursor-[url('/pointer.svg'),auto]
text-[12vw] leading-[0.9] tracking-[-0.033em] mb-[0.5em]
landscape:text-[6vw] landscape:m-0
[&_em]:not-italic [&_em]:font-normal [&_em]:text-(--color-highlight)
"
>
Creative Development
</h2>
<p
class="
text-[0.66em] mb-[1em]
landscape:mt-4 landscape:mb-[0.25em] landscape:text-[0.6em] landscape:tracking-[-0.02em]
"
>
I create exquisitly tailored web experiences for discerning enterprises and their audiences.
</p>
<div class="cta mt-2 pt-0 landscape:pt-4 landscape:mt-0">
<a href="/service" class="button select-none"
>Services <span class="hide-on-mobile"> I provide</span></a
>
<a href="/contact" class="button button--primary select-none">Contact me</a>
</div>
</section>
<section class="design">
<figure class="design-illu">
<section
class="
design overflow-hidden snap-start box-border
px-(--spacing-outer) min-h-svh
flex flex-col justify-center
landscape:max-w-[75vw] landscape:mx-auto
"
>
<figure
class="
design-illu relative left-0 w-3/5 mx-auto text-center block
my-0 mb-4 -z-2
landscape:mb-12 landscape:w-[45%]
"
>
<HomeIlluShapes />
</figure>
<h2>Visual Design</h2>
<p>I'm a seasoned designer, with a long list of succesful projects and happy clients.</p>
<div class="cta">
<a href="/work" class="button">Work <span class="hide-on-mobile"> I've done</span></a>
<a href="/contact" class="button button--primary">Contact me</a>
<h2
class="
select-none cursor-[url('/pointer.svg'),auto]
text-[12vw] leading-[0.9] tracking-[-0.033em] mb-[0.5em]
landscape:text-[6vw] landscape:m-0
[&_em]:not-italic [&_em]:font-normal [&_em]:text-(--color-highlight)
"
>
Visual Design
</h2>
<p
class="
text-[0.66em] mb-[1em]
landscape:mt-4 landscape:mb-[0.25em] landscape:text-[0.6em] landscape:tracking-[-0.02em]
"
>
I'm a seasoned designer, with a long list of succesful projects and happy clients.
</p>
<div class="cta mt-2 pt-0 landscape:pt-4 landscape:mt-0">
<a href="/work" class="button select-none"
>Work <span class="hide-on-mobile"> I've done</span></a
>
<a href="/contact" class="button button--primary select-none">Contact me</a>
</div>
</section>
<section class="canvasized hireme">
<h2 class="align-middle">I am currently available <em>for freelance work.</em></h2>
<section
class="
canvasized hireme snap-start box-border
px-(--spacing-outer) min-h-svh
flex flex-col justify-center
landscape:max-w-[95vw] landscape:mx-auto
[&_h2]:text-[12.5vw] [&_h2]:mb-6
landscape:[&_h2]:text-[8vw] landscape:[&_h2]:mb-2 landscape:[&_h2]:mt-0 landscape:[&_h2]:px-4
"
>
<div class="
mb-16 align-middle
">
<h2
class="
align-middle select-none cursor-[url('/pointer.svg'),auto]
text-[12.5vw] leading-[0.9] tracking-[-0.033em] mb-6
landscape:text-[8vw] landscape:mb-16 landscape:mt-0 landscape:px-4
opacity-0 invisible
[&_em]:not-italic [&_em]:font-normal [&_em]:text-(--color-highlight)
"
>
I am currently available <em>for freelance work.</em>
</h2>
</div>
{#if mounted}
<div class="hireme-date">(as of { currentMonth })</div>
<div class="hireme-date text-[0.66em] text-center tracking-tight">
(as of {currentMonth})
</div>
{/if}
<div class="cta" style="text-align: center">
<a href="/contact" class="button button--xl button--primary">Get in touch</a>
<div class="cta text-center mt-2 pt-0 landscape:pt-4 landscape:mt-0">
<a href="/contact" class="button button--xl button--primary select-none">Get in touch</a>
</div>
</section>
</article>
<HomeCanvas textsToCanvas={canvasElems} imgsToCanvas={imgElems}/>
<div class="canvasResizeToThis"></div>
<style lang="scss">
.canvasResizeToThis {
position: fixed;
top: -10vh;
left: -10vw;
width: 120vw;
height: 120vh;
pointer-events: none;
z-index: -1;
}
.scroller {
font-size: clamp(32px, 4.5vw, 4.5vw);
}
section {
scroll-snap-align: start;
box-sizing: border-box;
padding: 0 var(--spacing-outer);
min-height: 100svh;
display: flex;
flex-direction: column;
gap: 0;
justify-content: center;
@media screen and (orientation: landscape) {
max-width: 75vw;
margin: 0 auto;
}
}
.splash {
display: flex;
flex-direction: column;
justify-content: start;
min-height: 100svh;
justify-content: center;
max-width: 100%;
padding: var(--spacing-outer);
cursor: url('/pointer.svg'), auto;
@media screen and (orientation: landscape) {
padding: 0 calc(var(--spacing-outer) * 2);
& h1 {
margin: -.5em 0 0 0;
}
}
}
.dev-image {
position:relative;
left: 0;
text-align: center;
display: block;
z-index: -2;
margin: -2em 0 -.5em 0;
@media screen and (orientation: landscape) {
margin: -1.5em 0 -.5em 0;
}
}
.design {
overflow: hidden;
}
.design-illu {
position:relative;
left: 0;
width: 60%;
margin: 0 auto;
text-align: center;
display: block;
margin: 0 auto 1em auto;
z-index: -2;
@media screen and (orientation: landscape) {
margin: 0 auto .5em auto;
width: 45%;
}
}
.hireme {
@media screen and (orientation: landscape) {
max-width: 95vw;
}
}
.hireme h2 {
font-size: 12.5vw;
margin-bottom: 1.5em;
@media screen and (orientation: landscape) {
font-size: 8vw;
margin-bottom: 0.5em;
margin-top: 0;
padding: 0 .25em;
}
}
.hireme-date {
font-size: .66em;
text-align: center;
letter-spacing: -0.02em;
}
h2 {
font-size: 12vw;
line-height: .9;
letter-spacing: -0.033em;
margin: 0 0 0.5em 0;
-webkit-touch-callout: none; /* Safari */
-webkit-user-select: none; /* Chrome */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
cursor: url('/pointer.svg'), auto;
@media screen and (orientation: landscape) {
font-size: 6vw;
margin: 0;
}
& > em {
font-style: normal;
font-weight: 400;
color: var(--color-highlight);
}
}
h1 {
line-height: .85;
font-size: 14.75vw;
letter-spacing: -0.05em;
color: var(--color-highlight);
-webkit-touch-callout: none; /* Safari */
-webkit-user-select: none; /* Chrome */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
@media screen and (orientation: landscape) {
font-size: 10vw;
padding: 0 1em;
}
& > em {
font-style: normal;
font-weight: 400;
color: var(--color-text);
}
}
.canvasized h1, .canvasized h2 {
opacity: 0;
visibility: hidden;
}
p {
font-size: .66em;
margin: 0 0 1em 0;
@media screen and (orientation: landscape) {
margin: 1em 0 0.25em 0;
font-size: 0.6em;
letter-spacing: -0.02em;
}
}
a {
-webkit-touch-callout: none; /* Safari */
-webkit-user-select: none; /* Chrome */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
.cta {
margin-top: .5em;
padding-top: 0em;
@media screen and (orientation: landscape) {
// background-color: var(--color-bg);
// border-top: 1px solid var(--color-text);
padding-top: 1em;
margin-top: 0em;
}
}
</style>
<HomeCanvas textsToCanvas={canvasElems} imgsToCanvas={imgElems} />
<div
class="canvasResizeToThis fixed -top-[10vh] -left-[10vw] w-[120vw] h-[120vh] pointer-events-none -z-10"
></div>

View file

@ -2,6 +2,11 @@ export const prerender = true
export function load() {
return {
title: 'Simon Flöter, creative developer and designer',
description: 'Simon Flöter is a creative developer and designer you can hire as a freelancer'
description: 'Simon Flöter is a creative developer and designer you can hire as a freelancer',
theme: {
bg: 'rgb(0, 0, 90)',
text: 'rgb(255, 240, 240)',
highlight: 'rgb(250, 125, 0)',
}
}
}

View file

@ -78,9 +78,9 @@
</script>
<div class="pagewrapper">
<div class="intro">
<h1 class="toCanvas">Let's be strange,<br>not strangers.</h1>
<div class="alternatives">
<div class="intro w-full min-h-screen flex flex-col justify-center pb-16 md:pb-0">
<h1 class="toCanvas contact-h1">Let's be strange,<br>not strangers.</h1>
<div class="alternatives mx-auto my-0 mb-4 w-full text-center md:mb-8 md:pb-4 [&_p]:opacity-0 [&_.button]:opacity-0 [&_p]:text-base [&_p]:my-2 md:[&_p]:text-[1.33em] [&_ul]:mx-[var(--spacing-outer)] [&_ul]:list-none [&_ul]:flex [&_ul]:justify-center [&_ul]:gap-1 [&_ul_li]:block [&_ul_li]:m-0 [&_.button]:my-1 [&_.button]:block [&_.button]:text-[0.9em] md:[&_.button]:text-[1.2em]">
<p>Choose your flavour of contact:</p>
<ul>
<li><span class="button button--primary" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">Contact form</span></li>
@ -89,30 +89,27 @@
</ul>
</div>
</div>
<div class="formwrapper">
<span class="button button-back" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">← Back</span>
<form name="contact" method="POST" use:enhance>
<div class="inputs-flex-row">
<label for="name">
<!-- <p>How would you like to be addressed?</p> -->
<input type="text" name="name" id="name" placeholder="Your name" value={form?.fields?.name ?? ''}>
<div class="formwrapper fixed inset-0 w-full h-full p-[var(--spacing-outer)] pb-16 pl-[var(--spacing-outer)] pr-[var(--spacing-outer)] bg-[var(--color-bg)] z-0 opacity-0 invisible overflow-y-auto [--form-maxwidth:1000px]">
<span class="button button-back m-0" on:click={contactFormClickHandler} on:keydown={contactFormClickHandler} role="button" tabindex="0">← Back</span>
<form name="contact" method="POST" use:enhance class="box-border max-w-[var(--form-maxwidth)] mx-auto overflow-hidden py-4">
<div class="inputs-flex-row md:flex md:gap-4 md:justify-center md:max-w-[var(--form-maxwidth)] md:mx-auto [&_label]:md:basis-1/2">
<label for="name" class="contact-label">
<input type="text" name="name" id="name" placeholder="Your name" value={form?.fields?.name ?? ''} class="contact-input">
</label>
<label for="email">
<!-- <p>For receiving a reply, add your Email address:</p> -->
<input type="email" name="email" id="email" placeholder="Your Email?*" required value={form?.fields?.email ?? ''}>
<label for="email" class="contact-label">
<input type="email" name="email" id="email" placeholder="Your Email?*" required value={form?.fields?.email ?? ''} class="contact-input">
</label>
</div>
<label for="contact">
<!-- <p>Please describe your plight in a few words</p> -->
<textarea rows="8" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required>{form?.fields?.contact ?? ''}</textarea>
<label for="contact" class="contact-label">
<textarea rows="8" name="contact" id="contact" placeholder="Your business propositions, praise, complaints and/or threats" required class="contact-input">{form?.fields?.contact ?? ''}</textarea>
</label>
<div class="disclaimer">
<div class="disclaimer max-w-[var(--form-maxwidth)] my-4 mx-auto text-base">
<p>Disclaimer: I will only use the data you submit here (name, email, message) to respond. I will not pass it on to any third party. If I don't hear from you I will delete the data and keep no records of it.</p>
</div>
{#if form?.error}
<p class="form-error" role="alert" aria-live="polite">{form.error}</p>
<p class="form-error max-w-[var(--form-maxwidth)] mx-auto mb-4 p-3 border border-[var(--color-text)] rounded text-[0.95em] bg-white/10" role="alert" aria-live="polite">{form.error}</p>
{/if}
<div class="send">
<div class="send max-w-[var(--form-maxwidth)] mx-auto">
<button class="button button--xl button--primary" type="submit">Send it!</button>
</div>
</form>
@ -120,118 +117,30 @@
</div>
<ContactCanvas textsToCanvas={canvasTexts} />
<style lang="scss">
h1 {
<style>
.contact-h1 {
visibility: hidden;
font-size: 12vw;
line-height: .9;
letter-spacing: -0.04em;
margin: 1em var(--spacing-nav) 1em var(--spacing-nav);
@media screen and (min-width: 768px) {
}
@media screen and (min-width: 768px) {
.contact-h1 {
margin: 1em var(--spacing-outer) .75em var(--spacing-outer);
font-size: 4.5em;
}
}
.intro {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding-bottom: 4em;
@media screen and (min-width: 768px) {
padding-bottom: 0;
}
}
.formwrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: var(--spacing-outer) var(--spacing-outer) 4em var(--spacing-outer);
background-color: var(--color-bg);
z-index: 0;
opacity: 0;
visibility: hidden;
overflow-y: scroll;
--form-maxwidth: 1000px;
}
.button-back {
margin: 0;
}
form {
box-sizing: border-box;
max-width: inherit;
margin: 0 auto;
overflow: hidden;
padding: 1em 0;
}
.inputs-flex-row {
@media screen and (min-width: 768px) {
display: flex;
gap: 1em;
justify-content: center;
max-width: var(--form-maxwidth);
margin: auto;
& label {
flex-basis: 50%;
}
}
}
.alternatives {
margin: 0em auto 1em auto;
width: 100%;
text-align: center;
@media screen and (min-width: 768px) {
margin: 0 auto 2em auto;
padding-bottom: 1em;
}
& p, & .button {
opacity: 0;
}
& p {
font-size: 1em;
margin: .5em 0;
@media screen and (min-width: 768px) {
font-size: 1.33em;
}
}
& ul {
margin: 0 var(--spacing-outer);
list-style-type: none;
display: flex;
justify-content: center;
gap: .25em;
}
& ul li {
display: block;
margin: 0;
}
& .button {
margin: 0.25em 0;
display: block;
font-size: .9em;
@media screen and (min-width: 768px) {
font-size: 1.2em;
}
}
}
label {
.contact-label {
font-size: 1em;
@media screen and (min-width: 768px) {
font-size: 1.25em;
}
}
input[type='text'], input[type='email'], textarea {
@media screen and (min-width: 768px) {
.contact-label { font-size: 1.25em; }
}
.contact-input {
width: 100%;
border: 0 solid var(--color-text);
background-color: var(--color-text);//Overruled by placeholder-shown if no value
background-color: var(--color-text);
color: var(--color-bg);
border-radius: 4px;
font-family: 'Stratos', sans-serif;
@ -242,46 +151,20 @@
margin: 0 auto 1em auto;
display: block;
max-width: var(--form-maxwidth);
&:focus {
outline: none;
color: var(--color-bg);
background-color: var(--color-text);
}
&:placeholder-shown {
background-color: rgba(255, 255, 225, 0.2);
}
&:focus:placeholder-shown {
background-color: var(--color-text);
}
&::placeholder {
color: var(--color-text);
opacity: .8;
}
}
// label p {
// max-width: var(--form-maxwidth);
// margin: 0 auto 1em auto;
// display: block;
// font-size: .75em;
// margin-bottom: .5em;
// }
.disclaimer p{
max-width: var(--form-maxwidth);
margin: 1em auto;
font-size: 1rem;
.contact-input:focus {
outline: none;
color: var(--color-bg);
background-color: var(--color-text);
}
.form-error {
max-width: var(--form-maxwidth);
margin: 0 auto 1em auto;
padding: .75em;
border: 1px solid var(--color-text);
border-radius: 4px;
font-size: .95em;
background: rgba(255, 255, 255, 0.08);
.contact-input:placeholder-shown {
background-color: rgba(255, 255, 225, 0.2);
}
.send {
max-width: var(--form-maxwidth);
margin: auto;
.contact-input:focus:placeholder-shown {
background-color: var(--color-text);
}
.contact-input::placeholder {
color: var(--color-text);
opacity: .8;
}
</style>

View file

@ -9,6 +9,10 @@
</article>
<style>
:global(body) {
background-color: var(--color-text);
color: var(--color-highlight);
}
article {
max-width: 1200px;
padding: 0

View file

@ -4,6 +4,7 @@
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { SplitText } from 'gsap/SplitText';
import Faq from '$lib/components/Faq.svelte';
import { page } from '$app/state';
import ServiceCanvas from './ServiceCanvas.svelte';
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';
@ -69,7 +70,7 @@
})
</script>
<ServiceCanvas />
<ServiceCanvas theme={page.data.theme} />
<article>
<h1>Services I can provide<br><em>↓ Scroll down</em></h1>
<div class="services">
@ -136,7 +137,6 @@
</Faq>
</div>
</div>
</section>
<section>
<h2>UX/Visual Design</h2>
@ -178,14 +178,21 @@
</div>
</article>
<style lang="scss">
<style>
article {
margin: auto;
max-width: 1200px;
padding: calc(50vh - var(--spacing-outer) - 10vw) var(--spacing-outer) var(--spacing-outer) var(--spacing-outer);
@media screen and (min-width: 768px) {
padding: calc(50vh - var(--spacing-outer) - 6vw) var(--spacing-outer) var(--spacing-outer) var(--spacing-outer);
}
@media screen and (min-width: 768px) {
article {
padding: calc(50vh - var(--spacing-outer) - 6vw)
var(--spacing-outer)
var(--spacing-outer)
calc( 2 * var(--spacing-outer) )
;
/* padding: 0 var(--spacing-outer) 100px var(--spacing-outer); */
width: calc(100% - (2 * var(--spacing-outer)));
}
}
h1 {
@ -193,81 +200,59 @@
z-index: 3;
font-size: 10vw;
line-height: 1;
& em {
position: absolute;
top: 120%;
font-size: .4em;
letter-spacing: -0.02em;
font-style: normal;
font-weight: 400;
color: var(--color-text);
}
@media screen and (min-width: 768px) {
font-size: 6vw;
letter-spacing: -0.025em;
}
}
h1 em {
position: absolute;
top: 120%;
font-size: .4em;
letter-spacing: -0.02em;
font-style: normal;
font-weight: 400;
color: var(--color-text);
}
@media screen and (min-width: 768px) {
h1 { font-size: 6vw; letter-spacing: -0.025em; }
}
.services {
opacity: 0; //changed in ServiceCanvas
opacity: 0;
visibility: hidden;
padding-bottom: calc( 2 * var(--spacing-outer));
padding-top: calc(0.5 * var(--spacing-outer));
padding-bottom: calc(2 * var(--spacing-outer));
}
.service-content {
@media screen and (min-width: 768px) {
display: flex;
gap: 1.5em;
}
}
.service-content > :first-child {
flex: 0 0 40%;
}
.service-content > *,
.service-content > * > :first-child {
@media screen and (min-width: 768px) {
@media screen and (min-width: 768px) {
.service-content { display: flex; gap: 1.5em; }
.service-content > :first-child { flex: 0 0 40%; }
.service-content > *, .service-content > * > :first-child {
margin-top: 0;
padding-top: 0;
}
.faqs { margin-top: 0; }
}
.faqs {
margin-top: 2em;
width: 100%;
@media screen and (min-width: 768px) {
margin-top: 0;
}
}
h2 {
border-bottom: 1px solid;
padding-bottom: .5em;
font-size: 1.5em;
@media screen and (min-width: 768px) {
font-size: 1.5em;
}
margin: 1em 0 .5em 0;
}
h3 {
font-size: 1.25em;
@media screen and (min-width: 768px) {
font-size: 1.5em;
}
margin: 1em 0 .5em 0;
}
em {
font-weight: 800;
@media screen and (min-width: 768px) {
h3 { font-size: 1.5em; }
}
em { font-weight: 800; }
ul li {
margin-bottom: .75em;
line-height: 1.25;
}
li, p {
font-size: 1em;
margin: 1em 0 .5em 0;
}
p + .button, p + .button + .button {
margin-top: 0;
}
p + .button, p + .button + .button { margin-top: 0.33em; }
</style>

View file

@ -1,6 +1,11 @@
export function load() {
return {
title: 'Services I provide',
description: 'A list of profesional webdevelopment and design services I provide'
description: 'A list of profesional webdevelopment and design services I provide',
theme: {
bg: '#00005A',
text: '#FFF0F0',
highlight: '#FA7D00',
}
};
}

View file

@ -6,6 +6,8 @@
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { onMount } from 'svelte';
let { theme } = $props();
let canvas: HTMLCanvasElement;
let introDone = false;
@ -17,11 +19,25 @@
(async () => {
let highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
if (theme) {
highLightColor = theme.highlight;
} else {
highLightColor = window.getComputedStyle(document.body).getPropertyValue('--color-highlight');
}
let is_landscape = window.matchMedia('(orientation:landscape)').matches;
let isDestroyed = false;
gsap.registerPlugin(ScrollTrigger);
// Ensure Stratos is loaded before canvas text (fixes Firefox black blocks on long text)
try {
await document.fonts.load('bold italic 1em Stratos');
} catch {
await document.fonts.ready;
}
let app = new PIXI.Application();
await app.init({
canvas: canvas,
@ -45,15 +61,17 @@
let fontSize = window.innerHeight / 3;
const isFirefox = typeof navigator !== 'undefined' && /Firefox/i.test(navigator.userAgent);
function createText(string: string): PIXI.Text {
let text = getTextFromPool();
text.text = string;
text.style = {
fontFamily: 'Stratos',
fontSize: fontSize,
fontWeight: '800',
fontWeight: 'bold',
fontStyle: 'italic',
lineHeight: 0,
lineHeight: fontSize * 0.85,
letterSpacing: -10,
fill: highLightColor,
padding: 0
@ -63,6 +81,37 @@
return text
}
/** Firefox renders long canvas text as a black block; use a container of shorter segments. */
function createLongTextAsContainer(): PIXI.Container & { width: number; height: number } {
const parts = ['CONSULTATION ', 'DESIGN ', 'WEB ', 'DEVELOPMENT '];
const container = new PIXI.Container() as PIXI.Container & { width: number; height: number };
let x = 0;
for (const part of parts) {
const text = getTextFromPool();
text.text = part;
text.style = {
fontFamily: 'Stratos',
fontSize: fontSize,
fontWeight: 'bold',
fontStyle: 'italic',
lineHeight: fontSize * 0.85,
letterSpacing: -10,
fill: highLightColor,
padding: 0
};
text.anchor.set(0);
text.x = x;
text.y = 0;
container.addChild(text);
x += text.width;
}
const bounds = container.getBounds();
container.width = bounds.width;
container.height = bounds.height;
textgroup.addChild(container);
return container;
}
// Text object pool management
function getTextFromPool(): PIXI.Text {
if (textPool.length > 0) {
@ -75,11 +124,12 @@
textPool.push(text);
}
let allTexts = [
const longLine = 'CONSULTATION DESIGN WEB DEVELOPMENT ';
let allTexts: (PIXI.Text | (PIXI.Container & { width: number; height: number }))[] = [
createText('SERVICES SERVICES '), createText('SERVICES SERVICES '),
createText('CONSULTATION DESIGN WEB DEVELOPMENT '), createText('CONSULTATION DESIGN WEB DEVELOPMENT '),
isFirefox ? createLongTextAsContainer() : createText(longLine), isFirefox ? createLongTextAsContainer() : createText(longLine),
createText('SERVICES SERVICES '), createText('SERVICES SERVICES '),
createText('CONSULTATION DESIGN WEB DEVELOPMENT '), createText('CONSULTATION DESIGN WEB DEVELOPMENT ')
isFirefox ? createLongTextAsContainer() : createText(longLine), isFirefox ? createLongTextAsContainer() : createText(longLine)
]
let textRows = [
@ -224,8 +274,15 @@
}
});
// Return objects to pools
allTexts.forEach(text => returnTextToPool(text));
// Return objects to pools (containers: return child texts to pool, then destroy container)
allTexts.forEach((el) => {
if (el instanceof PIXI.Container && 'width' in el) {
el.children.forEach((child) => returnTextToPool(child as PIXI.Text));
el.destroy();
} else {
returnTextToPool(el as PIXI.Text);
}
});
// Now safe to destroy PixiJS app
app.destroy(true, { children: true, texture: true });
}

View file

@ -15,7 +15,12 @@ export async function load() {
return {
posts,
title: 'Work References',
description: 'A few of the projects I have worked on'
description: 'A few of the projects I have worked on',
theme: {
bg: '#FA7D00',
text: '#FFFFFF',
highlight: '#00005A',
}
}
} catch (error) {
console.error('Error loading work posts:', error)

View file

@ -9,6 +9,18 @@
return a.meta.order - b.meta.order;
}));
function getColorValue(cssValue: string): string {
// If it's a CSS variable reference, resolve it
if (cssValue.trim().startsWith('var(')) {
// Extract the variable name and resolve it
const varName = cssValue.match(/var\(([^)]+)\)/)?.[1];
if (varName) {
return getComputedStyle(document.documentElement).getPropertyValue(varName.trim()).trim() || cssValue;
}
}
return cssValue.trim();
}
onMount(() => {
document.querySelectorAll('.workclone')?.forEach(clone => {
clone.remove();
@ -17,8 +29,20 @@
let isZoomed = false;
const currentBgColor = getComputedStyle(document.documentElement).getPropertyValue('--color-bg');
const currentHighlightColor = getComputedStyle(document.documentElement).getPropertyValue('--color-highlight');
// const currentBgColor = getComputedStyle(document.documentElement).getPropertyValue('--color-bg');
// const currentHighlightColor = getComputedStyle(document.documentElement).getPropertyValue('--color-highlight');
// Read from data.theme if available, otherwise from CSS
let currentBgColor: string;
let currentHighlightColor: string;
if (data.theme) {
currentBgColor = getColorValue(data.theme.bg);
currentHighlightColor = getColorValue(data.theme.highlight);
} else {
currentBgColor = getComputedStyle(document.documentElement).getPropertyValue('--color-bg').trim();
currentHighlightColor = getComputedStyle(document.documentElement).getPropertyValue('--color-highlight').trim();
}
let works: Array<HTMLElement> = Array.from(document.querySelectorAll('.work'));
for ( let i = 0; i < 40; i++ ){
@ -241,4 +265,98 @@
</div>
</div>
<style src="./work.scss" lang="scss"></style>
<style>
:global(html) {
overflow: hidden;
overscroll-behavior: none;
}
.works-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
perspective: 700px;
transform-origin: 0 0;
will-change: transform;
}
.headline {
position: fixed;
pointer-events: none;
display: flex;
width: 100%;
height: 100%;
text-align: center;
flex-direction: column;
justify-content: center;
align-content: center;
letter-spacing: -0.05em;
z-index: 10;
transform: translateZ(500px);
margin-left: 40.5vw;
margin-top: 37.25vh;
font-size: 14vw;
}
@media screen and (min-width: 768px) {
.headline {
margin-left: 41.66vw;
margin-top: 40.5vh;
font-size: 7vw;
transform: translateZ(600px);
}
}
.works {
margin: 0 auto;
position: absolute;
top: 0;
left: 0;
width: 600vw;
height: 600vw;
transform: scale(.333);
transform-origin: 0 0;
display: grid;
grid-template-columns: repeat(6, 100vw);
grid-auto-rows: 100vh;
gap: 6px;
will-change: transform;
}
.work {
background-color: var(--color-highlight);
text-decoration: none;
text-align: center;
width: 100vw;
height: 100vh;
opacity: 0;
visibility: hidden;
transform: translateZ(700px);
will-change: transform;
}
:global(.work-logo) {
width: 100%;
height: 100%;
color: var(--color-bg);
padding: 3em;
margin: auto;
display: flex;
flex-direction: column;
justify-content: center;
will-change: transform;
}
@media screen and (min-width: 768px) {
:global(.work-logo) { width: 60%; }
}
:global(.work-logo svg) {
object-fit: fill;
width: 100%;
height: 100%;
}
.work-info {
display: none;
}
.work-info .tags {
display: flex;
list-style: none;
gap: .25em;
}
</style>

View file

@ -14,7 +14,10 @@ export async function load( { params }: { params: { slug: string }} ){
description = [],
images = [],
agency = '',
agencyName = ''
agencyName = '',
colorBg = 'var(--color-bg-variant-2)',
colorText = 'var(--color-text-variant-2)',
colorHighlight = 'var(--color-highlight-variant-2)',
} = post.metadata
// Don't pass the component - it's not serializable
@ -35,6 +38,18 @@ export async function load( { params }: { params: { slug: string }} ){
images,
agency,
agencyName,
colorBg,
colorText,
colorHighlight,
theme: [
colorBg,
colorText,
colorHighlight,
].every(Boolean) ? {
bg: colorBg,
text: colorText,
highlight: colorHighlight,
} : undefined,
}
} catch (error) {
console.error(error)

View file

@ -1,176 +1,167 @@
<script lang="ts">
import { onMount } from 'svelte';
import { gsap } from 'gsap';
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';
import { CldImage } from 'svelte-cloudinary';
import Carousel from '$lib/components/Carousel.svelte';
let { data } = $props();
gsap.registerPlugin(ScrollToPlugin);
onMount(() => {
let is_landscape = window.matchMedia('(orientation:landscape)').matches
window.addEventListener('resize', () => {
is_landscape = window.matchMedia('(orientation:landscape)').matches
})
if (document.querySelector('.workclone')) {
document.querySelector('.workclone')?.remove();
}
setTimeout( () => {
document.querySelectorAll('.gallery img')?.forEach( image => {
const img = image as HTMLImageElement;
if (!img.complete) {
img.classList.add('imageIsLoading');
img.onload = () => {
gsap.fromTo(img, {
autoAlpha: 0,
scale: 1.2,
}, {
autoAlpha: 1,
scale: 1,
duration: 1,
ease: 'power4.out',
})
}
}
})
}, 10)
gsap.to('.logo-wrapper', {
opacity: 0,
scale: 0.85,
zIndex: -1,
duration: .5,
duration: 0.5,
ease: 'power2.out',
})
});
gsap.to('.work', {
y: '-100vh',
duration: 1,
ease: 'power4.out',
})
gsap.to('.gallery-wrapper', {
});
gsap.to('.carousel', {
y: '100vh',
duration: 1,
ease: 'power4.out',
})
const gallery = document.querySelector('.gallery') as HTMLElement;
let isAnimating = false;
const galleryClickHandler = (e: MouseEvent) => {
if (isAnimating) return;
const allImgs = gallery.querySelectorAll('figure');
const scrollX = gallery.scrollLeft;
const imgWidth = allImgs[0].offsetWidth || 0;
const imgCount = allImgs.length;
const galleryOuterWidth = (document.querySelector('.gallery-wrapper') as HTMLElement)?.offsetWidth || 0;
const galleryOverlap = imgWidth - galleryOuterWidth % imgWidth;
const maxScrollX = imgWidth * (imgCount - 1);
const imgIndex = scrollX < maxScrollX ? Math.round(scrollX / imgWidth) : imgCount - 1;
let newScrollPos;
if (imgIndex < imgCount - 2) {
newScrollPos = scrollX + imgWidth;
}
else if ( imgIndex === imgCount - 2){
newScrollPos = is_landscape ? scrollX + galleryOverlap * .8 : scrollX + galleryOverlap * .85 ;
}
else {
newScrollPos = 0;
}
gsap.to(gallery, {
scrollTo: { x: newScrollPos , autoKill: false },
duration: .75,
ease: 'power4.out',
onStart: () => { isAnimating = true },
onComplete: () => { isAnimating = false },
})
}
const hoverElement = document.createElement('div');
hoverElement.classList.add('hoverElement');
document.querySelector('.gallery-wrapper')?.appendChild(hoverElement);
const galleryHoverHandler = (e: MouseEvent) => {
gsap.to(hoverElement, { x: e.clientX, y: e.clientY + 25, rotateX: 10, rotateZ: -5, autoAlpha: 1, duration: 0.3, overwrite: true })
}
const galleryLeaveHandler = (e: MouseEvent) => {
gsap.to(hoverElement, { autoAlpha: 0, duration: 0.3, overwrite: true })
}
if (gallery) {
gallery.addEventListener('click', galleryClickHandler )
gallery.addEventListener('mousemove', galleryHoverHandler )
gallery.addEventListener('mouseleave', galleryLeaveHandler )
}
});
return () => {
gsap.killTweensOf('.logo-wrapper, .work, .gallery-wrapper')
gallery.removeEventListener('click', galleryClickHandler )
gallery.removeEventListener('mousemove', galleryHoverHandler )
gallery.removeEventListener('mouseleave', galleryLeaveHandler )
}
})
gsap.killTweensOf('.logo-wrapper, .work, .carousel');
};
});
</script>
<div class="logo-wrapper">
<div class="svg-logo">{@html data.svg}</div>
</div>
<div class="subnav">
<a href="/work" class="subnav-item button">← Back</a>
</div>
<div class="gallery-wrapper">
<div class="gallery">
{#each data.images as image (image)}
<figure>
{#if image.includes('/video/')}
<video src={image} width="1400" height="840" autoplay muted loop></video>
{:else}
<CldImage
src={image}
alt={data.title}
sizes="(min-width: 768px) 67vw, 90vw"
width={1400}
height={840}
placeholder="blur"
loading="eager"
objectFit="cover"
/>
{/if}
</figure>
{/each}
<div
class="
logo-wrapper
fixed z-1
w-screen h-screen text-center
"
>
<div
class="
svg-logo
flex flex-col justify-center
w-full h-full m-auto p-[3em]
text-(--color-highlight)
[&_svg]:object-fill [&_svg]:w-full [&_svg]:h-full
md:w-3/5
"
>
{@html data.svg}
</div>
</div>
<article class="work">
<div class="description">
<div
class="
subnav
fixed z-22
md:top-0 md:left-0 md:inline-block
"
>
<a
href="/work"
class="
subnav-item button
inline-flex items-center gap-2
m-(--spacing-nav)
"
>
<svg
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
class="
w-6 h-6
inline-block
"
>
<path d="M7.82843 10.9999H20V12.9999H7.82843L13.1924 18.3638L11.7782 19.778L4 11.9999L11.7782 4.22168L13.1924 5.63589L7.82843 10.9999Z"></path>
</svg>
Back
</a>
</div>
<div
class="
carousel
relative md:landscape:absolute z-1
-top-[100vh] md:top-[calc(-100vh-10vw)]
"
>
<Carousel images={data.images} title={data.title} />
</div>
<article
class="
work
relative overflow-hidden
top-[100vh] md:landscape:top-[calc(200vh-11.25rem-2.5vw)]
px-(--spacing-outer) pb-[100px] pt-0
md:landscape:ml-[150px] md:landscape:mr-(--spacing-outer) md:landscape:my-0
md:landscape:px-(--spacing-outer) md:landscape:pb-[100px]
md:landscape:w-[calc(100%-150px-var(--spacing-outer))]
[&_h1]:mt-[0.125em] [&_h1]:mb-[0.5em] [&_h1]:text-[2.5rem] [&_h1]:text-(--color-highlight)
md:[&_h1]:text-[6rem]
"
>
<div
class="
description
mt-[1.5em] leading-[1.3] tracking-normal uppercase
text-xl md:text-[1.75rem]
"
>
{data.description}
</div>
<h1>{data.title}</h1>
<div class="work-content">
<div class="infobox">
<div
class="
work-content
relative z-2
md:flex md:gap-[2em]
"
>
<div
class="
infobox
text-xl pb-[2em]
md:flex-[0_0_25%]
"
>
<div class="tasks">
<div class="tasks-title">What I did:</div>
<ul>
<div class="tasks-title font-bold italic mb-[0.125em]">What I did:</div>
<ul class="m-0 flex flex-wrap gap-[0.125em]">
{#each data.tags as tag (tag)}
<li>{tag}</li>
<li
class="
text-base list-none border-none uppercase rounded-[3px]
m-0 py-[0.25em] px-[0.33em]
bg-(--color-highlight) text-(--color-bg)
"
>
{tag}
</li>
{/each}
</ul>
</div>
<div class="reference">
<div class="reference-title">Reference:</div>
<div class="reference-title font-bold italic mt-4 mb-0">Reference:</div>
<div><a href={data.reference}>{data.referenceName}</a></div>
</div>
{#if data.agency}
<div class="agency">
<div class="agency-title">Agency:</div>
<div class="agency-title font-bold italic mt-4 mb-0">Agency:</div>
<div><a href={data.agency}>{data.agencyName}</a></div>
</div>
{/if}
</div>
<div class="work-content-text">
<div
class="
work-content-text
[&_p]:mt-5
[&_h2]:mt-10 [&_h2]:text-[2rem] md:[&_h2]:text-[2.5rem]
*:first:mt-0 [&>h1:first-child]:mt-0
"
>
{#await import(`../md/${data.slug}.md`) then { default: Content }}
<Content />
{:catch error}
@ -179,227 +170,3 @@
</div>
</div>
</article>
<style lang="scss">
.subnav {
position: fixed;
z-index: 22;
@media screen and (min-width: 768px) {
top: 0;
left: 0;
display: inline-block;
}
}
.subnav-item {
margin: var(--spacing-nav);
}
.logo-wrapper {
position: fixed;
text-align: center;
width: 100vw;
height: 100vh;
z-index: 1;
}
.svg-logo {
color: var(--color-highlight);
padding: 3em;
width: 100%;
height: 100%;
margin: auto;
display: flex;
flex-direction: column;
justify-content: center;
@media screen and (min-width: 768px) {
width: 60%;
}
}
:global(.svg-logo svg) {
object-fit: fill;
width: 100%;
height: 100%;
}
.work {
position: relative;
top: 100vh;
overflow: hidden;
padding: 0 var(--spacing-outer) 100px var(--spacing-outer);
@media screen and (min-width: 768px) {
margin: 0 var(--spacing-outer) 0 150px;
padding: 0 var(--spacing-outer) 100px var(--spacing-outer);
width: calc(100% - 150px - var(--spacing-outer));
}
}
h1 {
margin: .125em 0 0.5em 0;
font-size: 2.5rem;
@media screen and (min-width: 768px) {
font-size: 6rem;
}
}
.description {
margin-top: 1.5em;
line-height: 1.3;
letter-spacing: 0;
text-transform: uppercase;
font-size: 1.25rem;
@media screen and (min-width: 768px) {
font-size: 1.75rem;
}
}
.infobox {
font-size: 1.25rem;
padding: 0 0 2em 0;
@media screen and (min-width: 768px) {
flex: 0 0 25%;
}
}
.reference-title, .agency-title, .tasks-title {
font-weight: bold;
font-style: italic;
margin: 0 0 .125em 0;
}
.reference-title, .agency-title {
margin-top: 1em;
margin-bottom: 0;
}
.tasks ul {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: .125em;
}
.tasks li {
font-size: 1rem;
list-style: none;
border: none;
text-transform: uppercase;
border-radius: 3px;
margin: 0;
padding: 0.25em 0.33em;
background-color: var(--color-highlight);
color: var(--color-bg);
}
.work-content {
position: relative;
z-index: 2;
@media screen and (min-width: 768px) {
display: flex;
gap: 2em;
}
}
.work-content-text :global(h2) {
font-size: 2rem;
@media screen and (min-width: 768px) {
font-size: 2.5rem;
}
}
.work-content-text > :global(:first-child) {
margin-top: 0;
}
.work-content-text :global(p) {
font-size: 1rem;
@media screen and (min-width: 768px) {
font-size: 1em;
}
}
.work-content-text > :global(h1):first-child {
margin-top: 0;
}
.gallery-wrapper {
position: relative;
top: -100vh;
width: 100vw;
overflow: hidden;
padding: 0 0 1em 0;
z-index: 1;
perspective: 250px;
perspective-origin: center bottom;
@media screen and (min-width: 768px) {
margin-bottom: -80px;
top: calc(-100vh - 80px);
padding: 2em 0 4em 0;
margin-bottom: -120px;
perspective: 500px;
}
}
.gallery {
transform-origin: 50% 50%;
position: relative;
display: flex;
scroll-behavior: smooth;
gap: 0;
overflow-x: scroll;
scroll-snap-type: x mandatory;
width: 115vw;
left: -4vw;
transform: rotate3d(-2, 0, 1, -10deg);
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
@media screen and (min-width: 768px) {
width: 110vw;
left: -3vw;
}
}
figure {
display: flex;
flex-direction: column;
overflow: hidden;
justify-content: center;
scroll-snap-align: start;
padding: 0 1px;
flex: 1 0 85%;
height: 100%;
aspect-ratio: 140/84;
width: auto;
margin: 0;
@media screen and (min-width: 768px) {
flex: 1 0 66.666%;
}
& :global(img), video {
opacity: 0.85;
width: 100%;
height: 100%;
object-position: left top;
}
video {
object-fit: cover;
}
}
:global(.imageIsLoading) {
visibility: hidden;
opacity: 0;
}
:global(.hoverElement) {
position: fixed;
top: 0;
left: 0;
width: 50px;
height: 50px;
background-color: var(--color-bg);
z-index: 100;
opacity: 0;
visibility: hidden;
box-shadow: -4px 4px 8px rgba(0,0,0,.2);
pointer-events: none;
@media screen and (pointer: coarse) {
display: none;
}
&:before {
content: url('/moveright.svg');
font-size: 33px;
line-height: 50px;
text-align: center;
display: block;
height: 50px;
width: 50px;
}
}
</style>

View file

@ -4,10 +4,13 @@ header_bg_image: adidas_hero_ceurd8
images: [ '/work/adidas/adidas_hero_ceurd8', '/work/adidas/adidas-mockup_ozihu3', '/work/adidas/adidas-illus-bg_t2t5ts' ]
order: 12
tags: ['graphic design', 'illustration']
description: Design & Illustration for the international sports and lifestyle clothing brand.
description: Design & Illustration
svg: '<svg id="adidas-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 1000 674.2" style="enable-background:new 0 0 1000 674.2; fill: currentColor" xml:space="preserve"><path d="M654.5 442.2 448.9 84.8 596.6 0l255.7 442.2H654.5"/><path d="m106.8 392.1 147.7-85.4 78.2 135.5H135.5l-28.7-50.1"/><path d="M396.7 670.2h42V500.1h-42v170.1z"/><path d="M923.6 674.2c-47 0-75.3-24.3-76.8-58.5h44.3c0 10.7 6.7 26.4 35.4 26.9 19.1 0 28.1-11.3 28.1-19.7-1.1-13.4-18-14.5-35.9-17.4-18-2.9-33.3-6.1-44.3-11.8-14.1-7.3-23.7-22.9-23.7-40.9 0-30.4 26.4-54.5 70.3-54.5 42.6 0 69.6 22.4 72.4 55.6h-42.8c-.4-9-2.1-23.1-27.3-23.1-17 0-28.3 3.4-29.2 15.3 0 17.4 35.4 16.2 62.9 23.5 26.4 6.7 43.2 23.1 43.2 46.1-.2 42.2-34.4 58.5-76.6 58.5"/><path d="m280 240.4 147.7-85.2 165.7 287H438.8v42h-42V442L280 240.4"/><path class="st0" d="M283.8 674.2c-48.9 0-88.7-39.9-88.7-88.3 0-48.9 39.7-87.5 88.7-87.5 18.5 0 35.4 5 50.1 15.1v-71.3h42v228h-42v-11.3c-14.8 9.6-31.6 15.3-50.1 15.3zm-48.4-88.3c0 26.4 22.5 48.3 49.5 48.3 26.4 0 48.9-22 48.9-48.3 0-26.4-22.5-48.9-48.9-48.9-26.9 0-49.5 22.5-49.5 48.9"/><path class="st0" d="M594.5 442.2H636v228h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3-48.3 0-88.1-39.9-88.1-88.3 0-48.9 39.7-87.5 88.1-87.5 19.1 0 35.9 5 50.6 15.1v-71.3zm-97.8 143.7c0 26.4 22.5 48.3 48.3 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-25.8 0-48.3 22.5-48.3 48.9"/><path class="st0" d="M738.2 674.2c-48.2 0-88.1-39.9-88.1-88.3 0-48.9 39.9-87.5 88.1-87.5 18.5 0 35.9 5 50.1 15.1v-13.6h42v170.3h-42v-11.3c-14.2 9.6-31 15.3-50.1 15.3zM691 585.9c0 26.4 22.5 48.3 48.9 48.3s48.3-22 48.3-48.3c0-26.4-22-48.9-48.3-48.9-26.4 0-48.9 22.5-48.9 48.9"/><path class="st0" d="M40.5 585.9c0 26.4 22.5 48.3 48.9 48.3 26.9 0 49.5-22 49.5-48.3 0-26.4-22.5-48.9-49.5-48.9-26.3 0-48.9 22.5-48.9 48.9zm47.8 88.3c-48.4 0-88.3-40-88.3-88.3 0-48.9 39.9-87.5 88.3-87.5 18.5 0 35.9 5 50.6 15.1v-13.6h41.5v170.3h-41.5v-11.3c-14.1 9.6-31.5 15.3-50.6 15.3"/></svg>'
reference: https://www.linkedin.com/in/stanislasdupart/
referenceName: Stan Dupart
colorBg: '#000033'
colorText: '#FFFFFF'
colorHighlight: '#D21A00'
---
As a freelancer embedded in adidas' ecommerce and digital experiences team, I worked on digital campaigns and assets.

28
src/routes/work/md/aqr.md Normal file
View file

@ -0,0 +1,28 @@
---
title: Association for Qualitative Research
images: [
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770633579/work/aqr/aqr_home_ecla11.png',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770640900/work/aqr/aqr_logo_spacing_qb0a4w.jpg',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770640900/work/aqr/aqr_handdrawn_varur8.jpg',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770641360/work/aqr/aqr_join_vx8nwf.png',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770633577/work/aqr/aqr_directory_vireyu.png',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770633578/work/aqr/aqr_business_vohd6a.png',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770641578/work/aqr/aqr_timeline2_q3eue7.png'
]
order: 12
tags: ['Tech consultancy', 'Frontend development', 'Backend Development']
description: Brand refresh & new website
reference: https://www.linkedin.com/in/judy-taylor-8859822/
referenceName: Judy Taylor
svg: '<svg width="88" height="77" viewBox="-29.333 -22.666 132 115.5" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M43.0239 67.0494C36.5058 67.0494 31.2033 61.7469 31.2033 55.2288C31.2033 48.7107 36.5058 43.4082 43.0239 43.4082C49.542 43.4082 54.8445 48.7107 54.8445 55.2288C54.8445 61.7469 49.542 67.0494 43.0239 67.0494ZM43.0239 46.7032C38.3241 46.7032 34.4998 50.5275 34.4998 55.2273C34.4998 59.9271 38.3241 63.7515 43.0239 63.7515C47.7237 63.7515 51.548 59.9271 51.548 55.2273C51.548 50.5275 47.7237 46.7032 43.0239 46.7032Z" fill="currentColor"></path><path d="M26.7191 66.1578H30.2179L22.358 44.3144H18.5498L10.6899 66.1578H14.1888L16.1889 60.3773H24.7233L26.7206 66.1578H26.7191ZM17.1465 57.6044L20.3725 48.2825H20.5426L23.7628 57.6044H17.1465Z" fill="currentColor"></path><path d="M75.2303 66.1578L70.3941 57.3272C70.62 57.2436 70.8385 57.1527 71.0437 57.0486C72.1641 56.487 73.0028 55.6995 73.5615 54.6862C74.1202 53.673 74.3989 52.4984 74.3989 51.161C74.3989 49.8237 74.1217 48.6315 73.5674 47.6036C73.0131 46.5756 72.1758 45.7706 71.0555 45.1884C69.9352 44.6048 68.5289 44.3144 66.8367 44.3144H59.0501V66.1578H62.3451V57.8918H66.8249C66.8968 57.8918 66.9657 57.8888 67.0361 57.8874L71.4749 66.1578H75.2303ZM62.3466 47.1416H66.4847C67.5728 47.1416 68.4526 47.3029 69.1242 47.627C69.7958 47.9511 70.29 48.4145 70.6068 49.0186C70.9235 49.6228 71.0819 50.3369 71.0819 51.1625C71.0819 51.9881 70.925 52.6802 70.6126 53.2638C70.3003 53.8475 69.8076 54.2918 69.136 54.5968C68.4644 54.9033 67.5948 55.0558 66.5287 55.0558H62.348V47.1416H62.3466Z" fill="currentColor"></path><path d="M44.3862 54.2991L52.9676 73.5645H83.8835V3.29645H3.29645V73.5645H32.8824L34.0687 76.861H0V0H87.18V76.861H50.4454L40.3962 54.2991H44.3862Z" fill="currentColor"></path></svg>'
colorBg: '#4c8618'
colorText: 'rgb(255, 228, 207)'
colorHighlight: 'rgb(57, 0, 17)'
---
Zyas mission is simple: to make better nutrition accessible without sacrificing the taste and enjoyment of everyday food.
In another collaboration with the ever-inspiring creative agency [forpeople](https://forpeople.com), we created a website with scroll-based storytelling animations that playfully brings the complex information behind the science to life.
In a second phase we added more storytelling features and a blog.
<a href="https://zya.co" class="button button--primary" target="_blank">Check it out live</a>

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,14 @@
---
title: etosis
images: ['work/etosis/etosis_og_ogqfae', 'https://res.cloudinary.com/dkvjosg5p/video/upload/v1696948648/work/etosis/etosis_mail_wawrua.mp4', 'work/etosis/scrnli_Oct_10_2023_3_._xkz9er', 'work/etosis/scrnli_Oct_10_2023_2_._lxh0xh' ]
description: Simple housestyle and website for a software company specialised in complexity
description: Housestyle and website
referenceName: Patrick Simpson
reference: https://www.linkedin.com/in/patricksimpson/
tags: ['Housestyle', 'Graphic Design', 'Web Development', 'Animation']
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1136.37 547.52"><path style="fill: currentColor" d="M412.35 274.93c.19 3.41.19 6.84 0 10.25H270.51c1.9 26.58 27.34 51.18 60.29 51.18a67.587 67.587 0 0 0 52.7-25.82l20.12 20.88a97.618 97.618 0 0 1-72.82 34.47c-49.3.89-90.04-38.25-91.12-87.55v-4.56c0-51.18 34.93-92.18 87.63-92.18 48.98 0 85.35 36.83 85.35 93.32m-31.13-16.33c-2.78-27.6-26.49-48.32-54.22-47.38-28.03-.97-52.18 19.56-55.73 47.38h109.95ZM421.84 186.16h34.17v-53.15h31.89v53.15h54.67v28.55H487.9v94.69c0 17.08 8.35 26.96 25.44 26.96a42.01 42.01 0 0 0 20.12-7.59l11.77 26.2c-11 6.7-23.57 10.37-36.45 10.63-25.06 0-53.15-16.33-53.15-55.05v-95.83h-34.17l.38-28.55ZM736.66 273.79c1.11 49.86-38.41 91.18-88.27 92.3s-91.18-38.41-92.3-88.27v-4.02c-1.13-49.86 38.37-91.2 88.23-92.33 49.86-1.13 91.2 38.37 92.33 88.23v4.1m-31.89 0c0-34.55-23.16-62.57-58.39-62.57s-58.39 28.1-58.39 62.57 22.78 62.57 58.39 62.57 58.39-28.1 58.39-62.57M772.35 308.64a67.313 67.313 0 0 0 53.15 28.47c17.84 0 37.97-7.59 37.97-21.64 0-15.19-12.91-22.78-39.79-28.1-37.97-7.59-64.09-22.32-64.09-55.35 0-25.44 25.36-50.42 64.47-50.42a91.14 91.14 0 0 1 64.85 26.58l-21.03 21.72a64.366 64.366 0 0 0-43.96-19.29c-22.78 0-33.41 10.93-33.41 22.32 0 13.29 13.67 20.5 42.45 26.58 33.41 7.21 61.81 19.74 61.81 55.43 0 33.71-35.69 51.18-69.4 51.18a95.993 95.993 0 0 1-75.1-36.83l22.1-20.65ZM944.56 108.41c11.95 0 21.64 9.69 21.64 21.64s-9.69 21.64-21.64 21.64c-11.95 0-21.64-9.69-21.64-21.64 0-11.95 9.69-21.64 21.64-21.64m-15.87 77.75h31.89v175.25h-31.89V186.16ZM1013.89 308.64a67.569 67.569 0 0 0 53.15 28.47c17.77 0 37.97-7.59 37.97-21.64 0-15.19-12.91-22.78-39.79-28.1-37.97-7.59-64.09-22.32-64.09-55.35 0-25.44 25.36-50.42 64.47-50.42a91.14 91.14 0 0 1 64.85 26.58l-20.96 21.72a64.583 64.583 0 0 0-43.96-19.29c-22.4 0-33.41 10.93-33.41 22.32 0 13.29 13.67 20.5 42.45 26.58 33.41 7.21 61.81 19.74 61.81 55.43 0 33.71-35.69 51.18-69.4 51.18a95.993 95.993 0 0 1-75.1-36.83l22.02-20.65ZM0 142.65l90.11 59.43-62.71 40.93-.11.09c-4.22 3.38-7.72 7.13-10.41 11.17-10.73 16.86-6.06 38.93 10.59 50.29l63.27 40.21L0 404.19V142.65Z"/><path style="fill: currentColor" d="m68.58 314.96-32.87-21.68c-4.98-3.11-8.65-8.65-9.9-14.86-1.29-6.43.08-13.12 3.65-17.89 1.9-2.54 3.8-4.44 6.32-6.34l66.8-44.31 44.44 28.95c4.92 3.53 8.65 7.47 11.73 12.4 12.08 18.44 6.8 44.18-11.73 57.46l-44.43 28.95-34.01-22.67ZM0 420.44l102.58-67.94 44.44 28.95c4.92 3.52 8.65 7.46 11.73 12.4 12.09 19.1 6.83 44.86-11.73 57.45L0 547.52V420.44ZM0 127.07V0l147.02 96.21c4.55 3.26 8.51 7.21 11.77 11.75 5.83 9.77 7.8 20.33 5.85 31.39-1.88 10.68-8.12 19.9-17.54 25.96l-44.52 29L0 127.07Z"/></svg>'
colorBg: '#FFFFFF'
colorText: '#000000'
colorHighlight: '#FD9C09'
---
Etosis is a small software consultancy and development company based in Helsinki, Finland that specialises in solving extremely complex software challenges.
@ -23,4 +26,4 @@ So I developed simplistic vector-based animations for each product that make the
The website was built in [Eleventy](https://www.11ty.dev/), a static site generator. The animations were created as SVGs and then animated with Javascript.
<a class="button" href="https://etosis.com">Live Site</a>
<a class="button button--primary" href="https://etosis.com">Live Site</a>

View file

@ -11,6 +11,9 @@ reference: https://www.linkedin.com/in/ozollmanthomas/
referenceName: Oscar Zollman
agency: https://forpeople.com
agencyName: forpeople
colorBg: '#0000FE'
colorText: '#FFF7E9'
colorHighlight: '#FFCA11'
---
Formo is using precision fermentation instead of cows to make dairy products and save the world. Creative agency [forpeople](https://forpeople.com) is in charge of branding and brought me on board to develop a brand new website.
@ -22,4 +25,4 @@ After consulting with the team, we decided that the comfort of the new visual ed
With custom-made, React-based content blocks and the Gutenberg editor, creating new pages and articles in the new formo identity framework is now easily achievable through a WYSIWYG interface.
<a class="button" href="https://formo.bio">Live site</a>
<a class="button button--primary text-[#0000FE]" href="https://formo.bio">Live site</a>

File diff suppressed because one or more lines are too long

View file

@ -2,13 +2,16 @@
title: JustPeace Labs
header_bg_image: jpl_hero_eukxaw
images: [ '/work/jpl/jpl_hero_eukxaw.jpg', '/work/jpl/website_01_zsk9oe.png', '/work/jpl/jpllogo_v7s2pl']
description: Housestyle, website and design for the peacebuilding tech non-profit organisation.
description: Branding & Website
order: 11
tags: ['branding', 'logo design', 'graphic design', 'illustration']
tasks: ['Housestyle', 'Visual Design', 'Product Design', 'Frontend Development']
reference: 'https://www.linkedin.com/in/jennifereasterday/'
referenceName: 'Jennifer Easterday'
svg: '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Layer_1" x="0" y="0" viewBox="0 0 240 63" style="enable-background:new 0 0 240 63" xml:space="preserve"><style>.st0{fill:currentColor}.st1{fill:currentColor}</style><g id="Layer_1-2"><path class="st0" d="M82.4 23.4c.4-2.3.6-4.7.5-7.1V3.1h4.6v13.1c.1 2.5 0 5-.3 7.5-.8 4.1-4.9 6-7.4 6.1l-1.1-2.4c1.9-.6 3.3-2.1 3.7-4zM92 3.1h4.5v12.3c0 5.1 1.6 6.2 4.4 6.2s4.3-1.3 4.3-6.2V3.1h4.5v12.7c0 6.1-3 9.5-8.9 9.5-6.6 0-8.8-3.8-8.8-9.6V3.1zM114 20.3c1.7.9 3.7 1.4 5.7 1.4 2.4 0 3.2-.8 3.2-2.2s-.9-2.2-4.2-3.9c-2.6-1.3-5.4-3.2-5.4-6.8 0-4.3 3.5-5.9 7.7-5.9 2-.1 3.9.4 5.7 1.3l-.8 3.6c-1.4-.9-3.1-1.4-4.7-1.4-2.4 0-3.3.9-3.3 2.2 0 1.6 1.1 2.2 3.8 3.7 3.4 1.8 6 3.4 6 7 0 4-3.1 6.1-7.5 6.1-2.3 0-4.5-.4-6.6-1.3l.4-3.8zM135.3 6.6H129V3.1h17.2v3.5h-6.3V25h-4.6V6.6zM149.4 3.1h1.4c2.4 0 4.4-.2 6.5-.2 5 0 8 2.2 8 7.6 0 5-3.1 8-8.8 8H154V25h-4.6V3.1zm7 11.8c2.2 0 4.3-.9 4.3-4.6 0-2.8-1.5-4.3-4.4-4.2-.8 0-1.6.1-2.4.2v8.4c.8.2 1.6.2 2.5.2zM168.4 3.1h13.4v3.5H173V12h6.8v3.5H173v5.9h9.7V25h-14.3V3.1zM192.8 2.9h4.6l8.2 22.1h-5l-1.5-4.6H191l-1.5 4.6h-4.9l8.2-22.1zm5.3 14.2-2.4-7.5-.6-2.3-.6 2.3-2.4 7.5h6zM216.4 2.9c2 0 4 .3 6 .9l-.7 4c-1.5-.9-3.2-1.3-5-1.3-3.6 0-6.3 2.7-6.3 7.3s2.5 7.9 6.7 7.9c1.8 0 3.5-.5 5.1-1.4v3.8c-1.7.8-3.6 1.2-5.5 1.1-6.8 0-11-4.5-11-11.1 0-7.3 4.7-11.2 10.7-11.2zM225.7 3.1h13.4v3.5h-8.8V12h6.7v3.5h-6.7v5.9h9.7V25h-14.3V3.1z"/><path class="st1" d="M80.4 39h2.5v20H92v2H80.4V39zM98 52.1c2.1-.7 4.2-1 6.4-1v-1.9c0-2.7-1.4-3.1-3.8-3.1-1.7.1-3.4.5-4.8 1.4l-.6-1.6c1.8-1 3.9-1.6 6-1.6 3.3 0 5.6.9 5.6 5v11.9h-1c-.8 0-1.3-.1-1.3-1.2v-.6c-1.4 1.2-3.2 1.8-5.1 1.8-2.9 0-5-1.7-5-4.6 0-2.1 1.3-3.6 3.6-4.5zm2.2 7.3c1.5 0 3-.5 4.2-1.4v-5.3c-1.7 0-3.4.2-5.1.8-1.6.6-2.5 1.5-2.5 3 0 1.7 1 2.9 3.4 2.9zM112 37h2.4v6.8l-.1 2.5c1.3-1.3 3.1-2 5-2 3.6 0 6.5 2.6 6.5 7.8s-3.3 9.1-8.3 9.1c-1.9 0-3.8-.4-5.5-1.2V37zm5.8 22.4c2.9 0 5.5-2.3 5.6-7 0-4.1-1.6-6.3-4.7-6.3-1.6 0-3.1.6-4.2 1.7v10.9c.9.6 2.1.8 3.3.7zM129.4 58.2c1.5.8 3.1 1.2 4.7 1.2 2.1 0 3.6-.8 3.6-2.5s-2-2.5-3.9-3.3c-2.5-1-4.7-2.4-4.7-5.1s2.7-4.1 5.7-4.2c1.5 0 2.9.3 4.2.9l-.4 1.8c-1.1-.6-2.4-.9-3.7-.9-2.4 0-3.6.9-3.6 2.2 0 1.7 1.4 2.5 3.6 3.4s5 2.2 5 5-2.2 4.5-5.7 4.5c-1.8 0-3.6-.4-5.2-1.2l.4-1.8zM7.2 31.7c0-12.2 8.9-22.6 21-24.4L27.1 0C11.6 2.4.1 15.7 0 31.5c0 6.8 2.2 13.5 6.2 19l5.8-4.3C8.9 42 7.2 36.9 7.2 31.7zM17.1 51.4l-4.2 5.7c4.2 3.1 9.1 5.1 14.2 5.9l1-7c-4-.6-7.8-2.2-11-4.6zM36.5 0l-1.1 7.3c13.5 2 22.7 14.5 20.7 28-.6 3.9-2.1 7.7-4.5 10.9l5.7 4.2c4.1-5.5 6.3-12.1 6.2-19 .1-15.7-11.4-29-27-31.4zM35.5 56l1 7c5.2-.8 10-2.8 14.2-5.9l-4.2-5.7c-3.2 2.4-7 4-11 4.6z"/></g></svg>'
colorBg: '#013563'
colorText: '#FFFFFF'
colorHighlight: '#F9A870'
---
JustPeace Labs is a non-profit organization that works with local communities to build peace and prevent violence.

View file

@ -1,11 +1,14 @@
---
title: Letshost.ie
description: Design and development for the irish hosting provider
description: Design & development
images: ['work/letshost/letshost_hero_ibd4sx', 'work/letshost/letshost_sharedhosting_desktop_sriv5t', 'work/letshost/letshost_testimonials_desktop_ijwxmy', 'work/letshost/letshost_wordpress_desktop_qaknlm']
tags: ['Graphc Design', 'Web Design', 'Frontend Development']
reference: https://www.linkedin.com/in/daraghmacloughlin/
referenceName: Daragh Mac Loughlin
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 115.2 16.4"><path fill="currentColor" d="M54.4.5H58v6.2h5.9V.5h3.6v15.6h-3.6v-6H58v6h-3.6V.5zM12.6 16.1V.5h11.1v3.3h-7.4v2.9H23v3.2h-6.7v2.9h7.8v3.4l-11.5-.1zM51.3 2.3l-1.7 2.4c-1.4-.5-2.6-.9-3.8-1.3-.4-.1-.9-.1-1.3.1s-.8.7-.8 1.1c.1.5.4.9.8 1.2.8.3 1.6.6 2.4.8.8.2 1.5.5 2.3.8 3.2 1.4 3.2 6 .6 7.8-3.2 2.2-8.2.9-10.6-1.8l2.1-2.6c.2.2.4.3.7.5 1.1.6 2.2 1.2 3.4 1.6.5.2 1 .1 1.5-.1.4-.2.7-.6.9-1 .2-.5-.1-1.1-.6-1.3h-.1c-.8-.3-1.5-.5-2.3-.8s-1.7-.5-2.5-.9c-1.4-.6-2.3-1.9-2.4-3.4-.3-1.7.5-3.5 1.9-4.4 3.1-2 7.1-.9 9.3.9.1.2.2.3.2.4zM88.9 13.6l2.2-2.7c.3.2.5.4.8.6 1 .6 2 1.1 3.1 1.5.5.2 1.1.1 1.6-.1s.8-.6.9-1.1c0-.5-.3-1-.7-1.2-.7-.3-1.4-.6-2.1-.7-.9-.3-1.8-.6-2.6-.9-1.6-.6-2.7-2.3-2.5-4-.1-1.7.9-3.3 2.4-4.1 2.7-1.4 7.2-.7 9.1 1.5l-1.7 2.4c-1.2-.5-2.4-.9-3.6-1.3-.5-.1-1-.1-1.5.1s-.8.6-.9 1.1c0 .5.3 1 .8 1.2.8.3 1.6.6 2.4.8.8.2 1.5.5 2.3.8 1.7.7 2.7 2.3 2.6 4.1 0 1.8-1 3.4-2.7 4.1-2.3 1.1-4.9 1-7.1-.3-1-.6-1.9-1.2-2.8-1.8zM38.1.5v3.1h-4.3v12.5h-3.7V3.6h-4.3V.5h12.3zM110.8 16.1h-3.6V3.7h-4.3V.6h12.3v3h-4.4v12.5zM3.7 12.8h6.6v3.3H0V.5h3.7v12.3zM86.2 5.9C85.4 3.7 83.7 2 81.6 1c-.1.1-3.1 1.3-4.7 3.6 2.2-.5 4-.1 5.4.6 1.8 1.1 3.2 2.8 4 4.8.3-1.2.3-2.7-.1-4.1z"/><path fill="currentColor" d="M81 15.8c2.2-.8 3.9-2.5 4.9-4.6-.1-.1-1.3-3.1-3.6-4.7.5 2.2.1 4-.6 5.4-1.1 1.8-2.8 3.2-4.8 4 1.3.3 2.8.3 4.1-.1z"/><path fill="currentColor" d="M71.1 10.7c.8 2.2 2.5 3.9 4.6 4.9.1-.1 3.1-1.3 4.7-3.6-2.2.5-4 .1-5.4-.6-1.8-1.1-3.2-2.8-4-4.8-.3 1.2-.3 2.7.1 4.1z"/><path fill="currentColor" d="M76.3.8c-2.2.8-3.9 2.5-4.9 4.6.1.1 1.3 3.1 3.6 4.7-.5-2.2-.1-4 .6-5.4 1.1-1.8 2.8-3.2 4.8-4-1.3-.3-2.8-.3-4.1.1z"/></svg>'
colorBg: '#011355'
colorText: '#FFFFFF'
colorHighlight: '#96DA1F'
---
LetsHost.ie is the leading irish webhosting provider. I worked with the marketing team to redesign their website and improve their user journeys.

View file

@ -1,13 +1,20 @@
---
title: peak.capital
header_bg_image: peak_hero_ewp1wb
images: ['/work/peak/peak-mobile-mockup_wtp3ea', '/work/peak/peak_hero_ewp1wb']
images: [
'https://res.cloudinary.com/dkvjosg5p/video/upload/v1770654489/work/peak/peak_home_out_x3ljcp.mp4',
'/work/peak/peak-mobile-mockup_wtp3ea',
'/work/peak/peak_hero_ewp1wb'
]
order: 12
description: A new website for the european investment fund
tags: ['Webdesign', 'Frontend Development']
svg: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 97 97"><path style="fill:currentColor" fill-rule="evenodd" d="M.1 48.5C.1 21.7 21.8 0 48.6 0 75.3.1 97 21.8 97 48.5 97 75.3 75.3 97 48.5 97S.1 75.3.1 48.5zm14.5 3.2H18c5.4 0 8.8-2.6 8.8-7s-3.4-7-8.8-7h-8v21.7h4.6v-7.7zm19.2-9.9h9.9v-4.1H29.2v21.7h14.5v-4.1h-9.9v-4.9h9.3v-4.1h-9.3v-4.5zm28.4 15.9.7 1.7h5l-8.7-21.7h-4.9l-8.7 21.7h5l.6-1.5c0-.1.1-.2.2-.4 1.1-2.2 3.2-3.6 5.4-3.6s4.2 1.3 5.3 3.4c0 .1 0 .3.1.4zm12.1-5.8 3.2-3.3 6 10.7h5.9L81.1 45l7.3-7.4h-5.8l-8.3 8.3v-8.3h-4.6v21.8h4.6v-7.5zM18 41.8h-3.3v5.8H18c2.6 0 4.1-1 4.1-2.9-.1-1.9-1.5-2.9-4.1-2.9zM54.2 50c.8-.2 1.6-.3 2.5-.3.8 0 1.7.1 2.5.3l-2.5-6.3-2.5 6.3z" clip-rule="evenodd"/></svg>'
referenceName: Madeline Lawrence
reference: https://www.linkedin.com/in/madelinelawren/
colorBg: '#000000'
colorText: '#FFFFFF'
colorHighlight: '#FF00FF'
---
Peak is an early-stage investment fund backed by entrepreneurs in Europe.
@ -15,4 +22,4 @@ In 2021 the creative agency [forpeople](https://forpeople.com) helped Peak with
Over the past years I have worked with the team at peak to develop the site further.
<a class="button" href="https://peak.capital">Live site</a>
<a class="button button--primary" href="https://peak.capital">Live site</a>

View file

@ -10,6 +10,9 @@ referenceName: Patrick Niall
agency: https://forpeople.com
agencyName: forpeople
svg: '<svg id="uncommonLogo" x="0" y="0" viewBox="0 0 932 110.7" style="enable-background:new 0 0 932 110.7; fill: currentColor;"><path id="n7" class="uncommonLogo-letter" d="M828.9 3.5h30.5v9.6C865.6 4.9 878.2 0 889.3 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.8c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="o6" class="uncommonLogo-letter" d="M801.2 17.2c-10.8-10.7-24.1-16-39.6-16s-28.7 5.3-39.5 16C711.4 27.7 706 40.6 706 56c0 15.3 5.4 28.3 16.1 39 10.7 10.5 23.9 15.8 39.5 15.8s28.8-5.3 39.6-16C812 84.3 817.5 71.3 817.5 56c0-15.4-5.4-28.3-16.3-38.8zm-21.7 57.1c-4.9 5-10.8 7.5-17.9 7.5s-13-2.5-17.7-7.5c-4.9-5-7.3-11.1-7.3-18.4 0-7.2 2.4-13.4 7.1-18.4 4.9-5 10.8-7.5 17.9-7.5s13 2.5 17.9 7.5c4.9 5 7.3 11.1 7.3 18.4 0 7.3-2.5 13.4-7.3 18.4z"></path><path id="mm5" class="uncommonLogo-letter" d="M446.9 3.5h30.5v9.6C483.6 4.9 496.2 0 507.3 0c10.2 0 21.6 3.1 28.1 9.1 3.3 2.9 6.3 6.7 8.7 10.8 3.7-4.2 6.3-6.8 6.3-6.8C557.1 6.2 569.1 0 580.2 0c10.2 0 21.6 3.1 28.1 9.1 3.3 3 6.3 6.7 8.7 10.9 3.7-4.2 6.4-6.9 6.4-6.9C630.1 6.2 642.1 0 653.2 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.7V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.6V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="o4" class="uncommonLogo-letter" d="M420 17.2c-10.8-10.7-24.1-16-39.6-16s-28.7 5.3-39.5 16c-10.7 10.5-16.1 23.5-16.1 38.8 0 15.3 5.4 28.3 16.1 39 10.7 10.5 23.9 15.8 39.5 15.8s28.8-5.3 39.6-16c10.8-10.5 16.3-23.5 16.3-38.8 0-15.4-5.4-28.3-16.3-38.8zm-21.7 57.1c-4.9 5-10.8 7.5-17.9 7.5s-13-2.5-17.7-7.5c-4.9-5-7.3-11.1-7.3-18.4 0-7.2 2.4-13.4 7.1-18.4 4.9-5 10.8-7.5 17.9-7.5s13 2.5 17.9 7.5c4.9 5 7.3 11.1 7.3 18.4 0 7.3-2.4 13.4-7.3 18.4z"></path><path id="c3" class="uncommonLogo-letter" d="M247.1 16.8c10.4-10.4 23.4-15.6 39.1-15.6 13.6 0 25.5 4.5 35.6 13.4l-11.3 26.5c-6.8-7.3-14.6-11-23.2-11-7.2 0-13.2 2.5-18.1 7.5-4.9 4.9-7.3 11.2-7.3 18.9 0 7.6 2.4 13.7 7.1 18.4 4.9 4.7 10.8 7 17.9 7 8.9 0 16.7-3.7 23.6-11l11.3 26.5c-10.1 8.9-22 13.4-35.6 13.4-15.7 0-28.8-5.2-39.3-15.6-10.4-10.4-15.5-23.5-15.5-39.1s5.2-28.9 15.7-39.3z"></path><path id="n2" class="uncommonLogo-letter" d="M117.8 3.5h30.5v9.6C154.5 4.9 167.1 0 178.2 0c10.2 0 21.6 3.1 28.1 9.1 8.4 7.5 14.7 19.9 14.7 32v66.4h-30.3V48.7c0-5-1.8-12.9-4.6-15.7-2.7-2.8-6.7-6.8-16.2-6.8s-14.8 4.9-17.5 8.9c-2.3 3.3-4.1 7.4-4.1 13.6v58.8h-30.5V3.5z"></path><path id="u1" class="uncommonLogo-letter" d="M103.1 107.2H72.6v-9.6c-6.2 8.2-18.8 13.1-29.9 13.1-10.2 0-21.6-3.1-28.1-9.1C6.3 94.1 0 81.7 0 69.6V3.2h30.3V62c0 5 1.8 12.9 4.6 15.7 2.7 2.8 6.7 6.8 16.2 6.8s14.8-4.9 17.5-8.9c2.3-3.3 4.1-7.4 4.1-13.6V3.2h30.5v104z"></path></svg>'
colorBg: '#4D2C7C'
colorText: '#F0EBE6'
colorHighlight: '#B3B6FE'
---
Uncommon's mission is to sell rashers and rashers of cultivated pork at price parity. For happy pigs and piggy banks.

27
src/routes/work/md/zya.md Normal file
View file

@ -0,0 +1,27 @@
---
title: zya.co
images: [
'https://res.cloudinary.com/dkvjosg5p/video/upload/v1770392733/work/zya/zya_home_2pxpms_opt_pjyitt.mp4',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770392695/work/zya/zya_approach_hzpelu.png',
'https://res.cloudinary.com/dkvjosg5p/video/upload/v1770392738/work/zya/zya_convero_2pxpms_opt_qqq5jt.mp4',
'https://res.cloudinary.com/dkvjosg5p/image/upload/v1770392695/work/zya/zya_mission_ygzf3t.png'
]
order: 12
tags: ['Tech consultancy', 'Frontend development',]
description: An enzyme that converts sugar into fiber
reference: https://www.linkedin.com/in/josh-sauer-phd/
referenceName: Joshua Sauer
agency: https://forpeople.com
agencyName: forpeople
svg: '<svg width="63" height="27" viewBox="0 0 63 27" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M41.3897 0.751722H36.7294L30.8702 14.1349C30.3853 15.2437 29.1064 15.26 28.5986 14.1615L23.6313 4.04019C22.3656 1.85951 20.8935 0.762177 17.7699 0.73737H0.0858154V4.20059H10.8866C11.9013 4.20059 12.3754 5.45712 11.6136 6.1274L0.0858154 16.2698V20.0347H18.3518V16.4572H8.49249C7.47837 16.4572 7.00397 15.2019 7.76461 14.5312L17.5524 5.90035C18.4396 5.10514 19.5278 5.18885 20.1305 6.33562L26.3524 18.2129C27.7389 20.8917 25.5924 22.6567 22.8737 22.6567H18.3767V26.3184H23.5596C26.4774 26.3184 27.3679 25.8441 28.5796 25.0435C29.7913 24.2429 31.0462 22.4471 32.2011 20.1197L41.3829 0.751722H41.3897Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M54.7502 4.08113C53.7068 1.86811 51.4799 0.456055 49.0332 0.456055C46.5875 0.456055 44.3612 1.86706 43.3173 4.07883L35.7943 20.018H40.5601C44.4933 12.2573 53.9182 12.5632 57.5545 20.018H62.2643L54.7502 4.08113ZM51.5265 6.28748C51.0983 5.36275 50.1463 4.65315 49.0531 4.65315C47.8995 4.65315 47.0475 5.36324 46.6167 6.28748L44.293 11.2673C44.2341 11.3935 44.3564 11.5299 44.4891 11.4877C47.2687 10.6047 50.9197 10.5062 53.6226 11.4649C53.757 11.5126 53.8871 11.3739 53.8271 11.2445L51.5265 6.28748Z" fill="currentColor"></path></svg>'
colorBg: '#083247'
colorText: '#FFFBF3'
colorHighlight: '#E681FF'
---
Zyas mission is simple: to make better nutrition accessible without sacrificing the taste and enjoyment of everyday food.
In another collaboration with the ever-inspiring creative agency [forpeople](https://forpeople.com), we created a website with scroll-based storytelling animations that playfully brings the complex information behind the science to life.
In a second phase we added more storytelling features and a blog.
<a href="https://zya.co" class="button" target="_blank">Check it out live</a>

View file

@ -1,93 +0,0 @@
:global(html){
overflow: hidden;
overscroll-behavior: none;
}
.works-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
perspective: 700px;
transform-origin: 0 0;
will-change: transform;
}
.headline {
position: fixed;
pointer-events: none;
display: flex;
width: 100%;
height: 100%;
text-align: center;
flex-direction: column;
justify-content: center;
align-content: center;
letter-spacing: -0.05em;
z-index: 10;
transform: translateZ(500px);
margin-left: 40.5vw;
margin-top: 37.25vh;
font-size: 14vw;
@media screen and (min-width: 768px) {
margin-left: 41.66vw;
margin-top: 40.5vh;
font-size: 7vw;
transform: translateZ(600px);
}
}
.works {
margin: 0 auto;
position: absolute;
top: 0;
left: 0;
width: 600vw;
height: 600vw;
transform: scale(.333);
transform-origin: 0 0;
display: grid;
grid-template-columns: repeat(6, 100vw);
grid-auto-rows: 100vh;
gap: 6px;
will-change: transform;
}
.work {
background-color: var(--color-highlight);
text-decoration: none;
text-align: center;
width: 100vw;
height: 100vh;
opacity: 0;
visibility: hidden;
transform: translateZ(700px);
will-change: transform;
}
:global(.work-logo) {
width: 100%;
height: 100%;
color: var(--color-bg);
padding: 3em;
margin: auto;
display: flex;
flex-direction: column;
justify-content: center;
will-change: transform;
@media screen and (min-width: 768px) {
width: 60%;
}
}
:global(.work-logo svg) {
object-fit: fill;
width: 100%;
height: 100%;
}
.work-info {
display: none;
}
.work-info .tags {
display: flex;
list-style: none;
gap: .25em;
}

View file

@ -1,6 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [ sveltekit() ]
plugins: [tailwindcss(), sveltekit()]
});