Compare commits
	
		
			9 Commits
		
	
	
		
			d9e8b4b56c
			...
			79f6e8e90b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 79f6e8e90b | |||
| 
						
						
							
						
						25f3db52ec
	
				 | 
					
					
						|||
| 
						
						
							
						
						a46ac458dc
	
				 | 
					
					
						|||
| 
						
						
							
						
						206c5665a2
	
				 | 
					
					
						|||
| 
						
						
							
						
						fd3c620cb9
	
				 | 
					
					
						|||
| 
						
						
							
						
						c52d185f76
	
				 | 
					
					
						|||
| 
						
						
							
						
						538d9593c2
	
				 | 
					
					
						|||
| 
						
						
							
						
						24a7ebf02a
	
				 | 
					
					
						|||
| 
						
						
							
						
						fc642a4ecd
	
				 | 
					
					
						
@@ -1,5 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"useTabs": true,
 | 
						"useTabs": false,
 | 
				
			||||||
 | 
						"tabWidth": 4,
 | 
				
			||||||
	"singleQuote": true,
 | 
						"singleQuote": true,
 | 
				
			||||||
	"trailingComma": "none",
 | 
						"trailingComma": "none",
 | 
				
			||||||
	"printWidth": 100,
 | 
						"printWidth": 100,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,5 +26,10 @@
 | 
				
			|||||||
		"typescript": "5.8.3",
 | 
							"typescript": "5.8.3",
 | 
				
			||||||
		"vite": "6.3.5"
 | 
							"vite": "6.3.5"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"type": "module"
 | 
						"type": "module",
 | 
				
			||||||
 | 
						"dependencies": {
 | 
				
			||||||
 | 
							"@tailwindcss/vite": "^4.1.6",
 | 
				
			||||||
 | 
							"svelte-toasts": "^1.1.2",
 | 
				
			||||||
 | 
							"tailwindcss": "^4.1.6"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										422
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										422
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -7,22 +7,32 @@ settings:
 | 
				
			|||||||
importers:
 | 
					importers:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .:
 | 
					  .:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@tailwindcss/vite':
 | 
				
			||||||
 | 
					        specifier: ^4.1.6
 | 
				
			||||||
 | 
					        version: 4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
 | 
					      svelte-toasts:
 | 
				
			||||||
 | 
					        specifier: ^1.1.2
 | 
				
			||||||
 | 
					        version: 1.1.2
 | 
				
			||||||
 | 
					      tailwindcss:
 | 
				
			||||||
 | 
					        specifier: ^4.1.6
 | 
				
			||||||
 | 
					        version: 4.1.6
 | 
				
			||||||
    devDependencies:
 | 
					    devDependencies:
 | 
				
			||||||
      '@rollup/plugin-json':
 | 
					      '@rollup/plugin-json':
 | 
				
			||||||
        specifier: ^6.0.0
 | 
					        specifier: ^6.0.0
 | 
				
			||||||
        version: 6.1.0(rollup@4.40.2)
 | 
					        version: 6.1.0(rollup@4.40.2)
 | 
				
			||||||
      '@sveltejs/adapter-auto':
 | 
					      '@sveltejs/adapter-auto':
 | 
				
			||||||
        specifier: 6.0.0
 | 
					        specifier: 6.0.0
 | 
				
			||||||
        version: 6.0.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5))
 | 
					        version: 6.0.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))
 | 
				
			||||||
      '@sveltejs/adapter-node':
 | 
					      '@sveltejs/adapter-node':
 | 
				
			||||||
        specifier: 5.2.12
 | 
					        specifier: 5.2.12
 | 
				
			||||||
        version: 5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5))
 | 
					        version: 5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))
 | 
				
			||||||
      '@sveltejs/kit':
 | 
					      '@sveltejs/kit':
 | 
				
			||||||
        specifier: 2.20.8
 | 
					        specifier: 2.20.8
 | 
				
			||||||
        version: 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)
 | 
					        version: 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      '@sveltejs/vite-plugin-svelte':
 | 
					      '@sveltejs/vite-plugin-svelte':
 | 
				
			||||||
        specifier: ^5.0.0
 | 
					        specifier: ^5.0.0
 | 
				
			||||||
        version: 5.0.3(svelte@5.28.2)(vite@6.3.5)
 | 
					        version: 5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      prettier:
 | 
					      prettier:
 | 
				
			||||||
        specifier: 3.5.3
 | 
					        specifier: 3.5.3
 | 
				
			||||||
        version: 3.5.3
 | 
					        version: 3.5.3
 | 
				
			||||||
@@ -43,7 +53,7 @@ importers:
 | 
				
			|||||||
        version: 5.8.3
 | 
					        version: 5.8.3
 | 
				
			||||||
      vite:
 | 
					      vite:
 | 
				
			||||||
        specifier: 6.3.5
 | 
					        specifier: 6.3.5
 | 
				
			||||||
        version: 6.3.5
 | 
					        version: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
packages:
 | 
					packages:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -201,6 +211,10 @@ packages:
 | 
				
			|||||||
    cpu: [x64]
 | 
					    cpu: [x64]
 | 
				
			||||||
    os: [win32]
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@isaacs/fs-minipass@4.0.1':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18.0.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@jridgewell/gen-mapping@0.3.8':
 | 
					  '@jridgewell/gen-mapping@0.3.8':
 | 
				
			||||||
    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
 | 
					    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
 | 
				
			||||||
    engines: {node: '>=6.0.0'}
 | 
					    engines: {node: '>=6.0.0'}
 | 
				
			||||||
@@ -397,6 +411,96 @@ packages:
 | 
				
			|||||||
      svelte: ^5.0.0
 | 
					      svelte: ^5.0.0
 | 
				
			||||||
      vite: ^6.0.0
 | 
					      vite: ^6.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/node@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-android-arm64@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [android]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-darwin-arm64@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [darwin]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-darwin-x64@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [darwin]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-freebsd-x64@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [freebsd]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm64-gnu@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm64-musl@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-x64-gnu@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-x64-musl@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-wasm32-wasi@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=14.0.0'}
 | 
				
			||||||
 | 
					    cpu: [wasm32]
 | 
				
			||||||
 | 
					    bundledDependencies:
 | 
				
			||||||
 | 
					      - '@napi-rs/wasm-runtime'
 | 
				
			||||||
 | 
					      - '@emnapi/core'
 | 
				
			||||||
 | 
					      - '@emnapi/runtime'
 | 
				
			||||||
 | 
					      - '@tybys/wasm-util'
 | 
				
			||||||
 | 
					      - '@emnapi/wasi-threads'
 | 
				
			||||||
 | 
					      - tslib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-win32-arm64-msvc@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-win32-x64-msvc@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 10'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/vite@4.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      vite: ^5.2.0 || ^6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/cookie@0.6.0':
 | 
					  '@types/cookie@0.6.0':
 | 
				
			||||||
    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 | 
					    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -423,6 +527,10 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
 | 
					    resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
 | 
				
			||||||
    engines: {node: '>= 14.16.0'}
 | 
					    engines: {node: '>= 14.16.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  chownr@3.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clsx@2.1.1:
 | 
					  clsx@2.1.1:
 | 
				
			||||||
    resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
 | 
					    resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
 | 
				
			||||||
    engines: {node: '>=6'}
 | 
					    engines: {node: '>=6'}
 | 
				
			||||||
@@ -447,9 +555,17 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
 | 
					    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
 | 
				
			||||||
    engines: {node: '>=0.10.0'}
 | 
					    engines: {node: '>=0.10.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detect-libc@2.0.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  devalue@5.1.1:
 | 
					  devalue@5.1.1:
 | 
				
			||||||
    resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
 | 
					    resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  enhanced-resolve@5.18.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=10.13.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  esbuild@0.25.4:
 | 
					  esbuild@0.25.4:
 | 
				
			||||||
    resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
 | 
					    resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
 | 
				
			||||||
    engines: {node: '>=18'}
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
@@ -480,6 +596,9 @@ packages:
 | 
				
			|||||||
  function-bind@1.1.2:
 | 
					  function-bind@1.1.2:
 | 
				
			||||||
    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 | 
					    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graceful-fs@4.2.11:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  hasown@2.0.2:
 | 
					  hasown@2.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
 | 
					    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
 | 
				
			||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
@@ -500,16 +619,97 @@ packages:
 | 
				
			|||||||
  is-reference@3.0.3:
 | 
					  is-reference@3.0.3:
 | 
				
			||||||
    resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
 | 
					    resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  jiti@2.4.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
 | 
				
			||||||
 | 
					    hasBin: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  kleur@4.1.5:
 | 
					  kleur@4.1.5:
 | 
				
			||||||
    resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
 | 
					    resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
 | 
				
			||||||
    engines: {node: '>=6'}
 | 
					    engines: {node: '>=6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-darwin-arm64@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [darwin]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-darwin-x64@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [darwin]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-freebsd-x64@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [freebsd]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm-gnueabihf@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [arm]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm64-gnu@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm64-musl@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-x64-gnu@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-x64-musl@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [linux]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-win32-arm64-msvc@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [arm64]
 | 
				
			||||||
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-win32-x64-msvc@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					    cpu: [x64]
 | 
				
			||||||
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss@1.29.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 12.0.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  locate-character@3.0.0:
 | 
					  locate-character@3.0.0:
 | 
				
			||||||
    resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
 | 
					    resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  magic-string@0.30.17:
 | 
					  magic-string@0.30.17:
 | 
				
			||||||
    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
 | 
					    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  minipass@7.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
 | 
				
			||||||
 | 
					    engines: {node: '>=16 || 14 >=14.17'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  minizlib@3.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mkdirp@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=10'}
 | 
				
			||||||
 | 
					    hasBin: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mri@1.2.0:
 | 
					  mri@1.2.0:
 | 
				
			||||||
    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
 | 
					    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
 | 
				
			||||||
    engines: {node: '>=4'}
 | 
					    engines: {node: '>=4'}
 | 
				
			||||||
@@ -592,10 +792,24 @@ packages:
 | 
				
			|||||||
      svelte: ^4.0.0 || ^5.0.0-next.0
 | 
					      svelte: ^4.0.0 || ^5.0.0-next.0
 | 
				
			||||||
      typescript: '>=5.0.0'
 | 
					      typescript: '>=5.0.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  svelte-toasts@1.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-m+yL4eEKXyJoyjTYaH1j1GFwF0Pi8YDqnVfwWPDmwi4712iZesv+TNCmToSNlav3R5Vkmc8ZBRkT8DOcu3sywQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  svelte@5.28.2:
 | 
					  svelte@5.28.2:
 | 
				
			||||||
    resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==}
 | 
					    resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==}
 | 
				
			||||||
    engines: {node: '>=18'}
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tailwindcss@4.1.6:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tapable@2.2.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tar@7.4.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  tinyglobby@0.2.13:
 | 
					  tinyglobby@0.2.13:
 | 
				
			||||||
    resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
 | 
					    resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
 | 
				
			||||||
    engines: {node: '>=12.0.0'}
 | 
					    engines: {node: '>=12.0.0'}
 | 
				
			||||||
@@ -660,6 +874,10 @@ packages:
 | 
				
			|||||||
      vite:
 | 
					      vite:
 | 
				
			||||||
        optional: true
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  yallist@5.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  zimmerframe@1.1.2:
 | 
					  zimmerframe@1.1.2:
 | 
				
			||||||
    resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
 | 
					    resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -745,6 +963,10 @@ snapshots:
 | 
				
			|||||||
  '@esbuild/win32-x64@0.25.4':
 | 
					  '@esbuild/win32-x64@0.25.4':
 | 
				
			||||||
    optional: true
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@isaacs/fs-minipass@4.0.1':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      minipass: 7.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@jridgewell/gen-mapping@0.3.8':
 | 
					  '@jridgewell/gen-mapping@0.3.8':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@jridgewell/set-array': 1.2.1
 | 
					      '@jridgewell/set-array': 1.2.1
 | 
				
			||||||
@@ -864,22 +1086,22 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      acorn: 8.14.1
 | 
					      acorn: 8.14.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@sveltejs/adapter-auto@6.0.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5))':
 | 
					  '@sveltejs/adapter-auto@6.0.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)
 | 
					      '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      import-meta-resolve: 4.1.0
 | 
					      import-meta-resolve: 4.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5))':
 | 
					  '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2)
 | 
					      '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2)
 | 
				
			||||||
      '@rollup/plugin-json': 6.1.0(rollup@4.40.2)
 | 
					      '@rollup/plugin-json': 6.1.0(rollup@4.40.2)
 | 
				
			||||||
      '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2)
 | 
					      '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2)
 | 
				
			||||||
      '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)
 | 
					      '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      rollup: 4.40.2
 | 
					      rollup: 4.40.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)':
 | 
					  '@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5)
 | 
					      '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      '@types/cookie': 0.6.0
 | 
					      '@types/cookie': 0.6.0
 | 
				
			||||||
      cookie: 0.6.0
 | 
					      cookie: 0.6.0
 | 
				
			||||||
      devalue: 5.1.1
 | 
					      devalue: 5.1.1
 | 
				
			||||||
