From 73aff9c92b0062c5bb97e86bd80b740283dd7807 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 14:26:22 +0000 Subject: [PATCH 01/17] Updated packages --- extract-figma-files.js | 88 ++++++++++++++++++++++++++++++++++++++++++ index.html | 1 + package-lock.json | 33 ++++++++-------- package.json | 8 ++-- 4 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 extract-figma-files.js diff --git a/extract-figma-files.js b/extract-figma-files.js new file mode 100644 index 0000000..e250e31 --- /dev/null +++ b/extract-figma-files.js @@ -0,0 +1,88 @@ +// Import the 'extract-zip' library +const extract = require('extract-zip'); + +// Import 'path' for resolving paths +const path = require('path'); + +// Import 'fs/promises' for asynchronous file system operations (like copying) +// We add 'node:' prefix as is standard practice for built-in modules. +const fs = require('node:fs/promises'); + +// --- Configuration --- +// This folder is used as a temporary staging area for extracted files +const INTERMEDIATE_FOLDER = path.resolve(__dirname, 'figma_extracted_files'); +// --- End of Configuration --- + +/** + * Extracts a zip file. + * This function is designed to be called by another 'async' function. + * If 'extract' fails, it will throw an error (reject the promise), + * which will be caught by the 'catch' block in our 'main' function. + * + * @param {string} source_file - The absolute path to the zip file. + * @param {string} target_dir - The absolute path to the extraction folder. + * @returns {Promise} A promise that resolves when extraction is complete. + */ +async function extract_zip_archive(source_file, target_dir) { + console.log(`Starting extraction of: ${source_file}`); + await extract(source_file, { dir: target_dir }); + console.log(`✅ Extraction complete. Files are in: ${target_dir}`); +} + +/** + * Recursively copies files from a source directory to a destination. + * + * @param {string} source_dir - The absolute path to the source folder. + * @param {string} destination_dir - The absolute path to the destination folder. + * @returns {Promise} A promise that resolves when copying is complete. + */ +async function copy_files_recursively(source_dir, destination_dir) { + console.log(`Starting recursive copy from: ${source_dir}`); + // fs.cp handles creating the destination directory if it doesn't exist + // and recursively copies all contents. + await fs.cp(source_dir, destination_dir, { recursive: true }); + console.log(`✅ Successfully copied files to: ${destination_dir}`); +} + +/** + * Main orchestration function to run the script. + */ +async function main() { + // --- Guard Clauses (Input Validation) --- + const source_file_arg = process.argv[2]; + const final_destination_arg = './' + + // Check if both arguments are provided + if (!source_file_arg || !final_destination_arg) { + console.error('Error: Missing required arguments.'); + console.log('Usage: node index.js '); + process.exit(1); + } + + // --- Path Resolution --- + const absolute_source = path.resolve(source_file_arg); + const absolute_intermediate = INTERMEDIATE_FOLDER; + const absolute_destination = path.resolve(final_destination_arg); + + // --- Main Execution --- + try { + // Step 1: Extract the zip file to the intermediate folder + await extract_zip_archive(absolute_source, absolute_intermediate); + + // Step 2: Copy files from the intermediate to the final folder + await copy_files_recursively(absolute_intermediate, absolute_destination); + + console.log('✨ All operations completed successfully.'); + + } catch (error) { + // If either 'extract_zip_archive' or 'copy_files_recursively' fails + // (rejects its promise), this 'catch' block will run. + console.error('An error occurred during the process:', error); + process.exit(1); // Exit with a failure code + } + + await fs.rm(INTERMEDIATE_FOLDER, { recursive: true }); +} + +// Run the main function +main(); \ No newline at end of file diff --git a/index.html b/index.html index dcde9d2..fd94c2a 100644 --- a/index.html +++ b/index.html @@ -25,6 +25,7 @@ + diff --git a/package-lock.json b/package-lock.json index 2fe2858..0d15636 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,8 @@ "@types/node": "^20.10.0", "@vitejs/plugin-react-swc": "^3.10.2", "cypress": "^15.3.0", - "vite": "6.3.6" + "extract-zip": "^2.0.1", + "vite": "7.1.11" } }, "node_modules/@babel/runtime": { @@ -5579,25 +5580,25 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5606,14 +5607,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" diff --git a/package.json b/package.json index 6eb98b7..459561d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "dev": "vite", "build": "vite build", "cy:open": "cypress open", - "cy:run": "cypress run" + "cy:run": "cypress run", + "extract:figma": "node extract-figma-files.js" }, "dependencies": { "@radix-ui/react-accordion": "^1.2.3", @@ -56,7 +57,8 @@ "devDependencies": { "@types/node": "^20.10.0", "@vitejs/plugin-react-swc": "^3.10.2", - "vite": "6.3.6", - "cypress": "^15.3.0" + "vite": "7.1.11", + "cypress": "^15.3.0", + "extract-zip": "^2.0.1" } } From 3ad080bc4f11445e8951922e1e0d016bd52a700a Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 14:50:53 +0000 Subject: [PATCH 02/17] Updated package.json --- package-lock.json | 316 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 319 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0d15636..86a19ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@tanstack/react-query": "*", "class-variance-authority": "^0.7.1", "clsx": "*", "cmdk": "^1.1.1", @@ -48,6 +49,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-syntax-highlighter": "*", "recharts": "^2.15.2", "sonner": "^2.0.3", "tailwind-merge": "*", @@ -2448,6 +2450,32 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.7", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.7.tgz", + "integrity": "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.7", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.7.tgz", + "integrity": "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@types/d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", @@ -2518,6 +2546,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/node": { "version": "20.19.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz", @@ -2529,6 +2566,12 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "license": "MIT" + }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", @@ -2550,6 +2593,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -2909,6 +2958,36 @@ "node": ">=8" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ci-info": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", @@ -3069,6 +3148,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -3352,6 +3441,19 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3663,6 +3765,19 @@ "node": ">=6.0.0" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -3734,6 +3849,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/framer-motion": { "version": "12.23.12", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", @@ -3981,6 +4104,51 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, "node_modules/http-signature": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", @@ -4066,6 +4234,40 @@ "node": ">=12" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -4076,6 +4278,16 @@ "node": ">=8" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-installed-globally": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", @@ -4328,6 +4540,20 @@ "loose-envify": "cli.js" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lucide-react": { "version": "0.487.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.487.0.tgz", @@ -4558,6 +4784,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4655,6 +4906,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -4682,6 +4942,16 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-from-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", @@ -4873,6 +5143,26 @@ } } }, + "node_modules/react-syntax-highlighter": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", + "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^5.0.0" + }, + "engines": { + "node": ">= 16.20.2" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -4921,6 +5211,22 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/refractor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", + "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^9.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -5194,6 +5500,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", diff --git a/package.json b/package.json index 459561d..db4778d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "my-portfolio", "version": "0.1.0", + "private": true, "scripts": { "dev": "vite", "build": "vite build", @@ -35,6 +36,7 @@ "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@tanstack/react-query": "*", "class-variance-authority": "^0.7.1", "clsx": "*", "cmdk": "^1.1.1", @@ -49,6 +51,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-syntax-highlighter": "*", "recharts": "^2.15.2", "sonner": "^2.0.3", "tailwind-merge": "*", From 70d289642399b44f07d820b77eb7ef77be6319d0 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 14:55:06 +0000 Subject: [PATCH 03/17] Refactored package.json --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index db4778d..00ae4e2 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,6 @@ "name": "my-portfolio", "version": "0.1.0", "private": true, - "scripts": { - "dev": "vite", - "build": "vite build", - "cy:open": "cypress open", - "cy:run": "cypress run", - "extract:figma": "node extract-figma-files.js" - }, "dependencies": { "@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-alert-dialog": "^1.1.6", @@ -63,5 +56,12 @@ "vite": "7.1.11", "cypress": "^15.3.0", "extract-zip": "^2.0.1" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "cy:open": "cypress open", + "cy:run": "cypress run", + "extract:figma": "node extract-figma-files.js" } } From f9f6ad9d5602f2df6487ea6289c0b2909b5d8896 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 14:55:43 +0000 Subject: [PATCH 04/17] Ignore zips in the root --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dda059f..0ad65c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ .idea/ -node_modules/ \ No newline at end of file +node_modules/ +/*.zip From 770c6f753e398386ddd7fa06983d7a7b0ed5b0f2 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 14:56:22 +0000 Subject: [PATCH 05/17] Adding in a figmaignore file --- .figmaignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .figmaignore diff --git a/.figmaignore b/.figmaignore new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/.figmaignore @@ -0,0 +1 @@ +README.md From b566e6e3dc75234e09a9665f4de36bc38a64ec35 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:02:19 +0000 Subject: [PATCH 06/17] Ignore vite.config.ts --- .figmaignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.figmaignore b/.figmaignore index b43bf86..633a42d 100644 --- a/.figmaignore +++ b/.figmaignore @@ -1 +1,2 @@ README.md +vite.config.ts From 6af4d30733917161106c8035cde509881f5967b3 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:03:15 +0000 Subject: [PATCH 07/17] Adding ignore library --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 00ae4e2..741e373 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,10 @@ "devDependencies": { "@types/node": "^20.10.0", "@vitejs/plugin-react-swc": "^3.10.2", - "vite": "7.1.11", "cypress": "^15.3.0", - "extract-zip": "^2.0.1" + "extract-zip": "^2.0.1", + "ignore": "^7.0.5", + "vite": "7.1.11" }, "scripts": { "dev": "vite", From ff94cd218b378de78f7b7997a1dae3b4e33feb0f Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:03:23 +0000 Subject: [PATCH 08/17] Adding ignore library --- package-lock.json | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86a19ca..35dd69d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "@vitejs/plugin-react-swc": "^3.10.2", "cypress": "^15.3.0", "extract-zip": "^2.0.1", + "ignore": "^7.0.5", "vite": "7.1.11" } }, @@ -2561,7 +2562,6 @@ "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3510,8 +3510,7 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -3558,7 +3557,6 @@ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -4195,6 +4193,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -4846,7 +4854,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4991,7 +4998,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5018,7 +5024,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5901,7 +5906,6 @@ "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", From 012757a41d0b561da080e473b92d5a60430da524 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:06:56 +0000 Subject: [PATCH 09/17] Extract figma with ignore files --- extract-figma-files.js | 141 ++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 24 deletions(-) diff --git a/extract-figma-files.js b/extract-figma-files.js index e250e31..4244b24 100644 --- a/extract-figma-files.js +++ b/extract-figma-files.js @@ -4,24 +4,22 @@ const extract = require('extract-zip'); // Import 'path' for resolving paths const path = require('path'); -// Import 'fs/promises' for asynchronous file system operations (like copying) -// We add 'node:' prefix as is standard practice for built-in modules. +// Import 'fs/promises' for asynchronous file system operations const fs = require('node:fs/promises'); +// --- NEW DEPENDENCY --- +// Import the 'ignore' library to handle .figmaignore rules +// Run: npm install ignore +const { Ignore } = require('ignore'); +// --- END NEW DEPENDENCY --- + // --- Configuration --- -// This folder is used as a temporary staging area for extracted files const INTERMEDIATE_FOLDER = path.resolve(__dirname, 'figma_extracted_files'); // --- End of Configuration --- /** * Extracts a zip file. - * This function is designed to be called by another 'async' function. - * If 'extract' fails, it will throw an error (reject the promise), - * which will be caught by the 'catch' block in our 'main' function. - * - * @param {string} source_file - The absolute path to the zip file. - * @param {string} target_dir - The absolute path to the extraction folder. - * @returns {Promise} A promise that resolves when extraction is complete. + * (This function is unchanged) */ async function extract_zip_archive(source_file, target_dir) { console.log(`Starting extraction of: ${source_file}`); @@ -31,28 +29,79 @@ async function extract_zip_archive(source_file, target_dir) { /** * Recursively copies files from a source directory to a destination. - * - * @param {string} source_dir - The absolute path to the source folder. - * @param {string} destination_dir - The absolute path to the destination folder. - * @returns {Promise} A promise that resolves when copying is complete. + * (This function is unchanged, used as a fast-path if no .figmaignore exists) */ async function copy_files_recursively(source_dir, destination_dir) { console.log(`Starting recursive copy from: ${source_dir}`); - // fs.cp handles creating the destination directory if it doesn't exist - // and recursively copies all contents. await fs.cp(source_dir, destination_dir, { recursive: true }); console.log(`✅ Successfully copied files to: ${destination_dir}`); } +// --- NEW FUNCTION --- +/** + * Recursively copies files, respecting .figmaignore rules. + * + * @param {string} root_source - The absolute path to the root source folder (e.g., INTERMEDIATE_FOLDER). + * @param {string} root_destination - The absolute path to the root destination folder. + * @param {Ignore} ig - An 'ignore' instance pre-loaded with rules. + */ +async function copy_with_ignore(root_source, root_destination, ig) { + console.log('Starting recursive copy with ignore rules...'); + + // We define an internal 'walk' function to handle the recursion + async function walk(current_source) { + // Read all entries (files/dirs) in the current directory + const entries = await fs.readdir(current_source, { withFileTypes: true }); + + for (const entry of entries) { + const source_path = path.join(current_source, entry.name); + + // Get the path *relative* to the root, which is what 'ignore' needs + // e.g., 'src/components/button.js' + const relative_path = path.relative(root_source, source_path); + + // Add a trailing slash for directories to match .gitignore behavior + // e.g., 'node_modules/' + const check_path = entry.isDirectory() ? `${relative_path}/` : relative_path; + + // Check if the 'ignore' instance filters this path + if (ig.ignores(check_path)) { + console.log(`- Ignoring: ${check_path}`); + continue; // Skip this file or directory + } + + // If not ignored, determine its destination path + const destination_path = path.join(root_destination, relative_path); + + if (entry.isDirectory()) { + // If it's a directory, create it in the destination + await fs.mkdir(destination_path, { recursive: true }); + // Recurse into this directory + await walk(source_path); + } else { + // If it's a file, ensure its parent directory exists + await fs.mkdir(path.dirname(destination_path), { recursive: true }); + // Copy the file + await fs.copyFile(source_path, destination_path); + } + } + } + + // Start the recursive walk from the root source directory + await walk(root_source); + console.log(`✅ Successfully copied files (with ignores) to: ${root_destination}`); +} +// --- END NEW FUNCTION --- + /** * Main orchestration function to run the script. + * (This function is MODIFIED) */ async function main() { // --- Guard Clauses (Input Validation) --- const source_file_arg = process.argv[2]; - const final_destination_arg = './' + const final_destination_arg = './' // process.argv[3]; // You had this hardcoded, I left it but you might want process.argv[3] - // Check if both arguments are provided if (!source_file_arg || !final_destination_arg) { console.error('Error: Missing required arguments.'); console.log('Usage: node index.js '); @@ -69,20 +118,64 @@ async function main() { // Step 1: Extract the zip file to the intermediate folder await extract_zip_archive(absolute_source, absolute_intermediate); - // Step 2: Copy files from the intermediate to the final folder - await copy_files_recursively(absolute_intermediate, absolute_destination); + // --- MODIFIED SECTION --- + // Step 2: Check for .figmaignore and decide which copy function to use + const figma_ignore_path = path.join(__dirname, '.figmaignore'); + let ignore_content = null; + + try { + // Try to read the .figmaignore file + ignore_content = await fs.readFile(figma_ignore_path, 'utf8'); + } catch (error) { + // If the error is *not* "file not found", re-throw it. + if (error.code !== 'ENOENT') { + console.error('Error reading .figmaignore file:', error); + throw error; // Propagate error to the main catch block + } + // If file is not found (ENOENT), ignore_content remains null. This is fine. + } + + if (ignore_content) { + // --- Path A: .figmaignore EXISTS --- + console.log('Found .figmaignore. Applying ignore rules...'); + // Create an 'ignore' instance and add the file content + const ig = new Ignore().add(ignore_content); + + // Use our new copy function that respects these rules + await copy_with_ignore(absolute_intermediate, absolute_destination, ig); + } else { + // --- Path B: .figmaignore does NOT exist --- + console.log('No .figmaignore file found. Copying all files...'); + + // Use the original fast-path copy function + await copy_files_recursively(absolute_intermediate, absolute_destination); + } + // --- END MODIFIED SECTION --- console.log('✨ All operations completed successfully.'); } catch (error) { - // If either 'extract_zip_archive' or 'copy_files_recursively' fails - // (rejects its promise), this 'catch' block will run. + // This catch block handles errors from extraction, file I/O, or copying console.error('An error occurred during the process:', error); process.exit(1); // Exit with a failure code } - await fs.rm(INTERMEDIATE_FOLDER, { recursive: true }); + // Step 3: Clean up the intermediate folder (this is unchanged) + // We do this outside the try/catch, or in a 'finally' block, + // but here is fine so it only runs on success. + // Let's move it to *after* the catch block to *always* run. + // --- Correction: Your original code had this *after* the catch, + // which is good, but it should be inside the main() function scope. + // I'm moving it to be *just before* the end of the main() function. + + try { + await fs.rm(INTERMEDIATE_FOLDER, { recursive: true, force: true }); + console.log('🧹 Cleaned up intermediate folder.'); + } catch (rm_error) { + // Don't fail the whole script if cleanup fails, just log it. + console.warn(`Warning: Failed to clean up intermediate folder: ${rm_error.message}`); + } } // Run the main function -main(); \ No newline at end of file +main(); From 4b55aa50704dd321481d74a9335288d1ed050b71 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:09:06 +0000 Subject: [PATCH 10/17] Updated ignore script --- extract-figma-files.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extract-figma-files.js b/extract-figma-files.js index 4244b24..804d591 100644 --- a/extract-figma-files.js +++ b/extract-figma-files.js @@ -11,6 +11,7 @@ const fs = require('node:fs/promises'); // Import the 'ignore' library to handle .figmaignore rules // Run: npm install ignore const { Ignore } = require('ignore'); +const ignore = require("ignore"); // --- END NEW DEPENDENCY --- // --- Configuration --- @@ -139,7 +140,7 @@ async function main() { // --- Path A: .figmaignore EXISTS --- console.log('Found .figmaignore. Applying ignore rules...'); // Create an 'ignore' instance and add the file content - const ig = new Ignore().add(ignore_content); + const ig = ignore().add(ignore_content); // Use our new copy function that respects these rules await copy_with_ignore(absolute_intermediate, absolute_destination, ig); From a48e3e7ddaa0f1fbafe6e37b7e3c39d0d81b56c8 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:29:21 +0000 Subject: [PATCH 11/17] Updates --- .figmaignore | 1 + src/main.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.figmaignore b/.figmaignore index 633a42d..ec60850 100644 --- a/.figmaignore +++ b/.figmaignore @@ -1,2 +1,3 @@ README.md vite.config.ts +index.html diff --git a/src/main.tsx b/src/main.tsx index 39bea25..d96f8d8 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,7 @@ -import {createRoot} from 'react-dom/client'; -import App from './App'; -import './styles/index.css'; -createRoot(document.getElementById('root')!).render(); + import { createRoot } from "react-dom/client"; + import App from "./App.tsx"; + import "./index.css"; + + createRoot(document.getElementById("root")!).render(); + \ No newline at end of file From dc284f72199a1020d9e97dfb20f084c1e18b1008 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 15:38:26 +0000 Subject: [PATCH 12/17] Updates --- src/components/Hero.tsx | 309 +++++++++++++++++------------------ src/components/Projects.tsx | 2 +- src/components/ui/button.tsx | 11 +- 3 files changed, 165 insertions(+), 157 deletions(-) diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 0a8d3b1..df03814 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -1,167 +1,166 @@ -import {Button} from "./ui/button"; -import {Badge} from "./ui/badge"; -import {ArrowDown, Github, Linkedin, Mail} from "lucide-react"; -import {motion} from "motion/react"; +import { Button } from "./ui/button"; +import { Badge } from "./ui/badge"; +import { ArrowDown, Github, Linkedin, Mail } from "lucide-react"; +import { motion } from "motion/react"; import HeroProfileImage from './HeroProfileImage/HeroProfileImage'; interface HeroProps { - data: { - title: string; - availabilityStatus: string; - description: string; - profileImage: string; - socialLinks: { - github: string; - linkedin: string; - }; + data: { + title: string; + availabilityStatus: string; + description: string; + profileImage: string; + socialLinks: { + github: string; + linkedin: string; }; + }; } -export function Hero({data}: HeroProps) { - const containerVariants = { - hidden: {opacity: 0}, - visible: { - opacity: 1, - transition: { - staggerChildren: 0.2, - delayChildren: 0.3 - } - } - }; - - const itemVariants = { - hidden: {opacity: 0, y: 30}, - visible: { - opacity: 1, - y: 0, - transition: {duration: 0.6, ease: "easeOut"} - } - }; - - const imageVariants = { - hidden: {opacity: 0, scale: 0.8, rotate: -5}, - visible: { - opacity: 1, - scale: 1, - rotate: 0, - transition: {duration: 0.8, ease: "easeOut", delay: 0.5} - } - }; - - return ( -
-
-
- - - - {data.availabilityStatus} - -

- {data.title.split(' ').slice(0, 2).join(' ')} - {data.title.split(' ').slice(2).join(' ')} -

-
+export function Hero({ data }: HeroProps) { + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.2, + delayChildren: 0.3 + } + } + }; - - {data.description} - + const itemVariants = { + hidden: { opacity: 0, y: 30 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.6, ease: "easeOut" } + } + }; - - - - + const imageVariants = { + hidden: { opacity: 0, scale: 0.8, rotate: -5 }, + visible: { + opacity: 1, + scale: 1, + rotate: 0, + transition: { duration: 0.8, ease: "easeOut", delay: 0.5 } + } + }; - - - - -
- - - - -
-
- - +
+
+ + + + {data.availabilityStatus} + +

+ {data.title.split(' ').slice(0, 2).join(' ')} + {data.title.split(' ').slice(2).join(' ')} +

+
+ + + {data.description} + + + + + + + + - - - + + -
- ); + + + + + + + + + + + + + + + ); } \ No newline at end of file diff --git a/src/components/Projects.tsx b/src/components/Projects.tsx index 61b5720..0ec4334 100644 --- a/src/components/Projects.tsx +++ b/src/components/Projects.tsx @@ -184,4 +184,4 @@ export function Projects({data}: ProjectsProps) { ); -} +} \ No newline at end of file diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 3f24494..308ad2b 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -34,7 +34,16 @@ const buttonVariants = cva( }, ); -function Button({className, variant, size, asChild = false, ...props}: React.ComponentProps<"button"> & VariantProps & { asChild?: boolean; }) { +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean; + }) { const Comp = asChild ? Slot : "button"; return ( From 4312814ac26f700e0ed651733742a805661badc0 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 16:00:41 +0000 Subject: [PATCH 13/17] Updates --- src/App.tsx | 51 +++++++++++++++++------------------ src/components/Experience.tsx | 23 +++++++++------- src/data/portfolio.ts | 6 ++--- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 49d6e2f..e776c52 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,7 @@ import { Experience } from "./components/Experience"; import { Contact } from "./components/Contact"; import { Button } from "./components/ui/button"; import { Menu, X, Download } from "lucide-react"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { motion } from "motion/react"; import { portfolioData } from "./data/portfolio"; import GitHubCorner from './components/GitHubCorner/GitHubCorner'; @@ -39,15 +39,16 @@ export default function App() { behavior: "smooth" }); } - // Close mobile menu if open + setIsMenuOpen(false); }; // Scroll to top function const scrollToTop = () => { + window.location.hash = ''; window.scrollTo({ top: 0, - behavior: "smooth" + behavior: "smooth", }); setIsMenuOpen(false); }; @@ -56,16 +57,16 @@ export default function App() { const sectionVariants = { hidden: { opacity: 0, - y: 50 + y: 50, }, visible: { opacity: 1, y: 0, transition: { duration: 0.6, - ease: "easeOut" - } - } + ease: "easeOut", + }, + }, }; const staggerVariants = { @@ -74,15 +75,14 @@ export default function App() { opacity: 1, transition: { staggerChildren: 0.1, - delayChildren: 0.2 - } - } + delayChildren: 0.2, + }, + }, }; return ( @@ -152,14 +153,14 @@ export default function App() { animate="visible" variants={staggerVariants} > - {navItems.map((item) => ( + {navItems.map((item, index) => ( scrollToSection(item.target)} className="text-sm hover:text-primary transition-colors text-left cursor-pointer" variants={{ hidden: { opacity: 0, x: -20 }, - visible: { opacity: 1, x: 0 } + visible: { opacity: 1, x: 0 }, }} > {item.label} @@ -168,17 +169,15 @@ export default function App() { - + + + + + ))} + + + + ); +} diff --git a/src/components/BlogPost.tsx b/src/components/BlogPost.tsx new file mode 100644 index 0000000..4d04057 --- /dev/null +++ b/src/components/BlogPost.tsx @@ -0,0 +1,368 @@ +import { Badge } from "./ui/badge"; +import { Button } from "./ui/button"; +import { Calendar, Clock, ArrowLeft, Tag, User, Ghost, Moon, Play, Flame, Wind } from "lucide-react"; +import { motion } from "motion/react"; +import { blogPosts, BlogPost as BlogPostType } from "../data/blog"; +import { ImageWithFallback } from "./figma/ImageWithFallback"; +import { CodeBlock } from "./CodeBlock"; +import { ComparisonTable, ComparisonColumn, ComparisonFeature } from "./ComparisonTable"; +import { useEffect } from "react"; + +interface BlogPostProps { + postId: string; + onBackClick: () => void; +} + +export function BlogPost({ postId, onBackClick }: BlogPostProps) { + const post = blogPosts.find(p => p.id === postId); + + useEffect(() => { + // Scroll to top when post changes + window.scrollTo({ top: 0, behavior: 'smooth' }); + }, [postId]); + + if (!post) { + return ( +
+
+
+

Post Not Found

+ +
+
+
+ ); + } + + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + }); + }; + + // Comparison table data + const getComparisonTableData = (type: string) => { + if (type === 'streaming') { + const columns: ComparisonColumn[] = [ + { + name: 'Ghost Watcher', + icon: , + isHighlighted: true, + price: { currency: 'EUR', amount: 20, period: 'mo' }, + cta: { text: 'Get now', onClick: () => console.log('Get Ghost Watcher') } + }, + { + name: 'Luna plus', + icon: , + price: { currency: 'EUR', amount: 50, period: 'mo' } + }, + { + name: 'PointTV', + icon: , + price: { currency: 'EUR', amount: 25, period: 'mo' } + }, + { + name: 'The Heat', + icon: , + price: { currency: 'EUR', amount: 30, period: 'mo' } + }, + { + name: 'Squeezy', + icon: , + price: { currency: 'EUR', amount: 32, period: 'mo' } + } + ]; + + const features: ComparisonFeature[] = [ + { name: 'Unlimited devices', values: [true, false, false, false, false] }, + { name: '4k streaming', values: [true, false, true, false, true] }, + { name: 'No ads', values: [true, true, true, false, true] }, + { name: 'Offline access', values: [true, true, false, true, false] }, + { name: 'Multiple profiles', values: [true, true, false, true, false] }, + { name: '24/7 support', values: [true, true, true, false, true] } + ]; + + return { columns, features }; + } + return null; + }; + + // Convert markdown-style content to HTML-like structure for display + const renderContent = (content: string) => { + const lines = content.trim().split('\n'); + const elements: JSX.Element[] = []; + let currentParagraph: string[] = []; + let inCodeBlock = false; + let codeBlockLines: string[] = []; + let codeLanguage = ''; + let inTable = false; + let tableLines: string[] = []; + let key = 0; + + const flushParagraph = () => { + if (currentParagraph.length > 0) { + const text = currentParagraph.join(' ').trim(); + if (text) { + // Process inline code (backticks) + const parts = text.split(/(`[^`]+`)/g); + const processedText = parts.map((part, idx) => { + if (part.startsWith('`') && part.endsWith('`')) { + return ( + + {part.slice(1, -1)} + + ); + } + return part; + }); + elements.push(