@@ -892,30 +1114,101 @@ snapshots:
 | 
				
			|||||||
      set-cookie-parser: 2.7.1
 | 
					      set-cookie-parser: 2.7.1
 | 
				
			||||||
      sirv: 3.0.1
 | 
					      sirv: 3.0.1
 | 
				
			||||||
      svelte: 5.28.2
 | 
					      svelte: 5.28.2
 | 
				
			||||||
      vite: 6.3.5
 | 
					      vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)':
 | 
					  '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5)
 | 
					      '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      debug: 4.4.0
 | 
					      debug: 4.4.0
 | 
				
			||||||
      svelte: 5.28.2
 | 
					      svelte: 5.28.2
 | 
				
			||||||
      vite: 6.3.5
 | 
					      vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
    transitivePeerDependencies:
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
      - supports-color
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5)':
 | 
					  '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5))(svelte@5.28.2)(vite@6.3.5)
 | 
					      '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
      debug: 4.4.0
 | 
					      debug: 4.4.0
 | 
				
			||||||
      deepmerge: 4.3.1
 | 
					      deepmerge: 4.3.1
 | 
				
			||||||
      kleur: 4.1.5
 | 
					      kleur: 4.1.5
 | 
				
			||||||
      magic-string: 0.30.17
 | 
					      magic-string: 0.30.17
 | 
				
			||||||
      svelte: 5.28.2
 | 
					      svelte: 5.28.2
 | 
				
			||||||
      vite: 6.3.5
 | 
					      vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
      vitefu: 1.0.6(vite@6.3.5)
 | 
					      vitefu: 1.0.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))
 | 
				
			||||||
    transitivePeerDependencies:
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
      - supports-color
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/node@4.1.6':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@ampproject/remapping': 2.3.0
 | 
				
			||||||
 | 
					      enhanced-resolve: 5.18.1
 | 
				
			||||||
 | 
					      jiti: 2.4.2
 | 
				
			||||||
 | 
					      lightningcss: 1.29.2
 | 
				
			||||||
 | 
					      magic-string: 0.30.17
 | 
				
			||||||
 | 
					      source-map-js: 1.2.1
 | 
				
			||||||
 | 
					      tailwindcss: 4.1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-android-arm64@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-darwin-arm64@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-darwin-x64@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-freebsd-x64@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm64-gnu@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-arm64-musl@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-x64-gnu@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-linux-x64-musl@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-wasm32-wasi@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-win32-arm64-msvc@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide-win32-x64-msvc@4.1.6':
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/oxide@4.1.6':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      detect-libc: 2.0.4
 | 
				
			||||||
 | 
					      tar: 7.4.3
 | 
				
			||||||
 | 
					    optionalDependencies:
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-android-arm64': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-darwin-arm64': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-darwin-x64': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-freebsd-x64': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-linux-arm64-musl': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-linux-x64-gnu': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-linux-x64-musl': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-wasm32-wasi': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide-win32-x64-msvc': 4.1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@tailwindcss/vite@4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@tailwindcss/node': 4.1.6
 | 
				
			||||||
 | 
					      '@tailwindcss/oxide': 4.1.6
 | 
				
			||||||
 | 
					      tailwindcss: 4.1.6
 | 
				
			||||||
 | 
					      vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/cookie@0.6.0': {}
 | 
					  '@types/cookie@0.6.0': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/estree@1.0.7': {}
 | 
					  '@types/estree@1.0.7': {}
 | 
				
			||||||
@@ -932,6 +1225,8 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      readdirp: 4.1.2
 | 
					      readdirp: 4.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  chownr@3.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clsx@2.1.1: {}
 | 
					  clsx@2.1.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  commondir@1.0.1: {}
 | 
					  commondir@1.0.1: {}
 | 
				
			||||||
@@ -944,8 +1239,15 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  deepmerge@4.3.1: {}
 | 
					  deepmerge@4.3.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detect-libc@2.0.4: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  devalue@5.1.1: {}
 | 
					  devalue@5.1.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  enhanced-resolve@5.18.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      graceful-fs: 4.2.11
 | 
				
			||||||
 | 
					      tapable: 2.2.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  esbuild@0.25.4:
 | 
					  esbuild@0.25.4:
 | 
				
			||||||
    optionalDependencies:
 | 
					    optionalDependencies:
 | 
				
			||||||
      '@esbuild/aix-ppc64': 0.25.4
 | 
					      '@esbuild/aix-ppc64': 0.25.4
 | 
				
			||||||
@@ -991,6 +1293,8 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  function-bind@1.1.2: {}
 | 
					  function-bind@1.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graceful-fs@4.2.11: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  hasown@2.0.2:
 | 
					  hasown@2.0.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      function-bind: 1.1.2
 | 
					      function-bind: 1.1.2
 | 
				
			||||||
@@ -1011,14 +1315,69 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@types/estree': 1.0.7
 | 
					      '@types/estree': 1.0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  jiti@2.4.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  kleur@4.1.5: {}
 | 
					  kleur@4.1.5: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-darwin-arm64@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-darwin-x64@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-freebsd-x64@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm-gnueabihf@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm64-gnu@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-arm64-musl@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-x64-gnu@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-linux-x64-musl@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-win32-arm64-msvc@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss-win32-x64-msvc@1.29.2:
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lightningcss@1.29.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      detect-libc: 2.0.4
 | 
				
			||||||
 | 
					    optionalDependencies:
 | 
				
			||||||
 | 
					      lightningcss-darwin-arm64: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-darwin-x64: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-freebsd-x64: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-linux-arm-gnueabihf: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-linux-arm64-gnu: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-linux-arm64-musl: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-linux-x64-gnu: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-linux-x64-musl: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-win32-arm64-msvc: 1.29.2
 | 
				
			||||||
 | 
					      lightningcss-win32-x64-msvc: 1.29.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  locate-character@3.0.0: {}
 | 
					  locate-character@3.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  magic-string@0.30.17:
 | 
					  magic-string@0.30.17:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@jridgewell/sourcemap-codec': 1.5.0
 | 
					      '@jridgewell/sourcemap-codec': 1.5.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  minipass@7.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  minizlib@3.0.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      minipass: 7.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mkdirp@3.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mri@1.2.0: {}
 | 
					  mri@1.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mrmime@2.0.1: {}
 | 
					  mrmime@2.0.1: {}
 | 
				
			||||||
@@ -1108,6 +1467,8 @@ snapshots:
 | 
				
			|||||||
    transitivePeerDependencies:
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
      - picomatch
 | 
					      - picomatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  svelte-toasts@1.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  svelte@5.28.2:
 | 
					  svelte@5.28.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@ampproject/remapping': 2.3.0
 | 
					      '@ampproject/remapping': 2.3.0
 | 
				
			||||||
@@ -1125,6 +1486,19 @@ snapshots:
 | 
				
			|||||||
      magic-string: 0.30.17
 | 
					      magic-string: 0.30.17
 | 
				
			||||||
      zimmerframe: 1.1.2
 | 
					      zimmerframe: 1.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tailwindcss@4.1.6: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tapable@2.2.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tar@7.4.3:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@isaacs/fs-minipass': 4.0.1
 | 
				
			||||||
 | 
					      chownr: 3.0.0
 | 
				
			||||||
 | 
					      minipass: 7.1.2
 | 
				
			||||||
 | 
					      minizlib: 3.0.2
 | 
				
			||||||
 | 
					      mkdirp: 3.0.1
 | 
				
			||||||
 | 
					      yallist: 5.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  tinyglobby@0.2.13:
 | 
					  tinyglobby@0.2.13:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      fdir: 6.4.4(picomatch@4.0.2)
 | 
					      fdir: 6.4.4(picomatch@4.0.2)
 | 
				
			||||||
@@ -1136,7 +1510,7 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  typescript@5.8.3: {}
 | 
					  typescript@5.8.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  vite@6.3.5:
 | 
					  vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      esbuild: 0.25.4
 | 
					      esbuild: 0.25.4
 | 
				
			||||||
      fdir: 6.4.4(picomatch@4.0.2)
 | 
					      fdir: 6.4.4(picomatch@4.0.2)
 | 
				
			||||||
@@ -1146,9 +1520,13 @@ snapshots:
 | 
				
			|||||||
      tinyglobby: 0.2.13
 | 
					      tinyglobby: 0.2.13
 | 
				
			||||||
    optionalDependencies:
 | 
					    optionalDependencies:
 | 
				
			||||||
      fsevents: 2.3.3
 | 
					      fsevents: 2.3.3
 | 
				
			||||||
 | 
					      jiti: 2.4.2
 | 
				
			||||||
 | 
					      lightningcss: 1.29.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  vitefu@1.0.6(vite@6.3.5):
 | 
					  vitefu@1.0.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)):
 | 
				
			||||||
    optionalDependencies:
 | 
					    optionalDependencies:
 | 
				
			||||||
      vite: 6.3.5
 | 
					      vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  yallist@5.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  zimmerframe@1.1.2: {}
 | 
					  zimmerframe@1.1.2: {}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								src/app.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					@import "tailwindcss"
 | 
				
			||||||
							
								
								
									
										154
									
								
								src/app.html
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								src/app.html
									
									
									
									
									
								
							@@ -1,133 +1,31 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					<!doctype html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
	<head>
 | 
					    <head>
 | 
				
			||||||
		<meta charset="utf-8" />
 | 
					        <meta charset="utf-8" />
 | 
				
			||||||
		<meta name="description" content="Luke Else - Software Developer at Thales UK. I specialise in developing distributed systems in C++ using highly scalable internal frameworks. I also develop backend and system applications in my spare time using both Svelte, Rust and C++. Feel free to check my work out at https://git.luke-else.co.uk." />
 | 
					        <meta
 | 
				
			||||||
 | 
					            name="description"
 | 
				
			||||||
 | 
					            content="Luke Else - Software Developer at Thales UK. I specialise in developing distributed systems in C++ using highly scalable internal frameworks. I also develop backend and system applications in my spare time using both Svelte, Rust and C++. Feel free to check my work out at https://git.luke-else.co.uk."
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
        <meta name="author" content="Luke Else (mail@luke-else.co.uk)" />
 | 
					        <meta name="author" content="Luke Else (mail@luke-else.co.uk)" />
 | 
				
			||||||
		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
 | 
					        <link rel="icon" href="%sveltekit.assets%/favicon.png" />
 | 
				
			||||||
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css">
 | 
					        <link
 | 
				
			||||||
		<meta name="viewport" content="width=device-width" />
 | 
					            rel="stylesheet"
 | 
				
			||||||
		<script async src="https://tracking.luke-else.co.uk/tracker.js" data-ackee-server="https://tracking.luke-else.co.uk" data-ackee-domain-id="6c59ab88-dc6d-4d53-9831-0d6bff919dcd" data-ackee-opts='{ "detailed": true }'></script>
 | 
					            href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css"
 | 
				
			||||||
		%sveltekit.head%
 | 
					        />
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width" />
 | 
				
			||||||
 | 
					        <script
 | 
				
			||||||
 | 
					            async
 | 
				
			||||||
 | 
					            src="https://tracking.luke-else.co.uk/tracker.js"
 | 
				
			||||||
 | 
					            data-ackee-server="https://tracking.luke-else.co.uk"
 | 
				
			||||||
 | 
					            data-ackee-domain-id="6c59ab88-dc6d-4d53-9831-0d6bff919dcd"
 | 
				
			||||||
 | 
					            data-ackee-opts='{ "detailed": true }'
 | 
				
			||||||
 | 
					        ></script>
 | 
				
			||||||
 | 
					        %sveltekit.head%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<style>
 | 
					        <style></style>
 | 
				
			||||||
			:root {
 | 
					    </head>
 | 
				
			||||||
				--font: Consolas, 'Cascadia Code', Monaco, 'SF Mono', 'DejaVu Sans Mono', 'Roboto Mono';
 | 
					 | 
				
			||||||
				
 | 
					 | 
				
			||||||
				background: var(--bg);
 | 
					 | 
				
			||||||
				color: var(--fg);
 | 
					 | 
				
			||||||
				font-family: var(--font);
 | 
					 | 
				
			||||||
				font-size: 110%;
 | 
					 | 
				
			||||||
				margin: 2rem;
 | 
					 | 
				
			||||||
				transition: all 0.3s;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			h1, h2, h3 {
 | 
					 | 
				
			||||||
				color: var(--header);
 | 
					 | 
				
			||||||
				border: 0;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			hr {
 | 
					    <body data-sveltekit-preload-data="hover">
 | 
				
			||||||
				border: .12em solid var(--accent);
 | 
					        <div style="display: contents">%sveltekit.body%</div>
 | 
				
			||||||
  				border-radius: 5em;
 | 
					    </body>
 | 
				
			||||||
				width: 100%;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			*::-webkit-scrollbar,
 | 
					 | 
				
			||||||
			*::-webkit-scrollbar-thumb {
 | 
					 | 
				
			||||||
				width: 26px;
 | 
					 | 
				
			||||||
				border-radius: 13px;
 | 
					 | 
				
			||||||
				background-clip: padding-box;
 | 
					 | 
				
			||||||
				border: 10px solid transparent;
 | 
					 | 
				
			||||||
				color: var(--fg);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			*::-webkit-scrollbar-thumb:hover{
 | 
					 | 
				
			||||||
				color: var(--link);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			*::-webkit-scrollbar-thumb {        
 | 
					 | 
				
			||||||
			box-shadow: inset 0 0 0 10px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			@media (max-width:600px) {
 | 
					 | 
				
			||||||
				.not-required {
 | 
					 | 
				
			||||||
					display: none;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			a {
 | 
					 | 
				
			||||||
				text-decoration: none;				
 | 
					 | 
				
			||||||
				position: relative;
 | 
					 | 
				
			||||||
				color: var(--link);
 | 
					 | 
				
			||||||
				white-space: nowrap;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			a:after {
 | 
					 | 
				
			||||||
				content: '';
 | 
					 | 
				
			||||||
				position: absolute;
 | 
					 | 
				
			||||||
				bottom: 0;
 | 
					 | 
				
			||||||
				left: 0;
 | 
					 | 
				
			||||||
				width: 0%;
 | 
					 | 
				
			||||||
				border-bottom: 2px solid var(--fg);
 | 
					 | 
				
			||||||
				transition: 0.4s;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			a:hover:after {
 | 
					 | 
				
			||||||
				width: 100%;
 | 
					 | 
				
			||||||
				color: var(--glow);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			a:hover {
 | 
					 | 
				
			||||||
				color: var(--glow);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			a:active {
 | 
					 | 
				
			||||||
				color: var(--header);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.container {
 | 
					 | 
				
			||||||
				max-width: 90%;
 | 
					 | 
				
			||||||
				margin: auto;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.cards {
 | 
					 | 
				
			||||||
				display: flex;
 | 
					 | 
				
			||||||
				flex-wrap: wrap;
 | 
					 | 
				
			||||||
				flex-direction: row;
 | 
					 | 
				
			||||||
				gap: 3em 3em;
 | 
					 | 
				
			||||||
				padding: 2em 0em 2em 0em;
 | 
					 | 
				
			||||||
				transition: all 0.2s;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			@keyframes animationName {
 | 
					 | 
				
			||||||
				0%   { opacity:0; }
 | 
					 | 
				
			||||||
				50%  { opacity:1; }
 | 
					 | 
				
			||||||
				100% { opacity:0; }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
				@-o-keyframes animationName{
 | 
					 | 
				
			||||||
				0%   { opacity:0; }
 | 
					 | 
				
			||||||
				50%  { opacity:1; }
 | 
					 | 
				
			||||||
				100% { opacity:0; }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
				@-moz-keyframes animationName{
 | 
					 | 
				
			||||||
				0%   { opacity:0; }
 | 
					 | 
				
			||||||
				50%  { opacity:1; }
 | 
					 | 
				
			||||||
				100% { opacity:0; }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
				@-webkit-keyframes animationName{
 | 
					 | 
				
			||||||
				0%   { opacity:0; }
 | 
					 | 
				
			||||||
				50%  { opacity:1; }
 | 
					 | 
				
			||||||
				100% { opacity:0; }
 | 
					 | 
				
			||||||
			}   
 | 
					 | 
				
			||||||
			.elementToFadeInAndOut {
 | 
					 | 
				
			||||||
				-webkit-animation: animationName 1.5s infinite;
 | 
					 | 
				
			||||||
				-moz-animation: animationName 1.5s infinite;
 | 
					 | 
				
			||||||
				-o-animation: animationName 1.5s infinite;
 | 
					 | 
				
			||||||
				animation: animationName 1.5s infinite;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		</style>
 | 
					 | 
				
			||||||
	</head>
 | 
					 | 
				
			||||||
	<body data-sveltekit-preload-data="hover">
 | 
					 | 
				
			||||||
		<div style="display: contents">%sveltekit.body%</div>
 | 
					 | 
				
			||||||
	</body>
 | 
					 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,69 +1,31 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
    import { createEventDispatcher } from 'svelte';
 | 
					    export let headerLeft: string = "";
 | 
				
			||||||
 | 
					    export let headerRight: string = "";
 | 
				
			||||||
 | 
					    export let headerColour: string = "text-red-500";
 | 
				
			||||||
 | 
					    export let footer: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dispatch = createEventDispatcher();
 | 
					    // Allows additional styling to be applied to the Card component's outer wrapping
 | 
				
			||||||
 | 
					    export let containerStyle: string = "";
 | 
				
			||||||
    function onClick() {
 | 
					 | 
				
			||||||
        dispatch('click');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<div class={containerStyle}>
 | 
				
			||||||
    .card {
 | 
					    <div class="bg-slate-100/10 dark:bg-slate-100/10 rounded-2xl shadow-2xl p-6 flex flex-col h-full w-full">
 | 
				
			||||||
        display: flex;
 | 
					        <div class="{headerColour} flex flex-row justify-between items-center mb-4">
 | 
				
			||||||
        flex-direction: column;
 | 
					            <p class="text-2xl md:text-3xl font-bold truncate">{headerLeft}</p>
 | 
				
			||||||
        justify-content: space-between;
 | 
					            
 | 
				
			||||||
        flex-wrap: wrap;
 | 
					            {#if headerRight}
 | 
				
			||||||
        flex: 2 1 15em;
 | 
					                <p class="max-md:hidden text-xl md:text-2xl truncate">{@html headerRight}</p>
 | 
				
			||||||
        padding: .5em 2.5em 2em 2.5em;
 | 
					            {/if}
 | 
				
			||||||
        background: var(--bg-secondary);
 | 
					        </div>
 | 
				
			||||||
        border-radius: .5em;
 | 
					        <hr class="mb-4 border-1" />
 | 
				
			||||||
        scroll-snap-align: start;
 | 
					        <div class="flex-1 flex flex-col justify-center p-5">
 | 
				
			||||||
		transition: all 0.2s;
 | 
					            <slot />
 | 
				
			||||||
        box-shadow: .25em .25em .5em var(--hover);
 | 
					        </div>
 | 
				
			||||||
    }
 | 
					        {#if footer}
 | 
				
			||||||
 | 
					            <hr class="my-4 border-1" />
 | 
				
			||||||
    .card:hover, .card:focus-within {
 | 
					            <div class="mt-2 text-base opacity-90">
 | 
				
			||||||
        box-shadow: .5em .5em .5em var(--hover);
 | 
					                {@html footer}
 | 
				
			||||||
        transform: scale(1.02);
 | 
					            </div>
 | 
				
			||||||
    }
 | 
					        {/if}
 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card .card-header :global(div) {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
        margin-bottom: 0em;
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card .card-content :global(div) {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        max-width: 100%;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card .card-footer :global(div){
 | 
					 | 
				
			||||||
        margin-bottom: 1em;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        gap: 1em;
 | 
					 | 
				
			||||||
        max-width: 100%;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
 | 
					 | 
				
			||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
					 | 
				
			||||||
<div class="card" on:click={onClick}>
 | 
					 | 
				
			||||||
    <div class="card-header">
 | 
					 | 
				
			||||||
        <slot name="header"></slot>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <hr />
 | 
					</div>
 | 
				
			||||||
    <div class="card-content">
 | 
					 | 
				
			||||||
        <slot name="content"></slot>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <hr class="not-required"/>
 | 
					 | 
				
			||||||
    <div class="card-footer">
 | 
					 | 
				
			||||||
        <slot name="footer"></slot>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,102 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
    import { createEventDispatcher } from 'svelte';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const dispatch = createEventDispatcher();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    function onClick() {
 | 
					 | 
				
			||||||
        dispatch('click');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
    .sliding-card {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        flex-direction: column;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
        flex-wrap: wrap;
 | 
					 | 
				
			||||||
        flex: 2 1 15em;
 | 
					 | 
				
			||||||
        padding: 0.5em 2.5em 2em 2.5em;
 | 
					 | 
				
			||||||
        background: var(--bg-secondary);
 | 
					 | 
				
			||||||
        border-radius: 0.5em;
 | 
					 | 
				
			||||||
        scroll-snap-align: start;
 | 
					 | 
				
			||||||
        transition: all 0.3s ease-in-out;
 | 
					 | 
				
			||||||
        overflow: hidden;
 | 
					 | 
				
			||||||
        position: relative;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-card:hover {
 | 
					 | 
				
			||||||
        box-shadow: .5em .5em .5em var(--hover);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-card .sliding-card-header :global(div) {
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
        margin-bottom: 0em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .content-wrapper {
 | 
					 | 
				
			||||||
        position: relative;
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        overflow: hidden; /* Ensure smooth sliding */
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-card-content {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        flex-direction: column;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        max-width: 100%;
 | 
					 | 
				
			||||||
        flex-grow: 1;
 | 
					 | 
				
			||||||
        z-index: 1; /* Keep it below the sliding content */
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-content {
 | 
					 | 
				
			||||||
        position: absolute; /* Now it sits on top */
 | 
					 | 
				
			||||||
        top: 0;
 | 
					 | 
				
			||||||
        left: 0;
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        height: 100%; /* Cover entire content */
 | 
					 | 
				
			||||||
        background: var(--bg-secondary);
 | 
					 | 
				
			||||||
        transform: translateY(100%); /* Start hidden */
 | 
					 | 
				
			||||||
        transition: transform 0.3s ease-in-out;
 | 
					 | 
				
			||||||
        z-index: 2; /* Now above main content */
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-card:hover .sliding-content {
 | 
					 | 
				
			||||||
        transform: translateY(0);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sliding-card .sliding-card-footer :global(div){
 | 
					 | 
				
			||||||
        margin-bottom: 1em;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        gap: 1em;
 | 
					 | 
				
			||||||
        max-width: 100%;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
 | 
					 | 
				
			||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
					 | 
				
			||||||
<div class="sliding-card" on:click={onClick}>
 | 
					 | 
				
			||||||
    <div class="sliding-card-header">
 | 
					 | 
				
			||||||
        <slot name="header"></slot>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <hr />
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    <!-- Wrapper to stack sliding-card-content and sliding-content -->
 | 
					 | 
				
			||||||
    <div class="content-wrapper">
 | 
					 | 
				
			||||||
        <div class="sliding-card-content">
 | 
					 | 
				
			||||||
            <slot name="content"></slot>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="sliding-content">
 | 
					 | 
				
			||||||
            <slot name="sliding-content"></slot>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <hr class="not-required"/>
 | 
					 | 
				
			||||||
    <div class="sliding-card-footer">
 | 
					 | 
				
			||||||
        <slot name="footer"></slot>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								src/lib/components/FlexGallery.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/lib/components/FlexGallery.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- FlexGallery.svelte -->
 | 
				
			||||||
 | 
					<div class="flex flex-wrap gap-10 w-full">
 | 
				
			||||||
 | 
					    <slot />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -1,82 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	export let showModal: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let dialog: HTMLDialogElement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	$: if (dialog && showModal) dialog.showModal();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	import CloseIcon from "./Toasts/CloseIcon.svelte";
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
 | 
					 | 
				
			||||||
<dialog
 | 
					 | 
				
			||||||
	bind:this={dialog}
 | 
					 | 
				
			||||||
	on:close={() => (showModal = false)}
 | 
					 | 
				
			||||||
	on:click|self={() => dialog.close()}
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<!-- svelte-ignore a11y-no-static-element-interactions -->
 | 
					 | 
				
			||||||
	<div on:click|stopPropagation>
 | 
					 | 
				
			||||||
		<slot name="header" />
 | 
					 | 
				
			||||||
		<hr />
 | 
					 | 
				
			||||||
		<slot />
 | 
					 | 
				
			||||||
		<hr />
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	<button class="close" on:click={() => dialog.close()}>
 | 
					 | 
				
			||||||
		<CloseIcon width="0.8em" />
 | 
					 | 
				
			||||||
	</button>
 | 
					 | 
				
			||||||
</dialog>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	dialog {
 | 
					 | 
				
			||||||
		max-width: 70%;
 | 
					 | 
				
			||||||
		border-radius: 0.2em;
 | 
					 | 
				
			||||||
		border: none;
 | 
					 | 
				
			||||||
		padding: 0em 2em 2em 2em;
 | 
					 | 
				
			||||||
        border-left: 2em;
 | 
					 | 
				
			||||||
        border-right: 2em;
 | 
					 | 
				
			||||||
        background: var(--bg);
 | 
					 | 
				
			||||||
        color: var(--fg);
 | 
					 | 
				
			||||||
        box-shadow: .5em .5em .5em var(--header);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dialog::backdrop {
 | 
					 | 
				
			||||||
		background: rgba(0, 0, 0, 0.7);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dialog > div {
 | 
					 | 
				
			||||||
		padding: 1em;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dialog[open] {
 | 
					 | 
				
			||||||
		animation: zoom 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	@keyframes zoom {
 | 
					 | 
				
			||||||
		from {
 | 
					 | 
				
			||||||
			transform: scale(0.95);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		to {
 | 
					 | 
				
			||||||
			transform: scale(1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dialog[open]::backdrop {
 | 
					 | 
				
			||||||
		animation: fade 0.3s ease-out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	@keyframes fade {
 | 
					 | 
				
			||||||
		from {
 | 
					 | 
				
			||||||
			opacity: 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		to {
 | 
					 | 
				
			||||||
			opacity: 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.close {
 | 
					 | 
				
			||||||
		position: absolute;
 | 
					 | 
				
			||||||
		top: 1em;
 | 
					 | 
				
			||||||
		right: 1.5em;
 | 
					 | 
				
			||||||
		color: var(--fg);
 | 
					 | 
				
			||||||
		background: transparent;
 | 
					 | 
				
			||||||
		border: 0 none;
 | 
					 | 
				
			||||||
		padding: 0;
 | 
					 | 
				
			||||||
		margin: 0 0 0 auto;
 | 
					 | 
				
			||||||
		line-height: 1;
 | 
					 | 
				
			||||||
		font-size: 1.2em;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
							
								
								
									
										26
									
								
								src/lib/components/Section.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/lib/components/Section.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					    export let label: string = "";
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div id={label} class="relative flex flex-row w-full min-h-[300px] mt-5 mb-25">
 | 
				
			||||||
 | 
					    <!-- Sticky/Sliding Label -->
 | 
				
			||||||
 | 
					    <div class="hidden md:flex flex-col items-center mr-6">
 | 
				
			||||||
 | 
					        <div class="sticky top-24 left-0 z-10">
 | 
				
			||||||
 | 
					            <span class="text-2xl font-bold text-blue-400 tracking-widest"
 | 
				
			||||||
 | 
					                  style="writing-mode: vertical-rl; text-orientation: mixed;">
 | 
				
			||||||
 | 
					                {label}
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <!-- Main Content -->
 | 
				
			||||||
 | 
					    <div class="flex-1 flex flex-col">
 | 
				
			||||||
 | 
					        <!-- Label for mobile -->
 | 
				
			||||||
 | 
					        <div class="md:hidden mb-2">
 | 
				
			||||||
 | 
					            <span class="text-2xl font-bold text-blue-400">{label}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <hr class="border-blue-400 mb-6" />
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <slot />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/lib/components/SkillProgress.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/lib/components/SkillProgress.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					    export let value: number = 0; // 0 to 100
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="w-full mt-3">
 | 
				
			||||||
 | 
					    <div class="flex justify-between mb-1">
 | 
				
			||||||
 | 
					        <span class="text-sm font-medium">Competency Level</span>
 | 
				
			||||||
 | 
					        <span class="text-sm font-medium">{value}%</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="w-full bg-gray-800 rounded-full h-5">
 | 
				
			||||||
 | 
					        <div
 | 
				
			||||||
 | 
					            class="bg-orange-400 h-5 rounded-full transition-all duration-500"
 | 
				
			||||||
 | 
					            style="width: {value}%"
 | 
				
			||||||
 | 
					        ></div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -1,95 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
    import { browser } from '$app/environment';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    export let darkMode: boolean = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    function onThemeSwitch() {
 | 
					 | 
				
			||||||
        darkMode = !darkMode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        localStorage.setItem('theme', darkMode ? 'dark' : 'light');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        darkMode
 | 
					 | 
				
			||||||
            ? document.documentElement.classList.add('dark')
 | 
					 | 
				
			||||||
            : document.documentElement.classList.remove('dark');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (browser) {
 | 
					 | 
				
			||||||
        if (
 | 
					 | 
				
			||||||
            localStorage.theme === 'dark' ||
 | 
					 | 
				
			||||||
            (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            document.documentElement.classList.add('dark');
 | 
					 | 
				
			||||||
            darkMode = true;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            document.documentElement.classList.remove('dark');
 | 
					 | 
				
			||||||
            darkMode = false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
    input {
 | 
					 | 
				
			||||||
        display: none;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .switch {
 | 
					 | 
				
			||||||
        position: absolute;
 | 
					 | 
				
			||||||
        top: 0em;
 | 
					 | 
				
			||||||
        right: 0em;
 | 
					 | 
				
			||||||
        display: inline-block;
 | 
					 | 
				
			||||||
        width: 3.75em;
 | 
					 | 
				
			||||||
        height: 2.125em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .slider {
 | 
					 | 
				
			||||||
        position: absolute;
 | 
					 | 
				
			||||||
        cursor: pointer;
 | 
					 | 
				
			||||||
        background-color: var(--accent);
 | 
					 | 
				
			||||||
        top: 0;
 | 
					 | 
				
			||||||
        left: 0;
 | 
					 | 
				
			||||||
        right: 0;
 | 
					 | 
				
			||||||
        bottom: 0;
 | 
					 | 
				
			||||||
        background-color: var(--accent);
 | 
					 | 
				
			||||||
        -webkit-transition: .4s;
 | 
					 | 
				
			||||||
        transition: .4s;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .slider:before {
 | 
					 | 
				
			||||||
        position: absolute;
 | 
					 | 
				
			||||||
        content: "";
 | 
					 | 
				
			||||||
        height: 80%;
 | 
					 | 
				
			||||||
        width: 45%;
 | 
					 | 
				
			||||||
        left: 4px;
 | 
					 | 
				
			||||||
        bottom: 4px;
 | 
					 | 
				
			||||||
        background-color: var(--bg);
 | 
					 | 
				
			||||||
        -webkit-transition: .4s;
 | 
					 | 
				
			||||||
        transition: .4s;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    input:checked + .slider:before {
 | 
					 | 
				
			||||||
        -webkit-transform: translateX(1.625em);
 | 
					 | 
				
			||||||
        -ms-transform: translateX(1.625em);
 | 
					 | 
				
			||||||
        transform: translateX(1.625em);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .slider.round {
 | 
					 | 
				
			||||||
        border-radius: 2.125em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .slider.round:before {
 | 
					 | 
				
			||||||
        border-radius: 50%;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<svelte:head>
 | 
					 | 
				
			||||||
    <link rel="stylesheet" href={`/themes/${darkMode ? 'dark' : 'light'}.css`} />
 | 
					 | 
				
			||||||
</svelte:head>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class="toggle-wrapper not-required">
 | 
					 | 
				
			||||||
    <label class="switch">
 | 
					 | 
				
			||||||
        <input type="checkbox" checked={darkMode} on:click={onThemeSwitch}>
 | 
					 | 
				
			||||||
        <span class="slider round"></span>
 | 
					 | 
				
			||||||
    </label>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
							
								
								
									
										41
									
								
								src/lib/components/Timeline.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/lib/components/Timeline.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					    export let timelineData: Array<{
 | 
				
			||||||
 | 
					        title: string;
 | 
				
			||||||
 | 
					        description: string;
 | 
				
			||||||
 | 
					        duration: string;
 | 
				
			||||||
 | 
					    }>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Track open/closed state for each entry
 | 
				
			||||||
 | 
					    let openStates = timelineData.map(() => false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function toggle(index: number) {
 | 
				
			||||||
 | 
					        openStates[index] = !openStates[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    toggle(0); // Open the first entry by default
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="flex flex-col items-center justify-center">
 | 
				
			||||||
 | 
					  <div class="max-w-4xl w-full">
 | 
				
			||||||
 | 
					    {#each timelineData as entry, i}
 | 
				
			||||||
 | 
					      <div class="relative border-l border-gray-700 pl-8 pb-12">
 | 
				
			||||||
 | 
					        {#if openStates[i]}
 | 
				
			||||||
 | 
					        <div class="absolute top-0 left-[8px] text-green-400 w-4 h-4">♦</div>
 | 
				
			||||||
 | 
					        {:else}
 | 
				
			||||||
 | 
					          <div class="absolute top-0 left-[8px] text-green-400 w-4 h-4">⋄</div>
 | 
				
			||||||
 | 
					        {/if}
 | 
				
			||||||
 | 
					        <p class="text-sm opacity-70">{entry.duration}</p>
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          class="text-2lg font-semibold text-red-400 mt-1 focus:outline-none hover:underline transition"
 | 
				
			||||||
 | 
					          on:click={() => toggle(i)}
 | 
				
			||||||
 | 
					          aria-expanded={openStates[i]}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					        <h3 class="text-2lg font-semibold text-red-400 mt-1">{entry.title}</h3>
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					        {#if openStates[i]}
 | 
				
			||||||
 | 
					          <p class="mt-2 whitespace-pre-line transition-all duration-300">{@html entry.description}</p>
 | 
				
			||||||
 | 
					        {/if}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    {/each}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
<script>
 | 
					 | 
				
			||||||
	export let width = "1em"
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
  width={width}
 | 
					 | 
				
			||||||
	style="text-align: center; display: inline-block;"
 | 
					 | 
				
			||||||
  aria-hidden="true"
 | 
					 | 
				
			||||||
  focusable="false"
 | 
					 | 
				
			||||||
  role="img"
 | 
					 | 
				
			||||||
  xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
  viewBox="0 0 352 512"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
    fill="currentColor"
 | 
					 | 
				
			||||||
    d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
 | 
					 | 
				
			||||||
  />
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
<script>
 | 
					 | 
				
			||||||
	export let width = "1em"
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
  width={width}
 | 
					 | 
				
			||||||
	style="text-align: center; display: inline-block;"
 | 
					 | 
				
			||||||
  aria-hidden="true"
 | 
					 | 
				
			||||||
  focusable="false"
 | 
					 | 
				
			||||||
  role="img"
 | 
					 | 
				
			||||||
  xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
  viewBox="0 0 512 512"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
    fill="currentColor"
 | 
					 | 
				
			||||||
    d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-11.49 120h22.979c6.823 0 12.274 5.682 11.99 12.5l-7 168c-.268 6.428-5.556 11.5-11.99 11.5h-8.979c-6.433 0-11.722-5.073-11.99-11.5l-7-168c-.283-6.818 5.167-12.5 11.99-12.5zM256 340c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28z"
 | 
					 | 
				
			||||||
    class=""
 | 
					 | 
				
			||||||
  ></path>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
<script>
 | 
					 | 
				
			||||||
	export let width = "1em"
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
  width={width}
 | 
					 | 
				
			||||||
	style="text-align: center; display: inline-block;"
 | 
					 | 
				
			||||||
  aria-hidden="true"
 | 
					 | 
				
			||||||
  focusable="false"
 | 
					 | 
				
			||||||
  role="img"
 | 
					 | 
				
			||||||
  xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
  viewBox="0 0 512 512"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
    fill="currentColor"
 | 
					 | 
				
			||||||
    d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z"
 | 
					 | 
				
			||||||
  />
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
<script>
 | 
					 | 
				
			||||||
	export let width = "1em"
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
  width={width}
 | 
					 | 
				
			||||||
	style="text-align: center; display: inline-block;"
 | 
					 | 
				
			||||||
  aria-hidden="true"
 | 
					 | 
				
			||||||
  focusable="false"
 | 
					 | 
				
			||||||
  role="img"
 | 
					 | 
				
			||||||
  xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
  viewBox="0 0 512 512"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
    fill="currentColor"
 | 
					 | 
				
			||||||
    d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 464c-118.664 0-216-96.055-216-216 0-118.663 96.055-216 216-216 118.664 0 216 96.055 216 216 0 118.663-96.055 216-216 216zm141.63-274.961L217.15 376.071c-4.705 4.667-12.303 4.637-16.97-.068l-85.878-86.572c-4.667-4.705-4.637-12.303.068-16.97l8.52-8.451c4.705-4.667 12.303-4.637 16.97.068l68.976 69.533 163.441-162.13c4.705-4.667 12.303-4.637 16.97.068l8.451 8.52c4.668 4.705 4.637 12.303-.068 16.97z"
 | 
					 | 
				
			||||||
  />
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
@@ -1,67 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
  import { createEventDispatcher } from "svelte";
 | 
					 | 
				
			||||||
  import { fade } from "svelte/transition";
 | 
					 | 
				
			||||||
  import { Toast, ToastType } from "$lib/toast";
 | 
					 | 
				
			||||||
  import SuccessIcon from "./SuccessIcon.svelte";
 | 
					 | 
				
			||||||
  import ErrorIcon from "./ErrorIcon.svelte";
 | 
					 | 
				
			||||||
  import InfoIcon from "./InfoIcon.svelte";
 | 
					 | 
				
			||||||
  import CloseIcon from "./CloseIcon.svelte";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const dispatch = createEventDispatcher();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  export let toastData: Toast;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<article class={toastData.type.toString().toLowerCase()} role="alert" transition:fade>
 | 
					 | 
				
			||||||
  {#if toastData.type === ToastType.Success}
 | 
					 | 
				
			||||||
    <SuccessIcon width="1.1em" />
 | 
					 | 
				
			||||||
  {:else if toastData.type === ToastType.Error}
 | 
					 | 
				
			||||||
    <ErrorIcon width="1.1em" />
 | 
					 | 
				
			||||||
  {:else}
 | 
					 | 
				
			||||||
    <InfoIcon width="1.1em" />
 | 
					 | 
				
			||||||
  {/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <div class="text">
 | 
					 | 
				
			||||||
    {toastData.text}
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  {#if toastData.dismissable}
 | 
					 | 
				
			||||||
    <button class="close" on:click={() => dispatch("dismiss")}>
 | 
					 | 
				
			||||||
      <CloseIcon width="0.8em" />
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
  {/if}
 | 
					 | 
				
			||||||
</article>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="postcss">
 | 
					 | 
				
			||||||
  article {
 | 
					 | 
				
			||||||
    color: white;
 | 
					 | 
				
			||||||
    padding: 0.75rem 1.5rem;
 | 
					 | 
				
			||||||
    border-radius: 0.2rem;
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    margin: 0 auto 0.5rem auto;
 | 
					 | 
				
			||||||
    width: 20rem;
 | 
					 | 
				
			||||||
    max-width: 80%;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .error {
 | 
					 | 
				
			||||||
    background: var(--red);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .success {
 | 
					 | 
				
			||||||
    background: var(--green);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .info {
 | 
					 | 
				
			||||||
    background: var(--blue);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .text {
 | 
					 | 
				
			||||||
    margin-left: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .close {
 | 
					 | 
				
			||||||
    color: white;
 | 
					 | 
				
			||||||
    background: transparent;
 | 
					 | 
				
			||||||
    border: 0 none;
 | 
					 | 
				
			||||||
    padding: 0;
 | 
					 | 
				
			||||||
    margin: 0 0 0 auto;
 | 
					 | 
				
			||||||
    line-height: 1;
 | 
					 | 
				
			||||||
    font-size: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
  import Toast from "./Toast.svelte";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  import { dismissToast, toasts } from "$lib/stores";
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{#if $toasts}
 | 
					 | 
				
			||||||
  <section>
 | 
					 | 
				
			||||||
    {#each $toasts as toast (toast.id)}
 | 
					 | 
				
			||||||
      <Toast toastData = {toast} on:dismiss={() => dismissToast(toast.id)} />
 | 
					 | 
				
			||||||
    {/each}
 | 
					 | 
				
			||||||
  </section>
 | 
					 | 
				
			||||||
{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="postcss">
 | 
					 | 
				
			||||||
  section {
 | 
					 | 
				
			||||||
    position: fixed;
 | 
					 | 
				
			||||||
    bottom: 0;
 | 
					 | 
				
			||||||
    left: 0;
 | 
					 | 
				
			||||||
    right: 0;
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    margin-top: 1rem;
 | 
					 | 
				
			||||||
    justify-content: center;
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
    z-index: 10;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { setContext } from 'svelte';
 | 
					 | 
				
			||||||
	import type { TimelinePosition, TimelineConfig } from '../types';
 | 
					 | 
				
			||||||
	export let position: TimelinePosition = 'right';
 | 
					 | 
				
			||||||
	export let style: string = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setContext<TimelineConfig>('TimelineConfig', { rootPosition: position });
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<ul class="timeline" {style}>
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</ul>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline {
 | 
					 | 
				
			||||||
		display: flex;
 | 
					 | 
				
			||||||
		flex-direction: column;
 | 
					 | 
				
			||||||
		padding: 6px 16px;
 | 
					 | 
				
			||||||
		flex-grow: 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	export let style: string = "";
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<span class="timeline-connector" {style}></span>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline-connector {
 | 
					 | 
				
			||||||
		width: 2px;
 | 
					 | 
				
			||||||
		background-color: #bdbdbd;
 | 
					 | 
				
			||||||
		flex-grow: 1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { getContext } from 'svelte';
 | 
					 | 
				
			||||||
	import type { TimelineConfig, TimelinePosition } from '../types';
 | 
					 | 
				
			||||||
	export let style: string = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const config = getContext<TimelineConfig>('TimelineConfig');
 | 
					 | 
				
			||||||
	const parentPosition = getContext<TimelinePosition>('ParentPosition');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const itemPosition = parentPosition ? parentPosition : config.rootPosition;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class={`timeline-content ${itemPosition}`} {style}>
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline-content {
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		flex: 1;
 | 
					 | 
				
			||||||
		margin: 6px 16px;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.left {
 | 
					 | 
				
			||||||
		text-align: right;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.right {
 | 
					 | 
				
			||||||
		text-align: left;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	export let style: string = null;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<span class="timeline-dot" {style}>
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</span>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline-dot {
 | 
					 | 
				
			||||||
		background-color: #121212;
 | 
					 | 
				
			||||||
		border: solid 2px #121212;
 | 
					 | 
				
			||||||
		display: flex;
 | 
					 | 
				
			||||||
		align-self: baseline;
 | 
					 | 
				
			||||||
		padding: 4px;
 | 
					 | 
				
			||||||
		border-radius: 50%;
 | 
					 | 
				
			||||||
		margin: 11.5px 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,58 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { getContext, setContext } from 'svelte';
 | 
					 | 
				
			||||||
	import type { TimelinePosition, ParentPosition, TimelineConfig } from '$lib/types';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	export let position: ParentPosition | null = null;
 | 
					 | 
				
			||||||
	export let style: string = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const config = getContext<TimelineConfig>('TimelineConfig');
 | 
					 | 
				
			||||||
	const itemPosition = position ? position : config.rootPosition;
 | 
					 | 
				
			||||||
	setContext<TimelinePosition>('ParentPosition', itemPosition);
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<li class={`timeline-item ${itemPosition}`} {style}>
 | 
					 | 
				
			||||||
	{#if !$$slots['opposite-content']}
 | 
					 | 
				
			||||||
		<div class="opposite-block"></div>
 | 
					 | 
				
			||||||
	{:else}
 | 
					 | 
				
			||||||
		<slot name="opposite-content" />
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	:global(.alternate:nth-of-type(even) > .timeline-content) {
 | 
					 | 
				
			||||||
		text-align: right;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	:global(.alternate:nth-of-type(odd) > .timeline-opposite-content) {
 | 
					 | 
				
			||||||
		text-align: right;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.opposite-block {
 | 
					 | 
				
			||||||
		flex: 1;
 | 
					 | 
				
			||||||
		margin: 6px 16px;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.timeline-item {
 | 
					 | 
				
			||||||
		list-style: none;
 | 
					 | 
				
			||||||
		display: flex;
 | 
					 | 
				
			||||||
		position: relative;
 | 
					 | 
				
			||||||
		min-height: 70px;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.left {
 | 
					 | 
				
			||||||
		flex-direction: row-reverse;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.right {
 | 
					 | 
				
			||||||
		flex-direction: row;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.alternate:nth-of-type(even) {
 | 
					 | 
				
			||||||
		flex-direction: row-reverse;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.alternate:nth-of-type(odd) {
 | 
					 | 
				
			||||||
		flex-direction: row;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,31 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { getContext } from 'svelte';
 | 
					 | 
				
			||||||
	import type { TimelineConfig, TimelinePosition } from '../types';
 | 
					 | 
				
			||||||
	export let style: string = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const config = getContext<TimelineConfig>('TimelineConfig');
 | 
					 | 
				
			||||||
	const parentPosition = getContext<TimelinePosition>('ParentPosition');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const itemPosition = parentPosition ? parentPosition : config.rootPosition;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class={`timeline-opposite-content ${itemPosition}`} {style}>
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline-opposite-content {
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		flex: 1;
 | 
					 | 
				
			||||||
		margin-right: auto;
 | 
					 | 
				
			||||||
		margin: 6px 16px;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.left {
 | 
					 | 
				
			||||||
		text-align: left;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.right {
 | 
					 | 
				
			||||||
		text-align: right;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,16 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	export let style: string = null;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class="timeline-separator" {style}>
 | 
					 | 
				
			||||||
	<slot />
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.timeline-separator {
 | 
					 | 
				
			||||||
		display: flex;
 | 
					 | 
				
			||||||
		flex-direction: column;
 | 
					 | 
				
			||||||
		flex: 0;
 | 
					 | 
				
			||||||
		align-items: center;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,41 +1,9 @@
 | 
				
			|||||||
// place files you want to import through the `$lib` alias in this folder.
 | 
					// place files you want to import through the `$lib` alias in this folder.
 | 
				
			||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
 | 
					 | 
				
			||||||
import TimelineItem from '$lib/components/timeline/TimelineItem.svelte';
 | 
					 | 
				
			||||||
import TimelineSeparator from '$lib/components/timeline/TimelineSeparator.svelte';
 | 
					 | 
				
			||||||
import TimelineDot from '$lib/components/timeline/TimelineDot.svelte';
 | 
					 | 
				
			||||||
import TimelineConnector from '$lib/components/timeline/TimelineConnector.svelte';
 | 
					 | 
				
			||||||
import TimelineContent from '$lib/components/timeline/TimelineContent.svelte';
 | 
					 | 
				
			||||||
import TimelineOppositeContent from '$lib/components/timeline/TimelineOppositeContent.svelte';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Toasts from '$lib/components/Toasts/Toasts.svelte';
 | 
					 | 
				
			||||||
import Toast from '$lib/components/Toasts/Toast.svelte';
 | 
					 | 
				
			||||||
import CloseIcon from '$lib/components/Toasts/CloseIcon.svelte';
 | 
					 | 
				
			||||||
import InfoIcon from '$lib/components/Toasts/InfoIcon.svelte';
 | 
					 | 
				
			||||||
import SuccessIcon from '$lib/components/Toasts/SuccessIcon.svelte';
 | 
					 | 
				
			||||||
import ErrorIcon from '$lib/components/Toasts/ErrorIcon.svelte';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Card from '$lib/components/Cards/Card.svelte';
 | 
					import Card from '$lib/components/Cards/Card.svelte';
 | 
				
			||||||
import SlidingCard from '$lib/components/Cards/SlidingCard.svelte';
 | 
					import FlexGallery from './components/FlexGallery.svelte';
 | 
				
			||||||
import Modal from '$lib/components/Modal.svelte';
 | 
					import Loading from './components/Loading.svelte';
 | 
				
			||||||
 | 
					import Section from './components/Section.svelte';
 | 
				
			||||||
 | 
					import SkillProgress from './components/SkillProgress.svelte';
 | 
				
			||||||
 | 
					import Timeline from './components/Timeline.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Card, FlexGallery, Loading, Section, SkillProgress, Timeline };
 | 
				
			||||||
export {
 | 
					 | 
				
			||||||
	Timeline,
 | 
					 | 
				
			||||||
	TimelineItem,
 | 
					 | 
				
			||||||
	TimelineSeparator,
 | 
					 | 
				
			||||||
	TimelineDot,
 | 
					 | 
				
			||||||
	TimelineConnector,
 | 
					 | 
				
			||||||
	TimelineContent,
 | 
					 | 
				
			||||||
	TimelineOppositeContent,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Toasts,
 | 
					 | 
				
			||||||
    Toast,
 | 
					 | 
				
			||||||
    CloseIcon,
 | 
					 | 
				
			||||||
    InfoIcon,
 | 
					 | 
				
			||||||
    SuccessIcon,
 | 
					 | 
				
			||||||
    ErrorIcon,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Card,
 | 
					 | 
				
			||||||
    SlidingCard,
 | 
					 | 
				
			||||||
    Modal
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,38 +1,7 @@
 | 
				
			|||||||
import { ToastType, type Toast } from "$lib/toast";
 | 
					import { writable } from "svelte/store";
 | 
				
			||||||
import { writable, type Writable } from "svelte/store";
 | 
					 | 
				
			||||||
import type { GitRepo } from "./types";
 | 
					import type { GitRepo } from "./types";
 | 
				
			||||||
import { fetchRepos } from "./api/git";
 | 
					import { fetchRepos } from "./api/git";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
////////////////////////////////////////
 | 
					 | 
				
			||||||
// Toast Stores
 | 
					 | 
				
			||||||
////////////////////////////////////////
 | 
					 | 
				
			||||||
export const toasts: Writable<Toast[]> = writable([]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const addToast = (toast: Toast) => {
 | 
					 | 
				
			||||||
  // Create a unique ID so we can easily find/remove it
 | 
					 | 
				
			||||||
  // if it is dismissible/has a timeout.
 | 
					 | 
				
			||||||
  toast.id = Math.floor(Math.random() * 10000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Setup some sensible defaults for a toast.
 | 
					 | 
				
			||||||
  const defaults = {
 | 
					 | 
				
			||||||
    id: toast.id,
 | 
					 | 
				
			||||||
    type: ToastType.Info,
 | 
					 | 
				
			||||||
    dismissible: true,
 | 
					 | 
				
			||||||
    timeout: 3000,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Push the toast to the top of the list of toasts
 | 
					 | 
				
			||||||
  toasts.update((all) => [{ ...defaults, ...toast }, ...all]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // If toast is dismissible, dismiss it after "timeout" amount of time.
 | 
					 | 
				
			||||||
  if (toast.timeout) setTimeout(() => dismissToast(toast.id), toast.timeout);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const dismissToast = (id: number) => {
 | 
					 | 
				
			||||||
  toasts.update((all) => all.filter((t) => t.id !== id));
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
////////////////////////////////////////
 | 
					////////////////////////////////////////
 | 
				
			||||||
// Git Repo Stores
 | 
					// Git Repo Stores
 | 
				
			||||||
////////////////////////////////////////
 | 
					////////////////////////////////////////
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * @enum Used to refer to the type of toast being displayed
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export enum ToastType {
 | 
					 | 
				
			||||||
    Info = "info",
 | 
					 | 
				
			||||||
    Success = "success",
 | 
					 | 
				
			||||||
    Error = "error"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @class Toast Notification
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export class Toast {
 | 
					 | 
				
			||||||
    constructor(text: String, type: ToastType, dismissable: boolean, timeout: number ) {
 | 
					 | 
				
			||||||
        this.text = text;
 | 
					 | 
				
			||||||
        this.type = type;
 | 
					 | 
				
			||||||
        this.dismissable = dismissable;
 | 
					 | 
				
			||||||
        this.timeout = timeout;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    id: number = 0;
 | 
					 | 
				
			||||||
    text: String;
 | 
					 | 
				
			||||||
    type: ToastType;
 | 
					 | 
				
			||||||
    dismissable: Boolean;
 | 
					 | 
				
			||||||
    timeout: number;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -3,7 +3,7 @@ export type TimelinePosition = 'right' | 'left' | 'alternate';
 | 
				
			|||||||
export type ParentPosition = 'right' | 'left';
 | 
					export type ParentPosition = 'right' | 'left';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TimelineConfig = {
 | 
					export type TimelineConfig = {
 | 
				
			||||||
	rootPosition: TimelinePosition;
 | 
					    rootPosition: TimelinePosition;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GitRepo {
 | 
					export interface GitRepo {
 | 
				
			||||||
@@ -19,4 +19,4 @@ export interface GitRepo {
 | 
				
			|||||||
        login: string;
 | 
					        login: string;
 | 
				
			||||||
        avatar_url: string;
 | 
					        avatar_url: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										145
									
								
								src/main.svelte
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/main.svelte
									
									
									
									
									
								
							@@ -1,107 +1,66 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
    import { getJson } from "$lib/data";
 | 
					    import { getJson } from '$lib/data';
 | 
				
			||||||
	import { Toast, ToastType } from "$lib/toast";
 | 
					    import { toasts } from 'svelte-toasts';
 | 
				
			||||||
    import { addToast } from "$lib/stores";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import Skills from './skills.svelte';
 | 
					    import Loading from '$lib/components/Loading.svelte';
 | 
				
			||||||
 | 
					    import Section from '$lib/components/Section.svelte';
 | 
				
			||||||
    import Timeline from "./timeline.svelte";
 | 
					    import Card from '$lib/components/Cards/Card.svelte';
 | 
				
			||||||
	import Loading from "$lib/components/Loading.svelte";
 | 
					    import FlexGallery from "$lib/components/FlexGallery.svelte";
 | 
				
			||||||
 | 
					    import SkillProgress from "$lib/components/SkillProgress.svelte";
 | 
				
			||||||
 | 
					    import Timeline from '$lib/components/Timeline.svelte';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#await getJson('/json/me.json')}
 | 
					{#await getJson('/json/me.json')}
 | 
				
			||||||
    <Loading />
 | 
					    <Loading />
 | 
				
			||||||
{:then info}
 | 
					{:then info}
 | 
				
			||||||
    <div class="main-card">
 | 
					    <div style="display: none;">
 | 
				
			||||||
        <div class="card-header">
 | 
					        {toasts.add({
 | 
				
			||||||
            <h1>{info.name}</h1>
 | 
					            title: 'Welcome',
 | 
				
			||||||
            <h3 class="not-required">{info.job_title}</h3>
 | 
					            duration: 5000,
 | 
				
			||||||
        </div>
 | 
					            type: 'success',
 | 
				
			||||||
        <hr />
 | 
					            placement: 'bottom-center',
 | 
				
			||||||
        <div class="flex-container">
 | 
					            showProgress: true
 | 
				
			||||||
            <img class="profile not-required" src={info.profile_photo} alt="{info.name}'s Profile Photo">
 | 
					        })}
 | 
				
			||||||
            <p class="about">{@html info.about}</p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="container">
 | 
					    <!-- Main Card -->
 | 
				
			||||||
        <h1>Skills</h1>
 | 
					    <Section label="[Experience]">
 | 
				
			||||||
        <hr />
 | 
					        <Card headerLeft={info.name} headerRight={info.job_title} footer={info.location}>
 | 
				
			||||||
        <div class="cards">
 | 
					            <div class="flex flex-row items-center gap-5">
 | 
				
			||||||
            <Skills skills="{info.skills}"></Skills>
 | 
					                <img
 | 
				
			||||||
        </div>
 | 
					                    src={info.profile_photo}
 | 
				
			||||||
    </div>
 | 
					                    alt="Avatar"
 | 
				
			||||||
 | 
					                    class="max-md:hidden rounded-full w-32 h-32 md:w-48 md:h-48 mt-2 mb-2 p-2 border-3"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <p>{@html info.about}</p>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					    </Section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="container">
 | 
					    <!-- SKills -->
 | 
				
			||||||
        <h1>Experience</h1>
 | 
					    <Section label="[Skills]">
 | 
				
			||||||
        <hr />
 | 
					        <FlexGallery>
 | 
				
			||||||
        <!-- https://github.com/K-Sato1995/svelte-vertical-timeline -->
 | 
					            {#each info.skills as skill}
 | 
				
			||||||
        <Timeline timelineData="{info.timeline}"></Timeline>
 | 
					                <Card headerLeft={skill.name} footer={skill.link} containerStyle="flex-1 min-w-[250px] max-w-full md:min-w-[33%] opacity-100 hover:opacity-100 hover:scale-[105%] md:opacity-70 transition-all duration-300">
 | 
				
			||||||
    </div>
 | 
					                    {skill.about}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div style="display: none;">{addToast(new Toast("Click on a skill to open a prompt", ToastType.Info, true, 8_000))}</div>
 | 
					                    <SkillProgress value={skill.competency} />
 | 
				
			||||||
    <div style="display: none;">{addToast(new Toast("Welcome!", ToastType.Success, true, 7_000))}</div>
 | 
					                </Card>
 | 
				
			||||||
 | 
					            {/each}
 | 
				
			||||||
 | 
					        </FlexGallery>
 | 
				
			||||||
 | 
					    </Section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Section label="[Experience]">
 | 
				
			||||||
 | 
					        <Timeline timelineData={info.timeline} />
 | 
				
			||||||
 | 
					    </Section>
 | 
				
			||||||
{:catch}
 | 
					{:catch}
 | 
				
			||||||
    <div class="card">
 | 
					    <div style="display: none;">
 | 
				
			||||||
        <div class="card-header">
 | 
					        {toasts.add({
 | 
				
			||||||
            <h1>Unable to load portfolio overview data</h1>
 | 
					            title: 'Error',
 | 
				
			||||||
        </div>
 | 
					            description: 'There was an error loading static site data',
 | 
				
			||||||
 | 
					            duration: 0,
 | 
				
			||||||
 | 
					            placement: 'bottom-center',
 | 
				
			||||||
 | 
					            showProgress: true
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div style="display: none;">{addToast(new Toast("Unable to load me.json", ToastType.Error, true, 3000))}</div>
 | 
					 | 
				
			||||||
{/await}
 | 
					{/await}
 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
    .main-card {
 | 
					 | 
				
			||||||
        background-color: var(--bg-secondary);
 | 
					 | 
				
			||||||
        border-radius: 1em;
 | 
					 | 
				
			||||||
        padding: .2em 2em 2em 2em;
 | 
					 | 
				
			||||||
        box-shadow: .5em .5em .5em var(--glow);
 | 
					 | 
				
			||||||
        margin-bottom: 4em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    .flex-container {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .profile {
 | 
					 | 
				
			||||||
        border-radius: 100%;
 | 
					 | 
				
			||||||
        height: 8em;
 | 
					 | 
				
			||||||
        width: 8em;
 | 
					 | 
				
			||||||
        padding: 1em 1em 1em 1em;
 | 
					 | 
				
			||||||
        border: .25em solid var(--accent);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    .about {
 | 
					 | 
				
			||||||
        padding: 0em 5% 0em 5%;
 | 
					 | 
				
			||||||
        font-size: 125%;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    @media (max-width: 800px) {
 | 
					 | 
				
			||||||
        .flex-container {
 | 
					 | 
				
			||||||
            align-items: center;
 | 
					 | 
				
			||||||
            flex-direction: column;
 | 
					 | 
				
			||||||
            padding: 0px;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .about {
 | 
					 | 
				
			||||||
            font-size: 100%;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card-header {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
        margin-bottom: 0em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card-header h1 {
 | 
					 | 
				
			||||||
        font-size: 2em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .card-header h3 {
 | 
					 | 
				
			||||||
        font-size: 1.5em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,60 +1,23 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import Toasts from "$lib/components/Toasts/Toasts.svelte";
 | 
					    import { ToastContainer, FlatToast } from 'svelte-toasts';
 | 
				
			||||||
    import ThemeSwitcher from "$lib/components/ThemeSwitcher.svelte";
 | 
					    import '../app.css';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<nav>
 | 
					<div
 | 
				
			||||||
    <a href = "/">//Profile</a>
 | 
					    class="min-h-screen px-8 py-4 bg-white text-slate-600 dark:bg-slate-900/90 dark:text-slate-200/60 md:text-2xl sm:text-md font-mono flex flex-col gap-5 transition duration-1000 ease-in-out"
 | 
				
			||||||
    <a href = "/repos">//Repos</a>
 | 
					>
 | 
				
			||||||
    <a href = "/contact">//Contact</a>
 | 
					    <nav
 | 
				
			||||||
    <ThemeSwitcher />
 | 
					        class="w-full px-8 py-4 flex gap-10 text-xl justify-center items-center text-green-600 font-semibold"
 | 
				
			||||||
</nav>
 | 
					    >
 | 
				
			||||||
 | 
					        <a href="/" class="hover:underline">//Profile</a>
 | 
				
			||||||
 | 
					        <a href="/repos" class="hover:underline">//Repos</a>
 | 
				
			||||||
 | 
					        <a href="/contact" class="hover:underline">//Contact</a>
 | 
				
			||||||
 | 
					    </nav>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="main-container fade">
 | 
					    <div class="container mx-auto justify-center items-center flex flex-col">
 | 
				
			||||||
    <Toasts />
 | 
					        <slot />
 | 
				
			||||||
    <slot />
 | 
					        <ToastContainer let:data>
 | 
				
			||||||
 | 
					            <FlatToast {data} />
 | 
				
			||||||
 | 
					        </ToastContainer>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
    .main-container {
 | 
					 | 
				
			||||||
        margin-left: 10%;
 | 
					 | 
				
			||||||
        margin-right: 10%;
 | 
					 | 
				
			||||||
        padding-top: 2em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @media (max-width: 800px) {
 | 
					 | 
				
			||||||
        .main-container {
 | 
					 | 
				
			||||||
            margin: 0em;
 | 
					 | 
				
			||||||
            padding-top: 1em;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    nav {
 | 
					 | 
				
			||||||
        position: relative;
 | 
					 | 
				
			||||||
        font-weight: bold;
 | 
					 | 
				
			||||||
        font-size: 110%;
 | 
					 | 
				
			||||||
        overflow: visible;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        gap: 1.5em;
 | 
					 | 
				
			||||||
        padding: 0em 0em 1.5em 0em;
 | 
					 | 
				
			||||||
        z-index: 2;
 | 
					 | 
				
			||||||
        height: 1.5em;
 | 
					 | 
				
			||||||
        border-radius: 4px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .fade {
 | 
					 | 
				
			||||||
        -webkit-animation: fadeinout 1s linear forwards;
 | 
					 | 
				
			||||||
        animation: fadeinout 1s linear forwards;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @-webkit-keyframes fadeinout {
 | 
					 | 
				
			||||||
        0% { opacity: 0; }
 | 
					 | 
				
			||||||
        100% { opacity: 1; }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @keyframes fadeinout {
 | 
					 | 
				
			||||||
        0% { opacity: 0; }
 | 
					 | 
				
			||||||
        100% { opacity: 1; }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -2,4 +2,6 @@
 | 
				
			|||||||
    import Main from '../main.svelte';
 | 
					    import Main from '../main.svelte';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Main></Main>
 | 
					<div>
 | 
				
			||||||
 | 
					    <Main></Main>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -1,149 +1,95 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					    import { toasts } from 'svelte-toasts';
 | 
				
			||||||
    import Card from '$lib/components/Cards/Card.svelte';
 | 
					    import Card from '$lib/components/Cards/Card.svelte';
 | 
				
			||||||
    import { Toast, ToastType } from '$lib/toast';
 | 
					 | 
				
			||||||
    import { addToast } from '$lib/stores';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import { page } from '$app/stores';
 | 
					    import { page } from '$app/state';
 | 
				
			||||||
    const sent = $page.url.searchParams.get('sent');
 | 
					    const sent = page.url.searchParams.get('sent');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (sent == 'true') {
 | 
					    if (sent == 'true') {
 | 
				
			||||||
        addToast(
 | 
					        toasts.add({
 | 
				
			||||||
            new Toast(
 | 
					            title: 'Message sent!',
 | 
				
			||||||
                'Thank you! Your E-Mail has been sent. I will reply as soon as possible!',
 | 
					            description: 'Thank you for contacting me.',
 | 
				
			||||||
                ToastType.Success,
 | 
					            type: 'success',
 | 
				
			||||||
                true,
 | 
					            duration: 4000,
 | 
				
			||||||
                5000
 | 
					            placement: 'bottom-center'
 | 
				
			||||||
            )
 | 
					        });
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // Can't use else otherwise the warning will display on load
 | 
					
 | 
				
			||||||
    if (sent == 'false') {
 | 
					    if (sent == 'false') {
 | 
				
			||||||
        addToast(
 | 
					        toasts.add({
 | 
				
			||||||
    new Toast(
 | 
					            title: 'Message not sent!',
 | 
				
			||||||
        'Sorry, your E-Mail could not be sent... Please try again later!',
 | 
					            description: 'Please try again later.',
 | 
				
			||||||
        ToastType.Error,
 | 
					            type: 'error',
 | 
				
			||||||
        true,
 | 
					            duration: 4000,
 | 
				
			||||||
        5000
 | 
					            placement: 'bottom-center'
 | 
				
			||||||
    )
 | 
					        });
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Card>
 | 
					<Card headerLeft="Contact Me">
 | 
				
			||||||
    <div slot="header">
 | 
					    <!-- Contact Form -->
 | 
				
			||||||
        <h2>Contact</h2>
 | 
					     <form class="w-full max-w-3xl mx-auto flex flex-col gap-4 text-lg" action="https://api.staticforms.xyz/submit" method="post">
 | 
				
			||||||
    </div>
 | 
					        <div class="hidden">
 | 
				
			||||||
    <div slot="content">
 | 
					 | 
				
			||||||
        <form action="https://api.staticforms.xyz/submit" method="post" class="contact-form">
 | 
					 | 
				
			||||||
            <input type="hidden" name="accessKey" value="fbb5ec04-506b-448a-a445-a2e47579a966">
 | 
					            <input type="hidden" name="accessKey" value="fbb5ec04-506b-448a-a445-a2e47579a966">
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Form Items-->
 | 
					 | 
				
			||||||
            <div class="input-group">
 | 
					 | 
				
			||||||
                <input type="text" id="name" name="name" required placeholder="Your Name" />
 | 
					 | 
				
			||||||
                <input type="email" id="email" name="email" required placeholder="Your Email" />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="input-group">
 | 
					 | 
				
			||||||
                <input type="text" name="subject" placeholder="Subject" required>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="input-group">
 | 
					 | 
				
			||||||
                <textarea id="message" name="message" rows="4" required placeholder="Your Message"></textarea>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            <!-- Hidden Attributes-->
 | 
					 | 
				
			||||||
            <input type="hidden" name="replyTo" value="@">
 | 
					            <input type="hidden" name="replyTo" value="@">
 | 
				
			||||||
            <input type="text" name="honeypot" style="display: none;">
 | 
					            <input type="text" name="honeypot" style="display: none;">
 | 
				
			||||||
            <input type="hidden" name="redirectTo" value="https://luke-else.co.uk/contact?sent=true">
 | 
					            <input type="hidden" name="redirectTo" value="https://luke-else.co.uk/contact?sent=true">
 | 
				
			||||||
            
 | 
					        </div>
 | 
				
			||||||
            <!-- reCAPTCHA integration -->
 | 
					        <div class="flex flex-col md:flex-row gap-3">
 | 
				
			||||||
            <div class="input-group">
 | 
					            <div class="flex-1">
 | 
				
			||||||
                <div class="g-recaptcha" data-sitekey="6LfjQAwrAAAAAIF57u8Wt4w5L5vBEWi5DfXXBuGy"></div>
 | 
					                <label class="block text-xs font-medium mb-1" for="name">Name</label>
 | 
				
			||||||
                <script src="https://www.google.com/recaptcha/api.js" async defer></script>
 | 
					                <input
 | 
				
			||||||
 | 
					                    id="name"
 | 
				
			||||||
 | 
					                    name="name"
 | 
				
			||||||
 | 
					                    type="text"
 | 
				
			||||||
 | 
					                    class="w-full rounded-lg border border-gray-400 px-3 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-600 transition placeholder-gray-400"
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
 | 
					                    placeholder="Your name"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="flex-1">
 | 
				
			||||||
            <div class="input-group">
 | 
					                <label class="block text-xs font-medium mb-1" for="email">Email</label>
 | 
				
			||||||
                <button type="submit" class="submit-button">Send Message</button>
 | 
					                <input
 | 
				
			||||||
 | 
					                    id="email"
 | 
				
			||||||
 | 
					                    name="email"
 | 
				
			||||||
 | 
					                    type="email"
 | 
				
			||||||
 | 
					                    class="w-full rounded-lg border border-gray-400 px-3 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-600 transition placeholder-gray-400"
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
 | 
					                    placeholder="you@email.com"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </form>
 | 
					            <div class="flex-1">
 | 
				
			||||||
    </div>
 | 
					                <label class="block text-xs font-medium mb-1" for="subject">Subject</label>
 | 
				
			||||||
    <div slot="footer">
 | 
					                <input
 | 
				
			||||||
        <a href="/Luke Else - CV.pdf" target="_blank" rel="noopener noreferrer">Curriculum Vitae</a>
 | 
					                    id="subject"
 | 
				
			||||||
        <a href="mailto:contact@luke-else.co.uk">E-Mail</a>
 | 
					                    name="subject"
 | 
				
			||||||
    </div>
 | 
					                    type="text"
 | 
				
			||||||
 | 
					                    class="w-full rounded-lg border border-gray-400 px-3 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-600 transition placeholder-gray-400"
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
 | 
					                    placeholder="Subject"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <label class="block text-xs font-medium mb-1" for="message">Message</label>
 | 
				
			||||||
 | 
					            <textarea
 | 
				
			||||||
 | 
					                id="message"
 | 
				
			||||||
 | 
					                name="message"
 | 
				
			||||||
 | 
					                class="w-full rounded-lg border border-gray-400 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-600 transition min-h-[80px] placeholder-gray-400"
 | 
				
			||||||
 | 
					                required
 | 
				
			||||||
 | 
					                placeholder="Your message"
 | 
				
			||||||
 | 
					            ></textarea>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <!-- reCAPTCHA integration -->
 | 
				
			||||||
 | 
					        <div class="">
 | 
				
			||||||
 | 
					            <div class="g-recaptcha" data-sitekey="6LfjQAwrAAAAAIF57u8Wt4w5L5vBEWi5DfXXBuGy"></div>
 | 
				
			||||||
 | 
					            <script src="https://www.google.com/recaptcha/api.js" async defer></script>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					            type="submit"
 | 
				
			||||||
 | 
					            class="self-end bg-blue-600 hover:bg-blue-700 text-white font-semibold py-1.5 px-8 rounded-lg transition"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            Send Message
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
</Card>
 | 
					</Card>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Contact form styling */
 | 
					 | 
				
			||||||
    .contact-form {
 | 
					 | 
				
			||||||
        background: none;
 | 
					 | 
				
			||||||
        padding: 1rem;
 | 
					 | 
				
			||||||
        width: 80%;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        flex-direction: column;
 | 
					 | 
				
			||||||
        gap: 1rem;
 | 
					 | 
				
			||||||
        transition: all 0.3s ease;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Input groups */
 | 
					 | 
				
			||||||
    .input-group {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        flex-direction: row;
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        gap: 1rem;
 | 
					 | 
				
			||||||
        flex-wrap: wrap;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Input fields and textarea */
 | 
					 | 
				
			||||||
    .contact-form input,
 | 
					 | 
				
			||||||
    .contact-form textarea {
 | 
					 | 
				
			||||||
        padding: 0.8rem 1rem;
 | 
					 | 
				
			||||||
        border: 1px solid var(--fg);
 | 
					 | 
				
			||||||
        border-radius: 0.5rem;
 | 
					 | 
				
			||||||
        background: var(--input);
 | 
					 | 
				
			||||||
        color: var(--fg);
 | 
					 | 
				
			||||||
        font-size: 1rem;
 | 
					 | 
				
			||||||
        transition: border-color 0.3s ease, background 0.3s ease;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .contact-form button {
 | 
					 | 
				
			||||||
        border: 1px solid var(--fg);
 | 
					 | 
				
			||||||
        width: 60%;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .contact-form textarea {
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        min-width: none;
 | 
					 | 
				
			||||||
        resize: vertical;
 | 
					 | 
				
			||||||
        min-height: fit-content;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .contact-form input:focus,
 | 
					 | 
				
			||||||
    .contact-form textarea:focus {
 | 
					 | 
				
			||||||
        border-color: var(--glow);
 | 
					 | 
				
			||||||
        outline: none;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Submit button */
 | 
					 | 
				
			||||||
    .submit-button {
 | 
					 | 
				
			||||||
        padding: 0.8rem 1rem;
 | 
					 | 
				
			||||||
        background: var(--accent);
 | 
					 | 
				
			||||||
        color: var(--fg);
 | 
					 | 
				
			||||||
        border: none;
 | 
					 | 
				
			||||||
        border-radius: 0.5rem;
 | 
					 | 
				
			||||||
        font-size: 1rem;
 | 
					 | 
				
			||||||
        cursor: pointer;
 | 
					 | 
				
			||||||
        transition: background 0.3s ease;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .submit-button:hover {
 | 
					 | 
				
			||||||
        background: var(--link);
 | 
					 | 
				
			||||||
        color: var(--input);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .g-recaptcha {
 | 
					 | 
				
			||||||
        width: fit-content;
 | 
					 | 
				
			||||||
        overflow: hidden;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,64 +1,46 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
    import { onMount } from "svelte";
 | 
					    import { loadRepos, repos } from '$lib/stores';    import { onMount } from 'svelte';
 | 
				
			||||||
    import { Toast, ToastType } from "$lib/toast";
 | 
					    import { timeSince, checkImage, IMAGE_URL_SUFFIX } from '$lib/api/git';
 | 
				
			||||||
    import { repos, loadRepos, addToast } from "$lib/stores";
 | 
					
 | 
				
			||||||
    import { timeSince, checkImage, IMAGE_URL_SUFFIX } from "$lib/api/git";
 | 
					    import FlexGallery from '$lib/components/FlexGallery.svelte';
 | 
				
			||||||
    import Card from "$lib/components/Cards/Card.svelte";
 | 
					    import Card from '$lib/components/Cards/Card.svelte';
 | 
				
			||||||
    import SlidingCard from "$lib/components/Cards/SlidingCard.svelte";
 | 
					
 | 
				
			||||||
    import Loading from "$lib/components/Loading.svelte";
 | 
					    let repoImages: Record<string, string | null> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // When repos load, check for images
 | 
				
			||||||
 | 
					    $: if ($repos.length) {
 | 
				
			||||||
 | 
					        (async () => {
 | 
				
			||||||
 | 
					            for (const repo of $repos) {
 | 
				
			||||||
 | 
					                if (repoImages[repo.name] === undefined) {
 | 
				
			||||||
 | 
					                    const url = repo.html_url + IMAGE_URL_SUFFIX;
 | 
				
			||||||
 | 
					                    repoImages[repo.name] = (await checkImage(repo)) ? url : null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onMount(loadRepos);
 | 
					    onMount(loadRepos);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<h1>My Projects</h1>
 | 
					<FlexGallery>
 | 
				
			||||||
<p>This here is a list of my most recently worked on projects. Note this does not show any private repositories. For more in depth information <a href="https://git.luke-else.co.uk">Click Here</a>.</p>
 | 
					    {#each $repos as repo}
 | 
				
			||||||
 | 
					        <Card
 | 
				
			||||||
<div class="container">
 | 
					            headerLeft={repo.name}
 | 
				
			||||||
    {#if $repos.length > 0}
 | 
					            headerRight={repo.language}
 | 
				
			||||||
        <div style="display: none;">{addToast(new Toast("See a snapshot of my latest work.", ToastType.Info, true, 8_000))}</div>
 | 
					            footer={timeSince(repo.updated_at)}
 | 
				
			||||||
        <div class="cards">
 | 
					            containerStyle="group relative flex-1 min-w-[250px] max-w-full md:min-w-[33%] opacity-100 hover:opacity-100 hover:scale-[105%] md:opacity-70 transition-all duration-300 overflow-hidden"
 | 
				
			||||||
            {#each $repos as repo}
 | 
					        >
 | 
				
			||||||
                {#await checkImage(repo)}
 | 
					            <div class="relative z-0">
 | 
				
			||||||
                    <Loading />
 | 
					                {repo.description}
 | 
				
			||||||
                {:then hasImage}
 | 
					            </div>
 | 
				
			||||||
                    {#if hasImage}
 | 
					            {#if repoImages[repo.name]}
 | 
				
			||||||
                        <SlidingCard>
 | 
					                <!-- svelte-ignore a11y_img_redundant_alt -->
 | 
				
			||||||
                            <div slot="header">
 | 
					                <img
 | 
				
			||||||
                                <h2>{repo.name}</h2>
 | 
					                    src={repoImages[repo.name]}
 | 
				
			||||||
                                {repo.language}
 | 
					                    alt="repo image"
 | 
				
			||||||
                            </div>
 | 
					                    class="absolute left-0 bottom-0 h-full w-full object-cover rounded-2xl transition-transform duration-500 translate-y-full group-hover:translate-y-0 z-10 pointer-events-none"
 | 
				
			||||||
                            <div slot="content">
 | 
					                />
 | 
				
			||||||
                                <p class="not-required">{@html repo.description}</p>
 | 
					            {/if}
 | 
				
			||||||
                            </div>
 | 
					        </Card>
 | 
				
			||||||
                            <div slot="sliding-content">
 | 
					    {/each}
 | 
				
			||||||
                                <img width="100%" src="{repo.html_url}{IMAGE_URL_SUFFIX}" alt="{repo.name}" />
 | 
					</FlexGallery>
 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div slot="footer">
 | 
					 | 
				
			||||||
                                <!-- svelte-ignore a11y-invalid-attribute -->
 | 
					 | 
				
			||||||
                                <a href="{repo.html_url}">{repo.name}</a>
 | 
					 | 
				
			||||||
                                {timeSince(repo.updated_at)}
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </SlidingCard>
 | 
					 | 
				
			||||||
                    {:else}
 | 
					 | 
				
			||||||
                        <Card>
 | 
					 | 
				
			||||||
                            <div slot="header">
 | 
					 | 
				
			||||||
                                <h2>{repo.name}</h2>
 | 
					 | 
				
			||||||
                                {repo.language}
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div slot="content">
 | 
					 | 
				
			||||||
                                <p class="not-required">{@html repo.description}</p>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div slot="footer">
 | 
					 | 
				
			||||||
                                <!-- svelte-ignore a11y-invalid-attribute -->
 | 
					 | 
				
			||||||
                                <a href="{repo.html_url}">{repo.name}</a>
 | 
					 | 
				
			||||||
                                {timeSince(repo.updated_at)}
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </Card>
 | 
					 | 
				
			||||||
                    {/if}
 | 
					 | 
				
			||||||
                {/await}
 | 
					 | 
				
			||||||
            {/each}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    {:else}
 | 
					 | 
				
			||||||
        <Loading />
 | 
					 | 
				
			||||||
    {/if}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
    export let skills: any;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import Card from '$lib/components/Cards/Card.svelte';
 | 
					 | 
				
			||||||
    import Modal from '$lib/components/Modal.svelte';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let showModal: boolean = false;
 | 
					 | 
				
			||||||
    let activeModal: any = null;
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{#each skills as skill}
 | 
					 | 
				
			||||||
    <Card on:click={() => {showModal = true; activeModal = skill}}>
 | 
					 | 
				
			||||||
        <div slot="header">
 | 
					 | 
				
			||||||
            <h2>{skill.skill}</h2>
 | 
					 | 
				
			||||||
            <i class="{skill.logo} logo"></i>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div slot="content">
 | 
					 | 
				
			||||||
            <p class="not-required">{@html skill.usage}</p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div slot="footer">
 | 
					 | 
				
			||||||
            <!-- svelte-ignore a11y-invalid-attribute -->
 | 
					 | 
				
			||||||
            <a href="#">View More</a>
 | 
					 | 
				
			||||||
            <a href="/repos">Repos</a>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </Card>
 | 
					 | 
				
			||||||
{/each}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!--Modal to be displayed on click-->
 | 
					 | 
				
			||||||
{#if activeModal != null}
 | 
					 | 
				
			||||||
    <Modal bind:showModal>
 | 
					 | 
				
			||||||
        <h2 slot="header" class="card-header">
 | 
					 | 
				
			||||||
            {activeModal.skill}
 | 
					 | 
				
			||||||
            <i class="{activeModal.logo} logo"></i>
 | 
					 | 
				
			||||||
        </h2>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
            {activeModal.about}
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="card-footer">
 | 
					 | 
				
			||||||
            <a href="{activeModal.link}" target="_blank" rel="noopener noreferrer">Learn More</a>
 | 
					 | 
				
			||||||
            <a href="/repos">Repos</a>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </Modal>
 | 
					 | 
				
			||||||
{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
    .card-footer {
 | 
					 | 
				
			||||||
        margin-bottom: 1em;
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        gap: 1.5em;
 | 
					 | 
				
			||||||
        justify-content: space-between;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .logo {
 | 
					 | 
				
			||||||
        color: var(--fg);
 | 
					 | 
				
			||||||
        font-size: 3em;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,63 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
    export let timelineData: any;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	import Timeline from '$lib/components/timeline/Timeline.svelte';
 | 
					 | 
				
			||||||
	import TimelineItem from '$lib/components/timeline/TimelineItem.svelte';
 | 
					 | 
				
			||||||
	import TimelineSeparator from '$lib/components/timeline/TimelineSeparator.svelte';
 | 
					 | 
				
			||||||
	import TimelineDot from '$lib/components/timeline/TimelineDot.svelte';
 | 
					 | 
				
			||||||
	import TimelineConnector from '$lib/components/timeline/TimelineConnector.svelte';
 | 
					 | 
				
			||||||
	import TimelineContent from '$lib/components/timeline/TimelineContent.svelte';
 | 
					 | 
				
			||||||
	import TimelineOppositeContent from '$lib/components/timeline/TimelineOppositeContent.svelte';
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<Timeline
 | 
					 | 
				
			||||||
	position="alternate"
 | 
					 | 
				
			||||||
	style={`
 | 
					 | 
				
			||||||
  border-radius: 3%;
 | 
					 | 
				
			||||||
  padding: 1rem;
 | 
					 | 
				
			||||||
  `}
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	{#each timelineData as item}
 | 
					 | 
				
			||||||
		<TimelineItem>
 | 
					 | 
				
			||||||
			<TimelineOppositeContent slot="opposite-content">
 | 
					 | 
				
			||||||
				<p class="oposite-content-title">{item.duration}</p>
 | 
					 | 
				
			||||||
			</TimelineOppositeContent>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<TimelineSeparator>
 | 
					 | 
				
			||||||
				{#if item.duration.includes('Present') || !item.duration.includes('-')}
 | 
					 | 
				
			||||||
					<div class="elementToFadeInAndOut">
 | 
					 | 
				
			||||||
						<TimelineDot style={`background-color: var(--link); border-color: var(--accent);`} />
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				{:else}
 | 
					 | 
				
			||||||
					<TimelineDot style={`background-color: var(--link); border-color: var(--accent);`} />
 | 
					 | 
				
			||||||
				{/if}
 | 
					 | 
				
			||||||
				<TimelineConnector />
 | 
					 | 
				
			||||||
			</TimelineSeparator>
 | 
					 | 
				
			||||||
			<TimelineContent>
 | 
					 | 
				
			||||||
				<h3 class="content-title">{item.title}</h3>
 | 
					 | 
				
			||||||
				<p class="content-description">{@html item.description}</p>
 | 
					 | 
				
			||||||
			</TimelineContent>
 | 
					 | 
				
			||||||
		</TimelineItem>
 | 
					 | 
				
			||||||
	{/each}
 | 
					 | 
				
			||||||
</Timeline>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style>
 | 
					 | 
				
			||||||
	.oposite-content-title {
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		padding: 0;
 | 
					 | 
				
			||||||
		color: var(--accent);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	.content-title {
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		padding: 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.content-description {
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		padding: 0;
 | 
					 | 
				
			||||||
		margin-top: 1rem;
 | 
					 | 
				
			||||||
		color: var(--fg);
 | 
					 | 
				
			||||||
		font-weight: lighter;
 | 
					 | 
				
			||||||
		padding: 0.5rem 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,42 +1,48 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "Luke Else",
 | 
					  "name": "Luke Else",
 | 
				
			||||||
  "job_title": "Software Engineer",
 | 
					  "job_title": "Software Engineer",
 | 
				
			||||||
 | 
					  "location": "Crawley, Sussex <br /> UK",
 | 
				
			||||||
  "profile_photo": "/profile.jpg",
 | 
					  "profile_photo": "/profile.jpg",
 | 
				
			||||||
  "skills": [
 | 
					  "skills": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "skill": "Rust",
 | 
					      "name": "Rust",
 | 
				
			||||||
      "logo": "devicon-rust-plain",
 | 
					      "logo": "devicon-rust-plain",
 | 
				
			||||||
      "link": "https://rust-lang.org",
 | 
					      "link": "https://rust-lang.org",
 | 
				
			||||||
      "usage": "Rust is a memory-safe language with zero-cost abstractions, making it ideal for embedded systems. I used Rust to build a <a href='https://git.luke-else.co.uk/luke-else/esp32_gps_display'>GPS-based speedometer</a> for my car and a <a href='https://git.luke-else.co.uk/luke-else/subnet_calculator'>Subnet Calculator</a> for university studies.",
 | 
					      "usage": "Rust is a memory-safe language with zero-cost abstractions, making it ideal for embedded systems. I used Rust to build a <a href='https://git.luke-else.co.uk/luke-else/esp32_gps_display'>GPS-based speedometer</a> for my car and a <a href='https://git.luke-else.co.uk/luke-else/subnet_calculator'>Subnet Calculator</a> for university studies.",
 | 
				
			||||||
      "about": "Rust combines safety, efficiency, and clean code, making it a powerful choice for reliable software development."
 | 
					      "about": "Rust combines safety, efficiency, and clean code, making it a powerful choice for reliable software development.",
 | 
				
			||||||
 | 
					      "competency": 70
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "skill": "C++",
 | 
					      "name": "C++",
 | 
				
			||||||
      "logo": "devicon-cplusplus-plain",
 | 
					      "logo": "devicon-cplusplus-plain",
 | 
				
			||||||
      "link": "https://cplusplus.com/",
 | 
					      "link": "https://cplusplus.com/",
 | 
				
			||||||
      "usage": "Since joining Thales in 2022, I’ve worked on a distributed simulation system using C++, primarily with <a href='https://www.qt.io'>Qt</a> and <a href='https://github.com/ocornut/imgui'>ImGui</a> to develop customer-facing applications.",
 | 
					      "usage": "Since joining Thales in 2022, I’ve worked on a distributed simulation system using C++, primarily with <a href='https://www.qt.io'>Qt</a> and <a href='https://github.com/ocornut/imgui'>ImGui</a> to develop customer-facing applications.",
 | 
				
			||||||
      "about": "C++ offers high-level abstractions with low-level control, making it essential for performance-critical applications."
 | 
					      "about": "C++ offers high-level abstractions with low-level control, making it essential for performance-critical applications.",
 | 
				
			||||||
 | 
					      "competency": 80
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "skill": "Git",
 | 
					      "name": "Git",
 | 
				
			||||||
      "logo": "devicon-git-plain",
 | 
					      "logo": "devicon-git-plain",
 | 
				
			||||||
      "link": "https://git-scm.com",
 | 
					      "link": "https://git-scm.com",
 | 
				
			||||||
      "usage": "I have extensive experience with Git, including advanced features like <a href='https://www.atlassian.com/git/tutorials/advanced-overview'>branching, merging and hooks</a>. I've also set up self-hosted <a href='https://git.luke-else.co.uk/luke-else/'>Git services</a> with CI/CD automation.",
 | 
					      "usage": "I have extensive experience with Git, including advanced features like <a href='https://www.atlassian.com/git/tutorials/advanced-overview'>branching, merging and hooks</a>. I've also set up self-hosted <a href='https://git.luke-else.co.uk/luke-else/'>Git services</a> with CI/CD automation.",
 | 
				
			||||||
      "about": "Git is an essential tool for version control, enabling efficient collaboration and streamlined code management."
 | 
					      "about": "Git is an essential tool for version control, enabling efficient collaboration and streamlined code management.",
 | 
				
			||||||
 | 
					      "competency": 80
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "skill": "Docker",
 | 
					      "name": "Docker",
 | 
				
			||||||
      "logo": "devicon-docker-plain",
 | 
					      "logo": "devicon-docker-plain",
 | 
				
			||||||
      "link": "https://docker.com",
 | 
					      "link": "https://docker.com",
 | 
				
			||||||
      "usage": "I use Docker and Docker Compose for containerized deployments, including hosting <a href='https://git.luke-else.co.uk/luke-else/server'>home-lab services</a> such as this <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>website</a> and remote Git repositories.",
 | 
					      "usage": "I use Docker and Docker Compose for containerized deployments, including hosting <a href='https://git.luke-else.co.uk/luke-else/server'>home-lab services</a> such as this <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>website</a> and remote Git repositories.",
 | 
				
			||||||
      "about": "Docker simplifies deployment by packaging applications in lightweight containers, ensuring consistency across environments."
 | 
					      "about": "Docker simplifies deployment by packaging applications in lightweight containers, ensuring consistency across environments.",
 | 
				
			||||||
 | 
					      "competency": 100
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "skill": "Svelte",
 | 
					      "name": "Svelte",
 | 
				
			||||||
      "logo": "devicon-svelte-plain",
 | 
					      "logo": "devicon-svelte-plain",
 | 
				
			||||||
      "link": "https://svelte.dev",
 | 
					      "link": "https://svelte.dev",
 | 
				
			||||||
      "usage": "I built <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>this website</a> using Svelte and plan to explore <a href='https://github.com/tauri-apps/tauri'>Tauri</a> for building desktop apps.",
 | 
					      "usage": "I built <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>this website</a> using Svelte and plan to explore <a href='https://github.com/tauri-apps/tauri'>Tauri</a> for building desktop apps.",
 | 
				
			||||||
      "about": "Svelte compiles to optimized JavaScript, offering a fast, efficient, and maintainable front-end development experience."
 | 
					      "about": "Svelte compiles to optimized JavaScript, offering a fast, efficient, and maintainable front-end development experience.",
 | 
				
			||||||
 | 
					      "competency": 40
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "about": "Hello! I'm an enthusiastic, dedicated software engineer passionate about backend development, networking, and embedded systems. I am currently employed at <a href='https://www.thalesgroup.com/en'>Thales UK</a> and thrive on architecting robust backend solutions, optimizing data transmission, and crafting efficient embedded software. I love tackling complex challenges, collaborating with fellow professionals, and staying up-to-date with tech trends such as my current venture in learning <a href='https://rust-lang.org'>Rust-Lang</a>.",
 | 
					  "about": "Hello! I'm an enthusiastic, dedicated software engineer passionate about backend development, networking, and embedded systems. I am currently employed at <a href='https://www.thalesgroup.com/en'>Thales UK</a> and thrive on architecting robust backend solutions, optimizing data transmission, and crafting efficient embedded software. I love tackling complex challenges, collaborating with fellow professionals, and staying up-to-date with tech trends such as my current venture in learning <a href='https://rust-lang.org'>Rust-Lang</a>.",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #f2f6f7; /* Soft blue-tinted white */
 | 
					 | 
				
			||||||
    --bg-secondary: #d7e1e4; /* Cool grey-blue */
 | 
					 | 
				
			||||||
    --accent: #92a9b0; /* Subtle blue-green */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #5d7075; /* Deep slate blue-green */
 | 
					 | 
				
			||||||
    --fg: #3c4649; /* Rich dark grey */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #e0e6e8; /* Light desaturated blue-grey */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #678d97; /* Muted sea blue */
 | 
					 | 
				
			||||||
    --glow: #b2c4c8; /* Gentle cool glow */
 | 
					 | 
				
			||||||
    --hover: #8fa7af; /* Soft grey-blue hover */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #78a890; /* Balanced green */
 | 
					 | 
				
			||||||
    --red: #e08c96; /* Soft dusty red */
 | 
					 | 
				
			||||||
    --blue: #729da5; /* Medium desaturated blue */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #282c34;
 | 
					 | 
				
			||||||
    --bg-secondary: #3e434b;
 | 
					 | 
				
			||||||
    --accent: #59616d;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #E06C75;
 | 
					 | 
				
			||||||
    --fg: #9eaac0;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #2b3136;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #98C379;
 | 
					 | 
				
			||||||
    --glow: #C678DD;
 | 
					 | 
				
			||||||
    --hover: #56B6C2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #98C379;
 | 
					 | 
				
			||||||
    --red: #E06C75;
 | 
					 | 
				
			||||||
    --blue: #79aec3;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #1e1f2a;
 | 
					 | 
				
			||||||
    --bg-secondary: #3a3f4b;
 | 
					 | 
				
			||||||
    --accent: #777f8d;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #cad1da;
 | 
					 | 
				
			||||||
    --fg: #e4e1db;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #2e3438;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #95add8;
 | 
					 | 
				
			||||||
    --glow: #bcc3ca;
 | 
					 | 
				
			||||||
    --hover: #cdd8e2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #98C379;
 | 
					 | 
				
			||||||
    --red: #E06C75;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #2b2b2b; /* Dark but not too harsh */
 | 
					 | 
				
			||||||
    --bg-secondary: #3c3f41; /* Deep warm grey */
 | 
					 | 
				
			||||||
    --accent: #6897bb; /* Muted but clear blue */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #a9b7c6; /* Softer contrast */
 | 
					 | 
				
			||||||
    --fg: #bbbbbb; /* Light but not pure white */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #414141; /* Dark grey input */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #519aba; /* Soft coding blue */
 | 
					 | 
				
			||||||
    --glow: #4e5d68; /* Subtle bluish glow */
 | 
					 | 
				
			||||||
    --hover: #8c9da8; /* Brighter on hover */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #6a8759; /* Classic Darcula green */
 | 
					 | 
				
			||||||
    --red: #cc6666; /* Softer, warm red */
 | 
					 | 
				
			||||||
    --blue: #6897bb; /* Standard coding blue */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #f5f5f5; /* Slightly deeper light grey for subtle contrast */
 | 
					 | 
				
			||||||
    --bg-secondary: #d9dddf; /* More defined soft grey */
 | 
					 | 
				
			||||||
    --accent: #8ea29b; /* Stronger muted sage green */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #4a5a56; /* Darker desaturated green-grey for better contrast */
 | 
					 | 
				
			||||||
    --fg: #2f3739; /* Richer dark grey for improved readability */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #e4e7e8; /* Slightly deeper soft grey input background */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #5f8480; /* Darker muted teal for contrast */
 | 
					 | 
				
			||||||
    --glow: #b0bdb9; /* More noticeable but soft glow */
 | 
					 | 
				
			||||||
    --hover: #85a29c; /* Stronger pastel hover effect */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #6fa984; /* More vibrant pastel green */
 | 
					 | 
				
			||||||
    --red: #e8858f; /* Slightly deeper pastel red for contrast */
 | 
					 | 
				
			||||||
    --blue: #6fa9a4; /* Same as accent */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #272822; /* Classic Monokai dark */
 | 
					 | 
				
			||||||
    --bg-secondary: #3e3d32; /* Darker olive grey */
 | 
					 | 
				
			||||||
    --accent: #f92672; /* Monokai’s signature pink-red */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #a6e22e; /* Neon green */
 | 
					 | 
				
			||||||
    --fg: #f8f8f2; /* Soft off-white */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #373831; /* Slightly lighter grey-green */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #66d9ef; /* Monokai cyan */
 | 
					 | 
				
			||||||
    --glow: #49483e; /* Muted background glow */
 | 
					 | 
				
			||||||
    --hover: #fd7c95; /* Lighter pink hover */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #a6e22e; /* Bright Monokai green */
 | 
					 | 
				
			||||||
    --red: #f92672; /* Soft pinkish-red */
 | 
					 | 
				
			||||||
    --blue: #66d9ef; /* Bright cyan */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
:root {
 | 
					 | 
				
			||||||
    --bg: #1e1e1e; /* Dark neutral background */
 | 
					 | 
				
			||||||
    --bg-secondary: #252526; /* Slightly lighter for separation */
 | 
					 | 
				
			||||||
    --accent: #569cd6; /* Signature VS Code blue */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --header: #9cdcfe; /* Brighter cyan for contrast */
 | 
					 | 
				
			||||||
    --fg: #d4d4d4; /* Light grey for readability */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --input: #333; /* Dark but still visible */
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    --link: #4fc1ff; /* Brighter blue for hyperlinks */
 | 
					 | 
				
			||||||
    --glow: #2a3f5f; /* Soft deep blue glow */
 | 
					 | 
				
			||||||
    --hover: #7bb8e8; /* Brighter accent on hover */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --green: #4ec9b0; /* Soft green */
 | 
					 | 
				
			||||||
    --red: #d16969; /* Coding red */
 | 
					 | 
				
			||||||
    --blue: #9cdcfe; /* Soft bright cyan */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										19
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  theme: {
 | 
				
			||||||
 | 
					    extend: {
 | 
				
			||||||
 | 
					      colors: {
 | 
				
			||||||
 | 
					        'one-bg':      '#282c34',
 | 
				
			||||||
 | 
					        'one-bg-light': '#3a3f4b',
 | 
				
			||||||
 | 
					        'one-fg':      '#abb2bf',
 | 
				
			||||||
 | 
					        'one-accent':  '#61afef',
 | 
				
			||||||
 | 
					        'one-green':   '#98c379',
 | 
				
			||||||
 | 
					        'one-orange':  '#d19a66',
 | 
				
			||||||
 | 
					        'one-red':     '#e06c75',
 | 
				
			||||||
 | 
					        'one-yellow':  '#e5c07b',
 | 
				
			||||||
 | 
					        'one-purple':  '#c678dd',
 | 
				
			||||||
 | 
					        'one-cyan':    '#56b6c2',
 | 
				
			||||||
 | 
					        'one-comment': '#5c6370',
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,10 @@
 | 
				
			|||||||
import { sveltekit } from '@sveltejs/kit/vite';
 | 
					import { sveltekit } from '@sveltejs/kit/vite';
 | 
				
			||||||
import { defineConfig } from 'vite';
 | 
					import { defineConfig } from 'vite';
 | 
				
			||||||
 | 
					import tailwindcss from '@tailwindcss/vite';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineConfig({
 | 
					export default defineConfig({
 | 
				
			||||||
	plugins: [sveltekit()]
 | 
						plugins: [
 | 
				
			||||||
 | 
							tailwindcss(),
 | 
				
			||||||
 | 
							sveltekit(),
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user