{processedText}

); + } + currentParagraph = []; + } + }; + + const flushTable = () => { + if (tableLines.length > 0) { + // Parse table: first line is header, second is separator, rest are rows + const headers = tableLines[0].split('|').map(h => h.trim()).filter(h => h); + const rows = tableLines.slice(2).map(row => + row.split('|').map(cell => cell.trim()).filter(cell => cell !== '') + ).filter(row => row.length > 0); + + // Use legacy table format for markdown tables + elements.push( +
+
+ + + + {headers.map((header, index) => ( + + ))} + + + + {rows.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + + ))} + + ))} + +
{header}
{cell}
+
+
+ ); + tableLines = []; + inTable = false; + } + }; + + lines.forEach((line, index) => { + // Special comparison table syntax: [comparison_table:type] + if (line.trim().match(/^\[comparison_table:(\w+)\]$/)) { + flushParagraph(); + flushTable(); + const tableType = line.trim().match(/^\[comparison_table:(\w+)\]$/)?.[1]; + if (tableType) { + const tableData = getComparisonTableData(tableType); + if (tableData) { + elements.push( + + ); + } + } + return; + } + + // Code blocks + if (line.startsWith('```')) { + if (inCodeBlock) { + flushParagraph(); + flushTable(); + elements.push( + + ); + codeBlockLines = []; + codeLanguage = ''; + inCodeBlock = false; + } else { + flushParagraph(); + flushTable(); + inCodeBlock = true; + codeLanguage = line.substring(3).trim(); + } + return; + } + + if (inCodeBlock) { + codeBlockLines.push(line); + return; + } + + // Table detection (markdown tables start with |) + if (line.trim().startsWith('|') && line.trim().endsWith('|')) { + flushParagraph(); + inTable = true; + tableLines.push(line); + return; + } else if (inTable && line.trim() === '') { + flushTable(); + return; + } else if (inTable) { + flushTable(); + } + + // Headings + if (line.startsWith('# ')) { + flushParagraph(); + elements.push(

{line.substring(2)}

); + } else if (line.startsWith('## ')) { + flushParagraph(); + elements.push(

{line.substring(3)}

); + } else if (line.startsWith('### ')) { + flushParagraph(); + elements.push(

{line.substring(4)}

); + } + // Lists + else if (line.match(/^\d+\.\s/)) { + flushParagraph(); + const text = line.replace(/^\d+\.\s/, ''); + elements.push( +
  • + {text} +
  • + ); + } else if (line.startsWith('- ')) { + flushParagraph(); + elements.push( +
  • + {line.substring(2)} +
  • + ); + } + // Empty lines + else if (line.trim() === '') { + flushParagraph(); + } + // Regular text + else { + currentParagraph.push(line); + } + }); + + flushParagraph(); + flushTable(); + return elements; + }; + + return ( + +
    + + + +
    + + {post.category} + +

    {post.title}

    + +
    + + + {post.author} + + + + {formatDate(post.date)} + + + + {post.readTime} + +
    + +
    + {post.tags.map((tag, index) => ( + + + {tag} + + ))} +
    +
    + + + +
    + + + +
    + {renderContent(post.content)} +
    +
    + + + + + +
    + + ); +} diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx new file mode 100644 index 0000000..b5e8c13 --- /dev/null +++ b/src/components/CodeBlock.tsx @@ -0,0 +1,70 @@ +import { useState } from 'react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { Check, Copy } from 'lucide-react'; +import { Button } from './ui/button'; + +interface CodeBlockProps { + code: string; + language?: string; +} + +export function CodeBlock({ code, language = 'typescript' }: CodeBlockProps) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + // Detect if we're in dark mode + const isDark = document.documentElement.classList.contains('dark'); + + return ( +
    +
    + {language} + +
    +
    + + {code} + +
    +
    + ); +} diff --git a/src/components/ComparisonTable.tsx b/src/components/ComparisonTable.tsx new file mode 100644 index 0000000..f802722 --- /dev/null +++ b/src/components/ComparisonTable.tsx @@ -0,0 +1,127 @@ +import { Check, X } from 'lucide-react'; +import { Button } from './ui/button'; + +export interface ComparisonColumn { + name: string; + icon?: React.ReactNode; + isHighlighted?: boolean; + price?: { + currency: string; + amount: number; + period: string; + }; + cta?: { + text: string; + onClick?: () => void; + }; +} + +export interface ComparisonFeature { + name: string; + values: (boolean | string)[]; +} + +export interface ComparisonTableProps { + title?: string; + columns: ComparisonColumn[]; + features: ComparisonFeature[]; +} + +export function ComparisonTable({ title, columns, features }: ComparisonTableProps) { + return ( +
    + {title && ( +

    {title}

    + )} + +
    +
    +
    + {columns.map((column, columnIndex) => ( +
    + {/* Header */} +
    +
    + {column.icon && ( +
    + {column.icon} +
    + )} + + {column.name} + +
    +
    + + {/* Features */} +
    + {features.map((feature, featureIndex) => { + const value = feature.values[columnIndex]; + return ( +
    + {columnIndex === 0 ? ( + // First column shows the feature name +
    + {typeof value === 'boolean' && value && ( + + )} + + {feature.name} + +
    + ) : ( + // Other columns show check/x marks +
    + {typeof value === 'boolean' ? ( + value ? ( + + ) : ( + + ) + ) : ( + {value} + )} +
    + )} +
    + ); + })} +
    + + {/* Pricing */} + {column.price && ( +
    +
    + {column.price.currency} + {column.price.amount} + /{column.price.period} +
    +
    + )} + + {/* CTA Button */} + {column.cta && ( +
    + +
    + )} +
    + ))} +
    +
    +
    +
    + ); +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 308ad2b..1f034f0 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,6 +1,9 @@ import * as React from "react"; import { Slot } from "@radix-ui/react-slot@1.1.2"; -import { cva, type VariantProps } from "class-variance-authority@0.7.1"; +import { + cva, + type VariantProps, +} from "class-variance-authority@0.7.1"; import { cn } from "./utils"; @@ -9,7 +12,8 @@ const buttonVariants = cva( { variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", + default: + "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: @@ -49,10 +53,12 @@ function Button({ return ( ); } -export { Button, buttonVariants }; +export { Button, buttonVariants }; \ No newline at end of file diff --git a/src/data/blog.ts b/src/data/blog.ts new file mode 100644 index 0000000..9052ccc --- /dev/null +++ b/src/data/blog.ts @@ -0,0 +1,555 @@ +export interface BlogPost { + id: string; + title: string; + excerpt: string; + content: string; + date: string; + author: string; + category: string; + tags: string[]; + readTime: string; + image: string; +} + +export const blogPosts: BlogPost[] = [ + { + id: "streaming-services-comparison-2025", + title: "Streaming Services Comparison: Which One is Right for You?", + excerpt: "A comprehensive comparison of top streaming services in 2025, helping you choose the best option for your entertainment needs.", + content: ` +# Streaming Services Comparison: Which One is Right for You? + +The streaming landscape has become increasingly crowded in 2025. With so many options available, choosing the right service can be overwhelming. Let's break down the key differences between the major players. + +## How We Compare + +[comparison_table:streaming] + +## Key Features Explained + +### Unlimited Devices +Some services restrict how many devices can be logged in simultaneously. Ghost Watcher stands out by offering unlimited device support, perfect for families or those who like to share their account across multiple locations. + +### 4K Streaming +High-quality 4K streaming is becoming standard, but not all services offer it at every price point. PointTV and Squeezy provide 4K streaming, while others require premium tiers or don't support it at all. + +### Ad-Free Experience +Nothing interrupts immersion like ads. Most premium services like Luna plus, PointTV, and Squeezy offer ad-free viewing, though some basic tiers still include advertisements. + +### Offline Access +Download content for offline viewing is perfect for travel or areas with poor connectivity. Luna plus and The Heat excel in this area, allowing you to download your favorite shows and movies. + +### Multiple Profiles +Different family members want different recommendations and watch histories. Most services now support multiple profiles, though The Heat notably lacks this feature. + +### 24/7 Customer Support +When something goes wrong, responsive support matters. Luna plus, PointTV, and Squeezy all offer round-the-clock support to help resolve issues quickly. + +## Pricing Breakdown + +At **EUR 20/mo**, Ghost Watcher offers the most comprehensive feature set at the lowest price point, making it an exceptional value proposition. While competitors range from EUR 25 to EUR 50 per month, none offer the complete package that Ghost Watcher provides. + +## Making Your Choice + +Consider these factors when choosing: + +1. **Budget**: Ghost Watcher offers premium features at the lowest price +2. **Device flexibility**: If you need unlimited devices, Ghost Watcher is your only option +3. **Content quality**: For 4K streaming, consider PointTV or Squeezy +4. **Offline viewing**: Luna plus and The Heat excel here +5. **Support needs**: Most premium services offer 24/7 support + +## Conclusion + +While each streaming service has its strengths, Ghost Watcher emerges as the most well-rounded option for most users. Its combination of unlimited devices, competitive pricing, and comprehensive feature set makes it the top choice for 2025. + +Ready to start streaming? Choose the service that best fits your needs and start enjoying unlimited entertainment today. + `, + date: "2025-01-20", + author: "Craig Wayne", + category: "Reviews", + tags: ["Streaming", "Comparison", "Entertainment", "Reviews"], + readTime: "6 min read", + image: "https://images.unsplash.com/photo-1522869635100-9f4c5e86aa37?w=800&q=80" + }, + { + id: "modern-react-patterns-2025", + title: "Modern React Patterns in 2025", + excerpt: "Exploring the latest React patterns and best practices that are shaping the future of frontend development.", + content: ` +# Modern React Patterns in 2025 + +React has evolved significantly over the years, and 2025 brings even more powerful patterns and practices. In this post, we'll explore the most impactful patterns that are changing how we build web applications. + +## Server Components: The Game Changer + +React Server Components have revolutionized how we think about data fetching and rendering. By allowing components to run on the server, we can: + +- Reduce bundle sizes significantly +- Improve initial page load times +- Access backend resources directly +- Maintain better security for sensitive operations + +Here's a simple example of a Server Component: + +\`\`\`tsx +// app/users/page.tsx +async function UsersPage() { + const users = await fetch('https://api.example.com/users'); + const data = await users.json(); + + return ( +
    + {data.map(user => ( + + ))} +
    + ); +} +\`\`\` + +## The Rise of Suspense Everywhere + +Suspense is no longer just for lazy loading. It's become the standard way to handle async operations: + +\`\`\`tsx +import { Suspense } from 'react'; + +function App() { + return ( + }> + + + ); +} +\`\`\` + +## Modern State Management + +The landscape has shifted from complex state management libraries to simpler, more composable solutions. Here's a comparison of modern state management approaches: + +| State Type | Solution | Use Case | Bundle Impact | +| --- | --- | --- | --- | +| Server State | TanStack Query | API data, caching | ~15KB | +| Client State | useState + Context | UI state, theme | Built-in | +| Form State | React Hook Form | Complex forms, validation | ~9KB | +| URL State | Next.js searchParams | Filters, pagination | Built-in | +| Global State | Zustand | Shared app state | ~3KB | + +Example using React Query: + +\`\`\`tsx +import { useQuery } from '@tanstack/react-query'; + +function UserProfile({ userId }: { userId: string }) { + const { data, isLoading } = useQuery({ + queryKey: ['user', userId], + queryFn: () => fetchUser(userId) + }); + + if (isLoading) return ; + return ; +} +\`\`\` + +## Composition Over Configuration + +Modern React favors composition patterns over complex configuration objects. This makes code more readable and maintainable. + +## Conclusion + +These patterns represent the evolution of React development. As we move forward, expect even more innovations that make building web applications faster, more efficient, and more enjoyable. + `, + date: "2025-01-15", + author: "Craig Wayne", + category: "React", + tags: ["React", "TypeScript", "Web Development", "Best Practices"], + readTime: "5 min read", + image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&q=80" + }, + { + id: "typescript-advanced-types", + title: "Advanced TypeScript: Type-Safe APIs", + excerpt: "Deep dive into advanced TypeScript features that help build rock-solid, type-safe APIs and applications.", + content: ` +# Advanced TypeScript: Type-Safe APIs + +TypeScript has become the de facto standard for building robust JavaScript applications. Let's explore advanced type patterns that take your code to the next level. + +## Discriminated Unions + +One of TypeScript's most powerful features for modeling complex domains: + +\`\`\`typescript +type Result = + | { success: true; data: T } + | { success: false; error: E }; + +function handleResult(result: Result) { + if (result.success) { + console.log(result.data); // TypeScript knows this is T + } else { + console.error(result.error); // TypeScript knows this is E + } +} +\`\`\` + +## Template Literal Types + +Create powerful string type constraints for building type-safe APIs: + +\`\`\`typescript +type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; +type APIRoute = \\\`/api/\\\${string}\\\`; +type APICall = \\\`\\\${HTTPMethod} \\\${APIRoute}\\\`; + +// Valid +const call1: APICall = 'GET /api/users'; +const call2: APICall = 'POST /api/products'; + +// TypeScript error +const call3: APICall = 'INVALID /api/users'; +\`\`\` + +## Branded Types + +Prevent mixing up similar primitive types to catch errors at compile time: + +\`\`\`typescript +type UserId = string & { readonly brand: unique symbol }; +type ProductId = string & { readonly brand: unique symbol }; + +function getUser(id: UserId) { /* ... */ } +function getProduct(id: ProductId) { /* ... */ } + +const userId = '123' as UserId; +const productId = '456' as ProductId; + +getUser(userId); // ✓ Valid +getUser(productId); // ✗ TypeScript error! +\`\`\` + +## Conditional Types + +Build types that adapt based on input: + +\`\`\`typescript +type AsyncReturnType = + T extends (...args: any[]) => Promise + ? U + : never; + +async function fetchUser() { + return { id: 1, name: 'John' }; +} + +type User = AsyncReturnType; +// User is { id: number; name: string; } +\`\`\` + +## Best Practices + +1. Start with the strictest types possible +2. Use \`unknown\` instead of \`any\` for safer type handling +3. Leverage type inference to reduce boilerplate +4. Create reusable utility types for common patterns +5. Document complex types with JSDoc comments + +## Conclusion + +Mastering these advanced TypeScript features will help you catch bugs at compile time and create more maintainable codebases. + `, + date: "2025-01-10", + author: "Craig Wayne", + category: "TypeScript", + tags: ["TypeScript", "Type Safety", "Advanced", "Programming"], + readTime: "7 min read", + image: "https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=800&q=80" + }, + { + id: "web-performance-optimization", + title: "Web Performance Optimization in 2025", + excerpt: "Essential techniques and strategies for building lightning-fast web applications that users love.", + content: ` +# Web Performance Optimization in 2025 + +Performance is not just a feature—it's a fundamental requirement. Here's how to build fast web applications in 2025. + +## Core Web Vitals + +Understanding and optimizing the metrics that matter: + +### Largest Contentful Paint (LCP) +- Target: Under 2.5 seconds +- Optimize images and fonts +- Implement lazy loading strategically + +### First Input Delay (FID) +- Target: Under 100ms +- Break up long tasks +- Use web workers for heavy computation + +### Cumulative Layout Shift (CLS) +- Target: Under 0.1 +- Reserve space for dynamic content +- Use aspect ratios for images + +## Image Optimization + +Modern image formats and techniques: + +1. **WebP and AVIF**: Use next-gen formats +2. **Responsive Images**: Serve appropriate sizes +3. **Lazy Loading**: Load images as needed +4. **CDN**: Leverage edge networks + +Example of responsive images: + +\`\`\`tsx +Optimized image +\`\`\` + +## Code Splitting Strategies + +Split your code at route and component boundaries: + +\`\`\`tsx +import { lazy, Suspense } from 'react'; + +// Route-based splitting +const BlogPage = lazy(() => import('./pages/Blog')); +const Dashboard = lazy(() => import('./pages/Dashboard')); + +// Component-based splitting +const HeavyChart = lazy(() => import('./components/Chart')); + +function App() { + return ( + }> + + } /> + } /> + + + ); +} +\`\`\` + +## Caching Strategies + +1. **HTTP Caching**: Leverage browser cache +2. **Service Workers**: Offline-first approach +3. **CDN Caching**: Edge-side caching +4. **API Caching**: Reduce backend calls + +## Monitoring + +Use tools like: +- Lighthouse +- WebPageTest +- Real User Monitoring (RUM) +- Synthetic Monitoring + +## Conclusion + +Performance optimization is an ongoing process. Measure, optimize, and monitor continuously to deliver the best user experience. + `, + date: "2025-01-05", + author: "Craig Wayne", + category: "Performance", + tags: ["Performance", "Web Vitals", "Optimization", "Speed"], + readTime: "6 min read", + image: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&q=80" + }, + { + id: "css-container-queries", + title: "CSS Container Queries: The Future of Responsive Design", + excerpt: "How container queries are revolutionizing responsive design and replacing many media query use cases.", + content: ` +# CSS Container Queries: The Future of Responsive Design + +Container queries have finally arrived, and they're changing how we think about responsive design. + +## What Are Container Queries? + +Unlike media queries that respond to viewport size, container queries respond to the size of a containing element. This enables truly component-based responsive design. + +## Why Container Queries Matter + +1. **Component-Level Responsiveness**: Components adapt to their container, not the viewport +2. **Reusability**: Components work in any context +3. **Maintainability**: Responsive logic stays with the component +4. **Flexibility**: Build truly modular design systems + +## Real-World Use Cases + +### Card Components +Cards that adapt based on available space, switching between horizontal and vertical layouts automatically. + +### Navigation Patterns +Navigation that changes based on container width, not viewport width. + +### Grid Layouts +Grids that respond to their container size, enabling perfect nesting. + +## Best Practices + +1. Define container contexts explicitly +2. Use logical properties (inline-size, block-size) +3. Combine with CSS Grid for maximum flexibility +4. Test across different container sizes + +## Browser Support + +Container queries are now supported in all modern browsers. Use feature detection for older browsers to provide fallbacks. + +## Conclusion + +Container queries represent a fundamental shift in responsive design. Start using them today to build more flexible, maintainable components. + `, + date: "2024-12-28", + author: "Craig Wayne", + category: "CSS", + tags: ["CSS", "Responsive Design", "Container Queries", "Modern CSS"], + readTime: "5 min read", + image: "https://images.unsplash.com/photo-1507721999472-8ed4421c4af2?w=800&q=80" + }, + { + id: "ai-assisted-development", + title: "AI-Assisted Development: Tools and Best Practices", + excerpt: "Leveraging AI tools to enhance productivity while maintaining code quality and understanding.", + content: ` +# AI-Assisted Development: Tools and Best Practices + +AI coding assistants have become indispensable tools for modern developers. Here's how to use them effectively. + +## The AI Developer Toolkit + +### Code Completion +- GitHub Copilot +- TabNine +- Amazon CodeWhisperer + +### Code Review +- DeepCode +- CodeGuru +- SonarQube AI + +### Documentation +- GPT-based doc generators +- Automated README creation +- API documentation tools + +## Best Practices + +### 1. Verify Everything +AI suggestions should be reviewed and understood, not blindly accepted. + +### 2. Use AI for Boilerplate +Let AI handle repetitive code while you focus on architecture and business logic. + +### 3. Learn from Suggestions +AI suggestions can teach you new patterns and approaches. + +### 4. Context is Key +Provide clear comments and context for better AI suggestions. + +## Common Pitfalls + +1. **Over-reliance**: Don't let AI replace your problem-solving skills +2. **Security**: Review AI-generated code for security vulnerabilities +3. **Licensing**: Be aware of code licensing implications +4. **Testing**: Always test AI-generated code thoroughly + +## Maximizing Productivity + +AI can help generate comprehensive test suites, boilerplate code, and documentation. Use it to accelerate development while maintaining quality standards. + +## The Future + +AI will continue to evolve, but the core skills of problem-solving, architecture, and critical thinking remain essential. + +## Conclusion + +AI is a powerful tool in the developer's arsenal. Use it wisely to enhance your productivity while continuing to grow your skills and understanding. + `, + date: "2024-12-20", + author: "Craig Wayne", + category: "AI", + tags: ["AI", "Productivity", "Tools", "Development"], + readTime: "6 min read", + image: "https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&q=80" + }, + { + id: "frontend-testing-strategies", + title: "Comprehensive Frontend Testing Strategies", + excerpt: "Building a robust testing strategy that catches bugs early and gives you confidence to ship.", + content: ` +# Comprehensive Frontend Testing Strategies + +A solid testing strategy is crucial for maintaining code quality and shipping with confidence. Let's explore a comprehensive approach. + +## The Testing Pyramid + +### Unit Tests (70%) +Test individual functions and components in isolation. Focus on pure functions and component logic. + +### Integration Tests (20%) +Test how components work together. Verify that different parts of your application integrate correctly. + +### E2E Tests (10%) +Test complete user flows with tools like Playwright or Cypress. + +## Testing Best Practices + +### 1. Test Behavior, Not Implementation +Focus on what users see and do, not internal component state. + +### 2. Use Testing Library Queries +Prefer queries that mirror how users interact: +- getByRole +- getByLabelText +- getByText + +### 3. Avoid Test IDs When Possible +Use semantic HTML and ARIA attributes instead. + +### 4. Test Accessibility +Ensure your app works for all users by testing with accessibility tools. + +## Visual Regression Testing + +Use tools like: +- Chromatic +- Percy +- BackstopJS + +## Performance Testing + +Monitor key metrics: +- Component render times +- Bundle size +- Time to Interactive + +## Conclusion + +A comprehensive testing strategy provides confidence, catches bugs early, and enables faster development. Invest in testing infrastructure early. + `, + date: "2024-12-15", + author: "Craig Wayne", + category: "Testing", + tags: ["Testing", "Quality", "Frontend", "Best Practices"], + readTime: "8 min read", + image: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?w=800&q=80" + } +]; diff --git a/src/data/chart-data.json b/src/data/chart-data.json new file mode 100644 index 0000000..c69fa41 --- /dev/null +++ b/src/data/chart-data.json @@ -0,0 +1,329 @@ +export const chartData = { + "data": [ + { + "color": "#16ce40", + "name": "Other", + "percent": 66.08 + }, + { + "color": "#3178c6", + "name": "TypeScript", + "percent": 15.2 + }, + { + "color": "#e34c26", + "name": "HTML", + "percent": 4.82 + }, + { + "color": "#f1e05a", + "name": "JavaScript", + "percent": 1.81 + }, + { + "color": "#c6538c", + "name": "SCSS", + "percent": 1.79 + }, + { + "color": "#292929", + "name": "JSON", + "percent": 1.72 + }, + { + "color": "#083fa1", + "name": "Markdown", + "percent": 1.62 + }, + { + "color": "#cb171e", + "name": "YAML", + "percent": 1.58 + }, + { + "color": "#4F5D95", + "name": "PHP", + "percent": 0.57 + }, + { + "color": "#178600", + "name": "C#", + "percent": 0.46 + }, + { + "color": "#dc9658", + "name": "Image (svg)", + "percent": 0.45 + }, + { + "color": "#89e051", + "name": "Shell", + "percent": 0.43 + }, + { + "color": "#d62728", + "name": "Jade", + "percent": 0.41 + }, + { + "color": "#563d7c", + "name": "CSS", + "percent": 0.41 + }, + { + "color": "#355570", + "name": "Godot Resource", + "percent": 0.36 + }, + { + "color": "#a86454", + "name": "Pug", + "percent": 0.23 + }, + { + "color": "#512be4", + "name": "HTML+Razor", + "percent": 0.21 + }, + { + "color": "#000000", + "name": "Ignore List", + "percent": 0.18 + }, + { + "color": "#355570", + "name": "GDScript", + "percent": 0.18 + }, + { + "color": "#9467bd", + "name": "textmate", + "percent": 0.14 + }, + { + "color": "#3178c6", + "name": "TSX", + "percent": 0.14 + }, + { + "color": "#f34b7d", + "name": "C++", + "percent": 0.13 + }, + { + "color": "#8c564b", + "name": "Text", + "percent": 0.13 + }, + { + "color": "#aec7e8", + "name": "Docker", + "percent": 0.12 + }, + { + "color": "#e38c00", + "name": "SQL", + "percent": 0.11 + }, + { + "color": "#3572A5", + "name": "Python", + "percent": 0.1 + }, + { + "color": "#e377c2", + "name": "Bash", + "percent": 0.09 + }, + { + "color": "#f7b6d2", + "name": "GitIgnore file", + "percent": 0.09 + }, + { + "color": "#7f7f7f", + "name": "Nginx Configuration", + "percent": 0.08 + }, + { + "color": "#c7c7c7", + "name": "TSConfig", + "percent": 0.06 + }, + { + "color": "#bcbd22", + "name": "Shell Script", + "percent": 0.04 + }, + { + "color": "#dbdb8d", + "name": "SourceMap", + "percent": 0.04 + }, + { + "color": "#384d54", + "name": "Dockerfile", + "percent": 0.04 + }, + { + "color": "#d1dbe0", + "name": "INI", + "percent": 0.03 + }, + { + "color": "#701516", + "name": "Ruby", + "percent": 0.03 + }, + { + "color": "#17becf", + "name": "Log", + "percent": 0.02 + }, + { + "color": "#237346", + "name": "CSV", + "percent": 0.02 + }, + { + "color": "#9edae5", + "name": ".env file", + "percent": 0.01 + }, + { + "color": "#ffbb78", + "name": "Diff", + "percent": 0.01 + }, + { + "color": "#9c4221", + "name": "TOML", + "percent": 0.01 + }, + { + "color": "#0060ac", + "name": "XML", + "percent": 0.01 + }, + { + "color": "#fff1f2", + "name": "EditorConfig", + "percent": 0.01 + }, + { + "color": "#98df8a", + "name": "Liquid Template", + "percent": 0.0 + }, + { + "color": "#012456", + "name": "PowerShell", + "percent": 0.0 + }, + { + "color": "#ff9896", + "name": "HTTP Request", + "percent": 0.0 + }, + { + "color": "#c5b0d5", + "name": "Nginx configuration file", + "percent": 0.0 + }, + { + "color": "#c49c94", + "name": "GLSL shader", + "percent": 0.0 + }, + { + "color": "#16ce40", + "name": "DockerIgnore file", + "percent": 0.0 + }, + { + "color": "#5686a5", + "name": "GLSL", + "percent": 0.0 + }, + { + "color": "#F44D27", + "name": "Git Attributes", + "percent": 0.0 + }, + { + "color": "#DA5B0B", + "name": "Jupyter Notebook", + "percent": 0.0 + }, + { + "color": "#67b8de", + "name": "Liquid", + "percent": 0.0 + }, + { + "color": "#dc9658", + "name": "Glsl File", + "percent": 0.0 + }, + { + "color": "#ff9900", + "name": "SVG", + "percent": 0.0 + }, + { + "color": "#e5d559", + "name": "Dotenv", + "percent": 0.0 + }, + { + "color": "#b845fc", + "name": "F#", + "percent": 0.0 + }, + { + "color": "#222c37", + "name": "Unity3D Asset", + "percent": 0.0 + }, + { + "color": "#d62728", + "name": "Tscn file", + "percent": 0.0 + }, + { + "color": "#c065db", + "name": "Emacs Lisp", + "percent": 0.0 + }, + { + "color": "#00ADD8", + "name": "Go", + "percent": 0.0 + }, + { + "color": "#9467bd", + "name": "BrightScript", + "percent": 0.0 + }, + { + "color": "#fcb32c", + "name": "MDX", + "percent": 0.0 + }, + { + "color": "#b07219", + "name": "Java", + "percent": 0.0 + }, + { + "color": "#8c564b", + "name": "Figma Design", + "percent": 0.0 + }, + { + "color": "#1d365d", + "name": "Less", + "percent": 0.0 + } + ] +}; \ No newline at end of file diff --git a/src/data/chart-data.ts b/src/data/chart-data.ts new file mode 100644 index 0000000..c69fa41 --- /dev/null +++ b/src/data/chart-data.ts @@ -0,0 +1,329 @@ +export const chartData = { + "data": [ + { + "color": "#16ce40", + "name": "Other", + "percent": 66.08 + }, + { + "color": "#3178c6", + "name": "TypeScript", + "percent": 15.2 + }, + { + "color": "#e34c26", + "name": "HTML", + "percent": 4.82 + }, + { + "color": "#f1e05a", + "name": "JavaScript", + "percent": 1.81 + }, + { + "color": "#c6538c", + "name": "SCSS", + "percent": 1.79 + }, + { + "color": "#292929", + "name": "JSON", + "percent": 1.72 + }, + { + "color": "#083fa1", + "name": "Markdown", + "percent": 1.62 + }, + { + "color": "#cb171e", + "name": "YAML", + "percent": 1.58 + }, + { + "color": "#4F5D95", + "name": "PHP", + "percent": 0.57 + }, + { + "color": "#178600", + "name": "C#", + "percent": 0.46 + }, + { + "color": "#dc9658", + "name": "Image (svg)", + "percent": 0.45 + }, + { + "color": "#89e051", + "name": "Shell", + "percent": 0.43 + }, + { + "color": "#d62728", + "name": "Jade", + "percent": 0.41 + }, + { + "color": "#563d7c", + "name": "CSS", + "percent": 0.41 + }, + { + "color": "#355570", + "name": "Godot Resource", + "percent": 0.36 + }, + { + "color": "#a86454", + "name": "Pug", + "percent": 0.23 + }, + { + "color": "#512be4", + "name": "HTML+Razor", + "percent": 0.21 + }, + { + "color": "#000000", + "name": "Ignore List", + "percent": 0.18 + }, + { + "color": "#355570", + "name": "GDScript", + "percent": 0.18 + }, + { + "color": "#9467bd", + "name": "textmate", + "percent": 0.14 + }, + { + "color": "#3178c6", + "name": "TSX", + "percent": 0.14 + }, + { + "color": "#f34b7d", + "name": "C++", + "percent": 0.13 + }, + { + "color": "#8c564b", + "name": "Text", + "percent": 0.13 + }, + { + "color": "#aec7e8", + "name": "Docker", + "percent": 0.12 + }, + { + "color": "#e38c00", + "name": "SQL", + "percent": 0.11 + }, + { + "color": "#3572A5", + "name": "Python", + "percent": 0.1 + }, + { + "color": "#e377c2", + "name": "Bash", + "percent": 0.09 + }, + { + "color": "#f7b6d2", + "name": "GitIgnore file", + "percent": 0.09 + }, + { + "color": "#7f7f7f", + "name": "Nginx Configuration", + "percent": 0.08 + }, + { + "color": "#c7c7c7", + "name": "TSConfig", + "percent": 0.06 + }, + { + "color": "#bcbd22", + "name": "Shell Script", + "percent": 0.04 + }, + { + "color": "#dbdb8d", + "name": "SourceMap", + "percent": 0.04 + }, + { + "color": "#384d54", + "name": "Dockerfile", + "percent": 0.04 + }, + { + "color": "#d1dbe0", + "name": "INI", + "percent": 0.03 + }, + { + "color": "#701516", + "name": "Ruby", + "percent": 0.03 + }, + { + "color": "#17becf", + "name": "Log", + "percent": 0.02 + }, + { + "color": "#237346", + "name": "CSV", + "percent": 0.02 + }, + { + "color": "#9edae5", + "name": ".env file", + "percent": 0.01 + }, + { + "color": "#ffbb78", + "name": "Diff", + "percent": 0.01 + }, + { + "color": "#9c4221", + "name": "TOML", + "percent": 0.01 + }, + { + "color": "#0060ac", + "name": "XML", + "percent": 0.01 + }, + { + "color": "#fff1f2", + "name": "EditorConfig", + "percent": 0.01 + }, + { + "color": "#98df8a", + "name": "Liquid Template", + "percent": 0.0 + }, + { + "color": "#012456", + "name": "PowerShell", + "percent": 0.0 + }, + { + "color": "#ff9896", + "name": "HTTP Request", + "percent": 0.0 + }, + { + "color": "#c5b0d5", + "name": "Nginx configuration file", + "percent": 0.0 + }, + { + "color": "#c49c94", + "name": "GLSL shader", + "percent": 0.0 + }, + { + "color": "#16ce40", + "name": "DockerIgnore file", + "percent": 0.0 + }, + { + "color": "#5686a5", + "name": "GLSL", + "percent": 0.0 + }, + { + "color": "#F44D27", + "name": "Git Attributes", + "percent": 0.0 + }, + { + "color": "#DA5B0B", + "name": "Jupyter Notebook", + "percent": 0.0 + }, + { + "color": "#67b8de", + "name": "Liquid", + "percent": 0.0 + }, + { + "color": "#dc9658", + "name": "Glsl File", + "percent": 0.0 + }, + { + "color": "#ff9900", + "name": "SVG", + "percent": 0.0 + }, + { + "color": "#e5d559", + "name": "Dotenv", + "percent": 0.0 + }, + { + "color": "#b845fc", + "name": "F#", + "percent": 0.0 + }, + { + "color": "#222c37", + "name": "Unity3D Asset", + "percent": 0.0 + }, + { + "color": "#d62728", + "name": "Tscn file", + "percent": 0.0 + }, + { + "color": "#c065db", + "name": "Emacs Lisp", + "percent": 0.0 + }, + { + "color": "#00ADD8", + "name": "Go", + "percent": 0.0 + }, + { + "color": "#9467bd", + "name": "BrightScript", + "percent": 0.0 + }, + { + "color": "#fcb32c", + "name": "MDX", + "percent": 0.0 + }, + { + "color": "#b07219", + "name": "Java", + "percent": 0.0 + }, + { + "color": "#8c564b", + "name": "Figma Design", + "percent": 0.0 + }, + { + "color": "#1d365d", + "name": "Less", + "percent": 0.0 + } + ] +}; \ No newline at end of file diff --git a/src/data/portfolio.ts b/src/data/portfolio.ts index 9c72ac1..101ac21 100644 --- a/src/data/portfolio.ts +++ b/src/data/portfolio.ts @@ -1,281 +1,394 @@ const abstracted_data = { - name: "Craig Wayne", - username: "craigiswayne", - email: "craigiswayne@gmail.com" -} + name: "Craig Wayne", + username: "craigiswayne", + email: "craigiswayne@gmail.com", +}; export const portfolioData = { - "personal": { - "name": abstracted_data.name, - "title": "Full Stack Javascript Engineer", - "availabilityStatus": "Available for new opportunities", - "description": "Crafting exceptional digital experiences with modern web technologies. Specializing in TypeScript stacks, and creating scalable architectures that drive business growth", - "yearsOfExperience": 8, - "email": abstracted_data.email, - "cvPath": "/CV-CraigWayne.pdf", - "cvFileName": "CV-CraigWayne.pdf", - "profileImage": "/profile.png", - "socialLinks": { - "github": "https://github.com/craigiswayne", - "linkedin": "https://www.linkedin.com/in/craigiswayne" - } + personal: { + name: abstracted_data.name, + title: "Full Stack Javascript Engineer", + availabilityStatus: "Available for new opportunities", + description: + "Crafting exceptional digital experiences with modern web technologies. Specializing in TypeScript stacks, and creating scalable architectures that drive business growth.", + yearsOfExperience: 8, + email: abstracted_data.email, + cvPath: "/CV-CraigWayne.pdf", + cvFileName: "CV-CraigWayne.pdf", + profileImage: "/profile.png", + socialLinks: { + github: "https://github.com/craigiswayne", + linkedin: "https://www.linkedin.com/in/craigiswayne", }, - "about": { - "subtitle": "Building the Future of Web", - "description": "With over 10 years of experience in javascript development, I've helped startups and enterprise companies create digital products that users love. My passion lies in crafting intuitive interfaces and scalable architectures.", - "journey": [ - "I started my career as a curious developer fascinated by the intersection of design and technology. Over the years, I've evolved from writing my first HTML page to architecting complex applications used by millions.", - "Today, I focus on building scalable full stack systems, mentoring teams, and staying at the forefront of web technology. I believe great software is not just about code—it's about solving real problems for real people." + }, + about: { + subtitle: "Building the Future of Web", + description: + "With over 10 years of experience in javascript development, I've helped startups and enterprise companies create digital products that users love. My passion lies in crafting intuitive interfaces and scalable architectures.", + journey: [ + "I started my career as a curious developer fascinated by the intersection of design and technology. Over the years, I've evolved from writing my first HTML page to architecting complex applications used by millions.", + "Today, I focus on building scalable full stack systems, mentoring teams, and staying at the forefront of web technology. I believe great software is not just about code—it's about solving real problems for real people.", + ], + highlights: [ + { + icon: "Code", + title: "Clean Code Advocate", + description: + "Writing maintainable, scalable code that follows industry best practices and modern standards.", + }, + { + icon: "Users", + title: "Team Leadership", + description: + "Leading frontend teams and mentoring junior developers to achieve collective success.", + }, + { + icon: "Zap", + title: "Performance Focused", + description: + "Optimizing applications for speed, accessibility, and exceptional user experiences.", + }, + { + icon: "Target", + title: "Business Impact", + description: + "Translating business requirements into technical solutions that drive measurable results.", + }, + ], + expertise: [ + { + skill: "JavaScript/TypeScript", + level: "Expert", + }, + { + skill: "CSS/SCSS", + level: "Expert", + }, + { + skill: "Cloud Architecture", + level: "Expert", + }, + { + skill: "API Design", + level: "Expert", + }, + { + skill: "React/Vue/Next.js", + level: "Advanced", + }, + { + skill: "Team Leadership", + level: "Advanced", + }, + ], + }, + skills: { + subtitle: "Technologies I Work With", + description: + "A comprehensive toolkit built over years of experience, constantly evolving with the latest industry trends and best practices.", + categories: [ + { + title: "Frontend Technologies", + skills: [ + "TypeScript", + "Angular", + "React", + "Vue", + "CSS3", + "SCSS", + "Next.js", + "JavaScript (ES6+)", + "HTML5", + "Tailwind CSS", + "Styled Components", + "WebGL", + "WordPress", ], - "highlights": [ - { - "icon": "Code", - "title": "Clean Code Advocate", - "description": "Writing maintainable, scalable code that follows industry best practices and modern standards." - }, - { - "icon": "Users", - "title": "Team Leadership", - "description": "Leading frontend teams and mentoring junior developers to achieve collective success." - }, - { - "icon": "Zap", - "title": "Performance Focused", - "description": "Optimizing applications for speed, accessibility, and exceptional user experiences." - }, - { - "icon": "Target", - "title": "Business Impact", - "description": "Translating business requirements into technical solutions that drive measurable results." - } + }, + { + title: "Development Tools", + skills: [ + "Git", + "Webpack", + "Vite", + "npm/yarn", + "ESLint", + "Prettier", + "Jest", + "Cypress", + "Storybook", + "Figma", + "Adobe XD", ], - "expertise": [ - { - "skill": "JavaScript/TypeScript", - "level": "Expert" - }, - { - "skill": "CSS/SCSS", - "level": "Expert" - }, - { - "skill": "Cloud Architecture", - "level": "Expert" - }, - { - "skill": "API Design", - "level": "Expert" - }, - { - "skill": "React/Vue/Next.js", - "level": "Advanced" - }, - { - "skill": "Team Leadership", - "level": "Advanced" - } + }, + { + title: "Architecture & Best Practices", + skills: [ + "Component Architecture", + "Design Patterns", + "Performance Optimization", + "Accessibility (WCAG)", + "SEO", + "Code Review", + "CI/CD", + "Agile/Scrum", ], - }, - "skills": { - "subtitle": "Technologies I Work With", - "description": "A comprehensive toolkit built over years of experience, constantly evolving with the latest industry trends and best practices.", - "categories": [ - { - "title": "Frontend Technologies", - "skills": [ - "TypeScript", "Angular", "React", "Vue", "CSS3", "SCSS", "Next.js", "JavaScript (ES6+)", - "HTML5", "Tailwind CSS", "Styled Components", "WebGL", "WordPress" - ] - }, - { - "title": "Development Tools", - "skills": [ - "Git", "Webpack", "Vite", "npm/yarn", "ESLint", "Prettier", - "Jest", "Cypress", "Storybook", "Figma", "Adobe XD" - ] - }, - { - "title": "Architecture & Best Practices", - "skills": [ - "Component Architecture", "Design Patterns", "Performance Optimization", - "Accessibility (WCAG)", "SEO", "Code Review", "CI/CD", "Agile/Scrum" - ] - }, - { - "title": "Backend & Cloud", - "skills": [ - "Node.js", "REST APIs", "Firebase", "PHP", ".NET", - "AWS", "Netlify", "Docker", "Azure DevOps" - ] - } - ] - }, - "projects": { - "subtitle": "Recent Work", - "description": "A selection of projects that showcase my expertise in building scalable web applications and solving complex technical challenges.", - "featured": [ - { - "title": "Teefz Crash Game", - "description": "Casino Web Crash Game developed using Three.JS", - "image": "/teefz.png", - "technologies": ["Three.JS", "Angular", "TypeScript", "WebGL"], - "liveUrl": "", - "githubUrl": "https://github.com/craigiswayne/angular-casino" - }, - { - "title": "SAASQuash", - "description": "Locally hosted image compression tool", - "image": "/saasquash.png", - "technologies": ["Angular", "Typescript", "Node.js", "Node.js", "Agentic"], - "liveUrl": "", - "githubUrl": "https://github.com/craigiswayne/saasquash" - }, - { - "title": "UROC Demo Launcher", - "description": "Album Cover style demo launcher for games built within UROC", - "image": "/uroc_demo_launcher.png", - "technologies": ["TypeScript", "GSAP", "SCSS"], - "liveUrl": "https://gamedemos.uroc.com", - "githubUrl": "https://github.com/craigiswayne/uroc-demo-launcher-coverflow" - } - ] - }, - "experience": { - "subtitle": "Professional Journey", - "description": "Over 10 years of experience building exceptional web applications, leading teams, and driving technical innovation.", - "positions": [ - { - "company": "UROC Studios", - "company_url": "https://uroc.com/", - "companyLogo": "/logo_uroc_studios.jpeg", - "position": "Full Stack Javascript Engineer", - "date_start": "2024-11", - "location": "Douglas, Isle of Man", - "description": "Design and Develop Slot, Scratch and Crash Games", - "achievements": [ - "Maintain and Deploy our various games", - "Work closely with the design and qa team to ensure that the games are according to spec", - "Develop and maintain unit tests", - "Automation wrt deployments, builds, testing", - "Build bespoke tools to facilitate with development of our games", - "Maintenance and development of the corporate website", - ], - "technologies": ["PixiJS", "Unity", "WebGL", "2D/3D", "Angular", "Three.JS", "Typescript", "Game Development"] - }, - { - "company": "Microgaming", - "company_url": "https://www.microgaming.co.uk/", - "companyLogo": "/logo_microgaming.png", - "position": "Senior Frontend Developer", - "date_start": "2022-10", - "date_end": "2024-10", - "location": "Cape Town, South Africa", - "description": "Creating and maintaining in-house solutions to improve business processes", - "achievements": [ - "2IC to the Tech Lead. Providing insights into architecting several micro-services to improve performance, delivery and disaster recovery", - "Mentoring of juniors in tech and business acumen.", - "Service Desk Support", - "Desktop Support", - "Software Installation for Games Global Employees", - "Maintaining Power Automate Workflows", - "Maintenance of Customer Facing Applications", - "Studio Website Development and Maintenance", - "Project Management" - ], - "technologies": [".NET", "PHP", "TypeScript", "Angular", "React", "Typescript", "Docker", "Azure DevOps", "Shell Scripting", "Back Office"] - }, - { - "company": "Rank Interactive", - "company_url": "https://www.rank.com/", - "companyLogo": "/rank_interactive_logo.jpeg", - "position": "Senior Frontend Engineer", - "date_start": "2020-09", - "date_end": "2022-08", - "location": "Cape Town, South Africa", - "description": "Development and maintenance of the in-house cashier services on several Casinos and Bingo operators in the UK.", - "achievements": [ - "Responsible for capturing Player Card Details and integrating with payment systems such as VISA, PayPal and Barclays.", - "Our cashier systems also handle promoting bonuses and promotions to the player which they could redeem through the app.", - "Our cashier app is built to withstand large amounts of traffic whilst implementing UI and UX best practices. On any given day, it handles approximately 80,000 transactions.", - "Maintaining unit tests coupled with continuous integration pipelines", - ], - "technologies": ["Angular", "Typescript", "Elastic Stack (ELK)", "Docker", "Node.js", "CI/CD", "Docker"] - }, - { - "company": "Digioutsource", - "company_url": "https://supergroup.com/companies/digi-outsource/", - "companyLogo": "/logo_digioutsource.webp", - "position": "Intermediate Web Developer", - "date_start": "2019-02", - "date_end": "2020-08", - "location": "Cape Town, South Africa", - "description": "Primarily responsible for updating and maintaining an in house CMS that serves a multinational casino base", - "achievements": [ - "Mentoring colleagues and trainees on the business landscape as well as current best practices within our industry.", - "Rotational After hours support and Tech assistance.", - "2IC to the Team Lead and responsible for mentoring the junior / intermediate colleagues.", - "The CI/CD process leverages GitLabs Pipelines integrating tightly with Kubernetes" - ], - "technologies": [".NET", "MongoDB", "Elastic Stack (ELK)", "Angular", "Typescript", "Typescript"] - }, - { - "company": "Media24", - "company_url": "https://www.media24.com/", - "companyLogo": "/logo_media24.png", - "position": "Senior WordPress Developer", - "date_start": "2015-03", - "date_end": "2018-12", - "location": "Cape Town, South Africa", - "description": "Design and Develop WordPress Sites to promote the various media brands under the Media24 umbrella", - "achievements": [ - "Design and Develop WordPress Websites", - "Develop bespoke Themes and Plugins", - "Collaborate with Designers to bring their ideas to fruition", - "Mentoring Juniors and upskilling team members", - "Evaluate and provide solutions to security concerns", - "Manage the infrastructure and deployment of over 20 brands" - ], - "technologies": ["PHP", "WordPress", "React", "SCSS", "Typescript", "Javascript", "CI/CD", "Docker"] - } - ] - }, - "contact": { - "subtitle": "Let's Work Together", - "description": "I'm always interested in new opportunities and exciting projects. Whether you're looking for a team lead, consultant, or collaborator, let's discuss how we can create something amazing together.", - "methods": [ - { - "type": "email", - "icon": "Mail", - "title": "Email Me", - "description": "For project inquiries and collaboration opportunities", - "action": abstracted_data.email, - "actionType": "email" - } + }, + { + title: "Backend & Cloud", + skills: [ + "Node.js", + "REST APIs", + "Firebase", + "PHP", + ".NET", + "AWS", + "Netlify", + "Docker", + "Azure DevOps", ], - "lookingFor": [ - { - "title": "Senior/Lead Roles", - "description": "Technical leadership positions where I can mentor teams and drive architectural decisions." - }, - { - "title": "Innovative Projects", - "description": "Cutting-edge applications using the latest web technologies and modern development practices." - }, - { - "title": "Remote-First Culture", - "description": "Companies with strong remote collaboration practices and flexible work arrangements." - } - ] - }, - "navigation": [ - {"label": "About", "target": "about"}, - {"label": "Skills", "target": "skills"}, - {"label": "Projects", "target": "projects"}, - {"label": "Experience", "target": "experience"}, - {"label": "Contact", "target": "contact"} + }, + ], + }, + projects: { + subtitle: "Recent Work", + description: + "A selection of projects that showcase my expertise in building scalable web applications and solving complex technical challenges.", + featured: [ + { + title: "Teefz Crash Game", + description: + "Casino Web Crash Game developed using Three.JS", + image: "/teefz.png", + technologies: [ + "Three.JS", + "Angular", + "TypeScript", + "WebGL", + ], + liveUrl: "", + githubUrl: + "https://github.com/craigiswayne/angular-casino", + }, + { + title: "SAASQuash", + description: "Locally hosted image compression tool", + image: "/saasquash.png", + technologies: [ + "Angular", + "Typescript", + "Node.js", + "Node.js", + "Agentic", + ], + liveUrl: "", + githubUrl: "https://github.com/craigiswayne/saasquash", + }, + { + title: "UROC Demo Launcher", + description: + "Album Cover style demo launcher for games built within UROC", + image: "/uroc_demo_launcher.png", + technologies: ["TypeScript", "GSAP", "SCSS"], + liveUrl: "https://gamedemos.uroc.com", + githubUrl: + "https://github.com/craigiswayne/uroc-demo-launcher-coverflow", + }, + ], + }, + experience: { + subtitle: "Professional Journey", + description: + "Over 10 years of experience building exceptional web applications, leading teams, and driving technical innovation.", + positions: [ + { + company: "UROC Studios", + company_url: "https://uroc.com/", + companyLogo: "/logo_uroc_studios.jpeg", + position: "Full Stack Javascript Engineer", + date_start: "2024-11", + location: "Douglas, Isle of Man", + description: + "Design and Develop Slot, Scratch and Crash Games", + achievements: [ + "Maintain and Deploy our various games", + "Work closely with the design and qa team to ensure that the games are according to spec", + "Develop and maintain unit tests", + "Automation wrt deployments, builds, testing", + "Build bespoke tools to facilitate with development of our games", + "Maintenance and development of the corporate website", + ], + technologies: [ + "PixiJS", + "Unity", + "WebGL", + "2D/3D", + "Angular", + "Three.JS", + "Typescript", + "Game Development", + ], + }, + { + company: "Microgaming", + company_url: "https://www.microgaming.co.uk/", + companyLogo: "/logo_microgaming.png", + position: "Senior Frontend Developer", + date_start: "2022-10", + date_end: "2024-10", + location: "Cape Town, South Africa", + description: + "Creating and maintaining in-house solutions to improve business processes", + achievements: [ + "2IC to the Tech Lead. Providing insights into architecting several micro-services to improve performance, delivery and disaster recovery", + "Mentoring of juniors in tech and business acumen.", + "Service Desk Support", + "Desktop Support", + "Software Installation for Games Global Employees", + "Maintaining Power Automate Workflows", + "Maintenance of Customer Facing Applications", + "Studio Website Development and Maintenance", + "Project Management", + ], + technologies: [ + ".NET", + "PHP", + "TypeScript", + "Angular", + "React", + "Typescript", + "Docker", + "Azure DevOps", + "Shell Scripting", + "Back Office", + ], + }, + { + company: "Rank Interactive", + company_url: "https://www.rank.com/", + companyLogo: "/rank_interactive_logo.jpeg", + position: "Senior Frontend Engineer", + date_start: "2020-09", + date_end: "2022-08", + location: "Cape Town, South Africa", + description: + "Development and maintenance of the in-house cashier services on several Casinos and Bingo operators in the UK.", + achievements: [ + "Responsible for capturing Player Card Details and integrating with payment systems such as VISA, PayPal and Barclays.", + "Our cashier systems also handle promoting bonuses and promotions to the player which they could redeem through the app.", + "Our cashier app is built to withstand large amounts of traffic whilst implementing UI and UX best practices. On any given day, it handles approximately 80,000 transactions.", + "Maintaining unit tests coupled with continuous integration pipelines", + ], + technologies: [ + "Angular", + "Typescript", + "Elastic Stack (ELK)", + "Docker", + "Node.js", + "CI/CD", + "Docker", + ], + }, + { + company: "Digioutsource", + company_url: + "https://supergroup.com/companies/digi-outsource/", + companyLogo: "/logo_digioutsource.webp", + position: "Intermediate Web Developer", + date_start: "2019-02", + date_end: "2020-08", + location: "Cape Town, South Africa", + description: + "Primarily responsible for updating and maintaining an in house CMS that serves a multinational casino base", + achievements: [ + "Mentoring colleagues and trainees on the business landscape as well as current best practices within our industry.", + "Rotational After hours support and Tech assistance.", + "2IC to the Team Lead and responsible for mentoring the junior / intermediate colleagues.", + "The CI/CD process leverages GitLabs Pipelines integrating tightly with Kubernetes", + ], + technologies: [ + ".NET", + "MongoDB", + "Elastic Stack (ELK)", + "Angular", + "Typescript", + "Typescript", + ], + }, + { + company: "Media24", + company_url: "https://www.media24.com/", + companyLogo: "/logo_media24.png", + position: "Senior WordPress Developer", + date_start: "2015-03", + date_end: "2018-12", + location: "Cape Town, South Africa", + description: + "Design and Develop WordPress Sites to promote the various media brands under the Media24 umbrella", + achievements: [ + "Design and Develop WordPress Websites", + "Develop bespoke Themes and Plugins", + "Collaborate with Designers to bring their ideas to fruition", + "Mentoring Juniors and upskilling team members", + "Evaluate and provide solutions to security concerns", + "Manage the infrastructure and deployment of over 20 brands", + ], + technologies: [ + "PHP", + "WordPress", + "React", + "SCSS", + "Typescript", + "Javascript", + "CI/CD", + "Docker", + ], + }, + ], + }, + contact: { + subtitle: "Let's Work Together", + description: + "I'm always interested in new opportunities and exciting projects. Whether you're looking for a team lead, consultant, or collaborator, let's discuss how we can create something amazing together.", + methods: [ + { + type: "email", + icon: "Mail", + title: "Email Me", + description: + "For project inquiries and collaboration opportunities", + action: abstracted_data.email, + actionType: "email", + }, + ], + lookingFor: [ + { + title: "Senior/Lead Roles", + description: + "Technical leadership positions where I can mentor teams and drive architectural decisions.", + }, + { + title: "Innovative Projects", + description: + "Cutting-edge applications using the latest web technologies and modern development practices.", + }, + { + title: "Remote-First Culture", + description: + "Companies with strong remote collaboration practices and flexible work arrangements.", + }, ], - "footer": { - "copyright": `© 2025 ${abstracted_data.name}. Built with React, TypeScript, and Tailwind CSS.`, - "tagline": "Open to new opportunities • Available for freelance projects" - } + }, + navigation: [ + { label: "About", target: "about" }, + { label: "Skills", target: "skills" }, + { label: "Projects", target: "projects" }, + { label: "Experience", target: "experience" }, + { label: "Contact", target: "contact" }, + ], + footer: { + copyright: `© 2025 ${abstracted_data.name}. Built with React, TypeScript, and Tailwind CSS.`, + tagline: + "Open to new opportunities • Available for freelance projects", + }, }; \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..79bd30a --- /dev/null +++ b/src/index.css @@ -0,0 +1,2898 @@ +/*! tailwindcss v4.1.3 | MIT License | https://tailwindcss.com */ +@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + --tw-rotate-x: rotateX(0); + --tw-rotate-y: rotateY(0); + --tw-rotate-z: rotateZ(0); + --tw-skew-x: skewX(0); + --tw-skew-y: skewY(0); + --tw-space-y-reverse: 0; + --tw-border-style: solid; + --tw-gradient-position: initial; + --tw-gradient-from: #0000; + --tw-gradient-via: #0000; + --tw-gradient-to: #0000; + --tw-gradient-stops: initial; + --tw-gradient-via-stops: initial; + --tw-gradient-from-position: 0%; + --tw-gradient-via-position: 50%; + --tw-gradient-to-position: 100%; + --tw-leading: initial; + --tw-font-weight: initial; + --tw-tracking: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + --tw-backdrop-blur: initial; + --tw-backdrop-brightness: initial; + --tw-backdrop-contrast: initial; + --tw-backdrop-grayscale: initial; + --tw-backdrop-hue-rotate: initial; + --tw-backdrop-invert: initial; + --tw-backdrop-opacity: initial; + --tw-backdrop-saturate: initial; + --tw-backdrop-sepia: initial; + --tw-duration: initial; + --tw-ease: initial; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-scale-z: 1; + } + } +} + +@layer theme { + :root, :host { + --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --color-red-400: oklch(.704 .191 22.216); + --color-green-500: oklch(.723 .219 149.579); + --color-blue-600: oklch(.546 .245 262.881); + --color-blue-700: oklch(.488 .243 264.376); + --color-gray-50: oklch(.985 .002 247.839); + --color-gray-100: oklch(.967 .003 264.542); + --color-gray-200: oklch(.928 .006 264.531); + --color-gray-400: oklch(.707 .022 261.325); + --color-gray-500: oklch(.551 .027 264.364); + --color-gray-700: oklch(.373 .034 259.733); + --color-gray-900: oklch(.21 .034 264.665); + --color-black: #000; + --color-white: #fff; + --spacing: .25rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --text-xs: .75rem; + --text-xs--line-height: calc(1 / .75); + --text-sm: .875rem; + --text-sm--line-height: calc(1.25 / .875); + --text-base: 1rem; + --text-lg: 1.125rem; + --text-lg--line-height: calc(1.75 / 1.125); + --text-xl: 1.25rem; + --text-xl--line-height: calc(1.75 / 1.25); + --text-2xl: 1.5rem; + --text-3xl: 1.875rem; + --text-3xl--line-height: calc(2.25 / 1.875); + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5 / 2.25); + --text-5xl: 3rem; + --text-5xl--line-height: 1; + --text-6xl: 3.75rem; + --text-6xl--line-height: 1; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + --tracking-tight: -.025em; + --leading-relaxed: 1.625; + --radius-2xl: 1rem; + --ease-out: cubic-bezier(0, 0, .2, 1); + --animate-spin: spin 1s linear infinite; + --blur-sm: 8px; + --blur-md: 12px; + --default-transition-duration: .15s; + --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); + --default-font-family: var(--font-sans); + --default-font-feature-settings: var(--font-sans--font-feature-settings); + --default-font-variation-settings: var(--font-sans--font-variation-settings); + --default-mono-font-family: var(--font-mono); + --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); + --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); + } +} + +@layer base { + *, :after, :before, ::backdrop { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + + ::file-selector-button { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + + html, :host { + -webkit-text-size-adjust: 100%; + tab-size: 4; + line-height: 1.5; + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + + body { + line-height: inherit; + } + + hr { + height: 0; + color: inherit; + border-top-width: 1px; + } + + abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + } + + h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: inherit; + } + + a { + color: inherit; + -webkit-text-decoration: inherit; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + + b, strong { + font-weight: bolder; + } + + code, kbd, samp, pre { + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); + font-feature-settings: var(--default-mono-font-feature-settings, normal); + font-variation-settings: var(--default-mono-font-variation-settings, normal); + font-size: 1em; + } + + small { + font-size: 80%; + } + + sub, sup { + vertical-align: baseline; + font-size: 75%; + line-height: 0; + position: relative; + } + + sub { + bottom: -.25em; + } + + sup { + top: -.5em; + } + + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + + :-moz-focusring { + outline: auto; + } + + progress { + vertical-align: baseline; + } + + summary { + display: list-item; + } + + ol, ul, menu { + list-style: none; + } + + img, svg, video, canvas, audio, iframe, embed, object { + vertical-align: middle; + display: block; + } + + img, video { + max-width: 100%; + height: auto; + } + + button, input, select, optgroup, textarea { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + + ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + + ::file-selector-button { + margin-inline-end: 4px; + } + + ::placeholder { + opacity: 1; + color: currentColor; + } + + @supports (color: color-mix(in lab, red, red)) { + ::placeholder { + color: color-mix(in oklab, currentColor 50%, transparent); + } + } + + textarea { + resize: vertical; + } + + ::-webkit-search-decoration { + -webkit-appearance: none; + } + + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + + ::-webkit-datetime-edit { + display: inline-flex; + } + + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + + ::-webkit-datetime-edit { + padding-block: 0; + } + + ::-webkit-datetime-edit-year-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-month-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-day-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-hour-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-minute-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-second-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-millisecond-field { + padding-block: 0; + } + + ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + + :-moz-ui-invalid { + box-shadow: none; + } + + button, input:where([type="button"], [type="reset"], [type="submit"]) { + appearance: button; + } + + ::file-selector-button { + appearance: button; + } + + ::-webkit-inner-spin-button { + height: auto; + } + + ::-webkit-outer-spin-button { + height: auto; + } + + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } + + * { + border-color: var(--border); + outline-color: var(--ring); + } + + @supports (color: color-mix(in lab, red, red)) { + * { + outline-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + + * { + border-color: var(--border); + outline-color: var(--ring); + } + + @supports (color: color-mix(in lab, red, red)) { + * { + outline-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + + body { + background-color: var(--background); + color: var(--foreground); + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h1 { + font-size: var(--text-2xl); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h2 { + font-size: var(--text-xl); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h3 { + font-size: var(--text-lg); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h4 { + font-size: var(--text-base); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) p { + font-size: var(--text-base); + font-weight: var(--font-weight-normal); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) label, :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) button { + font-size: var(--text-base); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) input { + font-size: var(--text-base); + font-weight: var(--font-weight-normal); + line-height: 1.5; + } +} + +@layer utilities { + .\@container\/card-header { + container: card-header / inline-size; + } + + .visible { + visibility: visible; + } + + .absolute { + position: absolute; + } + + .fixed { + position: fixed; + } + + .relative { + position: relative; + } + + .inset-0 { + inset: calc(var(--spacing) * 0); + } + + .top-0 { + top: calc(var(--spacing) * 0); + } + + .top-4 { + top: calc(var(--spacing) * 4); + } + + .top-6 { + top: calc(var(--spacing) * 6); + } + + .top-9 { + top: calc(var(--spacing) * 9); + } + + .right-0 { + right: calc(var(--spacing) * 0); + } + + .right-4 { + right: calc(var(--spacing) * 4); + } + + .-bottom-2 { + bottom: calc(var(--spacing) * -2); + } + + .bottom-0 { + bottom: calc(var(--spacing) * 0); + } + + .bottom-4 { + bottom: calc(var(--spacing) * 4); + } + + .bottom-8 { + bottom: calc(var(--spacing) * 8); + } + + .left-0 { + left: calc(var(--spacing) * 0); + } + + .left-1\/2 { + left: 50%; + } + + .left-4 { + left: calc(var(--spacing) * 4); + } + + .left-6 { + left: calc(var(--spacing) * 6); + } + + .left-8 { + left: calc(var(--spacing) * 8); + } + + .z-50 { + z-index: 50; + } + + .col-start-2 { + grid-column-start: 2; + } + + .row-span-2 { + grid-row: span 2 / span 2; + } + + .row-start-1 { + grid-row-start: 1; + } + + .container { + width: 100%; + } + + @media (width >= 40rem) { + .container { + max-width: 40rem; + } + } + + @media (width >= 48rem) { + .container { + max-width: 48rem; + } + } + + @media (width >= 64rem) { + .container { + max-width: 64rem; + } + } + + @media (width >= 80rem) { + .container { + max-width: 80rem; + } + } + + @media (width >= 96rem) { + .container { + max-width: 96rem; + } + } + + .mx-auto { + margin-inline: auto; + } + + .my-8 { + margin-block: calc(var(--spacing) * 8); + } + + .mt-1 { + margin-top: calc(var(--spacing) * 1); + } + + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } + + .mt-6 { + margin-top: calc(var(--spacing) * 6); + } + + .mt-8 { + margin-top: calc(var(--spacing) * 8); + } + + .mt-12 { + margin-top: calc(var(--spacing) * 12); + } + + .mb-1 { + margin-bottom: calc(var(--spacing) * 1); + } + + .mb-2 { + margin-bottom: calc(var(--spacing) * 2); + } + + .mb-3 { + margin-bottom: calc(var(--spacing) * 3); + } + + .mb-4 { + margin-bottom: calc(var(--spacing) * 4); + } + + .mb-6 { + margin-bottom: calc(var(--spacing) * 6); + } + + .mb-8 { + margin-bottom: calc(var(--spacing) * 8); + } + + .mb-12 { + margin-bottom: calc(var(--spacing) * 12); + } + + .mb-16 { + margin-bottom: calc(var(--spacing) * 16); + } + + .-ml-2 { + margin-left: calc(var(--spacing) * -2); + } + + .ml-1\.5 { + margin-left: calc(var(--spacing) * 1.5); + } + + .ml-6 { + margin-left: calc(var(--spacing) * 6); + } + + .line-clamp-1 { + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + + .block { + display: block; + } + + .flex { + display: flex; + } + + .grid { + display: grid; + } + + .hidden { + display: none; + } + + .inline { + display: inline; + } + + .inline-block { + display: inline-block; + } + + .inline-flex { + display: inline-flex; + } + + .table { + display: table; + } + + .size-4 { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + + .size-9 { + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + } + + .h-0\.5 { + height: calc(var(--spacing) * .5); + } + + .h-2 { + height: calc(var(--spacing) * 2); + } + + .h-3 { + height: calc(var(--spacing) * 3); + } + + .h-3\.5 { + height: calc(var(--spacing) * 3.5); + } + + .h-4 { + height: calc(var(--spacing) * 4); + } + + .h-5 { + height: calc(var(--spacing) * 5); + } + + .h-6 { + height: calc(var(--spacing) * 6); + } + + .h-8 { + height: calc(var(--spacing) * 8); + } + + .h-9 { + height: calc(var(--spacing) * 9); + } + + .h-10 { + height: calc(var(--spacing) * 10); + } + + .h-12 { + height: calc(var(--spacing) * 12); + } + + .h-14 { + height: calc(var(--spacing) * 14); + } + + .h-16 { + height: calc(var(--spacing) * 16); + } + + .h-48 { + height: calc(var(--spacing) * 48); + } + + .h-56 { + height: calc(var(--spacing) * 56); + } + + .h-\[400px\] { + height: 400px; + } + + .h-\[500px\] { + height: 500px; + } + + .h-full { + height: 100%; + } + + .min-h-0 { + min-height: calc(var(--spacing) * 0); + } + + .min-h-\[24px\] { + min-height: 24px; + } + + .min-h-screen { + min-height: 100vh; + } + + .w-0\.5 { + width: calc(var(--spacing) * .5); + } + + .w-2 { + width: calc(var(--spacing) * 2); + } + + .w-3 { + width: calc(var(--spacing) * 3); + } + + .w-3\.5 { + width: calc(var(--spacing) * 3.5); + } + + .w-4 { + width: calc(var(--spacing) * 4); + } + + .w-5 { + width: calc(var(--spacing) * 5); + } + + .w-6 { + width: calc(var(--spacing) * 6); + } + + .w-8 { + width: calc(var(--spacing) * 8); + } + + .w-12 { + width: calc(var(--spacing) * 12); + } + + .w-14 { + width: calc(var(--spacing) * 14); + } + + .w-fit { + width: fit-content; + } + + .w-full { + width: 100%; + } + + .max-w-3xl { + max-width: var(--container-3xl); + } + + .max-w-4xl { + max-width: var(--container-4xl); + } + + .max-w-5xl { + max-width: var(--container-5xl); + } + + .max-w-6xl { + max-width: var(--container-6xl); + } + + .max-w-lg { + max-width: var(--container-lg); + } + + .max-w-md { + max-width: var(--container-md); + } + + .max-w-none { + max-width: none; + } + + .min-w-0 { + min-width: calc(var(--spacing) * 0); + } + + .min-w-16 { + min-width: calc(var(--spacing) * 16); + } + + .min-w-\[180px\] { + min-width: 180px; + } + + .min-w-full { + min-width: 100%; + } + + .flex-1 { + flex: 1; + } + + .flex-shrink-0, .shrink-0 { + flex-shrink: 0; + } + + .grow { + flex-grow: 1; + } + + .-translate-x-1\/2 { + --tw-translate-x: calc(calc(1 / 2 * 100%) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .-translate-x-full { + --tw-translate-x: -100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + + .transform { + transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); + } + + .animate-spin { + animation: var(--animate-spin); + } + + .cursor-default { + cursor: default; + } + + .cursor-pointer { + cursor: pointer; + } + + .list-decimal { + list-style-type: decimal; + } + + .list-disc { + list-style-type: disc; + } + + .auto-rows-min { + grid-auto-rows: min-content; + } + + .grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .grid-rows-\[auto_auto\] { + grid-template-rows: auto auto; + } + + .flex-col { + flex-direction: column; + } + + .flex-wrap { + flex-wrap: wrap; + } + + .items-baseline { + align-items: baseline; + } + + .items-center { + align-items: center; + } + + .items-start { + align-items: flex-start; + } + + .justify-between { + justify-content: space-between; + } + + .justify-center { + justify-content: center; + } + + .gap-1 { + gap: calc(var(--spacing) * 1); + } + + .gap-1\.5 { + gap: calc(var(--spacing) * 1.5); + } + + .gap-2 { + gap: calc(var(--spacing) * 2); + } + + .gap-3 { + gap: calc(var(--spacing) * 3); + } + + .gap-4 { + gap: calc(var(--spacing) * 4); + } + + .gap-6 { + gap: calc(var(--spacing) * 6); + } + + .gap-8 { + gap: calc(var(--spacing) * 8); + } + + .gap-12 { + gap: calc(var(--spacing) * 12); + } + + :where(.space-y-2 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); + } + + :where(.space-y-3 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse))); + } + + :where(.space-y-4 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); + } + + :where(.space-y-6 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse))); + } + + :where(.space-y-8 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse))); + } + + .self-start { + align-self: flex-start; + } + + .justify-self-end { + justify-self: flex-end; + } + + .overflow-hidden { + overflow: hidden; + } + + .overflow-x-auto { + overflow-x: auto; + } + + .rounded { + border-radius: .25rem; + } + + .rounded-2xl { + border-radius: var(--radius-2xl); + } + + .rounded-full { + border-radius: 3.40282e38px; + } + + .rounded-lg { + border-radius: var(--radius); + } + + .rounded-md { + border-radius: calc(var(--radius) - 2px); + } + + .rounded-xl { + border-radius: calc(var(--radius) + 4px); + } + + .border { + border-style: var(--tw-border-style); + border-width: 1px; + } + + .border-2 { + border-style: var(--tw-border-style); + border-width: 2px; + } + + .border-4 { + border-style: var(--tw-border-style); + border-width: 4px; + } + + .border-t { + border-top-style: var(--tw-border-style); + border-top-width: 1px; + } + + .border-b { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + + .border-background { + border-color: var(--background); + } + + .border-border { + border-color: var(--border); + } + + .border-gray-100 { + border-color: var(--color-gray-100); + } + + .border-gray-200 { + border-color: var(--color-gray-200); + } + + .border-primary\/20 { + border-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .border-primary\/20 { + border-color: color-mix(in oklab, var(--primary) 20%, transparent); + } + } + + .border-transparent { + border-color: #0000; + } + + .bg-background { + background-color: var(--background); + } + + .bg-background\/50 { + background-color: var(--background); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/50 { + background-color: color-mix(in oklab, var(--background) 50%, transparent); + } + } + + .bg-background\/80 { + background-color: var(--background); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/80 { + background-color: color-mix(in oklab, var(--background) 80%, transparent); + } + } + + .bg-blue-600 { + background-color: var(--color-blue-600); + } + + .bg-border { + background-color: var(--border); + } + + .bg-card { + background-color: var(--card); + } + + .bg-destructive { + background-color: var(--destructive); + } + + .bg-gray-50 { + background-color: var(--color-gray-50); + } + + .bg-gray-100 { + background-color: var(--color-gray-100); + } + + .bg-primary { + background-color: var(--primary); + } + + .bg-primary\/5 { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/5 { + background-color: color-mix(in oklab, var(--primary) 5%, transparent); + } + } + + .bg-primary\/10 { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/10 { + background-color: color-mix(in oklab, var(--primary) 10%, transparent); + } + } + + .bg-primary\/70 { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/70 { + background-color: color-mix(in oklab, var(--primary) 70%, transparent); + } + } + + .bg-primary\/90 { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/90 { + background-color: color-mix(in oklab, var(--primary) 90%, transparent); + } + } + + .bg-secondary { + background-color: var(--secondary); + } + + .bg-secondary\/10 { + background-color: var(--secondary); + } + + @supports (color: color-mix(in lab, red, red)) { + .bg-secondary\/10 { + background-color: color-mix(in oklab, var(--secondary) 10%, transparent); + } + } + + .bg-white { + background-color: var(--color-white); + } + + .bg-gradient-to-b { + --tw-gradient-position: to bottom in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + + .bg-gradient-to-br { + --tw-gradient-position: to bottom right in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + + .bg-gradient-to-t { + --tw-gradient-position: to top in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + + .from-background { + --tw-gradient-from: var(--background); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + .from-black\/40 { + --tw-gradient-from: #0006; + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .from-black\/40 { + --tw-gradient-from: color-mix(in oklab, var(--color-black) 40%, transparent); + } + } + + .from-black\/60 { + --tw-gradient-from: #0009; + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .from-black\/60 { + --tw-gradient-from: color-mix(in oklab, var(--color-black) 60%, transparent); + } + } + + .from-primary { + --tw-gradient-from: var(--primary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + .from-primary\/20 { + --tw-gradient-from: var(--primary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .from-primary\/20 { + --tw-gradient-from: color-mix(in oklab, var(--primary) 20%, transparent); + } + } + + .from-primary\/60 { + --tw-gradient-from: var(--primary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .from-primary\/60 { + --tw-gradient-from: color-mix(in oklab, var(--primary) 60%, transparent); + } + } + + .via-primary { + --tw-gradient-via: var(--primary); + --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-via-stops); + } + + .via-transparent { + --tw-gradient-via: transparent; + --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-via-stops); + } + + .to-primary\/30 { + --tw-gradient-to: var(--primary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .to-primary\/30 { + --tw-gradient-to: color-mix(in oklab, var(--primary) 30%, transparent); + } + } + + .to-primary\/60 { + --tw-gradient-to: var(--primary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .to-primary\/60 { + --tw-gradient-to: color-mix(in oklab, var(--primary) 60%, transparent); + } + } + + .to-secondary\/20 { + --tw-gradient-to: var(--secondary); + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + @supports (color: color-mix(in lab, red, red)) { + .to-secondary\/20 { + --tw-gradient-to: color-mix(in oklab, var(--secondary) 20%, transparent); + } + } + + .to-transparent { + --tw-gradient-to: transparent; + --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); + } + + .object-cover { + object-fit: cover; + } + + .p-3 { + padding: calc(var(--spacing) * 3); + } + + .p-6 { + padding: calc(var(--spacing) * 6); + } + + .p-8 { + padding: calc(var(--spacing) * 8); + } + + .px-2 { + padding-inline: calc(var(--spacing) * 2); + } + + .px-3 { + padding-inline: calc(var(--spacing) * 3); + } + + .px-4 { + padding-inline: calc(var(--spacing) * 4); + } + + .px-6 { + padding-inline: calc(var(--spacing) * 6); + } + + .py-0\.5 { + padding-block: calc(var(--spacing) * .5); + } + + .py-1 { + padding-block: calc(var(--spacing) * 1); + } + + .py-1\.5 { + padding-block: calc(var(--spacing) * 1.5); + } + + .py-2 { + padding-block: calc(var(--spacing) * 2); + } + + .py-4 { + padding-block: calc(var(--spacing) * 4); + } + + .py-8 { + padding-block: calc(var(--spacing) * 8); + } + + .py-16 { + padding-block: calc(var(--spacing) * 16); + } + + .py-20 { + padding-block: calc(var(--spacing) * 20); + } + + .pt-2 { + padding-top: calc(var(--spacing) * 2); + } + + .pt-4 { + padding-top: calc(var(--spacing) * 4); + } + + .pt-6 { + padding-top: calc(var(--spacing) * 6); + } + + .pt-8 { + padding-top: calc(var(--spacing) * 8); + } + + .pr-20 { + padding-right: calc(var(--spacing) * 20); + } + + .pb-6 { + padding-bottom: calc(var(--spacing) * 6); + } + + .pl-0 { + padding-left: calc(var(--spacing) * 0); + } + + .text-center { + text-align: center; + } + + .text-left { + text-align: left; + } + + .align-middle { + vertical-align: middle; + } + + .text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } + + .text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } + + .text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + + .text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + + .leading-none { + --tw-leading: 1; + line-height: 1; + } + + .leading-relaxed { + --tw-leading: var(--leading-relaxed); + line-height: var(--leading-relaxed); + } + + .font-bold { + --tw-font-weight: var(--font-weight-bold); + font-weight: var(--font-weight-bold); + } + + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + + .font-semibold { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + + .tracking-tight { + --tw-tracking: var(--tracking-tight); + letter-spacing: var(--tracking-tight); + } + + .whitespace-nowrap { + white-space: nowrap; + } + + .text-card-foreground { + color: var(--card-foreground); + } + + .text-destructive { + color: var(--destructive); + } + + .text-foreground { + color: var(--foreground); + } + + .text-gray-400 { + color: var(--color-gray-400); + } + + .text-gray-500 { + color: var(--color-gray-500); + } + + .text-gray-700 { + color: var(--color-gray-700); + } + + .text-gray-900 { + color: var(--color-gray-900); + } + + .text-green-500 { + color: var(--color-green-500); + } + + .text-muted-foreground { + color: var(--muted-foreground); + } + + .text-primary { + color: var(--primary); + } + + .text-primary-foreground { + color: var(--primary-foreground); + } + + .text-red-400 { + color: var(--color-red-400); + } + + .text-secondary-foreground { + color: var(--secondary-foreground); + } + + .text-white { + color: var(--color-white); + } + + .uppercase { + text-transform: uppercase; + } + + .underline-offset-4 { + text-underline-offset: 4px; + } + + .opacity-0 { + opacity: 0; + } + + .opacity-75 { + opacity: .75; + } + + .opacity-90 { + opacity: .9; + } + + .shadow-lg { + --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, #0000001a), 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + + .shadow-md { + --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a), 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + + .shadow-sm { + --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + + .filter { + filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, ); + } + + .backdrop-blur-md { + --tw-backdrop-blur: blur(var(--blur-md)); + -webkit-backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + } + + .backdrop-blur-sm { + --tw-backdrop-blur: blur(var(--blur-sm)); + -webkit-backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + } + + .transition { + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-\[color\,box-shadow\] { + transition-property: color, box-shadow; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-all { + transition-property: all; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-colors { + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-opacity { + transition-property: opacity; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-shadow { + transition-property: box-shadow; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .transition-transform { + transition-property: transform, translate, scale, rotate; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + + .duration-300 { + --tw-duration: .3s; + transition-duration: .3s; + } + + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } + + .outline-none { + --tw-outline-style: none; + outline-style: none; + } + + .\[comparison_table\:\(\\w\+\)\\\] { + comparison_table: (w + ) undefined; + } + + .\[comparison_table\:streaming\] { + comparison_table: streaming; + } + + .\[comparison_table\:type\] { + comparison_table: type; + } + + @media (hover: hover) { + .group-hover\:scale-105:is(:where(.group):hover *) { + --tw-scale-x: 105%; + --tw-scale-y: 105%; + --tw-scale-z: 105%; + scale: var(--tw-scale-x) var(--tw-scale-y); + } + } + + @media (hover: hover) { + .group-hover\:text-primary:is(:where(.group):hover *) { + color: var(--primary); + } + } + + @media (hover: hover) { + .group-hover\:opacity-100:is(:where(.group):hover *) { + opacity: 1; + } + } + + @media (hover: hover) { + .group-hover\/button\:translate-x-1:is(:where(.group\/button):hover *) { + --tw-translate-x: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + } + + .first\:mt-0:first-child { + margin-top: calc(var(--spacing) * 0); + } + + @media (hover: hover) { + .hover\:bg-accent:hover { + background-color: var(--accent); + } + } + + @media (hover: hover) { + .hover\:bg-blue-700:hover { + background-color: var(--color-blue-700); + } + } + + @media (hover: hover) { + .hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-destructive\/90:hover { + background-color: color-mix(in oklab, var(--destructive) 90%, transparent); + } + } + } + + @media (hover: hover) { + .hover\:bg-primary:hover { + background-color: var(--primary); + } + } + + @media (hover: hover) { + .hover\:bg-primary\/10:hover { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/10:hover { + background-color: color-mix(in oklab, var(--primary) 10%, transparent); + } + } + } + + @media (hover: hover) { + .hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/90:hover { + background-color: color-mix(in oklab, var(--primary) 90%, transparent); + } + } + } + + @media (hover: hover) { + .hover\:bg-secondary\/80:hover { + background-color: var(--secondary); + } + + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-secondary\/80:hover { + background-color: color-mix(in oklab, var(--secondary) 80%, transparent); + } + } + } + + @media (hover: hover) { + .hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + } + + @media (hover: hover) { + .hover\:text-primary:hover { + color: var(--primary); + } + } + + @media (hover: hover) { + .hover\:text-primary-foreground:hover { + color: var(--primary-foreground); + } + } + + @media (hover: hover) { + .hover\:underline:hover { + text-decoration-line: underline; + } + } + + @media (hover: hover) { + .hover\:shadow-lg:hover { + --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, #0000001a), 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + } + + @media (hover: hover) { + .hover\:shadow-md:hover { + --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a), 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + } + + @media (hover: hover) { + .hover\:shadow-xl:hover { + --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, #0000001a), 0 8px 10px -6px var(--tw-shadow-color, #0000001a); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + } + + .focus-visible\:border-ring:focus-visible { + border-color: var(--ring); + } + + .focus-visible\:ring-\[3px\]:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent); + } + } + + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: var(--ring); + } + + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + + .disabled\:pointer-events-none:disabled { + pointer-events: none; + } + + .disabled\:opacity-50:disabled { + opacity: .5; + } + + .has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot="card-action"]) { + grid-template-columns: 1fr auto; + } + + .has-\[\>svg\]\:px-2\.5:has( > svg) { + padding-inline: calc(var(--spacing) * 2.5); + } + + .has-\[\>svg\]\:px-3:has( > svg) { + padding-inline: calc(var(--spacing) * 3); + } + + .has-\[\>svg\]\:px-4:has( > svg) { + padding-inline: calc(var(--spacing) * 4); + } + + .aria-invalid\:border-destructive[aria-invalid="true"] { + border-color: var(--destructive); + } + + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent); + } + } + + @media (width >= 48rem) { + .md\:mt-0 { + margin-top: calc(var(--spacing) * 0); + } + } + + @media (width >= 48rem) { + .md\:block { + display: block; + } + } + + @media (width >= 48rem) { + .md\:flex { + display: flex; + } + } + + @media (width >= 48rem) { + .md\:hidden { + display: none; + } + } + + @media (width >= 48rem) { + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + + @media (width >= 48rem) { + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } + + @media (width >= 48rem) { + .md\:pl-24 { + padding-left: calc(var(--spacing) * 24); + } + } + + @media (width >= 64rem) { + .lg\:block { + display: block; + } + } + + @media (width >= 64rem) { + .lg\:h-\[600px\] { + height: 600px; + } + } + + @media (width >= 64rem) { + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + + @media (width >= 64rem) { + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + } + + @media (width >= 64rem) { + .lg\:text-5xl { + font-size: var(--text-5xl); + line-height: var(--tw-leading, var(--text-5xl--line-height)); + } + } + + @media (width >= 80rem) { + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } + + @media (width >= 80rem) { + .xl\:text-6xl { + font-size: var(--text-6xl); + line-height: var(--tw-leading, var(--text-6xl--line-height)); + } + } + + .dark\:border-input:is(.dark *) { + border-color: var(--input); + } + + .dark\:bg-destructive\/60:is(.dark *) { + background-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-destructive\/60:is(.dark *) { + background-color: color-mix(in oklab, var(--destructive) 60%, transparent); + } + } + + .dark\:bg-input\/30:is(.dark *) { + background-color: var(--input); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-input\/30:is(.dark *) { + background-color: color-mix(in oklab, var(--input) 30%, transparent); + } + } + + @media (hover: hover) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: var(--accent); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: color-mix(in oklab, var(--accent) 50%, transparent); + } + } + } + + @media (hover: hover) { + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: var(--input); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: color-mix(in oklab, var(--input) 50%, transparent); + } + } + } + + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent); + } + } + + .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { + --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent); + } + } + + .\[\&_svg\]\:pointer-events-none svg { + pointer-events: none; + } + + .\[\&_svg\]\:shrink-0 svg { + flex-shrink: 0; + } + + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*="size-"]) { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + + .\[\.border-b\]\:pb-6.border-b { + padding-bottom: calc(var(--spacing) * 6); + } + + .\[\.border-t\]\:pt-6.border-t { + padding-top: calc(var(--spacing) * 6); + } + + .\[\&\:last-child\]\:pb-6:last-child { + padding-bottom: calc(var(--spacing) * 6); + } + + .\[\&\>svg\]\:pointer-events-none > svg { + pointer-events: none; + } + + .\[\&\>svg\]\:size-3 > svg { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-accent:hover { + background-color: var(--accent); + } + } + + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: color-mix(in oklab, var(--destructive) 90%, transparent); + } + } + } + + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: color-mix(in oklab, var(--primary) 90%, transparent); + } + } + } + + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: var(--secondary); + } + + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: color-mix(in oklab, var(--secondary) 90%, transparent); + } + } + } + + @media (hover: hover) { + a.\[a\&\]\:hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + } +} + +.github-corner:hover .octo-arm { + animation: .56s ease-in-out octocat-wave; +} + +@keyframes octocat-wave { + 0%, 100% { + transform: rotate(0); + } + + 20%, 60% { + transform: rotate(-25deg); + } + + 40%, 80% { + transform: rotate(10deg); + } +} + +#hero-profile-image { + box-shadow: 0 50px 100px -20px #32325d40, 0 30px 60px -30px #0000004d; +} + +.morphing-border { + border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; + animation: 20s infinite alternate morphing-border; +} + +@keyframes morphing-border { + 0% { + border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; + } + + 25% { + border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%; + } + + 50% { + border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%; + } + + 75% { + border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%; + } +} + +:root { + --font-size: 14px; + --background: #fff; + --foreground: oklch(.145 0 0); + --card: #fff; + --card-foreground: oklch(.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(.145 0 0); + --primary: #030213; + --primary-foreground: oklch(1 0 0); + --secondary: oklch(.95 .0058 264.53); + --secondary-foreground: #030213; + --muted: #ececf0; + --muted-foreground: #717182; + --accent: #e9ebef; + --accent-foreground: #030213; + --destructive: #d4183d; + --destructive-foreground: #fff; + --border: #0000001a; + --input: transparent; + --input-background: #f3f3f5; + --switch-background: #cbced4; + --font-weight-medium: 500; + --font-weight-normal: 400; + --ring: oklch(.708 0 0); + --chart-1: oklch(.646 .222 41.116); + --chart-2: oklch(.6 .118 184.704); + --chart-3: oklch(.398 .07 227.392); + --chart-4: oklch(.828 .189 84.429); + --chart-5: oklch(.769 .188 70.08); + --radius: .625rem; + --sidebar: oklch(.985 0 0); + --sidebar-foreground: oklch(.145 0 0); + --sidebar-primary: #030213; + --sidebar-primary-foreground: oklch(.985 0 0); + --sidebar-accent: oklch(.97 0 0); + --sidebar-accent-foreground: oklch(.205 0 0); + --sidebar-border: oklch(.922 0 0); + --sidebar-ring: oklch(.708 0 0); +} + +.dark { + --background: oklch(.145 0 0); + --foreground: oklch(.985 0 0); + --card: oklch(.145 0 0); + --card-foreground: oklch(.985 0 0); + --popover: oklch(.145 0 0); + --popover-foreground: oklch(.985 0 0); + --primary: oklch(.985 0 0); + --primary-foreground: oklch(.205 0 0); + --secondary: oklch(.269 0 0); + --secondary-foreground: oklch(.985 0 0); + --muted: oklch(.269 0 0); + --muted-foreground: oklch(.708 0 0); + --accent: oklch(.269 0 0); + --accent-foreground: oklch(.985 0 0); + --destructive: oklch(.396 .141 25.723); + --destructive-foreground: oklch(.637 .237 25.331); + --border: oklch(.269 0 0); + --input: oklch(.269 0 0); + --ring: oklch(.439 0 0); + --font-weight-medium: 500; + --font-weight-normal: 400; + --chart-1: oklch(.488 .243 264.376); + --chart-2: oklch(.696 .17 162.48); + --chart-3: oklch(.769 .188 70.08); + --chart-4: oklch(.627 .265 303.9); + --chart-5: oklch(.645 .246 16.439); + --sidebar: oklch(.205 0 0); + --sidebar-foreground: oklch(.985 0 0); + --sidebar-primary: oklch(.488 .243 264.376); + --sidebar-primary-foreground: oklch(.985 0 0); + --sidebar-accent: oklch(.269 0 0); + --sidebar-accent-foreground: oklch(.985 0 0); + --sidebar-border: oklch(.269 0 0); + --sidebar-ring: oklch(.439 0 0); +} + +html { + font-size: var(--font-size); +} + +.code-inline { + background-color: oklch(.95 .005 264); + color: oklch(.4 .15 330); + border: 1px solid oklch(.9 .005 264); + border-radius: .25rem; + padding: .15rem .4rem; + font-family: ui-monospace, SFMono-Regular, SF Mono, Monaco, Consolas, Liberation Mono, Courier New, monospace; + font-size: .875em; + font-weight: 500; +} + +.dark .code-inline { + background-color: oklch(.25 .01 264); + color: oklch(.75 .1 180); + border-color: oklch(.3 .01 264); +} + +.code-block-container { + border: 1px solid oklch(.88 .005 264); + background: #fafafa; + border-radius: .625rem; + margin-bottom: 1.5rem; + overflow: hidden; + box-shadow: 0 1px 3px #0000000d; +} + +.dark .code-block-container { + background: oklch(.18 .01 264); + border-color: oklch(.28 .01 264); + box-shadow: 0 1px 3px #0000004d; +} + +.code-block-header { + background: oklch(.97 .005 264); + border-bottom: 1px solid oklch(.88 .005 264); + justify-content: space-between; + align-items: center; + padding: .625rem 1rem; + display: flex; +} + +.dark .code-block-header { + background: oklch(.22 .01 264); + border-bottom-color: oklch(.28 .01 264); +} + +.code-block-language { + text-transform: uppercase; + letter-spacing: .05em; + color: oklch(.5 .05 264); + font-family: ui-monospace, SFMono-Regular, SF Mono, Monaco, Consolas, Liberation Mono, Courier New, monospace; + font-size: .75rem; + font-weight: 600; +} + +.dark .code-block-language { + color: oklch(.65 .05 180); +} + +.copy-button { + gap: 0; + height: 1.75rem; + padding: 0 .625rem; + font-size: .75rem; + transition: all .2s; +} + +.copy-button:hover { + background-color: oklch(.92 .005 264); +} + +.dark .copy-button:hover { + background-color: oklch(.28 .01 264); +} + +.code-block-content { + background: #fafafa; + overflow-x: auto; +} + +.dark .code-block-content { + background: oklch(.18 .01 264); +} + +.code-block-content::-webkit-scrollbar { + height: .5rem; +} + +.code-block-content::-webkit-scrollbar-track { + background: none; +} + +.code-block-content::-webkit-scrollbar-thumb { + background: oklch(.75 .005 264); + border-radius: .25rem; +} + +.dark .code-block-content::-webkit-scrollbar-thumb { + background: oklch(.35 .01 264); +} + +.code-block-content::-webkit-scrollbar-thumb:hover { + background: oklch(.65 .005 264); +} + +.dark .code-block-content::-webkit-scrollbar-thumb:hover { + background: oklch(.45 .01 264); +} + +.comparison-table-wrapper { + border: 1px solid oklch(.88 .005 264); + border-radius: .625rem; + margin: 2rem 0; + overflow: hidden; + box-shadow: 0 1px 3px #0000000d; +} + +.dark .comparison-table-wrapper { + border-color: oklch(.28 .01 264); + box-shadow: 0 1px 3px #0000004d; +} + +.comparison-table { + border-collapse: collapse; + width: 100%; + font-size: .875rem; +} + +.comparison-table thead { + background: oklch(.97 .005 264); + border-bottom: 2px solid oklch(.88 .005 264); +} + +.dark .comparison-table thead { + background: oklch(.22 .01 264); + border-bottom-color: oklch(.35 .01 264); +} + +.comparison-table th { + text-align: left; + color: oklch(.3 .05 264); + border-right: 1px solid oklch(.9 .005 264); + padding: .875rem 1rem; + font-weight: 600; +} + +.dark .comparison-table th { + color: oklch(.85 .05 180); + border-right-color: oklch(.28 .01 264); +} + +.comparison-table th:last-child { + border-right: none; +} + +.comparison-table tbody tr { + border-bottom: 1px solid oklch(.92 .005 264); + transition: background-color .15s; +} + +.dark .comparison-table tbody tr { + border-bottom-color: oklch(.25 .01 264); +} + +.comparison-table tbody tr:hover { + background-color: oklch(.98 .005 264); +} + +.dark .comparison-table tbody tr:hover { + background-color: oklch(.19 .01 264); +} + +.comparison-table tbody tr:last-child { + border-bottom: none; +} + +.comparison-table td { + color: oklch(.4 .05 264); + border-right: 1px solid oklch(.94 .005 264); + vertical-align: top; + padding: .875rem 1rem; +} + +.dark .comparison-table td { + color: oklch(.75 .05 180); + border-right-color: oklch(.25 .01 264); +} + +.comparison-table td:last-child { + border-right: none; +} + +.comparison-table td:first-child { + color: oklch(.3 .05 264); + font-weight: 500; +} + +.dark .comparison-table td:first-child { + color: oklch(.85 .05 180); +} + +.comparison-table-wrapper::-webkit-scrollbar { + height: .5rem; +} + +.comparison-table-wrapper::-webkit-scrollbar-track { + background: oklch(.95 .005 264); +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-track { + background: oklch(.2 .01 264); +} + +.comparison-table-wrapper::-webkit-scrollbar-thumb { + background: oklch(.75 .005 264); + border-radius: .25rem; +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-thumb { + background: oklch(.35 .01 264); +} + +.comparison-table-wrapper::-webkit-scrollbar-thumb:hover { + background: oklch(.65 .005 264); +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-thumb:hover { + background: oklch(.45 .01 264); +} + +@property --tw-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} + +@property --tw-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} + +@property --tw-translate-z { + syntax: "*"; + inherits: false; + initial-value: 0; +} + +@property --tw-rotate-x { + syntax: "*"; + inherits: false; + initial-value: rotateX(0); +} + +@property --tw-rotate-y { + syntax: "*"; + inherits: false; + initial-value: rotateY(0); +} + +@property --tw-rotate-z { + syntax: "*"; + inherits: false; + initial-value: rotateZ(0); +} + +@property --tw-skew-x { + syntax: "*"; + inherits: false; + initial-value: skewX(0); +} + +@property --tw-skew-y { + syntax: "*"; + inherits: false; + initial-value: skewY(0); +} + +@property --tw-space-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} + +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} + +@property --tw-gradient-position { + syntax: "*"; + inherits: false +} + +@property --tw-gradient-from { + syntax: ""; + inherits: false; + initial-value: #0000; +} + +@property --tw-gradient-via { + syntax: ""; + inherits: false; + initial-value: #0000; +} + +@property --tw-gradient-to { + syntax: ""; + inherits: false; + initial-value: #0000; +} + +@property --tw-gradient-stops { + syntax: "*"; + inherits: false +} + +@property --tw-gradient-via-stops { + syntax: "*"; + inherits: false +} + +@property --tw-gradient-from-position { + syntax: ""; + inherits: false; + initial-value: 0%; +} + +@property --tw-gradient-via-position { + syntax: ""; + inherits: false; + initial-value: 50%; +} + +@property --tw-gradient-to-position { + syntax: ""; + inherits: false; + initial-value: 100%; +} + +@property --tw-leading { + syntax: "*"; + inherits: false +} + +@property --tw-font-weight { + syntax: "*"; + inherits: false +} + +@property --tw-tracking { + syntax: "*"; + inherits: false +} + +@property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} + +@property --tw-shadow-color { + syntax: "*"; + inherits: false +} + +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} + +@property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} + +@property --tw-inset-shadow-color { + syntax: "*"; + inherits: false +} + +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} + +@property --tw-ring-color { + syntax: "*"; + inherits: false +} + +@property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} + +@property --tw-inset-ring-color { + syntax: "*"; + inherits: false +} + +@property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} + +@property --tw-ring-inset { + syntax: "*"; + inherits: false +} + +@property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0; +} + +@property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; +} + +@property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} + +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} + +@property --tw-blur { + syntax: "*"; + inherits: false +} + +@property --tw-brightness { + syntax: "*"; + inherits: false +} + +@property --tw-contrast { + syntax: "*"; + inherits: false +} + +@property --tw-grayscale { + syntax: "*"; + inherits: false +} + +@property --tw-hue-rotate { + syntax: "*"; + inherits: false +} + +@property --tw-invert { + syntax: "*"; + inherits: false +} + +@property --tw-opacity { + syntax: "*"; + inherits: false +} + +@property --tw-saturate { + syntax: "*"; + inherits: false +} + +@property --tw-sepia { + syntax: "*"; + inherits: false +} + +@property --tw-drop-shadow { + syntax: "*"; + inherits: false +} + +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false +} + +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} + +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-blur { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-brightness { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-contrast { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-grayscale { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-hue-rotate { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-invert { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-opacity { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-saturate { + syntax: "*"; + inherits: false +} + +@property --tw-backdrop-sepia { + syntax: "*"; + inherits: false +} + +@property --tw-duration { + syntax: "*"; + inherits: false +} + +@property --tw-ease { + syntax: "*"; + inherits: false +} + +@property --tw-scale-x { + syntax: "*"; + inherits: false; + initial-value: 1; +} + +@property --tw-scale-y { + syntax: "*"; + inherits: false; + initial-value: 1; +} + +@property --tw-scale-z { + syntax: "*"; + inherits: false; + initial-value: 1; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* virtual-fs:file:///components/HeroProfileImage/HeroProfileImage.css */ +#hero-profile-image { + box-shadow: rgba(50, 50, 93, 0.25) 0 50px 100px -20px, rgba(0, 0, 0, 0.3) 0 30px 60px -30px; +} +.morphing-border { + border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; + animation: morphing-border 20s infinite alternate; +} +@keyframes morphing-border { + 0% { + border-radius: 30% 70% 70% 30%/30% 30% 70% 70%; + } + 25% { + border-radius: 58% 42% 75% 25%/76% 46% 54% 24%; + } + 50% { + border-radius: 50% 50% 33% 67%/55% 27% 73% 45%; + } + 75% { + border-radius: 33% 67% 58% 42%/63% 68% 32% 37%; + } +} + +/* virtual-fs:file:///components/GitHubCorner/GitHubCorner.css */ +.github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; +} +@keyframes octocat-wave { + 0%, 100% { + transform: rotate(0); + } + 20%, 60% { + transform: rotate(-25deg); + } + 40%, 80% { + transform: rotate(10deg); + } +} +/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29tcG9uZW50cy9IZXJvUHJvZmlsZUltYWdlL0hlcm9Qcm9maWxlSW1hZ2UuY3NzIiwgImNvbXBvbmVudHMvR2l0SHViQ29ybmVyL0dpdEh1YkNvcm5lci5jc3MiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIiNoZXJvLXByb2ZpbGUtaW1hZ2Uge1xuICAgIGJveC1zaGFkb3c6IHJnYmEoNTAsIDUwLCA5MywgMC4yNSkgMCA1MHB4IDEwMHB4IC0yMHB4LCByZ2JhKDAsIDAsIDAsIDAuMykgMCAzMHB4IDYwcHggLTMwcHg7XG59XG5cbi5tb3JwaGluZy1ib3JkZXIge1xuICAgIGJvcmRlci1yYWRpdXM6IDMwJSA3MCUgNzAlIDMwJSAvIDMwJSAzMCUgNzAlIDcwJTtcbiAgICBhbmltYXRpb246IG1vcnBoaW5nLWJvcmRlciAyMHMgaW5maW5pdGUgYWx0ZXJuYXRlO1xufVxuXG5Aa2V5ZnJhbWVzIG1vcnBoaW5nLWJvcmRlciB7XG4gICAgMCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMCUgNzAlIDcwJSAzMCUvMzAlIDMwJSA3MCUgNzAlO1xuICAgIH1cbiAgICAyNSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1OCUgNDIlIDc1JSAyNSUvNzYlIDQ2JSA1NCUgMjQlO1xuICAgIH1cbiAgICA1MCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCUgNTAlIDMzJSA2NyUvNTUlIDI3JSA3MyUgNDUlO1xuICAgIH1cbiAgICA3NSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMyUgNjclIDU4JSA0MiUvNjMlIDY4JSAzMiUgMzclO1xuICAgIH1cbn0iLCAiLmdpdGh1Yi1jb3JuZXI6aG92ZXIgLm9jdG8tYXJtIHtcbiAgYW5pbWF0aW9uOiBvY3RvY2F0LXdhdmUgNTYwbXMgZWFzZS1pbi1vdXQ7XG59XG5cbkBrZXlmcmFtZXMgb2N0b2NhdC13YXZlIHtcbiAgMCUsXG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogcm90YXRlKDApO1xuICB9XG4gIDIwJSxcbiAgNjAlIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgtMjVkZWcpO1xuICB9XG4gIDQwJSxcbiAgODAlIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgxMGRlZyk7XG4gIH1cbn0iXSwKICAibWFwcGluZ3MiOiAiO0FBQUEsQ0FBQztBQUNHLGNBQVksS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxNQUFNLEtBQUssRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEtBQUs7QUFDMUY7QUFFQSxDQUFDO0FBQ0csaUJBQWUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJO0FBQzdDLGFBQVcsZ0JBQWdCLElBQUksU0FBUztBQUM1QztBQUVBLFdBTEM7QUFNRztBQUNJLG1CQUFlLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSTtBQUMvQztBQUNBO0FBQ0ksbUJBQWUsSUFBSSxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJO0FBQy9DO0FBQ0E7QUFDSSxtQkFBZSxJQUFJLElBQUksSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLElBQUk7QUFDL0M7QUFDQTtBQUNJLG1CQUFlLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSTtBQUMvQztBQUNKOzs7QUN0QkEsQ0FBQyxhQUFhLE9BQU8sQ0FBQztBQUNwQixhQUFXLGFBQWEsTUFBTTtBQUNoQztBQUVBLFdBSGE7QUFJWDtBQUVFLGVBQVcsT0FBTztBQUNwQjtBQUNBO0FBRUUsZUFBVyxPQUFPO0FBQ3BCO0FBQ0E7QUFFRSxlQUFXLE9BQU87QUFDcEI7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K */ diff --git a/src/styles/globals.css b/src/styles/globals.css index 77d2fd8..6c225a7 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,189 +1,427 @@ @custom-variant dark (&:is(.dark *)); :root { - --font-size: 14px; - --background: #ffffff; - --foreground: oklch(0.145 0 0); - --card: #ffffff; - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: #030213; - --primary-foreground: oklch(1 0 0); - --secondary: oklch(0.95 0.0058 264.53); - --secondary-foreground: #030213; - --muted: #ececf0; - --muted-foreground: #717182; - --accent: #e9ebef; - --accent-foreground: #030213; - --destructive: #d4183d; - --destructive-foreground: #ffffff; - --border: rgba(0, 0, 0, 0.1); - --input: transparent; - --input-background: #f3f3f5; - --switch-background: #cbced4; - --font-weight-medium: 500; - --font-weight-normal: 400; - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: #030213; - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --font-size: 14px; + --background: #ffffff; + --foreground: oklch(0.145 0 0); + --card: #ffffff; + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: #030213; + --primary-foreground: oklch(1 0 0); + --secondary: oklch(0.95 0.0058 264.53); + --secondary-foreground: #030213; + --muted: #ececf0; + --muted-foreground: #717182; + --accent: #e9ebef; + --accent-foreground: #030213; + --destructive: #d4183d; + --destructive-foreground: #ffffff; + --border: rgba(0, 0, 0, 0.1); + --input: transparent; + --input-background: #f3f3f5; + --switch-background: #cbced4; + --font-weight-medium: 500; + --font-weight-normal: 400; + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: #030213; + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.145 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.145 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.985 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.396 0.141 25.723); - --destructive-foreground: oklch(0.637 0.237 25.331); - --border: oklch(0.269 0 0); - --input: oklch(0.269 0 0); - --ring: oklch(0.439 0 0); - --font-weight-medium: 500; - --font-weight-normal: 400; - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.269 0 0); - --sidebar-ring: oklch(0.439 0 0); + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --font-weight-medium: 500; + --font-weight-normal: 400; + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-input-background: var(--input-background); - --color-switch-background: var(--switch-background); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-input-background: var(--input-background); + --color-switch-background: var(--switch-background); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); } @layer base { - * { - @apply border-border outline-ring/50; - } + * { + @apply border-border outline-ring/50; + } - body { - @apply bg-background text-foreground; - } + body { + @apply bg-background text-foreground; + } } /** * Base typography. This is not applied to elements which have an ancestor with a Tailwind text class. */ @layer base { - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) { - h1 { - font-size: var(--text-2xl); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - h2 { - font-size: var(--text-xl); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - h3 { - font-size: var(--text-lg); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - h4 { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - p { - font-size: var(--text-base); - font-weight: var(--font-weight-normal); - line-height: 1.5; - } - - label { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - button { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - input { - font-size: var(--text-base); - font-weight: var(--font-weight-normal); - line-height: 1.5; - } + :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) { + h1 { + font-size: var(--text-2xl); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + h2 { + font-size: var(--text-xl); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + h3 { + font-size: var(--text-lg); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + h4 { + font-size: var(--text-base); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + p { + font-size: var(--text-base); + font-weight: var(--font-weight-normal); + line-height: 1.5; + } + + label { + font-size: var(--text-base); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + button { + font-size: var(--text-base); + font-weight: var(--font-weight-medium); + line-height: 1.5; + } + + input { + font-size: var(--text-base); + font-weight: var(--font-weight-normal); + line-height: 1.5; } + } } html { - font-size: var(--font-size); + font-size: var(--font-size); +} + +/* Code block styles for blog posts */ +.code-inline { + background-color: oklch(0.95 0.005 264); + color: oklch(0.4 0.15 330); + padding: 0.15rem 0.4rem; + border-radius: 0.25rem; + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + font-size: 0.875em; + font-weight: 500; + border: 1px solid oklch(0.9 0.005 264); +} + +.dark .code-inline { + background-color: oklch(0.25 0.01 264); + color: oklch(0.75 0.1 180); + border-color: oklch(0.3 0.01 264); +} + +/* Code block container */ +.code-block-container { + margin-bottom: 1.5rem; + border-radius: 0.625rem; + overflow: hidden; + border: 1px solid oklch(0.88 0.005 264); + background: #fafafa; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.dark .code-block-container { + background: oklch(0.18 0.01 264); + border-color: oklch(0.28 0.01 264); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); +} + +/* Code block header */ +.code-block-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.625rem 1rem; + background: oklch(0.97 0.005 264); + border-bottom: 1px solid oklch(0.88 0.005 264); +} + +.dark .code-block-header { + background: oklch(0.22 0.01 264); + border-bottom-color: oklch(0.28 0.01 264); +} + +.code-block-language { + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: oklch(0.5 0.05 264); +} + +.dark .code-block-language { + color: oklch(0.65 0.05 180); +} + +/* Copy button */ +.copy-button { + height: 1.75rem; + padding: 0 0.625rem; + font-size: 0.75rem; + gap: 0; + transition: all 0.2s; +} + +.copy-button:hover { + background-color: oklch(0.92 0.005 264); +} + +.dark .copy-button:hover { + background-color: oklch(0.28 0.01 264); +} + +/* Code block content */ +.code-block-content { + overflow-x: auto; + background: #fafafa; +} + +.dark .code-block-content { + background: oklch(0.18 0.01 264); +} + +/* Scrollbar styling for code blocks */ +.code-block-content::-webkit-scrollbar { + height: 0.5rem; +} + +.code-block-content::-webkit-scrollbar-track { + background: transparent; +} + +.code-block-content::-webkit-scrollbar-thumb { + background: oklch(0.75 0.005 264); + border-radius: 0.25rem; +} + +.dark .code-block-content::-webkit-scrollbar-thumb { + background: oklch(0.35 0.01 264); +} + +.code-block-content::-webkit-scrollbar-thumb:hover { + background: oklch(0.65 0.005 264); +} + +.dark .code-block-content::-webkit-scrollbar-thumb:hover { + background: oklch(0.45 0.01 264); +} + +/* Comparison table styles */ +.comparison-table-wrapper { + margin: 2rem 0; + border-radius: 0.625rem; + overflow: hidden; + border: 1px solid oklch(0.88 0.005 264); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.dark .comparison-table-wrapper { + border-color: oklch(0.28 0.01 264); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); +} + +.comparison-table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; +} + +.comparison-table thead { + background: oklch(0.97 0.005 264); + border-bottom: 2px solid oklch(0.88 0.005 264); +} + +.dark .comparison-table thead { + background: oklch(0.22 0.01 264); + border-bottom-color: oklch(0.35 0.01 264); +} + +.comparison-table th { + padding: 0.875rem 1rem; + text-align: left; + font-weight: 600; + color: oklch(0.3 0.05 264); + border-right: 1px solid oklch(0.9 0.005 264); +} + +.dark .comparison-table th { + color: oklch(0.85 0.05 180); + border-right-color: oklch(0.28 0.01 264); +} + +.comparison-table th:last-child { + border-right: none; +} + +.comparison-table tbody tr { + border-bottom: 1px solid oklch(0.92 0.005 264); + transition: background-color 0.15s ease; +} + +.dark .comparison-table tbody tr { + border-bottom-color: oklch(0.25 0.01 264); +} + +.comparison-table tbody tr:hover { + background-color: oklch(0.98 0.005 264); +} + +.dark .comparison-table tbody tr:hover { + background-color: oklch(0.19 0.01 264); +} + +.comparison-table tbody tr:last-child { + border-bottom: none; +} + +.comparison-table td { + padding: 0.875rem 1rem; + color: oklch(0.4 0.05 264); + border-right: 1px solid oklch(0.94 0.005 264); + vertical-align: top; +} + +.dark .comparison-table td { + color: oklch(0.75 0.05 180); + border-right-color: oklch(0.25 0.01 264); +} + +.comparison-table td:last-child { + border-right: none; +} + +.comparison-table td:first-child { + font-weight: 500; + color: oklch(0.3 0.05 264); +} + +.dark .comparison-table td:first-child { + color: oklch(0.85 0.05 180); +} + +/* Scrollbar for table overflow */ +.comparison-table-wrapper::-webkit-scrollbar { + height: 0.5rem; +} + +.comparison-table-wrapper::-webkit-scrollbar-track { + background: oklch(0.95 0.005 264); +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-track { + background: oklch(0.2 0.01 264); +} + +.comparison-table-wrapper::-webkit-scrollbar-thumb { + background: oklch(0.75 0.005 264); + border-radius: 0.25rem; +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-thumb { + background: oklch(0.35 0.01 264); +} + +.comparison-table-wrapper::-webkit-scrollbar-thumb:hover { + background: oklch(0.65 0.005 264); +} + +.dark .comparison-table-wrapper::-webkit-scrollbar-thumb:hover { + background: oklch(0.45 0.01 264); } diff --git a/src/styles/index.css b/src/styles/index.css index e699c17..e69de29 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,5233 +0,0 @@ -/*! tailwindcss v4.1.3 | MIT License | https://tailwindcss.com */ -@layer properties { - @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { - *, :before, :after, ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-translate-z: 0; - --tw-rotate-x: rotateX(0); - --tw-rotate-y: rotateY(0); - --tw-rotate-z: rotateZ(0); - --tw-skew-x: skewX(0); - --tw-skew-y: skewY(0); - --tw-space-y-reverse: 0; - --tw-space-x-reverse: 0; - --tw-border-style: solid; - --tw-gradient-position: initial; - --tw-gradient-from: #0000; - --tw-gradient-via: #0000; - --tw-gradient-to: #0000; - --tw-gradient-stops: initial; - --tw-gradient-via-stops: initial; - --tw-gradient-from-position: 0%; - --tw-gradient-via-position: 50%; - --tw-gradient-to-position: 100%; - --tw-leading: initial; - --tw-font-weight: initial; - --tw-tracking: initial; - --tw-ordinal: initial; - --tw-slashed-zero: initial; - --tw-numeric-figure: initial; - --tw-numeric-spacing: initial; - --tw-numeric-fraction: initial; - --tw-shadow: 0 0 #0000; - --tw-shadow-color: initial; - --tw-shadow-alpha: 100%; - --tw-inset-shadow: 0 0 #0000; - --tw-inset-shadow-color: initial; - --tw-inset-shadow-alpha: 100%; - --tw-ring-color: initial; - --tw-ring-shadow: 0 0 #0000; - --tw-inset-ring-color: initial; - --tw-inset-ring-shadow: 0 0 #0000; - --tw-ring-inset: initial; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; - --tw-blur: initial; - --tw-brightness: initial; - --tw-contrast: initial; - --tw-grayscale: initial; - --tw-hue-rotate: initial; - --tw-invert: initial; - --tw-opacity: initial; - --tw-saturate: initial; - --tw-sepia: initial; - --tw-drop-shadow: initial; - --tw-drop-shadow-color: initial; - --tw-drop-shadow-alpha: 100%; - --tw-drop-shadow-size: initial; - --tw-backdrop-blur: initial; - --tw-backdrop-brightness: initial; - --tw-backdrop-contrast: initial; - --tw-backdrop-grayscale: initial; - --tw-backdrop-hue-rotate: initial; - --tw-backdrop-invert: initial; - --tw-backdrop-opacity: initial; - --tw-backdrop-saturate: initial; - --tw-backdrop-sepia: initial; - --tw-duration: initial; - --tw-ease: initial; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-scale-z: 1; - --tw-content: ""; - } - } -} - -@layer theme { - :root, :host { - --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-gray-50: oklch(.985 .002 247.839); - --color-gray-100: oklch(.967 .003 264.542); - --color-gray-200: oklch(.928 .006 264.531); - --color-gray-500: oklch(.551 .027 264.364); - --color-gray-700: oklch(.373 .034 259.733); - --color-gray-900: oklch(.21 .034 264.665); - --color-black: #000; - --color-white: #fff; - --spacing: .25rem; - --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-5xl: 64rem; - --container-6xl: 72rem; - --text-xs: .75rem; - --text-xs--line-height: calc(1 / .75); - --text-sm: .875rem; - --text-sm--line-height: calc(1.25 / .875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; - --text-6xl: 3.75rem; - --text-6xl--line-height: 1; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --tracking-tight: -.025em; - --tracking-widest: .1em; - --leading-relaxed: 1.625; - --radius-xs: .125rem; - --radius-2xl: 1rem; - --drop-shadow-sm: 0 1px 2px #00000026; - --ease-out: cubic-bezier(0, 0, .2, 1); - --ease-in-out: cubic-bezier(.4, 0, .2, 1); - --animate-spin: spin 1s linear infinite; - --animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; - --blur-sm: 8px; - --blur-md: 12px; - --aspect-video: 16 / 9; - --default-transition-duration: .15s; - --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - --default-font-family: var(--font-sans); - --default-font-feature-settings: var(--font-sans--font-feature-settings); - --default-font-variation-settings: var(--font-sans--font-variation-settings); - --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); - --color-border: var(--border); - } -} - -@layer base { - *, :after, :before, ::backdrop { - box-sizing: border-box; - border: 0 solid; - margin: 0; - padding: 0; - } - - ::file-selector-button { - box-sizing: border-box; - border: 0 solid; - margin: 0; - padding: 0; - } - - html, :host { - -webkit-text-size-adjust: 100%; - tab-size: 4; - line-height: 1.5; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); - font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var(--default-font-variation-settings, normal); - -webkit-tap-highlight-color: transparent; - } - - body { - line-height: inherit; - } - - hr { - height: 0; - color: inherit; - border-top-width: 1px; - } - - abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } - - h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; - } - - a { - color: inherit; - -webkit-text-decoration: inherit; - -webkit-text-decoration: inherit; - text-decoration: inherit; - } - - b, strong { - font-weight: bolder; - } - - code, kbd, samp, pre { - font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; - } - - small { - font-size: 80%; - } - - sub, sup { - vertical-align: baseline; - font-size: 75%; - line-height: 0; - position: relative; - } - - sub { - bottom: -.25em; - } - - sup { - top: -.5em; - } - - table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; - } - - :-moz-focusring { - outline: auto; - } - - progress { - vertical-align: baseline; - } - - summary { - display: list-item; - } - - ol, ul, menu { - list-style: none; - } - - img, svg, video, canvas, audio, iframe, embed, object { - vertical-align: middle; - display: block; - } - - img, video { - max-width: 100%; - height: auto; - } - - button, input, select, optgroup, textarea { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - opacity: 1; - background-color: #0000; - border-radius: 0; - } - - ::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - opacity: 1; - background-color: #0000; - border-radius: 0; - } - - :where(select:is([multiple], [size])) optgroup { - font-weight: bolder; - } - - :where(select:is([multiple], [size])) optgroup option { - padding-inline-start: 20px; - } - - ::file-selector-button { - margin-inline-end: 4px; - } - - ::placeholder { - opacity: 1; - color: currentColor; - } - - @supports (color: color-mix(in lab, red, red)) { - ::placeholder { - color: color-mix(in oklab, currentColor 50%, transparent); - } - } - - textarea { - resize: vertical; - } - - ::-webkit-search-decoration { - -webkit-appearance: none; - } - - ::-webkit-date-and-time-value { - min-height: 1lh; - text-align: inherit; - } - - ::-webkit-datetime-edit { - display: inline-flex; - } - - ::-webkit-datetime-edit-fields-wrapper { - padding: 0; - } - - ::-webkit-datetime-edit { - padding-block: 0; - } - - ::-webkit-datetime-edit-year-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-month-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-day-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-hour-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-minute-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-second-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-millisecond-field { - padding-block: 0; - } - - ::-webkit-datetime-edit-meridiem-field { - padding-block: 0; - } - - :-moz-ui-invalid { - box-shadow: none; - } - - button, input:where([type="button"], [type="reset"], [type="submit"]) { - appearance: button; - } - - ::file-selector-button { - appearance: button; - } - - ::-webkit-inner-spin-button { - height: auto; - } - - ::-webkit-outer-spin-button { - height: auto; - } - - [hidden]:where(:not([hidden="until-found"])) { - display: none !important; - } - - * { - border-color: var(--border); - outline-color: var(--ring); - } - - @supports (color: color-mix(in lab, red, red)) { - * { - outline-color: color-mix(in oklab, var(--ring) 50%, transparent); - } - } - - * { - border-color: var(--border); - outline-color: var(--ring); - } - - @supports (color: color-mix(in lab, red, red)) { - * { - outline-color: color-mix(in oklab, var(--ring) 50%, transparent); - } - } - - body { - background-color: var(--background); - color: var(--foreground); - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h1 { - font-size: var(--text-2xl); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h2 { - font-size: var(--text-xl); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h3 { - font-size: var(--text-lg); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) h4 { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) p { - font-size: var(--text-base); - font-weight: var(--font-weight-normal); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) label, :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) button { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - line-height: 1.5; - } - - :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) input { - font-size: var(--text-base); - font-weight: var(--font-weight-normal); - line-height: 1.5; - } -} - -@layer utilities { - .\@container\/card-header { - container: card-header / inline-size; - } - - .pointer-events-none { - pointer-events: none; - } - - .invisible { - visibility: hidden; - } - - .visible { - visibility: visible; - } - - .sr-only { - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - position: absolute; - overflow: hidden; - } - - .absolute { - position: absolute; - } - - .fixed { - position: fixed; - } - - .relative { - position: relative; - } - - .inset-0 { - inset: calc(var(--spacing) * 0); - } - - .inset-x-0 { - inset-inline: calc(var(--spacing) * 0); - } - - .inset-y-0 { - inset-block: calc(var(--spacing) * 0); - } - - .-top-12 { - top: calc(var(--spacing) * -12); - } - - .top-0 { - top: calc(var(--spacing) * 0); - } - - .top-1\.5 { - top: calc(var(--spacing) * 1.5); - } - - .top-1\/2 { - top: 50%; - } - - .top-3\.5 { - top: calc(var(--spacing) * 3.5); - } - - .top-4 { - top: calc(var(--spacing) * 4); - } - - .top-6 { - top: calc(var(--spacing) * 6); - } - - .top-9 { - top: calc(var(--spacing) * 9); - } - - .top-\[1px\] { - top: 1px; - } - - .top-\[50\%\] { - top: 50%; - } - - .top-\[60\%\] { - top: 60%; - } - - .top-full { - top: 100%; - } - - .-right-12 { - right: calc(var(--spacing) * -12); - } - - .right-0 { - right: calc(var(--spacing) * 0); - } - - .right-1 { - right: calc(var(--spacing) * 1); - } - - .right-2 { - right: calc(var(--spacing) * 2); - } - - .right-3 { - right: calc(var(--spacing) * 3); - } - - .right-4 { - right: calc(var(--spacing) * 4); - } - - .-bottom-2 { - bottom: calc(var(--spacing) * -2); - } - - .-bottom-12 { - bottom: calc(var(--spacing) * -12); - } - - .bottom-0 { - bottom: calc(var(--spacing) * 0); - } - - .bottom-8 { - bottom: calc(var(--spacing) * 8); - } - - .-left-12 { - left: calc(var(--spacing) * -12); - } - - .left-0 { - left: calc(var(--spacing) * 0); - } - - .left-1 { - left: calc(var(--spacing) * 1); - } - - .left-1\/2 { - left: 50%; - } - - .left-2 { - left: calc(var(--spacing) * 2); - } - - .left-6 { - left: calc(var(--spacing) * 6); - } - - .left-8 { - left: calc(var(--spacing) * 8); - } - - .left-\[50\%\] { - left: 50%; - } - - .isolate { - isolation: isolate; - } - - .z-10 { - z-index: 10; - } - - .z-20 { - z-index: 20; - } - - .z-50 { - z-index: 50; - } - - .z-\[1\] { - z-index: 1; - } - - .col-start-2 { - grid-column-start: 2; - } - - .row-span-2 { - grid-row: span 2 / span 2; - } - - .row-start-1 { - grid-row-start: 1; - } - - .container { - width: 100%; - } - - @media (width >= 40rem) { - .container { - max-width: 40rem; - } - } - - @media (width >= 48rem) { - .container { - max-width: 48rem; - } - } - - @media (width >= 64rem) { - .container { - max-width: 64rem; - } - } - - @media (width >= 80rem) { - .container { - max-width: 80rem; - } - } - - @media (width >= 96rem) { - .container { - max-width: 96rem; - } - } - - .-mx-1 { - margin-inline: calc(var(--spacing) * -1); - } - - .mx-2 { - margin-inline: calc(var(--spacing) * 2); - } - - .mx-3\.5 { - margin-inline: calc(var(--spacing) * 3.5); - } - - .mx-auto { - margin-inline: auto; - } - - .my-0\.5 { - margin-block: calc(var(--spacing) * .5); - } - - .my-1 { - margin-block: calc(var(--spacing) * 1); - } - - .-mt-4 { - margin-top: calc(var(--spacing) * -4); - } - - .mt-1 { - margin-top: calc(var(--spacing) * 1); - } - - .mt-1\.5 { - margin-top: calc(var(--spacing) * 1.5); - } - - .mt-2 { - margin-top: calc(var(--spacing) * 2); - } - - .mt-4 { - margin-top: calc(var(--spacing) * 4); - } - - .mt-6 { - margin-top: calc(var(--spacing) * 6); - } - - .mt-8 { - margin-top: calc(var(--spacing) * 8); - } - - .mt-12 { - margin-top: calc(var(--spacing) * 12); - } - - .mt-auto { - margin-top: auto; - } - - .mb-1 { - margin-bottom: calc(var(--spacing) * 1); - } - - .mb-2 { - margin-bottom: calc(var(--spacing) * 2); - } - - .mb-4 { - margin-bottom: calc(var(--spacing) * 4); - } - - .mb-6 { - margin-bottom: calc(var(--spacing) * 6); - } - - .mb-12 { - margin-bottom: calc(var(--spacing) * 12); - } - - .mb-16 { - margin-bottom: calc(var(--spacing) * 16); - } - - .-ml-4 { - margin-left: calc(var(--spacing) * -4); - } - - .ml-1 { - margin-left: calc(var(--spacing) * 1); - } - - .ml-auto { - margin-left: auto; - } - - .line-clamp-1 { - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - - .block { - display: block; - } - - .flex { - display: flex; - } - - .grid { - display: grid; - } - - .hidden { - display: none; - } - - .inline-block { - display: inline-block; - } - - .inline-flex { - display: inline-flex; - } - - .table { - display: table; - } - - .table-caption { - display: table-caption; - } - - .table-cell { - display: table-cell; - } - - .table-row { - display: table-row; - } - - .field-sizing-content { - field-sizing: content; - } - - .aspect-square { - aspect-ratio: 1; - } - - .aspect-video { - aspect-ratio: var(--aspect-video); - } - - .size-2 { - width: calc(var(--spacing) * 2); - height: calc(var(--spacing) * 2); - } - - .size-2\.5 { - width: calc(var(--spacing) * 2.5); - height: calc(var(--spacing) * 2.5); - } - - .size-3 { - width: calc(var(--spacing) * 3); - height: calc(var(--spacing) * 3); - } - - .size-3\.5 { - width: calc(var(--spacing) * 3.5); - height: calc(var(--spacing) * 3.5); - } - - .size-4 { - width: calc(var(--spacing) * 4); - height: calc(var(--spacing) * 4); - } - - .size-7 { - width: calc(var(--spacing) * 7); - height: calc(var(--spacing) * 7); - } - - .size-8 { - width: calc(var(--spacing) * 8); - height: calc(var(--spacing) * 8); - } - - .size-9 { - width: calc(var(--spacing) * 9); - height: calc(var(--spacing) * 9); - } - - .size-10 { - width: calc(var(--spacing) * 10); - height: calc(var(--spacing) * 10); - } - - .size-full { - width: 100%; - height: 100%; - } - - .h-0\.5 { - height: calc(var(--spacing) * .5); - } - - .h-1\.5 { - height: calc(var(--spacing) * 1.5); - } - - .h-2 { - height: calc(var(--spacing) * 2); - } - - .h-2\.5 { - height: calc(var(--spacing) * 2.5); - } - - .h-3 { - height: calc(var(--spacing) * 3); - } - - .h-4 { - height: calc(var(--spacing) * 4); - } - - .h-5 { - height: calc(var(--spacing) * 5); - } - - .h-6 { - height: calc(var(--spacing) * 6); - } - - .h-7 { - height: calc(var(--spacing) * 7); - } - - .h-8 { - height: calc(var(--spacing) * 8); - } - - .h-9 { - height: calc(var(--spacing) * 9); - } - - .h-10 { - height: calc(var(--spacing) * 10); - } - - .h-12 { - height: calc(var(--spacing) * 12); - } - - .h-14 { - height: calc(var(--spacing) * 14); - } - - .h-16 { - height: calc(var(--spacing) * 16); - } - - .h-48 { - height: calc(var(--spacing) * 48); - } - - .h-\[1\.15rem\] { - height: 1.15rem; - } - - .h-\[400px\] { - height: 400px; - } - - .h-\[500px\] { - height: 500px; - } - - .h-\[calc\(100\%-1px\)\] { - height: calc(100% - 1px); - } - - .h-\[var\(--radix-navigation-menu-viewport-height\)\] { - height: var(--radix-navigation-menu-viewport-height); - } - - .h-\[var\(--radix-select-trigger-height\)\] { - height: var(--radix-select-trigger-height); - } - - .h-auto { - height: auto; - } - - .h-full { - height: 100%; - } - - .h-px { - height: 1px; - } - - .h-svh { - height: 100svh; - } - - .max-h-\(--radix-context-menu-content-available-height\) { - max-height: var(--radix-context-menu-content-available-height); - } - - .max-h-\(--radix-dropdown-menu-content-available-height\) { - max-height: var(--radix-dropdown-menu-content-available-height); - } - - .max-h-\(--radix-select-content-available-height\) { - max-height: var(--radix-select-content-available-height); - } - - .max-h-\[300px\] { - max-height: 300px; - } - - .min-h-0 { - min-height: calc(var(--spacing) * 0); - } - - .min-h-4 { - min-height: calc(var(--spacing) * 4); - } - - .min-h-16 { - min-height: calc(var(--spacing) * 16); - } - - .min-h-screen { - min-height: 100vh; - } - - .min-h-svh { - min-height: 100svh; - } - - .w-\(--sidebar-width\) { - width: var(--sidebar-width); - } - - .w-0 { - width: calc(var(--spacing) * 0); - } - - .w-0\.5 { - width: calc(var(--spacing) * .5); - } - - .w-1 { - width: calc(var(--spacing) * 1); - } - - .w-2 { - width: calc(var(--spacing) * 2); - } - - .w-2\.5 { - width: calc(var(--spacing) * 2.5); - } - - .w-3 { - width: calc(var(--spacing) * 3); - } - - .w-3\/4 { - width: 75%; - } - - .w-4 { - width: calc(var(--spacing) * 4); - } - - .w-5 { - width: calc(var(--spacing) * 5); - } - - .w-6 { - width: calc(var(--spacing) * 6); - } - - .w-8 { - width: calc(var(--spacing) * 8); - } - - .w-9 { - width: calc(var(--spacing) * 9); - } - - .w-12 { - width: calc(var(--spacing) * 12); - } - - .w-14 { - width: calc(var(--spacing) * 14); - } - - .w-64 { - width: calc(var(--spacing) * 64); - } - - .w-72 { - width: calc(var(--spacing) * 72); - } - - .w-\[100px\] { - width: 100px; - } - - .w-auto { - width: auto; - } - - .w-fit { - width: fit-content; - } - - .w-full { - width: 100%; - } - - .w-max { - width: max-content; - } - - .w-px { - width: 1px; - } - - .max-w-\(--skeleton-width\) { - max-width: var(--skeleton-width); - } - - .max-w-3xl { - max-width: var(--container-3xl); - } - - .max-w-4xl { - max-width: var(--container-4xl); - } - - .max-w-5xl { - max-width: var(--container-5xl); - } - - .max-w-6xl { - max-width: var(--container-6xl); - } - - .max-w-\[calc\(100\%-2rem\)\] { - max-width: calc(100% - 2rem); - } - - .max-w-lg { - max-width: var(--container-lg); - } - - .max-w-max { - max-width: max-content; - } - - .max-w-md { - max-width: var(--container-md); - } - - .min-w-0 { - min-width: calc(var(--spacing) * 0); - } - - .min-w-5 { - min-width: calc(var(--spacing) * 5); - } - - .min-w-8 { - min-width: calc(var(--spacing) * 8); - } - - .min-w-9 { - min-width: calc(var(--spacing) * 9); - } - - .min-w-10 { - min-width: calc(var(--spacing) * 10); - } - - .min-w-16 { - min-width: calc(var(--spacing) * 16); - } - - .min-w-\[8rem\] { - min-width: 8rem; - } - - .min-w-\[12rem\] { - min-width: 12rem; - } - - .min-w-\[var\(--radix-select-trigger-width\)\] { - min-width: var(--radix-select-trigger-width); - } - - .flex-1 { - flex: 1; - } - - .flex-shrink-0, .shrink-0 { - flex-shrink: 0; - } - - .grow { - flex-grow: 1; - } - - .grow-0 { - flex-grow: 0; - } - - .basis-full { - flex-basis: 100%; - } - - .caption-bottom { - caption-side: bottom; - } - - .border-collapse { - border-collapse: collapse; - } - - .origin-\(--radix-context-menu-content-transform-origin\) { - transform-origin: var(--radix-context-menu-content-transform-origin); - } - - .origin-\(--radix-dropdown-menu-content-transform-origin\) { - transform-origin: var(--radix-dropdown-menu-content-transform-origin); - } - - .origin-\(--radix-hover-card-content-transform-origin\) { - transform-origin: var(--radix-hover-card-content-transform-origin); - } - - .origin-\(--radix-menubar-content-transform-origin\) { - transform-origin: var(--radix-menubar-content-transform-origin); - } - - .origin-\(--radix-popover-content-transform-origin\) { - transform-origin: var(--radix-popover-content-transform-origin); - } - - .origin-\(--radix-select-content-transform-origin\) { - transform-origin: var(--radix-select-content-transform-origin); - } - - .origin-\(--radix-tooltip-content-transform-origin\) { - transform-origin: var(--radix-tooltip-content-transform-origin); - } - - .-translate-x-1\/2 { - --tw-translate-x: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .-translate-x-full { - --tw-translate-x: -100%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .-translate-x-px { - --tw-translate-x: -1px; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .translate-x-\[-50\%\] { - --tw-translate-x: -50%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .translate-x-px { - --tw-translate-x: 1px; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .-translate-y-1\/2 { - --tw-translate-y: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .translate-y-0\.5 { - --tw-translate-y: calc(var(--spacing) * .5); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .translate-y-\[-50\%\] { - --tw-translate-y: -50%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .translate-y-\[calc\(-50\%_-_2px\)\] { - --tw-translate-y: calc(-50% - 2px); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .rotate-45 { - rotate: 45deg; - } - - .rotate-90 { - rotate: 90deg; - } - - .transform { - transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y); - } - - .animate-caret-blink { - animation: 1.25s ease-out infinite caret-blink; - } - - .animate-in { - animation: enter var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .animate-pulse { - animation: var(--animate-pulse); - } - - .animate-spin { - animation: var(--animate-spin); - } - - .cursor-default { - cursor: default; - } - - .cursor-pointer { - cursor: pointer; - } - - .touch-none { - touch-action: none; - } - - .resize-none { - resize: none; - } - - .scroll-my-1 { - scroll-margin-block: calc(var(--spacing) * 1); - } - - .scroll-py-1 { - scroll-padding-block: calc(var(--spacing) * 1); - } - - .list-none { - list-style-type: none; - } - - .auto-rows-min { - grid-auto-rows: min-content; - } - - .grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .grid-cols-\[0_1fr\] { - grid-template-columns: 0 1fr; - } - - .grid-rows-\[auto_auto\] { - grid-template-rows: auto auto; - } - - .flex-col { - flex-direction: column; - } - - .flex-col-reverse { - flex-direction: column-reverse; - } - - .flex-row { - flex-direction: row; - } - - .flex-wrap { - flex-wrap: wrap; - } - - .items-center { - align-items: center; - } - - .items-end { - align-items: flex-end; - } - - .items-start { - align-items: flex-start; - } - - .items-stretch { - align-items: stretch; - } - - .justify-between { - justify-content: space-between; - } - - .justify-center { - justify-content: center; - } - - .justify-items-start { - justify-items: start; - } - - .gap-1 { - gap: calc(var(--spacing) * 1); - } - - .gap-1\.5 { - gap: calc(var(--spacing) * 1.5); - } - - .gap-2 { - gap: calc(var(--spacing) * 2); - } - - .gap-3 { - gap: calc(var(--spacing) * 3); - } - - .gap-4 { - gap: calc(var(--spacing) * 4); - } - - .gap-6 { - gap: calc(var(--spacing) * 6); - } - - .gap-8 { - gap: calc(var(--spacing) * 8); - } - - .gap-12 { - gap: calc(var(--spacing) * 12); - } - - :where(.space-y-2 > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); - } - - :where(.space-y-3 > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse))); - } - - :where(.space-y-4 > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); - } - - :where(.space-y-6 > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse))); - } - - :where(.space-y-8 > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse))); - } - - :where(.space-x-1 > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse))); - } - - .gap-y-0\.5 { - row-gap: calc(var(--spacing) * .5); - } - - .self-start { - align-self: flex-start; - } - - .justify-self-end { - justify-self: flex-end; - } - - .overflow-auto { - overflow: auto; - } - - .overflow-hidden { - overflow: hidden; - } - - .overflow-x-auto { - overflow-x: auto; - } - - .overflow-x-hidden { - overflow-x: hidden; - } - - .overflow-y-auto { - overflow-y: auto; - } - - .rounded { - border-radius: .25rem; - } - - .rounded-2xl { - border-radius: var(--radius-2xl); - } - - .rounded-\[2px\] { - border-radius: 2px; - } - - .rounded-\[4px\] { - border-radius: 4px; - } - - .rounded-\[inherit\] { - border-radius: inherit; - } - - .rounded-full { - border-radius: 3.40282e38px; - } - - .rounded-lg { - border-radius: var(--radius); - } - - .rounded-md { - border-radius: calc(var(--radius) - 2px); - } - - .rounded-none { - border-radius: 0; - } - - .rounded-sm { - border-radius: calc(var(--radius) - 4px); - } - - .rounded-xl { - border-radius: calc(var(--radius) + 4px); - } - - .rounded-xs { - border-radius: var(--radius-xs); - } - - .rounded-tl-sm { - border-top-left-radius: calc(var(--radius) - 4px); - } - - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } - - .border-2 { - border-style: var(--tw-border-style); - border-width: 2px; - } - - .border-4 { - border-style: var(--tw-border-style); - border-width: 4px; - } - - .border-\[1\.5px\] { - border-style: var(--tw-border-style); - border-width: 1.5px; - } - - .border-y { - border-block-style: var(--tw-border-style); - border-block-width: 1px; - } - - .border-t { - border-top-style: var(--tw-border-style); - border-top-width: 1px; - } - - .border-r { - border-right-style: var(--tw-border-style); - border-right-width: 1px; - } - - .border-b { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - - .border-l { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - - .border-dashed { - --tw-border-style: dashed; - border-style: dashed; - } - - .border-\(--color-border\) { - border-color: var(--color-border); - } - - .border-background { - border-color: var(--background); - } - - .border-border { - border-color: var(--border); - } - - .border-border\/50 { - border-color: var(--border); - } - - @supports (color: color-mix(in lab, red, red)) { - .border-border\/50 { - border-color: color-mix(in oklab, var(--border) 50%, transparent); - } - } - - .border-gray-200 { - border-color: var(--color-gray-200); - } - - .border-input { - border-color: var(--input); - } - - .border-primary { - border-color: var(--primary); - } - - .border-primary\/20 { - border-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .border-primary\/20 { - border-color: color-mix(in oklab, var(--primary) 20%, transparent); - } - } - - .border-sidebar-border { - border-color: var(--sidebar-border); - } - - .border-transparent { - border-color: #0000; - } - - .border-t-transparent { - border-top-color: #0000; - } - - .border-l-transparent { - border-left-color: #0000; - } - - .bg-\(--color-bg\) { - background-color: var(--color-bg); - } - - .bg-accent { - background-color: var(--accent); - } - - .bg-background { - background-color: var(--background); - } - - .bg-background\/50 { - background-color: var(--background); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-background\/50 { - background-color: color-mix(in oklab, var(--background) 50%, transparent); - } - } - - .bg-background\/80 { - background-color: var(--background); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-background\/80 { - background-color: color-mix(in oklab, var(--background) 80%, transparent); - } - } - - .bg-black\/50 { - background-color: #00000080; - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-black\/50 { - background-color: color-mix(in oklab, var(--color-black) 50%, transparent); - } - } - - .bg-border { - background-color: var(--border); - } - - .bg-card { - background-color: var(--card); - } - - .bg-destructive { - background-color: var(--destructive); - } - - .bg-foreground { - background-color: var(--foreground); - } - - .bg-gray-50 { - background-color: var(--color-gray-50); - } - - .bg-gray-100 { - background-color: var(--color-gray-100); - } - - .bg-input-background { - background-color: var(--input-background); - } - - .bg-muted { - background-color: var(--muted); - } - - .bg-muted\/50 { - background-color: var(--muted); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-muted\/50 { - background-color: color-mix(in oklab, var(--muted) 50%, transparent); - } - } - - .bg-popover { - background-color: var(--popover); - } - - .bg-primary { - background-color: var(--primary); - } - - .bg-primary\/5 { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-primary\/5 { - background-color: color-mix(in oklab, var(--primary) 5%, transparent); - } - } - - .bg-primary\/10 { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-primary\/10 { - background-color: color-mix(in oklab, var(--primary) 10%, transparent); - } - } - - .bg-primary\/20 { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-primary\/20 { - background-color: color-mix(in oklab, var(--primary) 20%, transparent); - } - } - - .bg-primary\/70 { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-primary\/70 { - background-color: color-mix(in oklab, var(--primary) 70%, transparent); - } - } - - .bg-secondary { - background-color: var(--secondary); - } - - .bg-secondary\/10 { - background-color: var(--secondary); - } - - @supports (color: color-mix(in lab, red, red)) { - .bg-secondary\/10 { - background-color: color-mix(in oklab, var(--secondary) 10%, transparent); - } - } - - .bg-sidebar { - background-color: var(--sidebar); - } - - .bg-sidebar-border { - background-color: var(--sidebar-border); - } - - .bg-transparent { - background-color: #0000; - } - - .bg-white { - background-color: var(--color-white); - } - - .bg-gradient-to-b { - --tw-gradient-position: to bottom in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } - - .bg-gradient-to-br { - --tw-gradient-position: to bottom right in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } - - .bg-gradient-to-t { - --tw-gradient-position: to top in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } - - .from-background { - --tw-gradient-from: var(--background); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - .from-black\/60 { - --tw-gradient-from: #0009; - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .from-black\/60 { - --tw-gradient-from: color-mix(in oklab, var(--color-black) 60%, transparent); - } - } - - .from-primary { - --tw-gradient-from: var(--primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - .from-primary\/20 { - --tw-gradient-from: var(--primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .from-primary\/20 { - --tw-gradient-from: color-mix(in oklab, var(--primary) 20%, transparent); - } - } - - .from-primary\/60 { - --tw-gradient-from: var(--primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .from-primary\/60 { - --tw-gradient-from: color-mix(in oklab, var(--primary) 60%, transparent); - } - } - - .via-primary { - --tw-gradient-via: var(--primary); - --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-via-stops); - } - - .via-transparent { - --tw-gradient-via: transparent; - --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-via-stops); - } - - .to-primary\/30 { - --tw-gradient-to: var(--primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .to-primary\/30 { - --tw-gradient-to: color-mix(in oklab, var(--primary) 30%, transparent); - } - } - - .to-primary\/60 { - --tw-gradient-to: var(--primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .to-primary\/60 { - --tw-gradient-to: color-mix(in oklab, var(--primary) 60%, transparent); - } - } - - .to-secondary\/20 { - --tw-gradient-to: var(--secondary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - @supports (color: color-mix(in lab, red, red)) { - .to-secondary\/20 { - --tw-gradient-to: color-mix(in oklab, var(--secondary) 20%, transparent); - } - } - - .to-transparent { - --tw-gradient-to: transparent; - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - - .fill-current { - fill: currentColor; - } - - .fill-primary { - fill: var(--primary); - } - - .object-cover { - object-fit: cover; - } - - .p-0 { - padding: calc(var(--spacing) * 0); - } - - .p-1 { - padding: calc(var(--spacing) * 1); - } - - .p-2 { - padding: calc(var(--spacing) * 2); - } - - .p-3 { - padding: calc(var(--spacing) * 3); - } - - .p-4 { - padding: calc(var(--spacing) * 4); - } - - .p-6 { - padding: calc(var(--spacing) * 6); - } - - .p-8 { - padding: calc(var(--spacing) * 8); - } - - .p-\[3px\] { - padding: 3px; - } - - .p-px { - padding: 1px; - } - - .px-1 { - padding-inline: calc(var(--spacing) * 1); - } - - .px-1\.5 { - padding-inline: calc(var(--spacing) * 1.5); - } - - .px-2 { - padding-inline: calc(var(--spacing) * 2); - } - - .px-2\.5 { - padding-inline: calc(var(--spacing) * 2.5); - } - - .px-3 { - padding-inline: calc(var(--spacing) * 3); - } - - .px-4 { - padding-inline: calc(var(--spacing) * 4); - } - - .px-6 { - padding-inline: calc(var(--spacing) * 6); - } - - .py-0\.5 { - padding-block: calc(var(--spacing) * .5); - } - - .py-1 { - padding-block: calc(var(--spacing) * 1); - } - - .py-1\.5 { - padding-block: calc(var(--spacing) * 1.5); - } - - .py-2 { - padding-block: calc(var(--spacing) * 2); - } - - .py-3 { - padding-block: calc(var(--spacing) * 3); - } - - .py-4 { - padding-block: calc(var(--spacing) * 4); - } - - .py-6 { - padding-block: calc(var(--spacing) * 6); - } - - .py-8 { - padding-block: calc(var(--spacing) * 8); - } - - .py-16 { - padding-block: calc(var(--spacing) * 16); - } - - .py-20 { - padding-block: calc(var(--spacing) * 20); - } - - .pt-0 { - padding-top: calc(var(--spacing) * 0); - } - - .pt-1 { - padding-top: calc(var(--spacing) * 1); - } - - .pt-3 { - padding-top: calc(var(--spacing) * 3); - } - - .pt-4 { - padding-top: calc(var(--spacing) * 4); - } - - .pt-6 { - padding-top: calc(var(--spacing) * 6); - } - - .pr-2 { - padding-right: calc(var(--spacing) * 2); - } - - .pr-2\.5 { - padding-right: calc(var(--spacing) * 2.5); - } - - .pr-8 { - padding-right: calc(var(--spacing) * 8); - } - - .pr-20 { - padding-right: calc(var(--spacing) * 20); - } - - .pb-3 { - padding-bottom: calc(var(--spacing) * 3); - } - - .pb-4 { - padding-bottom: calc(var(--spacing) * 4); - } - - .pb-6 { - padding-bottom: calc(var(--spacing) * 6); - } - - .pl-0 { - padding-left: calc(var(--spacing) * 0); - } - - .pl-2 { - padding-left: calc(var(--spacing) * 2); - } - - .pl-4 { - padding-left: calc(var(--spacing) * 4); - } - - .pl-8 { - padding-left: calc(var(--spacing) * 8); - } - - .text-center { - text-align: center; - } - - .text-left { - text-align: left; - } - - .align-middle { - vertical-align: middle; - } - - .font-mono { - font-family: var(--font-mono); - } - - .text-4xl { - font-size: var(--text-4xl); - line-height: var(--tw-leading, var(--text-4xl--line-height)); - } - - .text-base { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - - .text-lg { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - } - - .text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - - .text-xl { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - - .text-xs { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - - .text-\[0\.8rem\] { - font-size: .8rem; - } - - .leading-none { - --tw-leading: 1; - line-height: 1; - } - - .leading-relaxed { - --tw-leading: var(--leading-relaxed); - line-height: var(--leading-relaxed); - } - - .font-medium { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - - .font-normal { - --tw-font-weight: var(--font-weight-normal); - font-weight: var(--font-weight-normal); - } - - .font-semibold { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - - .tracking-tight { - --tw-tracking: var(--tracking-tight); - letter-spacing: var(--tracking-tight); - } - - .tracking-widest { - --tw-tracking: var(--tracking-widest); - letter-spacing: var(--tracking-widest); - } - - .text-balance { - text-wrap: balance; - } - - .break-words { - overflow-wrap: break-word; - } - - .whitespace-nowrap { - white-space: nowrap; - } - - .text-accent-foreground { - color: var(--accent-foreground); - } - - .text-card-foreground { - color: var(--card-foreground); - } - - .text-current { - color: currentColor; - } - - .text-destructive { - color: var(--destructive); - } - - .text-foreground { - color: var(--foreground); - } - - .text-gray-500 { - color: var(--color-gray-500); - } - - .text-gray-700 { - color: var(--color-gray-700); - } - - .text-gray-900 { - color: var(--color-gray-900); - } - - .text-muted-foreground { - color: var(--muted-foreground); - } - - .text-popover-foreground { - color: var(--popover-foreground); - } - - .text-primary { - color: var(--primary); - } - - .text-primary-foreground { - color: var(--primary-foreground); - } - - .text-secondary-foreground { - color: var(--secondary-foreground); - } - - .text-sidebar-foreground { - color: var(--sidebar-foreground); - } - - .text-sidebar-foreground\/70 { - color: var(--sidebar-foreground); - } - - @supports (color: color-mix(in lab, red, red)) { - .text-sidebar-foreground\/70 { - color: color-mix(in oklab, var(--sidebar-foreground) 70%, transparent); - } - } - - .text-white { - color: var(--color-white); - } - - .tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); - } - - .underline-offset-4 { - text-underline-offset: 4px; - } - - .opacity-0 { - opacity: 0; - } - - .opacity-50 { - opacity: .5; - } - - .opacity-70 { - opacity: .7; - } - - .opacity-75 { - opacity: .75; - } - - .opacity-90 { - opacity: .9; - } - - .shadow { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-2xl { - --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, #00000040); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\] { - --tw-shadow: 0 0 0 1px var(--tw-shadow-color, hsl(var(--sidebar-border))); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-lg { - --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, #0000001a), 0 4px 6px -4px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-md { - --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a), 0 2px 4px -2px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-none { - --tw-shadow: 0 0 #0000; - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-sm { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-xl { - --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, #0000001a), 0 8px 10px -6px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .shadow-xs { - --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .ring-0 { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .ring-ring\/50 { - --tw-ring-color: var(--ring); - } - - @supports (color: color-mix(in lab, red, red)) { - .ring-ring\/50 { - --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); - } - } - - .ring-sidebar-ring { - --tw-ring-color: var(--sidebar-ring); - } - - .ring-offset-background { - --tw-ring-offset-color: var(--background); - } - - .outline-hidden { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .outline-hidden { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - - .drop-shadow-sm { - --tw-drop-shadow-size: drop-shadow(0 1px 2px var(--tw-drop-shadow-color, #00000026)); - --tw-drop-shadow: drop-shadow(var(--drop-shadow-sm)); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - - .filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - - .backdrop-blur-md { - --tw-backdrop-blur: blur(var(--blur-md)); - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } - - .backdrop-blur-sm { - --tw-backdrop-blur: blur(var(--blur-sm)); - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } - - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-\[color\,box-shadow\] { - transition-property: color, box-shadow; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-\[left\,right\,width\] { - transition-property: left, right, width; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-\[margin\,opacity\] { - transition-property: margin, opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-\[width\,height\,padding\] { - transition-property: width, height, padding; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-\[width\] { - transition-property: width; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-all { - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-colors { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-opacity { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-shadow { - transition-property: box-shadow; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-transform { - transition-property: transform, translate, scale, rotate; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - - .transition-none { - transition-property: none; - } - - .duration-200 { - --tw-duration: .2s; - transition-duration: .2s; - } - - .duration-300 { - --tw-duration: .3s; - transition-duration: .3s; - } - - .duration-1000 { - --tw-duration: 1s; - transition-duration: 1s; - } - - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - - .ease-linear { - --tw-ease: linear; - transition-timing-function: linear; - } - - .ease-out { - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - } - - .outline-none { - --tw-outline-style: none; - outline-style: none; - } - - .select-none { - -webkit-user-select: none; - user-select: none; - } - - .fade-in-0 { - --tw-enter-opacity: 0; - } - - .zoom-in-95 { - --tw-enter-scale: .95; - } - - .group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *) { - opacity: 1; - } - - @media (hover: hover) { - .group-hover\:scale-105:is(:where(.group):hover *) { - --tw-scale-x: 105%; - --tw-scale-y: 105%; - --tw-scale-z: 105%; - scale: var(--tw-scale-x) var(--tw-scale-y); - } - } - - @media (hover: hover) { - .group-hover\:opacity-100:is(:where(.group):hover *) { - opacity: 1; - } - } - - @media (hover: hover) { - .group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *) { - opacity: 1; - } - } - - .group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar="menu-action"]) *) { - padding-right: calc(var(--spacing) * 8); - } - - .group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible="icon"] *) { - margin-top: calc(var(--spacing) * -8); - } - - .group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible="icon"] *) { - display: none; - } - - .group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible="icon"] *) { - width: calc(var(--spacing) * 8) !important; - height: calc(var(--spacing) * 8) !important; - } - - .group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible="icon"] *) { - width: var(--sidebar-width-icon); - } - - .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible="icon"] *) { - width: calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4))); - } - - .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible="icon"] *) { - width: calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px); - } - - .group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible="icon"] *) { - overflow: hidden; - } - - .group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible="icon"] *) { - padding: calc(var(--spacing) * 0) !important; - } - - .group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible="icon"] *) { - padding: calc(var(--spacing) * 2) !important; - } - - .group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible="icon"] *) { - opacity: 0; - } - - .group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible="offcanvas"] *) { - right: calc(var(--sidebar-width) * -1); - } - - .group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible="offcanvas"] *) { - left: calc(var(--sidebar-width) * -1); - } - - .group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible="offcanvas"] *) { - width: calc(var(--spacing) * 0); - } - - .group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible="offcanvas"] *) { - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled="true"] *) { - pointer-events: none; - } - - .group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled="true"] *) { - opacity: .5; - } - - .group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side="left"] *) { - right: calc(var(--spacing) * -4); - } - - .group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side="left"] *) { - border-right-style: var(--tw-border-style); - border-right-width: 1px; - } - - .group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side="right"] *) { - left: calc(var(--spacing) * 0); - } - - .group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side="right"] *) { - rotate: 180deg; - } - - .group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side="right"] *) { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - - .group-data-\[state\=open\]\:rotate-180:is(:where(.group)[data-state="open"] *) { - rotate: 180deg; - } - - .group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant="floating"] *) { - border-radius: var(--radius); - } - - .group-data-\[variant\=floating\]\:border:is(:where(.group)[data-variant="floating"] *) { - border-style: var(--tw-border-style); - border-width: 1px; - } - - .group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.group)[data-variant="floating"] *) { - border-color: var(--sidebar-border); - } - - .group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant="floating"] *) { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:block:is(:where(.group\/drawer-content)[data-vaul-drawer-direction="bottom"] *) { - display: block; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:top-full:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - top: 100%; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:mt-1\.5:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - margin-top: calc(var(--spacing) * 1.5); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:overflow-hidden:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - overflow: hidden; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:rounded-md:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - border-radius: calc(var(--radius) - 2px); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:border:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - border-style: var(--tw-border-style); - border-width: 1px; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:bg-popover:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - background-color: var(--popover); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:text-popover-foreground:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - color: var(--popover-foreground); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:shadow:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:duration-200:is(:where(.group\/navigation-menu)[data-viewport="false"] *) { - --tw-duration: .2s; - transition-duration: .2s; - } - - @media (hover: hover) { - .peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover ~ *) { - color: var(--sidebar-accent-foreground); - } - } - - .peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled ~ *) { - cursor: not-allowed; - } - - .peer-disabled\:opacity-50:is(:where(.peer):disabled ~ *) { - opacity: .5; - } - - .peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active="true"] ~ *) { - color: var(--sidebar-accent-foreground); - } - - .peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size="default"] ~ *) { - top: calc(var(--spacing) * 1.5); - } - - .peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size="lg"] ~ *) { - top: calc(var(--spacing) * 2.5); - } - - .peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size="sm"] ~ *) { - top: calc(var(--spacing) * 1); - } - - .selection\:bg-primary ::selection, .selection\:bg-primary::selection { - background-color: var(--primary); - } - - .selection\:text-primary-foreground ::selection, .selection\:text-primary-foreground::selection { - color: var(--primary-foreground); - } - - .file\:inline-flex::file-selector-button { - display: inline-flex; - } - - .file\:h-7::file-selector-button { - height: calc(var(--spacing) * 7); - } - - .file\:border-0::file-selector-button { - border-style: var(--tw-border-style); - border-width: 0; - } - - .file\:bg-transparent::file-selector-button { - background-color: #0000; - } - - .file\:text-sm::file-selector-button { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - - .file\:font-medium::file-selector-button { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - - .file\:text-foreground::file-selector-button { - color: var(--foreground); - } - - .placeholder\:text-muted-foreground::placeholder { - color: var(--muted-foreground); - } - - .after\:absolute:after { - content: var(--tw-content); - position: absolute; - } - - .after\:-inset-2:after { - content: var(--tw-content); - inset: calc(var(--spacing) * -2); - } - - .after\:inset-y-0:after { - content: var(--tw-content); - inset-block: calc(var(--spacing) * 0); - } - - .after\:left-1\/2:after { - content: var(--tw-content); - left: 50%; - } - - .after\:w-1:after { - content: var(--tw-content); - width: calc(var(--spacing) * 1); - } - - .after\:w-\[2px\]:after { - content: var(--tw-content); - width: 2px; - } - - .after\:-translate-x-1\/2:after { - content: var(--tw-content); - --tw-translate-x: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible="offcanvas"] *):after { - content: var(--tw-content); - left: 100%; - } - - .first\:rounded-l-md:first-child { - border-top-left-radius: calc(var(--radius) - 2px); - border-bottom-left-radius: calc(var(--radius) - 2px); - } - - .first\:border-l:first-child { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - - .last\:rounded-r-md:last-child { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); - } - - .last\:border-b-0:last-child { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0; - } - - .focus-within\:relative:focus-within { - position: relative; - } - - .focus-within\:z-20:focus-within { - z-index: 20; - } - - @media (hover: hover) { - .hover\:bg-accent:hover { - background-color: var(--accent); - } - } - - @media (hover: hover) { - .hover\:bg-destructive\/90:hover { - background-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .hover\:bg-destructive\/90:hover { - background-color: color-mix(in oklab, var(--destructive) 90%, transparent); - } - } - } - - @media (hover: hover) { - .hover\:bg-muted:hover { - background-color: var(--muted); - } - } - - @media (hover: hover) { - .hover\:bg-muted\/50:hover { - background-color: var(--muted); - } - - @supports (color: color-mix(in lab, red, red)) { - .hover\:bg-muted\/50:hover { - background-color: color-mix(in oklab, var(--muted) 50%, transparent); - } - } - } - - @media (hover: hover) { - .hover\:bg-primary:hover { - background-color: var(--primary); - } - } - - @media (hover: hover) { - .hover\:bg-primary\/10:hover { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .hover\:bg-primary\/10:hover { - background-color: color-mix(in oklab, var(--primary) 10%, transparent); - } - } - } - - @media (hover: hover) { - .hover\:bg-primary\/90:hover { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - .hover\:bg-primary\/90:hover { - background-color: color-mix(in oklab, var(--primary) 90%, transparent); - } - } - } - - @media (hover: hover) { - .hover\:bg-secondary\/80:hover { - background-color: var(--secondary); - } - - @supports (color: color-mix(in lab, red, red)) { - .hover\:bg-secondary\/80:hover { - background-color: color-mix(in oklab, var(--secondary) 80%, transparent); - } - } - } - - @media (hover: hover) { - .hover\:bg-sidebar-accent:hover { - background-color: var(--sidebar-accent); - } - } - - @media (hover: hover) { - .hover\:text-accent-foreground:hover { - color: var(--accent-foreground); - } - } - - @media (hover: hover) { - .hover\:text-foreground:hover { - color: var(--foreground); - } - } - - @media (hover: hover) { - .hover\:text-muted-foreground:hover { - color: var(--muted-foreground); - } - } - - @media (hover: hover) { - .hover\:text-primary:hover { - color: var(--primary); - } - } - - @media (hover: hover) { - .hover\:text-primary-foreground:hover { - color: var(--primary-foreground); - } - } - - @media (hover: hover) { - .hover\:text-sidebar-accent-foreground:hover { - color: var(--sidebar-accent-foreground); - } - } - - @media (hover: hover) { - .hover\:underline:hover { - text-decoration-line: underline; - } - } - - @media (hover: hover) { - .hover\:opacity-100:hover { - opacity: 1; - } - } - - @media (hover: hover) { - .hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover { - --tw-shadow: 0 0 0 1px var(--tw-shadow-color, hsl(var(--sidebar-accent))); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (hover: hover) { - .hover\:shadow-lg:hover { - --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, #0000001a), 0 4px 6px -4px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (hover: hover) { - .hover\:shadow-md:hover { - --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a), 0 2px 4px -2px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (hover: hover) { - .hover\:shadow-xl:hover { - --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, #0000001a), 0 8px 10px -6px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (hover: hover) { - .hover\:ring-4:hover { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (hover: hover) { - .hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible="offcanvas"] *) { - background-color: var(--sidebar); - } - } - - @media (hover: hover) { - .hover\:after\:bg-sidebar-border:hover:after { - content: var(--tw-content); - background-color: var(--sidebar-border); - } - } - - .focus\:z-10:focus { - z-index: 10; - } - - .focus\:bg-accent:focus { - background-color: var(--accent); - } - - .focus\:bg-primary:focus { - background-color: var(--primary); - } - - .focus\:text-accent-foreground:focus { - color: var(--accent-foreground); - } - - .focus\:text-primary-foreground:focus { - color: var(--primary-foreground); - } - - .focus\:ring-2:focus { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .focus\:ring-ring:focus { - --tw-ring-color: var(--ring); - } - - .focus\:ring-offset-2:focus { - --tw-ring-offset-width: 2px; - --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - } - - .focus\:outline-hidden:focus { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .focus\:outline-hidden:focus { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .focus-visible\:z-10:focus-visible { - z-index: 10; - } - - .focus-visible\:border-ring:focus-visible { - border-color: var(--ring); - } - - .focus-visible\:ring-1:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .focus-visible\:ring-2:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .focus-visible\:ring-4:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .focus-visible\:ring-\[3px\]:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .focus-visible\:ring-destructive\/20:focus-visible { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .focus-visible\:ring-destructive\/20:focus-visible { - --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent); - } - } - - .focus-visible\:ring-ring:focus-visible { - --tw-ring-color: var(--ring); - } - - .focus-visible\:ring-ring\/50:focus-visible { - --tw-ring-color: var(--ring); - } - - @supports (color: color-mix(in lab, red, red)) { - .focus-visible\:ring-ring\/50:focus-visible { - --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); - } - } - - .focus-visible\:ring-offset-1:focus-visible { - --tw-ring-offset-width: 1px; - --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - } - - .focus-visible\:outline-hidden:focus-visible { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .focus-visible\:outline-hidden:focus-visible { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .focus-visible\:outline-1:focus-visible { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - - .focus-visible\:outline-ring:focus-visible { - outline-color: var(--ring); - } - - .active\:bg-sidebar-accent:active { - background-color: var(--sidebar-accent); - } - - .active\:text-sidebar-accent-foreground:active { - color: var(--sidebar-accent-foreground); - } - - .disabled\:pointer-events-none:disabled { - pointer-events: none; - } - - .disabled\:cursor-not-allowed:disabled { - cursor: not-allowed; - } - - .disabled\:opacity-50:disabled { - opacity: .5; - } - - :where([data-side="left"]) .in-data-\[side\=left\]\:cursor-w-resize { - cursor: w-resize; - } - - :where([data-side="right"]) .in-data-\[side\=right\]\:cursor-e-resize { - cursor: e-resize; - } - - .has-disabled\:opacity-50:has(:disabled) { - opacity: .5; - } - - .has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot="card-action"]) { - grid-template-columns: 1fr auto; - } - - .has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant="inset"]) { - background-color: var(--sidebar); - } - - .has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has( > svg) { - grid-template-columns: calc(var(--spacing) * 4) 1fr; - } - - .has-\[\>svg\]\:gap-x-3:has( > svg) { - column-gap: calc(var(--spacing) * 3); - } - - .has-\[\>svg\]\:px-2\.5:has( > svg) { - padding-inline: calc(var(--spacing) * 2.5); - } - - .has-\[\>svg\]\:px-3:has( > svg) { - padding-inline: calc(var(--spacing) * 3); - } - - .has-\[\>svg\]\:px-4:has( > svg) { - padding-inline: calc(var(--spacing) * 4); - } - - .aria-disabled\:pointer-events-none[aria-disabled="true"] { - pointer-events: none; - } - - .aria-disabled\:opacity-50[aria-disabled="true"] { - opacity: .5; - } - - .aria-invalid\:border-destructive[aria-invalid="true"] { - border-color: var(--destructive); - } - - .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { - --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent); - } - } - - .aria-selected\:bg-accent[aria-selected="true"] { - background-color: var(--accent); - } - - .aria-selected\:bg-primary[aria-selected="true"] { - background-color: var(--primary); - } - - .aria-selected\:text-accent-foreground[aria-selected="true"] { - color: var(--accent-foreground); - } - - .aria-selected\:text-muted-foreground[aria-selected="true"] { - color: var(--muted-foreground); - } - - .aria-selected\:text-primary-foreground[aria-selected="true"] { - color: var(--primary-foreground); - } - - .aria-selected\:opacity-100[aria-selected="true"] { - opacity: 1; - } - - .data-\[active\=true\]\:z-10[data-active="true"] { - z-index: 10; - } - - .data-\[active\=true\]\:border-ring[data-active="true"] { - border-color: var(--ring); - } - - .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { - background-color: var(--accent); - } - - @supports (color: color-mix(in lab, red, red)) { - .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { - background-color: color-mix(in oklab, var(--accent) 50%, transparent); - } - } - - .data-\[active\=true\]\:bg-sidebar-accent[data-active="true"] { - background-color: var(--sidebar-accent); - } - - .data-\[active\=true\]\:font-medium[data-active="true"] { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - - .data-\[active\=true\]\:text-accent-foreground[data-active="true"] { - color: var(--accent-foreground); - } - - .data-\[active\=true\]\:text-sidebar-accent-foreground[data-active="true"] { - color: var(--sidebar-accent-foreground); - } - - .data-\[active\=true\]\:ring-\[3px\][data-active="true"] { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { - --tw-ring-color: var(--ring); - } - - @supports (color: color-mix(in lab, red, red)) { - .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { - --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); - } - } - - @media (hover: hover) { - .data-\[active\=true\]\:hover\:bg-accent[data-active="true"]:hover { - background-color: var(--accent); - } - } - - .data-\[active\=true\]\:focus\:bg-accent[data-active="true"]:focus { - background-color: var(--accent); - } - - .data-\[active\=true\]\:aria-invalid\:border-destructive[data-active="true"][aria-invalid="true"] { - border-color: var(--destructive); - } - - .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { - --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent); - } - } - - .data-\[disabled\]\:pointer-events-none[data-disabled] { - pointer-events: none; - } - - .data-\[disabled\]\:opacity-50[data-disabled] { - opacity: .5; - } - - .data-\[disabled\=true\]\:pointer-events-none[data-disabled="true"] { - pointer-events: none; - } - - .data-\[disabled\=true\]\:opacity-50[data-disabled="true"] { - opacity: .5; - } - - .data-\[error\=true\]\:text-destructive[data-error="true"] { - color: var(--destructive); - } - - .data-\[inset\]\:pl-8[data-inset] { - padding-left: calc(var(--spacing) * 8); - } - - .data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion="from-end"] { - --tw-enter-translate-x: calc(52 * var(--spacing)); - } - - .data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion="from-start"] { - --tw-enter-translate-x: calc(52 * var(--spacing) * -1); - } - - .data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion="to-end"] { - --tw-exit-translate-x: calc(52 * var(--spacing)); - } - - .data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion="to-start"] { - --tw-exit-translate-x: calc(52 * var(--spacing) * -1); - } - - .data-\[motion\^\=from-\]\:animate-in[data-motion^="from-"] { - animation: enter var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[motion\^\=from-\]\:fade-in[data-motion^="from-"] { - --tw-enter-opacity: 0; - } - - .data-\[motion\^\=to-\]\:animate-out[data-motion^="to-"] { - animation: exit var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[motion\^\=to-\]\:fade-out[data-motion^="to-"] { - --tw-exit-opacity: 0; - } - - .data-\[orientation\=horizontal\]\:h-4[data-orientation="horizontal"] { - height: calc(var(--spacing) * 4); - } - - .data-\[orientation\=horizontal\]\:h-full[data-orientation="horizontal"] { - height: 100%; - } - - .data-\[orientation\=horizontal\]\:h-px[data-orientation="horizontal"] { - height: 1px; - } - - .data-\[orientation\=horizontal\]\:w-full[data-orientation="horizontal"] { - width: 100%; - } - - .data-\[orientation\=vertical\]\:h-full[data-orientation="vertical"] { - height: 100%; - } - - .data-\[orientation\=vertical\]\:min-h-44[data-orientation="vertical"] { - min-height: calc(var(--spacing) * 44); - } - - .data-\[orientation\=vertical\]\:w-1\.5[data-orientation="vertical"] { - width: calc(var(--spacing) * 1.5); - } - - .data-\[orientation\=vertical\]\:w-auto[data-orientation="vertical"] { - width: auto; - } - - .data-\[orientation\=vertical\]\:w-full[data-orientation="vertical"] { - width: 100%; - } - - .data-\[orientation\=vertical\]\:w-px[data-orientation="vertical"] { - width: 1px; - } - - .data-\[orientation\=vertical\]\:flex-col[data-orientation="vertical"] { - flex-direction: column; - } - - .data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction="vertical"] { - height: 1px; - } - - .data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction="vertical"] { - width: 100%; - } - - .data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction="vertical"] { - flex-direction: column; - } - - .data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction="vertical"]:after { - content: var(--tw-content); - left: calc(var(--spacing) * 0); - } - - .data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction="vertical"]:after { - content: var(--tw-content); - height: calc(var(--spacing) * 1); - } - - .data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction="vertical"]:after { - content: var(--tw-content); - width: 100%; - } - - .data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction="vertical"]:after { - content: var(--tw-content); - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction="vertical"]:after { - content: var(--tw-content); - --tw-translate-y: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[placeholder\]\:text-muted-foreground[data-placeholder] { - color: var(--muted-foreground); - } - - .data-\[selected\=true\]\:bg-accent[data-selected="true"] { - background-color: var(--accent); - } - - .data-\[selected\=true\]\:text-accent-foreground[data-selected="true"] { - color: var(--accent-foreground); - } - - .data-\[side\=bottom\]\:translate-y-1[data-side="bottom"] { - --tw-translate-y: calc(var(--spacing) * 1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[side\=bottom\]\:slide-in-from-top-2[data-side="bottom"] { - --tw-enter-translate-y: calc(2 * var(--spacing) * -1); - } - - .data-\[side\=left\]\:-translate-x-1[data-side="left"] { - --tw-translate-x: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[side\=left\]\:slide-in-from-right-2[data-side="left"] { - --tw-enter-translate-x: calc(2 * var(--spacing)); - } - - .data-\[side\=right\]\:translate-x-1[data-side="right"] { - --tw-translate-x: calc(var(--spacing) * 1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[side\=right\]\:slide-in-from-left-2[data-side="right"] { - --tw-enter-translate-x: calc(2 * var(--spacing) * -1); - } - - .data-\[side\=top\]\:-translate-y-1[data-side="top"] { - --tw-translate-y: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[side\=top\]\:slide-in-from-bottom-2[data-side="top"] { - --tw-enter-translate-y: calc(2 * var(--spacing)); - } - - .data-\[size\=default\]\:h-9[data-size="default"] { - height: calc(var(--spacing) * 9); - } - - .data-\[size\=sm\]\:h-8[data-size="sm"] { - height: calc(var(--spacing) * 8); - } - - :is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > *)[data-slot="alert-description"] { - color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - :is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > *)[data-slot="alert-description"] { - color: color-mix(in oklab, var(--destructive) 90%, transparent); - } - } - - :is(.\*\*\:data-\[slot\=command-input-wrapper\]\:h-12 *)[data-slot="command-input-wrapper"] { - height: calc(var(--spacing) * 12); - } - - :is(.\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:ring-0 *)[data-slot="navigation-menu-link"]:focus { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - :is(.\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:outline-none *)[data-slot="navigation-menu-link"]:focus { - --tw-outline-style: none; - outline-style: none; - } - - :is(.\*\:data-\[slot\=select-value\]\:line-clamp-1 > *)[data-slot="select-value"] { - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - display: -webkit-box; - overflow: hidden; - } - - :is(.\*\:data-\[slot\=select-value\]\:flex > *)[data-slot="select-value"] { - display: flex; - } - - :is(.\*\:data-\[slot\=select-value\]\:items-center > *)[data-slot="select-value"] { - align-items: center; - } - - :is(.\*\:data-\[slot\=select-value\]\:gap-2 > *)[data-slot="select-value"] { - gap: calc(var(--spacing) * 2); - } - - .data-\[state\=active\]\:bg-card[data-state="active"] { - background-color: var(--card); - } - - .data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state="checked"] { - --tw-translate-x: calc(100% - 2px); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[state\=checked\]\:border-primary[data-state="checked"] { - border-color: var(--primary); - } - - .data-\[state\=checked\]\:bg-primary[data-state="checked"] { - background-color: var(--primary); - } - - .data-\[state\=checked\]\:text-primary-foreground[data-state="checked"] { - color: var(--primary-foreground); - } - - .data-\[state\=closed\]\:animate-accordion-up[data-state="closed"] { - animation: accordion-up var(--tw-duration, .2s) ease-out; - } - - .data-\[state\=closed\]\:animate-out[data-state="closed"] { - animation: exit var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[state\=closed\]\:duration-300[data-state="closed"] { - --tw-duration: .3s; - transition-duration: .3s; - } - - .data-\[state\=closed\]\:fade-out-0[data-state="closed"] { - --tw-exit-opacity: 0; - } - - .data-\[state\=closed\]\:slide-out-to-bottom[data-state="closed"] { - --tw-exit-translate-y: 100%; - } - - .data-\[state\=closed\]\:slide-out-to-left[data-state="closed"] { - --tw-exit-translate-x: -100%; - } - - .data-\[state\=closed\]\:slide-out-to-right[data-state="closed"] { - --tw-exit-translate-x: 100%; - } - - .data-\[state\=closed\]\:slide-out-to-top[data-state="closed"] { - --tw-exit-translate-y: -100%; - } - - .data-\[state\=closed\]\:zoom-out-95[data-state="closed"] { - --tw-exit-scale: .95; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:animate-out:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="closed"] { - animation: exit var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:fade-out-0:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="closed"] { - --tw-exit-opacity: 0; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:zoom-out-95:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="closed"] { - --tw-exit-scale: .95; - } - - .data-\[state\=hidden\]\:animate-out[data-state="hidden"] { - animation: exit var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[state\=hidden\]\:fade-out[data-state="hidden"] { - --tw-exit-opacity: 0; - } - - .data-\[state\=on\]\:bg-accent[data-state="on"] { - background-color: var(--accent); - } - - .data-\[state\=on\]\:text-accent-foreground[data-state="on"] { - color: var(--accent-foreground); - } - - .data-\[state\=open\]\:animate-accordion-down[data-state="open"] { - animation: accordion-down var(--tw-duration, .2s) ease-out; - } - - .data-\[state\=open\]\:animate-in[data-state="open"] { - animation: enter var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[state\=open\]\:bg-accent[data-state="open"] { - background-color: var(--accent); - } - - .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { - background-color: var(--accent); - } - - @supports (color: color-mix(in lab, red, red)) { - .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { - background-color: color-mix(in oklab, var(--accent) 50%, transparent); - } - } - - .data-\[state\=open\]\:bg-secondary[data-state="open"] { - background-color: var(--secondary); - } - - .data-\[state\=open\]\:text-accent-foreground[data-state="open"] { - color: var(--accent-foreground); - } - - .data-\[state\=open\]\:text-muted-foreground[data-state="open"] { - color: var(--muted-foreground); - } - - .data-\[state\=open\]\:opacity-100[data-state="open"] { - opacity: 1; - } - - .data-\[state\=open\]\:duration-500[data-state="open"] { - --tw-duration: .5s; - transition-duration: .5s; - } - - .data-\[state\=open\]\:fade-in-0[data-state="open"] { - --tw-enter-opacity: 0; - } - - .data-\[state\=open\]\:slide-in-from-bottom[data-state="open"] { - --tw-enter-translate-y: 100%; - } - - .data-\[state\=open\]\:slide-in-from-left[data-state="open"] { - --tw-enter-translate-x: -100%; - } - - .data-\[state\=open\]\:slide-in-from-right[data-state="open"] { - --tw-enter-translate-x: 100%; - } - - .data-\[state\=open\]\:slide-in-from-top[data-state="open"] { - --tw-enter-translate-y: -100%; - } - - .data-\[state\=open\]\:zoom-in-90[data-state="open"] { - --tw-enter-scale: .9; - } - - .data-\[state\=open\]\:zoom-in-95[data-state="open"] { - --tw-enter-scale: .95; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:animate-in:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="open"] { - animation: enter var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:fade-in-0:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="open"] { - --tw-enter-opacity: 0; - } - - .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:zoom-in-95:is(:where(.group\/navigation-menu)[data-viewport="false"] *)[data-state="open"] { - --tw-enter-scale: .95; - } - - @media (hover: hover) { - .data-\[state\=open\]\:hover\:bg-accent[data-state="open"]:hover { - background-color: var(--accent); - } - } - - @media (hover: hover) { - .data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state="open"]:hover { - background-color: var(--sidebar-accent); - } - } - - @media (hover: hover) { - .data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state="open"]:hover { - color: var(--sidebar-accent-foreground); - } - } - - .data-\[state\=open\]\:focus\:bg-accent[data-state="open"]:focus { - background-color: var(--accent); - } - - .data-\[state\=selected\]\:bg-muted[data-state="selected"] { - background-color: var(--muted); - } - - .data-\[state\=unchecked\]\:translate-x-0[data-state="unchecked"] { - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .data-\[state\=unchecked\]\:bg-switch-background[data-state="unchecked"] { - background-color: var(--switch-background); - } - - .data-\[state\=visible\]\:animate-in[data-state="visible"] { - animation: enter var(--tw-duration, .15s) var(--tw-ease, ease); - } - - .data-\[state\=visible\]\:fade-in[data-state="visible"] { - --tw-enter-opacity: 0; - } - - .data-\[variant\=destructive\]\:text-destructive[data-variant="destructive"] { - color: var(--destructive); - } - - .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { - background-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { - background-color: color-mix(in oklab, var(--destructive) 10%, transparent); - } - } - - .data-\[variant\=destructive\]\:focus\:text-destructive[data-variant="destructive"]:focus { - color: var(--destructive); - } - - .data-\[variant\=outline\]\:border-l-0[data-variant="outline"] { - border-left-style: var(--tw-border-style); - border-left-width: 0; - } - - .data-\[variant\=outline\]\:shadow-xs[data-variant="outline"] { - --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - - .data-\[variant\=outline\]\:first\:border-l[data-variant="outline"]:first-child { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - - .data-\[vaul-drawer-direction\=bottom\]\:inset-x-0[data-vaul-drawer-direction="bottom"] { - inset-inline: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=bottom\]\:bottom-0[data-vaul-drawer-direction="bottom"] { - bottom: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=bottom\]\:mt-24[data-vaul-drawer-direction="bottom"] { - margin-top: calc(var(--spacing) * 24); - } - - .data-\[vaul-drawer-direction\=bottom\]\:max-h-\[80vh\][data-vaul-drawer-direction="bottom"] { - max-height: 80vh; - } - - .data-\[vaul-drawer-direction\=bottom\]\:rounded-t-lg[data-vaul-drawer-direction="bottom"] { - border-top-left-radius: var(--radius); - border-top-right-radius: var(--radius); - } - - .data-\[vaul-drawer-direction\=bottom\]\:border-t[data-vaul-drawer-direction="bottom"] { - border-top-style: var(--tw-border-style); - border-top-width: 1px; - } - - .data-\[vaul-drawer-direction\=left\]\:inset-y-0[data-vaul-drawer-direction="left"] { - inset-block: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=left\]\:left-0[data-vaul-drawer-direction="left"] { - left: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=left\]\:w-3\/4[data-vaul-drawer-direction="left"] { - width: 75%; - } - - .data-\[vaul-drawer-direction\=left\]\:border-r[data-vaul-drawer-direction="left"] { - border-right-style: var(--tw-border-style); - border-right-width: 1px; - } - - .data-\[vaul-drawer-direction\=right\]\:inset-y-0[data-vaul-drawer-direction="right"] { - inset-block: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=right\]\:right-0[data-vaul-drawer-direction="right"] { - right: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=right\]\:w-3\/4[data-vaul-drawer-direction="right"] { - width: 75%; - } - - .data-\[vaul-drawer-direction\=right\]\:border-l[data-vaul-drawer-direction="right"] { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - - .data-\[vaul-drawer-direction\=top\]\:inset-x-0[data-vaul-drawer-direction="top"] { - inset-inline: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=top\]\:top-0[data-vaul-drawer-direction="top"] { - top: calc(var(--spacing) * 0); - } - - .data-\[vaul-drawer-direction\=top\]\:mb-24[data-vaul-drawer-direction="top"] { - margin-bottom: calc(var(--spacing) * 24); - } - - .data-\[vaul-drawer-direction\=top\]\:max-h-\[80vh\][data-vaul-drawer-direction="top"] { - max-height: 80vh; - } - - .data-\[vaul-drawer-direction\=top\]\:rounded-b-lg[data-vaul-drawer-direction="top"] { - border-bottom-right-radius: var(--radius); - border-bottom-left-radius: var(--radius); - } - - .data-\[vaul-drawer-direction\=top\]\:border-b[data-vaul-drawer-direction="top"] { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - - @media (width >= 40rem) { - .sm\:block { - display: block; - } - } - - @media (width >= 40rem) { - .sm\:flex { - display: flex; - } - } - - @media (width >= 40rem) { - .sm\:max-w-lg { - max-width: var(--container-lg); - } - } - - @media (width >= 40rem) { - .sm\:max-w-sm { - max-width: var(--container-sm); - } - } - - @media (width >= 40rem) { - .sm\:flex-row { - flex-direction: row; - } - } - - @media (width >= 40rem) { - .sm\:justify-end { - justify-content: flex-end; - } - } - - @media (width >= 40rem) { - .sm\:gap-2\.5 { - gap: calc(var(--spacing) * 2.5); - } - } - - @media (width >= 40rem) { - .sm\:pr-2\.5 { - padding-right: calc(var(--spacing) * 2.5); - } - } - - @media (width >= 40rem) { - .sm\:pl-2\.5 { - padding-left: calc(var(--spacing) * 2.5); - } - } - - @media (width >= 40rem) { - .sm\:text-left { - text-align: left; - } - } - - @media (width >= 40rem) { - .data-\[vaul-drawer-direction\=left\]\:sm\:max-w-sm[data-vaul-drawer-direction="left"] { - max-width: var(--container-sm); - } - } - - @media (width >= 40rem) { - .data-\[vaul-drawer-direction\=right\]\:sm\:max-w-sm[data-vaul-drawer-direction="right"] { - max-width: var(--container-sm); - } - } - - @media (width >= 48rem) { - .md\:absolute { - position: absolute; - } - } - - @media (width >= 48rem) { - .md\:mt-0 { - margin-top: calc(var(--spacing) * 0); - } - } - - @media (width >= 48rem) { - .md\:block { - display: block; - } - } - - @media (width >= 48rem) { - .md\:flex { - display: flex; - } - } - - @media (width >= 48rem) { - .md\:hidden { - display: none; - } - } - - @media (width >= 48rem) { - .md\:w-\[var\(--radix-navigation-menu-viewport-width\)\] { - width: var(--radix-navigation-menu-viewport-width); - } - } - - @media (width >= 48rem) { - .md\:w-auto { - width: auto; - } - } - - @media (width >= 48rem) { - .md\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - - @media (width >= 48rem) { - .md\:grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - - @media (width >= 48rem) { - .md\:pl-24 { - padding-left: calc(var(--spacing) * 24); - } - } - - @media (width >= 48rem) { - .md\:text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - } - - @media (width >= 48rem) { - .md\:opacity-0 { - opacity: 0; - } - } - - @media (width >= 48rem) { - .md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant="inset"] ~ *) { - margin: calc(var(--spacing) * 2); - } - } - - @media (width >= 48rem) { - .md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant="inset"] ~ *) { - margin-left: calc(var(--spacing) * 0); - } - } - - @media (width >= 48rem) { - .md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant="inset"] ~ *) { - border-radius: calc(var(--radius) + 4px); - } - } - - @media (width >= 48rem) { - .md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant="inset"] ~ *) { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - - @media (width >= 48rem) { - .md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant="inset"] ~ *):is(:where(.peer)[data-state="collapsed"] ~ *) { - margin-left: calc(var(--spacing) * 2); - } - } - - @media (width >= 48rem) { - .md\:after\:hidden:after { - content: var(--tw-content); - display: none; - } - } - - - - @media (width >= 64rem) { - .lg\:block { - display: block; - } - - .lg\:h-\[600px\] { - height: 600px; - } - } - - @media (width >= 64rem) { - .lg\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - - @media (width >= 64rem) { - .lg\:grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - } - - @media (width >= 64rem) { - .lg\:text-5xl { - font-size: var(--text-5xl); - line-height: var(--tw-leading, var(--text-5xl--line-height)); - } - } - - @media (width >= 80rem) { - .xl\:grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - - @media (width >= 80rem) { - .xl\:text-6xl { - font-size: var(--text-6xl); - line-height: var(--tw-leading, var(--text-6xl--line-height)); - } - } - - .dark\:border-input:is(.dark *) { - border-color: var(--input); - } - - .dark\:bg-destructive\/60:is(.dark *) { - background-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:bg-destructive\/60:is(.dark *) { - background-color: color-mix(in oklab, var(--destructive) 60%, transparent); - } - } - - .dark\:bg-input\/30:is(.dark *) { - background-color: var(--input); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:bg-input\/30:is(.dark *) { - background-color: color-mix(in oklab, var(--input) 30%, transparent); - } - } - - .dark\:text-muted-foreground:is(.dark *) { - color: var(--muted-foreground); - } - - @media (hover: hover) { - .dark\:hover\:bg-accent\/50:is(.dark *):hover { - background-color: var(--accent); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:hover\:bg-accent\/50:is(.dark *):hover { - background-color: color-mix(in oklab, var(--accent) 50%, transparent); - } - } - } - - @media (hover: hover) { - .dark\:hover\:bg-input\/50:is(.dark *):hover { - background-color: var(--input); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:hover\:bg-input\/50:is(.dark *):hover { - background-color: color-mix(in oklab, var(--input) 50%, transparent); - } - } - } - - .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { - --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent); - } - } - - .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { - --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent); - } - } - - .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is(.dark *)[data-active="true"][aria-invalid="true"] { - --tw-ring-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is(.dark *)[data-active="true"][aria-invalid="true"] { - --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent); - } - } - - .dark\:data-\[state\=active\]\:border-input:is(.dark *)[data-state="active"] { - border-color: var(--input); - } - - .dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state="active"] { - background-color: var(--input); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state="active"] { - background-color: color-mix(in oklab, var(--input) 30%, transparent); - } - } - - .dark\:data-\[state\=active\]\:text-foreground:is(.dark *)[data-state="active"] { - color: var(--foreground); - } - - .dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state="checked"] { - background-color: var(--primary); - } - - .dark\:data-\[state\=checked\]\:bg-primary-foreground:is(.dark *)[data-state="checked"] { - background-color: var(--primary-foreground); - } - - .dark\:data-\[state\=unchecked\]\:bg-card-foreground:is(.dark *)[data-state="unchecked"] { - background-color: var(--card-foreground); - } - - .dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state="unchecked"] { - background-color: var(--input); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:data-\[state\=unchecked\]\:bg-input\/80:is(.dark *)[data-state="unchecked"] { - background-color: color-mix(in oklab, var(--input) 80%, transparent); - } - } - - .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant="destructive"]:focus { - background-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant="destructive"]:focus { - background-color: color-mix(in oklab, var(--destructive) 20%, transparent); - } - } - - .\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-muted-foreground .recharts-cartesian-axis-tick text { - fill: var(--muted-foreground); - } - - .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 .recharts-cartesian-grid line[stroke="#ccc"] { - stroke: var(--border); - } - - @supports (color: color-mix(in lab, red, red)) { - .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 .recharts-cartesian-grid line[stroke="#ccc"] { - stroke: color-mix(in oklab, var(--border) 50%, transparent); - } - } - - .\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border .recharts-curve.recharts-tooltip-cursor { - stroke: var(--border); - } - - .\[\&_\.recharts-dot\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-dot[stroke="#fff"] { - stroke: #0000; - } - - .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .\[\&_\.recharts-polar-grid_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-polar-grid [stroke="#ccc"] { - stroke: var(--border); - } - - .\[\&_\.recharts-radial-bar-background-sector\]\:fill-muted .recharts-radial-bar-background-sector { - fill: var(--muted); - } - - .\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-muted .recharts-rectangle.recharts-tooltip-cursor { - fill: var(--muted); - } - - .\[\&_\.recharts-reference-line_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-reference-line [stroke="#ccc"] { - stroke: var(--border); - } - - .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .\[\&_\.recharts-sector\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-sector[stroke="#fff"] { - stroke: #0000; - } - - .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { - --tw-outline-style: none; - outline-style: none; - } - - @media (forced-colors: active) { - .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { - outline-offset: 2px; - outline: 2px solid #0000; - } - } - - .\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading] { - padding-inline: calc(var(--spacing) * 2); - } - - .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading] { - padding-block: calc(var(--spacing) * 1.5); - } - - .\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading] { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - - .\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading] { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - - .\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading] { - color: var(--muted-foreground); - } - - .\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group] { - padding-inline: calc(var(--spacing) * 2); - } - - .\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden]) ~ [cmdk-group] { - padding-top: calc(var(--spacing) * 0); - } - - .\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg { - height: calc(var(--spacing) * 5); - } - - .\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg { - width: calc(var(--spacing) * 5); - } - - .\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input] { - height: calc(var(--spacing) * 12); - } - - .\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item] { - padding-inline: calc(var(--spacing) * 2); - } - - .\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item] { - padding-block: calc(var(--spacing) * 3); - } - - .\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg { - height: calc(var(--spacing) * 5); - } - - .\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg { - width: calc(var(--spacing) * 5); - } - - .\[\&_p\]\:leading-relaxed p { - --tw-leading: var(--leading-relaxed); - line-height: var(--leading-relaxed); - } - - .\[\&_svg\]\:pointer-events-none svg { - pointer-events: none; - } - - .\[\&_svg\]\:shrink-0 svg { - flex-shrink: 0; - } - - .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*="size-"]) { - width: calc(var(--spacing) * 4); - height: calc(var(--spacing) * 4); - } - - .\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*="text-"]) { - color: var(--muted-foreground); - } - - .\[\&_tr\]\:border-b tr { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - - .\[\&_tr\:last-child\]\:border-0 tr:last-child { - border-style: var(--tw-border-style); - border-width: 0; - } - - .\[\&\:has\(\>\.day-range-end\)\]\:rounded-r-md:has( > .day-range-end) { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); - } - - .\[\&\:has\(\>\.day-range-start\)\]\:rounded-l-md:has( > .day-range-start) { - border-top-left-radius: calc(var(--radius) - 2px); - border-bottom-left-radius: calc(var(--radius) - 2px); - } - - .\[\&\:has\(\[aria-selected\]\)\]\:rounded-md:has([aria-selected]) { - border-radius: calc(var(--radius) - 2px); - } - - .\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]) { - background-color: var(--accent); - } - - .first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:first-child:has([aria-selected]) { - border-top-left-radius: calc(var(--radius) - 2px); - border-bottom-left-radius: calc(var(--radius) - 2px); - } - - .last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:last-child:has([aria-selected]) { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); - } - - .\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md:has([aria-selected].day-range-end) { - border-top-right-radius: calc(var(--radius) - 2px); - border-bottom-right-radius: calc(var(--radius) - 2px); - } - - .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role="checkbox"]) { - padding-right: calc(var(--spacing) * 0); - } - - .\[\.border-b\]\:pb-6.border-b { - padding-bottom: calc(var(--spacing) * 6); - } - - .\[\.border-t\]\:pt-6.border-t { - padding-top: calc(var(--spacing) * 6); - } - - :is(.\*\:\[span\]\:last\:flex > *):is(span):last-child { - display: flex; - } - - :is(.\*\:\[span\]\:last\:items-center > *):is(span):last-child { - align-items: center; - } - - :is(.\*\:\[span\]\:last\:gap-2 > *):is(span):last-child { - gap: calc(var(--spacing) * 2); - } - - :is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant="destructive"] > *):is(svg) { - color: var(--destructive) !important; - } - - .\[\&\:last-child\]\:pb-6:last-child { - padding-bottom: calc(var(--spacing) * 6); - } - - .\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\] > [role="checkbox"] { - --tw-translate-y: 2px; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .\[\&\>button\]\:hidden > button { - display: none; - } - - .\[\&\>span\:last-child\]\:truncate > span:last-child { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } - - .\[\&\>svg\]\:pointer-events-none > svg { - pointer-events: none; - } - - .\[\&\>svg\]\:size-3 > svg { - width: calc(var(--spacing) * 3); - height: calc(var(--spacing) * 3); - } - - .\[\&\>svg\]\:size-3\.5 > svg { - width: calc(var(--spacing) * 3.5); - height: calc(var(--spacing) * 3.5); - } - - .\[\&\>svg\]\:size-4 > svg { - width: calc(var(--spacing) * 4); - height: calc(var(--spacing) * 4); - } - - .\[\&\>svg\]\:h-2\.5 > svg { - height: calc(var(--spacing) * 2.5); - } - - .\[\&\>svg\]\:h-3 > svg { - height: calc(var(--spacing) * 3); - } - - .\[\&\>svg\]\:w-2\.5 > svg { - width: calc(var(--spacing) * 2.5); - } - - .\[\&\>svg\]\:w-3 > svg { - width: calc(var(--spacing) * 3); - } - - .\[\&\>svg\]\:shrink-0 > svg { - flex-shrink: 0; - } - - .\[\&\>svg\]\:translate-y-0\.5 > svg { - --tw-translate-y: calc(var(--spacing) * .5); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - - .\[\&\>svg\]\:text-current > svg { - color: currentColor; - } - - .\[\&\>svg\]\:text-muted-foreground > svg { - color: var(--muted-foreground); - } - - .\[\&\>svg\]\:text-sidebar-accent-foreground > svg { - color: var(--sidebar-accent-foreground); - } - - .\[\&\>tr\]\:last\:border-b-0 > tr:last-child { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0; - } - - .\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction="vertical"] > div { - rotate: 90deg; - } - - .\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state="open"] > svg { - rotate: 180deg; - } - - [data-side="left"][data-collapsible="offcanvas"] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2 { - right: calc(var(--spacing) * -2); - } - - [data-side="left"][data-state="collapsed"] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize { - cursor: e-resize; - } - - [data-side="right"][data-collapsible="offcanvas"] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2 { - left: calc(var(--spacing) * -2); - } - - [data-side="right"][data-state="collapsed"] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize { - cursor: w-resize; - } - - @media (hover: hover) { - a.\[a\&\]\:hover\:bg-accent:hover { - background-color: var(--accent); - } - } - - @media (hover: hover) { - a.\[a\&\]\:hover\:bg-destructive\/90:hover { - background-color: var(--destructive); - } - - @supports (color: color-mix(in lab, red, red)) { - a.\[a\&\]\:hover\:bg-destructive\/90:hover { - background-color: color-mix(in oklab, var(--destructive) 90%, transparent); - } - } - } - - @media (hover: hover) { - a.\[a\&\]\:hover\:bg-primary\/90:hover { - background-color: var(--primary); - } - - @supports (color: color-mix(in lab, red, red)) { - a.\[a\&\]\:hover\:bg-primary\/90:hover { - background-color: color-mix(in oklab, var(--primary) 90%, transparent); - } - } - } - - @media (hover: hover) { - a.\[a\&\]\:hover\:bg-secondary\/90:hover { - background-color: var(--secondary); - } - - @supports (color: color-mix(in lab, red, red)) { - a.\[a\&\]\:hover\:bg-secondary\/90:hover { - background-color: color-mix(in oklab, var(--secondary) 90%, transparent); - } - } - } - - @media (hover: hover) { - a.\[a\&\]\:hover\:text-accent-foreground:hover { - color: var(--accent-foreground); - } - } -} - -:root { - --font-size: 14px; - --background: #fff; - --foreground: oklch(.145 0 0); - --card: #fff; - --card-foreground: oklch(.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(.145 0 0); - --primary: #030213; - --primary-foreground: oklch(1 0 0); - --secondary: oklch(.95 .0058 264.53); - --secondary-foreground: #030213; - --muted: #ececf0; - --muted-foreground: #717182; - --accent: #e9ebef; - --accent-foreground: #030213; - --destructive: #d4183d; - --destructive-foreground: #fff; - --border: #0000001a; - --input: transparent; - --input-background: #f3f3f5; - --switch-background: #cbced4; - --font-weight-medium: 500; - --font-weight-normal: 400; - --ring: oklch(.708 0 0); - --chart-1: oklch(.646 .222 41.116); - --chart-2: oklch(.6 .118 184.704); - --chart-3: oklch(.398 .07 227.392); - --chart-4: oklch(.828 .189 84.429); - --chart-5: oklch(.769 .188 70.08); - --radius: .625rem; - --sidebar: oklch(.985 0 0); - --sidebar-foreground: oklch(.145 0 0); - --sidebar-primary: #030213; - --sidebar-primary-foreground: oklch(.985 0 0); - --sidebar-accent: oklch(.97 0 0); - --sidebar-accent-foreground: oklch(.205 0 0); - --sidebar-border: oklch(.922 0 0); - --sidebar-ring: oklch(.708 0 0); -} - -.dark { - --background: oklch(.145 0 0); - --foreground: oklch(.985 0 0); - --card: oklch(.145 0 0); - --card-foreground: oklch(.985 0 0); - --popover: oklch(.145 0 0); - --popover-foreground: oklch(.985 0 0); - --primary: oklch(.985 0 0); - --primary-foreground: oklch(.205 0 0); - --secondary: oklch(.269 0 0); - --secondary-foreground: oklch(.985 0 0); - --muted: oklch(.269 0 0); - --muted-foreground: oklch(.708 0 0); - --accent: oklch(.269 0 0); - --accent-foreground: oklch(.985 0 0); - --destructive: oklch(.396 .141 25.723); - --destructive-foreground: oklch(.637 .237 25.331); - --border: oklch(.269 0 0); - --input: oklch(.269 0 0); - --ring: oklch(.439 0 0); - --font-weight-medium: 500; - --font-weight-normal: 400; - --chart-1: oklch(.488 .243 264.376); - --chart-2: oklch(.696 .17 162.48); - --chart-3: oklch(.769 .188 70.08); - --chart-4: oklch(.627 .265 303.9); - --chart-5: oklch(.645 .246 16.439); - --sidebar: oklch(.205 0 0); - --sidebar-foreground: oklch(.985 0 0); - --sidebar-primary: oklch(.488 .243 264.376); - --sidebar-primary-foreground: oklch(.985 0 0); - --sidebar-accent: oklch(.269 0 0); - --sidebar-accent-foreground: oklch(.985 0 0); - --sidebar-border: oklch(.269 0 0); - --sidebar-ring: oklch(.439 0 0); -} - -html { - font-size: var(--font-size); -} - -@property --tw-translate-x { - syntax: "*"; - inherits: false; - initial-value: 0; -} - -@property --tw-translate-y { - syntax: "*"; - inherits: false; - initial-value: 0; -} - -@property --tw-translate-z { - syntax: "*"; - inherits: false; - initial-value: 0; -} - -@property --tw-rotate-x { - syntax: "*"; - inherits: false; - initial-value: rotateX(0); -} - -@property --tw-rotate-y { - syntax: "*"; - inherits: false; - initial-value: rotateY(0); -} - -@property --tw-rotate-z { - syntax: "*"; - inherits: false; - initial-value: rotateZ(0); -} - -@property --tw-skew-x { - syntax: "*"; - inherits: false; - initial-value: skewX(0); -} - -@property --tw-skew-y { - syntax: "*"; - inherits: false; - initial-value: skewY(0); -} - -@property --tw-space-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} - -@property --tw-space-x-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} - -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} - -@property --tw-gradient-position { - syntax: "*"; - inherits: false -} - -@property --tw-gradient-from { - syntax: ""; - inherits: false; - initial-value: #0000; -} - -@property --tw-gradient-via { - syntax: ""; - inherits: false; - initial-value: #0000; -} - -@property --tw-gradient-to { - syntax: ""; - inherits: false; - initial-value: #0000; -} - -@property --tw-gradient-stops { - syntax: "*"; - inherits: false -} - -@property --tw-gradient-via-stops { - syntax: "*"; - inherits: false -} - -@property --tw-gradient-from-position { - syntax: ""; - inherits: false; - initial-value: 0%; -} - -@property --tw-gradient-via-position { - syntax: ""; - inherits: false; - initial-value: 50%; -} - -@property --tw-gradient-to-position { - syntax: ""; - inherits: false; - initial-value: 100%; -} - -@property --tw-leading { - syntax: "*"; - inherits: false -} - -@property --tw-font-weight { - syntax: "*"; - inherits: false -} - -@property --tw-tracking { - syntax: "*"; - inherits: false -} - -@property --tw-ordinal { - syntax: "*"; - inherits: false -} - -@property --tw-slashed-zero { - syntax: "*"; - inherits: false -} - -@property --tw-numeric-figure { - syntax: "*"; - inherits: false -} - -@property --tw-numeric-spacing { - syntax: "*"; - inherits: false -} - -@property --tw-numeric-fraction { - syntax: "*"; - inherits: false -} - -@property --tw-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} - -@property --tw-shadow-color { - syntax: "*"; - inherits: false -} - -@property --tw-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} - -@property --tw-inset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} - -@property --tw-inset-shadow-color { - syntax: "*"; - inherits: false -} - -@property --tw-inset-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} - -@property --tw-ring-color { - syntax: "*"; - inherits: false -} - -@property --tw-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} - -@property --tw-inset-ring-color { - syntax: "*"; - inherits: false -} - -@property --tw-inset-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} - -@property --tw-ring-inset { - syntax: "*"; - inherits: false -} - -@property --tw-ring-offset-width { - syntax: ""; - inherits: false; - initial-value: 0; -} - -@property --tw-ring-offset-color { - syntax: "*"; - inherits: false; - initial-value: #fff; -} - -@property --tw-ring-offset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} - -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} - -@property --tw-blur { - syntax: "*"; - inherits: false -} - -@property --tw-brightness { - syntax: "*"; - inherits: false -} - -@property --tw-contrast { - syntax: "*"; - inherits: false -} - -@property --tw-grayscale { - syntax: "*"; - inherits: false -} - -@property --tw-hue-rotate { - syntax: "*"; - inherits: false -} - -@property --tw-invert { - syntax: "*"; - inherits: false -} - -@property --tw-opacity { - syntax: "*"; - inherits: false -} - -@property --tw-saturate { - syntax: "*"; - inherits: false -} - -@property --tw-sepia { - syntax: "*"; - inherits: false -} - -@property --tw-drop-shadow { - syntax: "*"; - inherits: false -} - -@property --tw-drop-shadow-color { - syntax: "*"; - inherits: false -} - -@property --tw-drop-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} - -@property --tw-drop-shadow-size { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-blur { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-brightness { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-contrast { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-grayscale { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-hue-rotate { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-invert { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-opacity { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-saturate { - syntax: "*"; - inherits: false -} - -@property --tw-backdrop-sepia { - syntax: "*"; - inherits: false -} - -@property --tw-duration { - syntax: "*"; - inherits: false -} - -@property --tw-ease { - syntax: "*"; - inherits: false -} - -@property --tw-scale-x { - syntax: "*"; - inherits: false; - initial-value: 1; -} - -@property --tw-scale-y { - syntax: "*"; - inherits: false; - initial-value: 1; -} - -@property --tw-scale-z { - syntax: "*"; - inherits: false; - initial-value: 1; -} - -@property --tw-content { - syntax: "*"; - inherits: false; - initial-value: ""; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -@keyframes enter { - from { - opacity: var(--tw-enter-opacity, 1); - transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0)); - } -} - -@keyframes exit { - to { - opacity: var(--tw-exit-opacity, 1); - transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0)); - } -} - -@keyframes accordion-down { - from { - height: 0; - } - - to { - height: var(--radix-accordion-content-height, var(--bits-accordion-content-height)); - } -} - -@keyframes accordion-up { - from { - height: var(--radix-accordion-content-height, var(--bits-accordion-content-height)); - } - - to { - height: 0; - } -} - -@keyframes caret-blink { - 0%, 70%, 100% { - opacity: 1; - } - - 20%, 50% { - opacity: 0; - } -} - -@property --mask-size { - syntax: ''; - inherits: false; - initial-value: 60%; -} - - -@keyframes jhey_background_mask_sway { - from { - --mask-size: 60%; - } - - to { - --mask-size: 30%; - } -} - -.jhey-background-grid { - position: relative; -} - -.jhey-background-grid:before { - --size: 40px; - --line: light-dark( color-mix(in hsl, canvasText, #a1a1a2 90%), color-mix(in hsl, canvasText, #0000 80%)); - content: ""; - background: linear-gradient(90deg, var(--line) 1px, transparent 1px var(--size)) calc(var(--size) * .36) 50% / var(--size) var(--size), linear-gradient(var(--line) 1px, transparent 1px var(--size)) 0% calc(var(--size) * .32) / var(--size) var(--size); - transform-style: flat; - pointer-events: none; - z-index: -1; - width: 100vw; - height: 100%; - position: absolute; - right: 0; - --mask-size: 60%; - -webkit-mask: linear-gradient(125deg, #0000 var(--mask-size), #fff); - mask: linear-gradient(125deg, #0000 var(--mask-size), #fff); - animation: jhey_background_mask_sway 10s infinite alternate ease-in-out; -} From 08f37932f2ab8b7ee7e2507624082b229fd58a54 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Mon, 10 Nov 2025 16:27:12 +0000 Subject: [PATCH 15/17] Blog Page on a separate file --- package-lock.json | 62 +++++++++++- package.json | 1 + src/App.tsx | 181 +++++++++--------------------------- src/data/portfolio.ts | 2 +- src/index.css | 34 +++---- src/pages/BlogListPage.tsx | 12 +++ src/pages/BlogPostPage.tsx | 18 ++++ src/pages/PortfolioPage.tsx | 106 +++++++++++++++++++++ 8 files changed, 258 insertions(+), 158 deletions(-) create mode 100644 src/pages/BlogListPage.tsx create mode 100644 src/pages/BlogPostPage.tsx create mode 100644 src/pages/PortfolioPage.tsx diff --git a/package-lock.json b/package-lock.json index 35dd69d..be1655f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-router-dom": "*", "react-syntax-highlighter": "*", "recharts": "^2.15.2", "sonner": "^2.0.3", @@ -3178,6 +3179,15 @@ "node": ">=4.0.0" } }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3510,7 +3520,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -3557,6 +3568,7 @@ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -4854,6 +4866,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4998,6 +5011,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5024,6 +5038,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5111,6 +5126,44 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/react-router": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz", + "integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.5.tgz", + "integrity": "sha512-mkEmq/K8tKN63Ae2M7Xgz3c9l9YNbY+NHH6NNeUmLA3kDkhKXRsNb/ZpxaEunvGo2/3YXdk5EJU3Hxp3ocaBPw==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.5" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -5364,6 +5417,12 @@ "node": ">=10" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5906,6 +5965,7 @@ "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/package.json b/package.json index 741e373..65cd195 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-router-dom": "*", "react-syntax-highlighter": "*", "recharts": "^2.15.2", "sonner": "^2.0.3", diff --git a/src/App.tsx b/src/App.tsx index 5aaa4e4..9d4b68b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,73 +1,51 @@ -import { Hero } from "./components/Hero"; -import { About } from "./components/About"; -import { Skills } from "./components/Skills"; -import { Projects } from "./components/Projects"; -import { Experience } from "./components/Experience"; -import { Contact } from "./components/Contact"; -import { BlogList } from "./components/BlogList"; -import { BlogPost } from "./components/BlogPost"; +import { BrowserRouter as Router, Routes, Route, useNavigate, useLocation } from "react-router-dom"; import { Button } from "./components/ui/button"; import { Menu, X, Download } from "lucide-react"; import { useState, useEffect } from "react"; import { motion } from "motion/react"; import { portfolioData } from "./data/portfolio"; import GitHubCorner from './components/GitHubCorner/GitHubCorner'; +import { PortfolioPage } from "./pages/PortfolioPage"; +import { BlogListPage } from "./pages/BlogListPage"; +import { BlogPostPage } from "./pages/BlogPostPage"; -export default function App() { +function AppContent() { const [isMenuOpen, setIsMenuOpen] = useState(false); - const [currentView, setCurrentView] = useState<'portfolio' | 'blog' | 'blogPost'>('portfolio'); - const [currentBlogPostId, setCurrentBlogPostId] = useState(null); + const navigate = useNavigate(); + const location = useLocation(); const navItems = portfolioData.navigation; const trackDownloadCV = () => { - if(window['gta'] !== undefined) { - window['gtag']('event', 'button_click', { - 'event_category': 'Engagement', - 'event_label': 'Download CV', - 'value': 1 - }); - } - } - - // Handle hash changes for routing - useEffect(() => { - const handleHashChange = () => { - const hash = window.location.hash; - - if (hash.startsWith('#/blog/')) { - const postId = hash.replace('#/blog/', ''); - setCurrentBlogPostId(postId); - setCurrentView('blogPost'); - } else if (hash === '#/blog') { - setCurrentView('blog'); - setCurrentBlogPostId(null); - } else { - setCurrentView('portfolio'); - setCurrentBlogPostId(null); - } - }; + if (window['gtag'] !== undefined) { + window['gtag']('event', 'button_click', { + 'event_category': 'Engagement', + 'event_label': 'Download CV', + 'value': 1 + }); + } + }; - // Handle initial load - handleHashChange(); + // Determine if we're on a blog page + const isBlogPage = location.pathname.startsWith('/blog'); - // Listen for hash changes - window.addEventListener('hashchange', handleHashChange); - return () => window.removeEventListener('hashchange', handleHashChange); - }, []); + // Scroll to top on route change + useEffect(() => { + window.scrollTo(0, 0); + }, [location.pathname]); // Smooth scroll function const scrollToSection = (targetId: string) => { // Handle blog navigation if (targetId === 'blog') { - window.location.hash = '/blog'; + navigate('/blog'); setIsMenuOpen(false); return; } - // Reset to portfolio view if not already there - if (currentView !== 'portfolio') { - window.location.hash = ''; + // If we're on blog page, navigate to home first + if (isBlogPage) { + navigate('/'); setTimeout(() => { const element = document.getElementById(targetId); if (element) { @@ -86,13 +64,13 @@ export default function App() { window.scrollTo({ top: offsetPosition, behavior: "smooth" }); } } - + setIsMenuOpen(false); }; // Scroll to top function const scrollToTop = () => { - window.location.hash = ''; + navigate('/'); window.scrollTo({ top: 0, behavior: "smooth", @@ -100,16 +78,6 @@ export default function App() { setIsMenuOpen(false); }; - // Handle blog post navigation - const handleBlogPostClick = (postId: string) => { - window.location.hash = `/blog/${postId}`; - }; - - // Handle back to blog list - const handleBackToBlog = () => { - window.location.hash = '/blog'; - }; - // Animation variants for scroll transitions const sectionVariants = { hidden: { @@ -232,9 +200,7 @@ export default function App() {
    ); -} \ No newline at end of file +} + +export default function App() { + return ( + + + + ); +} diff --git a/src/data/portfolio.ts b/src/data/portfolio.ts index 101ac21..50f16b7 100644 --- a/src/data/portfolio.ts +++ b/src/data/portfolio.ts @@ -391,4 +391,4 @@ export const portfolioData = { tagline: "Open to new opportunities • Available for freelance projects", }, -}; \ No newline at end of file +}; diff --git a/src/index.css b/src/index.css index 79bd30a..f251239 100644 --- a/src/index.css +++ b/src/index.css @@ -2857,6 +2857,22 @@ html { } } +/* virtual-fs:file:///components/GitHubCorner/GitHubCorner.css */ +.github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; +} +@keyframes octocat-wave { + 0%, 100% { + transform: rotate(0); + } + 20%, 60% { + transform: rotate(-25deg); + } + 40%, 80% { + transform: rotate(10deg); + } +} + /* virtual-fs:file:///components/HeroProfileImage/HeroProfileImage.css */ #hero-profile-image { box-shadow: rgba(50, 50, 93, 0.25) 0 50px 100px -20px, rgba(0, 0, 0, 0.3) 0 30px 60px -30px; @@ -2879,20 +2895,4 @@ html { border-radius: 33% 67% 58% 42%/63% 68% 32% 37%; } } - -/* virtual-fs:file:///components/GitHubCorner/GitHubCorner.css */ -.github-corner:hover .octo-arm { - animation: octocat-wave 560ms ease-in-out; -} -@keyframes octocat-wave { - 0%, 100% { - transform: rotate(0); - } - 20%, 60% { - transform: rotate(-25deg); - } - 40%, 80% { - transform: rotate(10deg); - } -} -/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29tcG9uZW50cy9IZXJvUHJvZmlsZUltYWdlL0hlcm9Qcm9maWxlSW1hZ2UuY3NzIiwgImNvbXBvbmVudHMvR2l0SHViQ29ybmVyL0dpdEh1YkNvcm5lci5jc3MiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIiNoZXJvLXByb2ZpbGUtaW1hZ2Uge1xuICAgIGJveC1zaGFkb3c6IHJnYmEoNTAsIDUwLCA5MywgMC4yNSkgMCA1MHB4IDEwMHB4IC0yMHB4LCByZ2JhKDAsIDAsIDAsIDAuMykgMCAzMHB4IDYwcHggLTMwcHg7XG59XG5cbi5tb3JwaGluZy1ib3JkZXIge1xuICAgIGJvcmRlci1yYWRpdXM6IDMwJSA3MCUgNzAlIDMwJSAvIDMwJSAzMCUgNzAlIDcwJTtcbiAgICBhbmltYXRpb246IG1vcnBoaW5nLWJvcmRlciAyMHMgaW5maW5pdGUgYWx0ZXJuYXRlO1xufVxuXG5Aa2V5ZnJhbWVzIG1vcnBoaW5nLWJvcmRlciB7XG4gICAgMCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMCUgNzAlIDcwJSAzMCUvMzAlIDMwJSA3MCUgNzAlO1xuICAgIH1cbiAgICAyNSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1OCUgNDIlIDc1JSAyNSUvNzYlIDQ2JSA1NCUgMjQlO1xuICAgIH1cbiAgICA1MCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCUgNTAlIDMzJSA2NyUvNTUlIDI3JSA3MyUgNDUlO1xuICAgIH1cbiAgICA3NSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMyUgNjclIDU4JSA0MiUvNjMlIDY4JSAzMiUgMzclO1xuICAgIH1cbn0iLCAiLmdpdGh1Yi1jb3JuZXI6aG92ZXIgLm9jdG8tYXJtIHtcbiAgYW5pbWF0aW9uOiBvY3RvY2F0LXdhdmUgNTYwbXMgZWFzZS1pbi1vdXQ7XG59XG5cbkBrZXlmcmFtZXMgb2N0b2NhdC13YXZlIHtcbiAgMCUsXG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogcm90YXRlKDApO1xuICB9XG4gIDIwJSxcbiAgNjAlIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgtMjVkZWcpO1xuICB9XG4gIDQwJSxcbiAgODAlIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgxMGRlZyk7XG4gIH1cbn0iXSwKICAibWFwcGluZ3MiOiAiO0FBQUEsQ0FBQztBQUNHLGNBQVksS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxNQUFNLEtBQUssRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEtBQUs7QUFDMUY7QUFFQSxDQUFDO0FBQ0csaUJBQWUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJO0FBQzdDLGFBQVcsZ0JBQWdCLElBQUksU0FBUztBQUM1QztBQUVBLFdBTEM7QUFNRztBQUNJLG1CQUFlLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSTtBQUMvQztBQUNBO0FBQ0ksbUJBQWUsSUFBSSxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJO0FBQy9DO0FBQ0E7QUFDSSxtQkFBZSxJQUFJLElBQUksSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLElBQUk7QUFDL0M7QUFDQTtBQUNJLG1CQUFlLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSTtBQUMvQztBQUNKOzs7QUN0QkEsQ0FBQyxhQUFhLE9BQU8sQ0FBQztBQUNwQixhQUFXLGFBQWEsTUFBTTtBQUNoQztBQUVBLFdBSGE7QUFJWDtBQUVFLGVBQVcsT0FBTztBQUNwQjtBQUNBO0FBRUUsZUFBVyxPQUFPO0FBQ3BCO0FBQ0E7QUFFRSxlQUFXLE9BQU87QUFDcEI7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K */ +/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29tcG9uZW50cy9HaXRIdWJDb3JuZXIvR2l0SHViQ29ybmVyLmNzcyIsICJjb21wb25lbnRzL0hlcm9Qcm9maWxlSW1hZ2UvSGVyb1Byb2ZpbGVJbWFnZS5jc3MiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi5naXRodWItY29ybmVyOmhvdmVyIC5vY3RvLWFybSB7XG4gIGFuaW1hdGlvbjogb2N0b2NhdC13YXZlIDU2MG1zIGVhc2UtaW4tb3V0O1xufVxuXG5Aa2V5ZnJhbWVzIG9jdG9jYXQtd2F2ZSB7XG4gIDAlLFxuICAxMDAlIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgwKTtcbiAgfVxuICAyMCUsXG4gIDYwJSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoLTI1ZGVnKTtcbiAgfVxuICA0MCUsXG4gIDgwJSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMTBkZWcpO1xuICB9XG59IiwgIiNoZXJvLXByb2ZpbGUtaW1hZ2Uge1xuICAgIGJveC1zaGFkb3c6IHJnYmEoNTAsIDUwLCA5MywgMC4yNSkgMCA1MHB4IDEwMHB4IC0yMHB4LCByZ2JhKDAsIDAsIDAsIDAuMykgMCAzMHB4IDYwcHggLTMwcHg7XG59XG5cbi5tb3JwaGluZy1ib3JkZXIge1xuICAgIGJvcmRlci1yYWRpdXM6IDMwJSA3MCUgNzAlIDMwJSAvIDMwJSAzMCUgNzAlIDcwJTtcbiAgICBhbmltYXRpb246IG1vcnBoaW5nLWJvcmRlciAyMHMgaW5maW5pdGUgYWx0ZXJuYXRlO1xufVxuXG5Aa2V5ZnJhbWVzIG1vcnBoaW5nLWJvcmRlciB7XG4gICAgMCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMCUgNzAlIDcwJSAzMCUvMzAlIDMwJSA3MCUgNzAlO1xuICAgIH1cbiAgICAyNSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1OCUgNDIlIDc1JSAyNSUvNzYlIDQ2JSA1NCUgMjQlO1xuICAgIH1cbiAgICA1MCUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCUgNTAlIDMzJSA2NyUvNTUlIDI3JSA3MyUgNDUlO1xuICAgIH1cbiAgICA3NSUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAzMyUgNjclIDU4JSA0MiUvNjMlIDY4JSAzMiUgMzclO1xuICAgIH1cbn0iXSwKICAibWFwcGluZ3MiOiAiO0FBQUEsQ0FBQyxhQUFhLE9BQU8sQ0FBQztBQUNwQixhQUFXLGFBQWEsTUFBTTtBQUNoQztBQUVBLFdBSGE7QUFJWDtBQUVFLGVBQVcsT0FBTztBQUNwQjtBQUNBO0FBRUUsZUFBVyxPQUFPO0FBQ3BCO0FBQ0E7QUFFRSxlQUFXLE9BQU87QUFDcEI7QUFDRjs7O0FDakJBLENBQUM7QUFDRyxjQUFZLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssTUFBTSxLQUFLLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxLQUFLO0FBQzFGO0FBRUEsQ0FBQztBQUNHLGlCQUFlLElBQUksSUFBSSxJQUFJLElBQUksRUFBRSxJQUFJLElBQUksSUFBSTtBQUM3QyxhQUFXLGdCQUFnQixJQUFJLFNBQVM7QUFDNUM7QUFFQSxXQUxDO0FBTUc7QUFDSSxtQkFBZSxJQUFJLElBQUksSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLElBQUk7QUFDL0M7QUFDQTtBQUNJLG1CQUFlLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksSUFBSTtBQUMvQztBQUNBO0FBQ0ksbUJBQWUsSUFBSSxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJO0FBQy9DO0FBQ0E7QUFDSSxtQkFBZSxJQUFJLElBQUksSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLElBQUk7QUFDL0M7QUFDSjsiLAogICJuYW1lcyI6IFtdCn0K */ diff --git a/src/pages/BlogListPage.tsx b/src/pages/BlogListPage.tsx new file mode 100644 index 0000000..c9e939c --- /dev/null +++ b/src/pages/BlogListPage.tsx @@ -0,0 +1,12 @@ +import { BlogList } from "../components/BlogList"; +import { useNavigate } from "react-router-dom"; + +export function BlogListPage() { + const navigate = useNavigate(); + + const handleBlogPostClick = (postId: string) => { + navigate(`/blog/${postId}`); + }; + + return ; +} diff --git a/src/pages/BlogPostPage.tsx b/src/pages/BlogPostPage.tsx new file mode 100644 index 0000000..b879191 --- /dev/null +++ b/src/pages/BlogPostPage.tsx @@ -0,0 +1,18 @@ +import { BlogPost } from "../components/BlogPost"; +import { useParams, useNavigate } from "react-router-dom"; + +export function BlogPostPage() { + const { postId } = useParams<{ postId: string }>(); + const navigate = useNavigate(); + + const handleBackClick = () => { + navigate('/blog'); + }; + + if (!postId) { + navigate('/blog'); + return null; + } + + return ; +} diff --git a/src/pages/PortfolioPage.tsx b/src/pages/PortfolioPage.tsx new file mode 100644 index 0000000..930c752 --- /dev/null +++ b/src/pages/PortfolioPage.tsx @@ -0,0 +1,106 @@ +import { Hero } from "../components/Hero"; +import { About } from "../components/About"; +import { Skills } from "../components/Skills"; +import { Projects } from "../components/Projects"; +import { Experience } from "../components/Experience"; +import { Contact } from "../components/Contact"; +import { motion } from "motion/react"; +import { portfolioData } from "../data/portfolio"; + +export function PortfolioPage() { + // Animation variants for scroll transitions + const sectionVariants = { + hidden: { + opacity: 0, + y: 50, + }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6, + ease: "easeOut", + }, + }, + }; + + const staggerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2, + }, + }, + }; + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + ); +} From e7b66666fef5f4e88a53cd935ca28ad9fcb64f53 Mon Sep 17 00:00:00 2001 From: Craig Wayne Date: Thu, 20 Nov 2025 08:33:24 +0000 Subject: [PATCH 16/17] Figma Updates --- _posts/ai-terms.md | 202 +++++++++++++ _posts/comparisons/angular-vs-vue-react.md | 6 + _posts/type-vs-interface.md | 13 + index.html | 8 +- package.json | 137 ++++----- src/App.tsx | 86 +++--- src/components/BlogList.tsx | 14 +- src/components/Footer.tsx | 46 +++ src/data/chart-data.json | 329 --------------------- src/data/chart-data.ts | 329 --------------------- src/pages/PortfolioPage.tsx | 7 +- 11 files changed, 402 insertions(+), 775 deletions(-) create mode 100644 _posts/ai-terms.md create mode 100644 _posts/comparisons/angular-vs-vue-react.md create mode 100644 _posts/type-vs-interface.md create mode 100644 src/components/Footer.tsx delete mode 100644 src/data/chart-data.json delete mode 100644 src/data/chart-data.ts diff --git a/_posts/ai-terms.md b/_posts/ai-terms.md new file mode 100644 index 0000000..3e3e699 --- /dev/null +++ b/_posts/ai-terms.md @@ -0,0 +1,202 @@ +--- +title: "AI Terms" +description: "" +image: "https://images.unsplash.com/photo-1713345248737-2698000f143d?w=800&q=80" +date: 2025-11-10T16:57:33+05:30 +draft: false +params: + author: Craig Wayne +tags: + - AI + - Terms + - Glossary +--- + +## Model + +## LLM (Large Language Model) +- type of model +- examples: + +| Model Family | Developer | Key Features & Models | +|------------------------------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------| +| GPT (Generative Pre-trained Transformer) | OpenAI | The models that launched the modern AI boom. | +| | | Current Flagship: GPT-4o (o for omni, highly multimodal, integrates text, audio, and vision seamlessly). | +| | | Previous Versions: GPT-4, GPT-3.5." | +| Gemini | Google DeepMind | Highly advanced, multimodal models designed to integrate into Google's vast product ecosystem. | +| | | Current Flagships: Gemini 2.5 Pro (most capable) and Gemini 2.5 Flash (optimized for speed and efficiency). | +| Claude | Anthropic | Known for their emphasis on AI safety and Constitutional AI. They excel at long-context comprehension and complex reasoning. | +| | | Current Flagships: Claude 3/4 Opus (most capable), Claude 3/4 Sonnet (balanced), and Claude 3/4 Haiku (fastest). | + +--- + +## Model Variants / Model Tiers + +The terms 2.5 Flash and 2.5 Pro are called Model Variants or Model Tiers within the Gemini family. + +This naming scheme is common across the entire industry (like with Claude's Haiku, Sonnet, and Opus) and is used to clearly communicate the specific trade-offs between the models. + + +| Part | Example | Meaning/Classification | +|-------------|------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| Gemini | (Family Name) | The Base Model Family. This is the core underlying architecture and intellectual property developed by Google DeepMind. | +| 2.5 | (Version Number) | The Major Iteration/Generation. This indicates a significant architectural leap or update from the previous version (like 1.0 or 2.0). | +| Flash / Pro | (Variant Name) | The Model Tier or Optimization. This is the most crucial part, indicating the model's primary use case and performance profile. | + +--- + +## Text-to-Video Generative AI Models + +Key Characteristics of this Classification: +Generative: Their purpose is to create new, original content (video) that did not exist before. + +Text-to-Video: They accept text input and produce video output, unlike Large Language Models (LLMs) which primarily accept and produce text. + +Architectural Overlap: While they are distinct from LLMs, they often use the same fundamental Transformer architecture (or variations like the Diffusion Transformer) for processing the input text and ensuring long-range consistency in the generated output. + + +| Model | Developer | Primary Classification | Key Technology | +|---------------|-----------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| Sora (Sora 2) | OpenAI | Text-to-Video Generative Model | Uses a Diffusion Transformer architecture, which is an adaptation of the technology behind their DALL·E image models. | +| Veo 3 | Google DeepMind | Text-to-Video Generative Model | Uses a Latent Diffusion model, often distributed via the Gemini APIs, and is noted for its strong integration of native audio generation. | + + +--- + +## MCP (Model Context Protocol) +an open standard designed to standardize how Large Language Models (LLMs) and other AI systems interact with the outside world, specifically with external data sources, applications, and tools. + +Think of MCP as the USB-C port for AI applications. Just as a USB-C cable provides a universal way to connect a computer to a charger, an external drive, or a monitor, the MCP provides a universal "plug-and-play" method for connecting an AI model to any source of context or capability it needs. + +Before MCP, every time a developer wanted an LLM (like Claude or GPT) to access a specific database, a company's internal file system, or an external API (like a weather service), they had to write custom code ("glue code") for that single connection. This created an unmanageable mess of integrations. MCP fixes this by providing a common protocol. + +--- + +## RAG (Retrieval-Augmented Generation) +- is a technique used to improve the accuracy and relevance of answers provided by LLMs. +- The Problem: Standard LLMs can only answer based on the data they were trained on, which is often outdated or lacks specific domain knowledge. +- The Solution: Before an LLM generates a response, the RAG system retrieves relevant information (like documents, company manuals, or knowledge base articles) from an external, up-to-date source. This retrieved context is then provided to the LLM as part of the prompt, allowing it to generate a more informed and accurate answer. + +https://encrypted-tbn2.gstatic.com/licensed-image?q=tbn:ANd9GcSBD4HDHXBpm8cyiKOz-3SgIGFkqfGFtDjRMPaAhJyjMltcHLngXZLnrwGoIu39GCu4cOTHIW9p19oos1riCheFpIkfZzT9vYTbqvORJ28BxSqY6Rk + + +| Name | Classification | Description | +|--------------|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Anthropic | AI Company | Anthropic is an American AI safety and research company dedicated to building reliable, interpretable, and steerable AI systems. | +| Claude | LLM/AI Assistant | A family of Large Language Models developed by Anthropic. The models (like Claude 3) are powerful competitors to GPT and Gemini. | +| Gemini | LLM/AI Assistant | A family of highly capable Large Language Models developed by Google. It serves as a foundational model for many Google products. | +| ChatGPT | Interface/Application | An application developed by OpenAI that provides a conversational interface to the underlying GPT (Generative Pre-trained Transformer) family of LLMs. | +| Hugging Face | AI Platform/Community | A massive online platform and open-source community for machine learning. It hosts thousands of open-source models (including LLMs), datasets, and tools for AI development. | +| Cursor | AI-Powered Code Editor | A software application (a code editor) that integrates various Large Language Models (like GPT-4) to help developers write, debug, and understand code more efficiently. | + +--- + +### Comparison of AI App Builders + +| | Bolt | Lovable | Figma | Vercel v0 | [Framer](https://www.framer.com/) | +|--|------|---------|-------|-----------|-----------------------------------| +| + +--- + +### Comparison of Workflow Automators (iPaaS) + +| | ActivePieces | n8n | Zapier | Make | +|---------------|--------------|-----|--------|------| +| Open Source | +| Can Self Host | + +--- + +## Ollama +- run multiple LLMs, locally on your computer + +--- + +### [n8n](https://n8n.io/) + +--- + +### [ActivePieces](https://www.activepieces.com/) + +It is best classified as a modern, Open-Source, AI-First iPaaS/Workflow Automation Platform. + +1. Primary Classification: Open-Source Workflow Automation Platform (iPaaS) +* Classification: Integration Platform as a Service (iPaaS) +* Description: ActivePieces' core function is to allow users to build multistep automated workflows (called "Flows") by connecting different software applications and services. This places it directly in competition with market leaders like Zapier and Make. +* Key Differentiator: It is an open-source platform, meaning users can view, modify, and self-host the core code. This appeals strongly to developers, technical teams, and businesses with strict data privacy or customization needs. + +2. The Modern AI Classification +* Classification: AI-First Automation Platform / AI Agent Orchestrator +* Description: ActivePieces is heavily marketed as "AI-first," meaning it is built to easily integrate advanced AI functionalities (like large language models) directly into the automation flows. + * AI Agents: A significant feature is the ability to build and deploy AI Agents—autonomous programs that can think, plan, and act to achieve a goal by utilizing the platform's app integrations ("Pieces") as their tools. + * Model Context Protocol (MCP): It is one of the platforms pioneering the use of protocols like MCP to enable external AI models (like Claude or Gemini) to securely and reliably interact with external apps connected to ActivePieces. + +3. By Technical and Deployment Model +* Classification: Open-Core Low-Code Tool +* Open-Core Model: The fundamental automation builder is open-source (MIT license), while advanced features (like Enterprise collaboration, SSO, and dedicated cloud hosting) are offered as proprietary add-ons in their commercial cloud versions. +* No-Code/Low-Code: It uses a no-code visual drag-and-drop builder for general users, but it fully supports low-code steps by allowing developers to write custom JavaScript code and import NPM packages for maximum flexibility. +* Deployment: It offers both Cloud-hosted and Self-hosted (on-premise/Docker) options, giving users complete control over their data and infrastructure. + +Classification Summary Table + +| Classification Category | ActivePieces Classification | Key Feature | +|-------------------------|-------------------------------------------|-----------------------------------------------------------------------------------------| +| Primary Domain | iPaaS (Integration Platform as a Service) | Connects 400+ apps using automated workflows ("Flows"). | +| AI Focus | AI Agent Orchestrator | Enables building, deploying, and managing goal-oriented AI Agents. | +| Source Code Model | Open-Core | Core platform is open-source (MIT license); commercial plans offer enterprise features. | +| Development Style | No-Code/Low-Code | Drag-and-drop builder for non-technical users, with custom code support for developers. | + +--- + +### [Bolt](https://bolt.new/) +Browser-Based, AI-Powered, Full-Stack Development Environment. + +1. Primary Classification: AI-Powered Development Agent +* Classification: Generative AI Development Tool / AI App Builder +* Core Function: It uses sophisticated Large Language Models (LLMs) to generate full-stack web applications (React, Tailwind CSS, Node.js) based on natural language prompts. +* Distinguishing Feature: often referred to as an AI Agent because the AI doesn't just suggest code; it can actually interact with and control the development environment (creating files, running the dev server, installing packages) directly within the browser, making the chat experience the core workflow. + +2. Technical Environment Classification +* Classification: In-Browser IDE (Integrated Development Environment) +* Core Function: It runs a complete, real development environment (editor, file tree, terminal, and live preview) entirely within the user's browser tab. +* Key Technology: It is built by StackBlitz and leverages their proprietary WebContainers technology, which runs a Node.js environment compiled to WebAssembly locally in the browser, providing a zero-setup development experience. + +3. Application Domain Classification +* Classification: Rapid Prototyping Platform / No-Code/Low-Code Bridge +* Description: It is excellent for quickly building and iterating on functional prototypes, MVPs (Minimum Viable Products), and demos. It serves both experienced developers (for speed) and non-coders (for accessibility). +* Ecosystem: It provides its own full infrastructure (known as Bolt Cloud) for hosting, databases, and user management, which simplifies the process of taking the generated app live with one-click deployment. + +Classification Summary + +| Aspect | Bolt.new Classification | +|-----------------|-------------------------------------------------| +| Core Technology | Generative AI Agent (Code Generation LLM) | +| User Experience | Conversational Development / Prompt-based | +| Environment | In-Browser IDE (powered by WebContainers) | +| Output | Full-Stack Web Application Source Code | +| Use Case | Rapid Prototyping & AI-Assisted Web Development | + +Bolt.new distinguishes itself by tightly integrating the AI model with the actual in-browser runtime environment, +creating a powerful, self-contained, and interactive development sandbox. + +--- + +### [Lovable](https://lovable.dev/) + +1. Primary Classification: AI-Powered Application Builder +* Classification: Generative Development Tool (or AI App Builder) +* Description: Its core purpose is to generate full-stack web applications (frontend, backend, and database logic) from a user's natural language description (text-to-app). It essentially automates the initial, tedious parts of the coding process. + +2. Secondary Classifications + +| Classification | Explanation | +|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| No-Code/Low-Code Platform | It allows users with little to no coding experience to build functional applications simply by using text prompts, placing it in the broader No-Code/Low-Code space, though it generates real code. | +| Developer Tooling | It is specifically designed to output developer-friendly, editable source code (React, Supabase, etc.) that can be synced to GitHub. This makes it a tool for developers to accelerate prototyping and remove boilerplate. | +| Full-Stack Generative AI | It covers the entire software stack (not just the UI or text) by generating front-end code, handling backend logic, and integrating with databases/cloud services (like Supabase and its own Lovable Cloud). | +| Conversational Development | The interface is a chat environment where you prompt the AI, ask for revisions, and debug issues, making it a highly interactive and iterative development experience. | + +--- + +### [Supabase](https://supabase.com/) diff --git a/_posts/comparisons/angular-vs-vue-react.md b/_posts/comparisons/angular-vs-vue-react.md new file mode 100644 index 0000000..12d5e0d --- /dev/null +++ b/_posts/comparisons/angular-vs-vue-react.md @@ -0,0 +1,6 @@ +| Feature | Angular | React | Vue | +|----------------------------------------|---------|-------------|-----| +| Scoped Styles | Yes | CSS Modules | Yes | +| Scoped Styles from External stylesheet | Yes | CSS Modules | Yes | +| Routing Built-in | Yes | CSS Modules | No | +| MVC | Yes | ? | - | diff --git a/_posts/type-vs-interface.md b/_posts/type-vs-interface.md new file mode 100644 index 0000000..d8abc57 --- /dev/null +++ b/_posts/type-vs-interface.md @@ -0,0 +1,13 @@ +| Feature | interface | type | +|----------------------|------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| Defines | Object shapes and class contracts. | Any TypeScript type, including object shapes, unions, intersections, tuples, and primitives | +| | | | +| Declaration Merging | Yes | No | +| | Multiple interface blocks with the same name are merged into one | A type alias is final once declared and cannot be added to | +| | | | +| Extending | Uses the extends keyword. Can extend multiple interfaces. | Uses the & (intersection) keyword to combine shapes. | +| | | | +| Implemented by Class | Yes | Yes | +| | Use class MyClass implements MyInterface { ... } | A class can implement a type if the type represents an object shape. | +| | | | +| Unions & Tuples | No | Yes | diff --git a/index.html b/index.html index fd94c2a..b97ad4b 100644 --- a/index.html +++ b/index.html @@ -15,15 +15,18 @@ + - - + + + + @@ -43,5 +46,6 @@
    + diff --git a/package.json b/package.json index 65cd195..903c97e 100644 --- a/package.json +++ b/package.json @@ -1,69 +1,70 @@ -{ - "name": "my-portfolio", - "version": "0.1.0", - "private": true, - "dependencies": { - "@radix-ui/react-accordion": "^1.2.3", - "@radix-ui/react-alert-dialog": "^1.1.6", - "@radix-ui/react-aspect-ratio": "^1.1.2", - "@radix-ui/react-avatar": "^1.1.3", - "@radix-ui/react-checkbox": "^1.1.4", - "@radix-ui/react-collapsible": "^1.1.3", - "@radix-ui/react-context-menu": "^2.2.6", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.6", - "@radix-ui/react-hover-card": "^1.1.6", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-menubar": "^1.1.6", - "@radix-ui/react-navigation-menu": "^1.2.5", - "@radix-ui/react-popover": "^1.1.6", - "@radix-ui/react-progress": "^1.1.2", - "@radix-ui/react-radio-group": "^1.2.3", - "@radix-ui/react-scroll-area": "^1.2.3", - "@radix-ui/react-select": "^2.1.6", - "@radix-ui/react-separator": "^1.1.2", - "@radix-ui/react-slider": "^1.2.3", - "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-switch": "^1.1.3", - "@radix-ui/react-tabs": "^1.1.3", - "@radix-ui/react-toggle": "^1.1.2", - "@radix-ui/react-toggle-group": "^1.1.2", - "@radix-ui/react-tooltip": "^1.1.8", - "@tanstack/react-query": "*", - "class-variance-authority": "^0.7.1", - "clsx": "*", - "cmdk": "^1.1.1", - "cypress": "^15.3.0", - "embla-carousel-react": "^8.6.0", - "input-otp": "^1.4.2", - "lucide-react": "^0.487.0", - "motion": "*", - "next-themes": "^0.4.6", - "react": "^18.3.1", - "react-day-picker": "^8.10.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.55.0", - "react-resizable-panels": "^2.1.7", - "react-router-dom": "*", - "react-syntax-highlighter": "*", - "recharts": "^2.15.2", - "sonner": "^2.0.3", - "tailwind-merge": "*", - "vaul": "^1.1.2" - }, - "devDependencies": { - "@types/node": "^20.10.0", - "@vitejs/plugin-react-swc": "^3.10.2", - "cypress": "^15.3.0", - "extract-zip": "^2.0.1", - "ignore": "^7.0.5", - "vite": "7.1.11" - }, - "scripts": { - "dev": "vite", - "build": "vite build", - "cy:open": "cypress open", - "cy:run": "cypress run", - "extract:figma": "node extract-figma-files.js" + + { + "name": "my-portfolio", + "version": "0.1.0", + "private": true, + "dependencies": { + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-aspect-ratio": "^1.1.2", + "@radix-ui/react-avatar": "^1.1.3", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-collapsible": "^1.1.3", + "@radix-ui/react-context-menu": "^2.2.6", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.6", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-menubar": "^1.1.6", + "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slider": "^1.2.3", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-switch": "^1.1.3", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-toggle": "^1.1.2", + "@radix-ui/react-toggle-group": "^1.1.2", + "@radix-ui/react-tooltip": "^1.1.8", + "@tanstack/react-query": "*", + "class-variance-authority": "^0.7.1", + "clsx": "*", + "cmdk": "^1.1.1", + "cypress": "^15.3.0", + "embla-carousel-react": "^8.6.0", + "input-otp": "^1.4.2", + "lucide-react": "^0.487.0", + "motion": "*", + "next-themes": "^0.4.6", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.55.0", + "react-resizable-panels": "^2.1.7", + "react-router-dom": "*", + "react-syntax-highlighter": "*", + "recharts": "^2.15.2", + "sonner": "^2.0.3", + "tailwind-merge": "*", + "vaul": "^1.1.2" + }, + "devDependencies": { + "@types/node": "^20.10.0", + "@vitejs/plugin-react-swc": "^3.10.2", + "cypress": "^15.3.0", + "extract-zip": "^2.0.1", + "ignore": "^7.0.5", + "vite": "7.1.11" + }, + "scripts": { + "build": "vite build", + "cy:open": "cypress open", + "cy:run": "cypress run", + "extract:figma": "node extract-figma-files.js", + "dev": "vite" + } } -} diff --git a/src/App.tsx b/src/App.tsx index 9d4b68b..31d0eae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,17 @@ -import { BrowserRouter as Router, Routes, Route, useNavigate, useLocation } from "react-router-dom"; +import { + BrowserRouter as Router, + Routes, + Route, + useNavigate, + useLocation, +} from "react-router-dom"; import { Button } from "./components/ui/button"; import { Menu, X, Download } from "lucide-react"; import { useState, useEffect } from "react"; import { motion } from "motion/react"; import { portfolioData } from "./data/portfolio"; -import GitHubCorner from './components/GitHubCorner/GitHubCorner'; +import GitHubCorner from "./components/GitHubCorner/GitHubCorner"; +import Footer from "./components/Footer"; import { PortfolioPage } from "./pages/PortfolioPage"; import { BlogListPage } from "./pages/BlogListPage"; import { BlogPostPage } from "./pages/BlogPostPage"; @@ -17,17 +24,17 @@ function AppContent() { const navItems = portfolioData.navigation; const trackDownloadCV = () => { - if (window['gtag'] !== undefined) { - window['gtag']('event', 'button_click', { - 'event_category': 'Engagement', - 'event_label': 'Download CV', - 'value': 1 + if (window["gtag"] !== undefined) { + window["gtag"]("event", "button_click", { + event_category: "Engagement", + event_label: "Download CV", + value: 1, }); } }; // Determine if we're on a blog page - const isBlogPage = location.pathname.startsWith('/blog'); + const isBlogPage = location.pathname.startsWith("/blog"); // Scroll to top on route change useEffect(() => { @@ -37,31 +44,41 @@ function AppContent() { // Smooth scroll function const scrollToSection = (targetId: string) => { // Handle blog navigation - if (targetId === 'blog') { - navigate('/blog'); + if (targetId === "blog") { + navigate("/blog"); setIsMenuOpen(false); return; } // If we're on blog page, navigate to home first if (isBlogPage) { - navigate('/'); + navigate("/"); setTimeout(() => { const element = document.getElementById(targetId); if (element) { const headerOffset = 80; - const elementPosition = element.getBoundingClientRect().top; - const offsetPosition = elementPosition + window.pageYOffset - headerOffset; - window.scrollTo({ top: offsetPosition, behavior: "smooth" }); + const elementPosition = + element.getBoundingClientRect().top; + const offsetPosition = + elementPosition + window.pageYOffset - headerOffset; + window.scrollTo({ + top: offsetPosition, + behavior: "smooth", + }); } }, 100); } else { const element = document.getElementById(targetId); if (element) { const headerOffset = 80; - const elementPosition = element.getBoundingClientRect().top; - const offsetPosition = elementPosition + window.pageYOffset - headerOffset; - window.scrollTo({ top: offsetPosition, behavior: "smooth" }); + const elementPosition = + element.getBoundingClientRect().top; + const offsetPosition = + elementPosition + window.pageYOffset - headerOffset; + window.scrollTo({ + top: offsetPosition, + behavior: "smooth", + }); } } @@ -70,7 +87,7 @@ function AppContent() { // Scroll to top function const scrollToTop = () => { - navigate('/'); + navigate("/"); window.scrollTo({ top: 0, behavior: "smooth", @@ -107,7 +124,9 @@ function AppContent() { return (
    ); } diff --git a/src/components/BlogList.tsx b/src/components/BlogList.tsx index 2378a0e..97273f4 100644 --- a/src/components/BlogList.tsx +++ b/src/components/BlogList.tsx @@ -37,10 +37,10 @@ export function BlogList({ onPostClick }: BlogListProps) { const formatDate = (dateString: string) => { const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric' + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' }); }; @@ -78,7 +78,7 @@ export function BlogList({ onPostClick }: BlogListProps) { className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
    - {post.category} @@ -113,8 +113,8 @@ export function BlogList({ onPostClick }: BlogListProps) { {post.tags.slice(0, 2).join(', ')}
    -