From b5a726e359451e34cff6c10d01209f2c537d31ea Mon Sep 17 00:00:00 2001 From: gibbyb Date: Thu, 28 Aug 2025 16:04:14 -0500 Subject: [PATCH] More changes that I would want my example to have I think --- bun.lock | 23 ++ components.json | 21 + package.json | 7 + public/appicon/icon-144.png | Bin 0 -> 10297 bytes public/appicon/icon-36.png | Bin 0 -> 2440 bytes public/appicon/icon-48.png | Bin 0 -> 3225 bytes public/appicon/icon-72.png | Bin 0 -> 4406 bytes public/appicon/icon-96.png | Bin 0 -> 6018 bytes public/appicon/icon.png | Bin 0 -> 14451 bytes public/convex.svg | 17 - public/favicon-16.png | Bin 0 -> 1904 bytes public/favicon-32.png | Bin 0 -> 3661 bytes public/favicon.ico | Bin 0 -> 63326 bytes public/favicon.png | Bin 0 -> 11610 bytes src/app/globals.css | 26 -- src/app/layout.tsx | 30 +- src/app/server/inner.tsx | 2 +- .../{ => providers}/ConvexClientProvider.tsx | 6 +- src/components/providers/ThemeProvider.tsx | 69 ++++ src/components/providers/index.tsx | 2 + src/components/ui/button.tsx | 59 +++ src/components/ui/index.tsx | 1 + src/lib/metadata.ts | 369 ++++++++++++++++++ src/lib/middleware/ban-suspicious-ips.ts | 201 ++++++++++ src/lib/utils.ts | 20 + src/styles/globals.css | 167 ++++++++ 26 files changed, 963 insertions(+), 57 deletions(-) create mode 100644 components.json create mode 100644 public/appicon/icon-144.png create mode 100644 public/appicon/icon-36.png create mode 100644 public/appicon/icon-48.png create mode 100644 public/appicon/icon-72.png create mode 100644 public/appicon/icon-96.png create mode 100644 public/appicon/icon.png delete mode 100644 public/convex.svg create mode 100644 public/favicon-16.png create mode 100644 public/favicon-32.png create mode 100644 public/favicon.ico create mode 100644 public/favicon.png delete mode 100644 src/app/globals.css rename src/components/{ => providers}/ConvexClientProvider.tsx (80%) create mode 100644 src/components/providers/ThemeProvider.tsx create mode 100644 src/components/providers/index.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/index.tsx create mode 100644 src/lib/metadata.ts create mode 100644 src/lib/middleware/ban-suspicious-ips.ts create mode 100644 src/lib/utils.ts create mode 100644 src/styles/globals.css diff --git a/bun.lock b/bun.lock index ca8be9f..2a95d77 100644 --- a/bun.lock +++ b/bun.lock @@ -5,15 +5,21 @@ "name": "example", "dependencies": { "@convex-dev/auth": "^0.0.81", + "@radix-ui/react-slot": "^1.2.3", "@sentry/nextjs": "^10.7.0", "@t3-oss/env-nextjs": "^0.13.8", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "convex": "^1.26.0", "eslint-plugin-prettier": "^5.5.4", + "lucide-react": "^0.542.0", "next": "15.2.3", "next-plausible": "^3.12.4", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "require-in-the-middle": "^7.5.2", + "tailwind-merge": "^3.3.1", "typescript-eslint": "^8.41.0", "zod": "^4.1.5", }, @@ -29,6 +35,7 @@ "npm-run-all": "^4.1.5", "prettier": "^3.5.3", "tailwindcss": "^4", + "tw-animate-css": "^1.3.7", "typescript": "^5", }, }, @@ -512,6 +519,10 @@ "@prisma/instrumentation": ["@prisma/instrumentation@6.14.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "sha512-Po/Hry5bAeunRDq0yAQueKookW3glpP+qjjvvyOfm6dI2KG5/Y6Bgg3ahyWd7B0u2E+Wf9xRk2rtdda7ySgK1A=="], + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], @@ -858,10 +869,14 @@ "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "clear-module": ["clear-module@4.1.2", "", { "dependencies": { "parent-module": "^2.0.0", "resolve-from": "^5.0.0" } }, "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], @@ -1254,6 +1269,8 @@ "lucia": ["lucia@3.2.2", "", { "dependencies": { "@oslojs/crypto": "^1.0.1", "@oslojs/encoding": "^1.1.0" } }, "sha512-P1FlFBGCMPMXu+EGdVD9W4Mjm0DqsusmKgO7Xc33mI5X1bklmsQb0hfzPhXomQr9waWIBDsiOjvr1e6BTaUqpA=="], + "lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="], + "magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -1300,6 +1317,8 @@ "next-plausible": ["next-plausible@3.12.4", "", { "peerDependencies": { "next": "^11.1.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 ", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cD3+ixJxf8yBYvsideTxqli3fvrB7R4BXcvsNJz8Sm2X1QN039WfiXjCyNWkub4h5++rRs6fHhchUMnOuJokcg=="], + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + "nice-try": ["nice-try@1.0.5", "", {}, "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], @@ -1528,6 +1547,8 @@ "synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], + "tailwindcss": ["tailwindcss@4.1.12", "", {}, "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA=="], "tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="], @@ -1550,6 +1571,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tw-animate-css": ["tw-animate-css@1.3.7", "", {}, "sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A=="], + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="], diff --git a/components.json b/components.json new file mode 100644 index 0000000..23bfe78 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/package.json b/package.json index 0d60cf0..a88b6f7 100644 --- a/package.json +++ b/package.json @@ -17,15 +17,21 @@ }, "dependencies": { "@convex-dev/auth": "^0.0.81", + "@radix-ui/react-slot": "^1.2.3", "@sentry/nextjs": "^10.7.0", "@t3-oss/env-nextjs": "^0.13.8", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "convex": "^1.26.0", "eslint-plugin-prettier": "^5.5.4", + "lucide-react": "^0.542.0", "next": "15.2.3", "next-plausible": "^3.12.4", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", "require-in-the-middle": "^7.5.2", + "tailwind-merge": "^3.3.1", "typescript-eslint": "^8.41.0", "zod": "^4.1.5" }, @@ -41,6 +47,7 @@ "npm-run-all": "^4.1.5", "prettier": "^3.5.3", "tailwindcss": "^4", + "tw-animate-css": "^1.3.7", "typescript": "^5" } } diff --git a/public/appicon/icon-144.png b/public/appicon/icon-144.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3cf04a09c852d5c2e7d0768d5abcc6d04b001b GIT binary patch literal 10297 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 QBLYBDl2@0jl(7i>f8G;(`2YX_ literal 0 HcmV?d00001 diff --git a/public/appicon/icon-36.png b/public/appicon/icon-36.png new file mode 100644 index 0000000000000000000000000000000000000000..0dd45e055187b7bbc979f1d105d28c7395104fb4 GIT binary patch literal 2440 zcma)+30M=?7RN_lRTM?7J1(WwiiJQjNeCfNMFavtz_17b!V-o_GC(GnOxWrY%B$c? zwV)!nJU?q&_JCR+sNjyEqO5@@qCVMo*#k&#sG`2te&3sK?%Z$Y{LVT5d+xcjpX+Ql zZ;t*P2!iHu>}_1ZyiPlG{sx}a&&6qA`b=cyWCcO01l=*g4DfD%*tvKRi*cPjKFeb5G)qJ>ZG+cE#VWuItcfG)>KK1kx)ZQ3XA{L&GDZ zmFkZ8#1dB|XL~@lKYk@N`dUcTwd3c?y1Iu32ZuU3dKzAJ@k1^(yz1%e8yp-Q9vT|G zThe&v!RyB6zMkIU5zQF5y1L$UcJ@}O+dNR!HlGY9zs!Q04ejkc8qMg$#KhaTZ^y>Q zZ{2_8?3d}}mldD*U}#t~IyyQ&K3=7+Px)t7R7_%6@~>mNuw^@}A3*+6$$Pv@?ufqT0V!-Xe^D6guEJ9htkdgarq z*25=CJcKzS#lK#@?v6j4O0w`WX89VKyBRUu$d)1+JA4C!$48?(yLz5fw#xS2kbGNU z@0FHxwyf^i%Z+S=W*2SdpfueaWW;baW_r@tk?>ZWY$ah@`_p>aPv-547 zMTT@2Lps;c%!S0<5g4qjs;S?fSZwE&=8s>lsA-FcEjpK0aU|)2pLmZeU(O4Rir9Nh zb-AdkyLVtfqlhWYx>EN`PR+ORw+4qs2L^|_dwTo&2Gq|wdE#6rxj?!m|9EgnJ@ z5mUJL;2j}xRS{d5f3u;vwSQz}Y<&D}U;jvXb=#SXPx&Evi0rrB2W}raQMz;QjZH-6 z0nn+_0=L=OSkypCTfoaKg5zX6t97QXp5feGG^_c*T*|Sra%Ywz0ZNFi^C13Vtc&AZ z>&28~7j(>YxTtE@G@7olp0QeO5kPV_#nclRBiqxQ&77Gsyg>!WuE1bBJj>S7hvw^Z+v zdyz`9k}Lble#QbG-te@L7O^Z}F*h_l!g{f?vZJ;@IuR2aM_IeCwP#_D#TK6g%@=>) z^J?ATo$$8)!_!MyuOWC_e)d}8)M zWpZswI+J8zM)_mVEs)CD!vI}7g|b5haxoW+{1~TOkPNhLbY^$$tPp@NnZh{|fe3X( zaS$p5d;`vOpzl%olk76I1}wf`W?6_rav7218@fnx+j{d$XE1wdE-NfzD* z7BZ*qrdm4x1?cq&_+ry@CIFXBMf9=`w3czDa!i{Q!$lCBF<*%Q#6XM4r9vV?AVVQB zL4?U!WHR_4iBK>wB*auI6J0A?eHyUKwXD0gx-6EB6j6Y!B3LXgK;;np^F#spznICM zV6p5`Oe6;VXhGfLyK~2TS?}V!WBzCifK+6BsRNM7zIcZ?fu10u!cb^Z6*&wSS>}3Z z>oB>z_t4}Kfz9a_3^S@Zjrl`1Cj}t$Ka%W*MWYa9>(<2cn>&EX<0I1!R}Lh^LQ$}E zvIHkWr6RIW5RM^ITu%P*t02=*1g6ocK?u_v6~I)Axp^QJMJ*UKCTvcH%`HrUhbho! r78!mVWSBf13OodtM&OWWQm6sV6sDTeJ=Yl;0s!LJI@@Gz4fyVNq!a@M literal 0 HcmV?d00001 diff --git a/public/appicon/icon-48.png b/public/appicon/icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..6a5d8f58ced8d681f5a313e3b6221b45ea5fe011 GIT binary patch literal 3225 zcma)-2{_dGAIEvPDggkX zN~c-7LOo`oDJnouPr2EnP`8Y|&1o9|l*A~{G3BA}a2Czg2>=f00YF#;0K9{S!iE7L z5Hh~l4FHr20I*J!T)Ev85)}L#?5u(Lg-?2Q?m1}W8!6q%=9>`(RQtf3&i`DSGt|2Pj60OG@W~V5^35~P-$t> z0WWaMOjMHDTuMy6^i$sLvCJwZf_+o;`6|Ung7!!|M^BaN%35p0b>ETq(jo23#zpD{ zNslfMgcQ7@+HW)9sE?02J?X-%%Ql+%F|azaMP5!}>7OYFXO*jy=VkF-E0XU9dPecB zlr$={s*57Sz4Xk4nQ>HEtF<95$aWyd9y*Y?O2-Vq?&gEDY_x&mB!0rUEgZP zy-?m9?v671FiRq#l$<2)@s1t;xj^p3iIa`≤uq&hUI24tDwrPs#aaHn8xR<$vz#nl$Yi`?} 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 UNNX7=6a)Z6eN(+69f$D$0qL2oGXMYp literal 0 HcmV?d00001 diff --git a/public/appicon/icon-96.png b/public/appicon/icon-96.png new file mode 100644 index 0000000000000000000000000000000000000000..608eb3f1af68b1674f7a4afa9d34601411fd8c3a GIT binary patch literal 6018 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 S6cGUckgk@IW|g|bjsF7xbGJVL literal 0 HcmV?d00001 diff --git a/public/appicon/icon.png b/public/appicon/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..098137fd63293579323b3557b5b34edbbefd24ca GIT binary patch literal 14451 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 W96()qCMFmFpd_az3z7a1{J#Jie7@fR literal 0 HcmV?d00001 diff --git a/public/convex.svg b/public/convex.svg deleted file mode 100644 index 7d70c4d..0000000 --- a/public/convex.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/public/favicon-16.png b/public/favicon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce5dd3932a4a9e6cec8d446b988e3d1caf75a4e GIT binary patch literal 1904 zcmb7EX;f236rPuz$nu^fA+jZaVhGDgB8iGX6ak3@lqDh@M3%A$qJqV?iY#IgVp~wK zA}WhZQ4vHIs~}Ji!2%*$6cyYlXu+lI(rP*ZtklyV?LB8QU+(+vH{ZRJywugME7Yjk zQ~-dQ#L3JTNv1rQa0z)e|$%)qm! zvGSSrGV;sy25FDVvJrgcX+?1xWcEcXRr2Z+0N8t+f42y{jWG}Gb3oI4_ z0Z$;b;)$&Id^T^fmC({kC;XRm z!>>>ckKiXADChBCIzez#j_sj++%!IVg`Ye9GsG~cRnQPP8|#{{zJeY86KN$z+X0fHc5;N zDSzTSbi*TJBL6^2eW>iBS1Y?Qf>o%IFNO+wPyr##5C8}U4EO0NXhPAzBQP*PhS#I{nWRRlgDM_QkaGb z{18FL$fL!g_7PzoAw8Oz+zm9;oU-GhhI*qTXfg+3RC;BDn^}}^O#9kjCYnd*My)@v zUVj3x)s_c)oc1h_^}pz)tKM`o@P5sZ`rOoKZ)*=OqMsZ&X;vMxWnq)4U51B3zS*6e zv19z&pH zMn5OGBB!9$m@5*B!!mkmwNhP{W>sxW} zrUc4(WO(8odbH1~l1_QyBNdsUaSG=`Ic?b{aYA%8X*)k>1g6 z%9B!}_0Jk`fJ}zDKl`eJ&9P}%K_2xTwpLV2x0n`&pE-7h+Jo1)`h)5ndMt{#(%Mqx zDJ>+Ifi9`yd}3R!%OzjY@c0vXn>4aiROLK(o{XO0Da)r?ZsX{*92zih{pN#t|E8Juc?*HMh8=k*0K6% zdhO5YhGTk~7W^Rx~01;^1muZ0Eo0 EFH<-@Z2$lO literal 0 HcmV?d00001 diff --git a/public/favicon-32.png b/public/favicon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..2f758f64514d089fae3ef82cc6d795b7e4bf39b7 GIT binary patch literal 3661 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 literal 0 HcmV?d00001 diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8c261b6354a008d4b32c85915a45ab256f23d690 GIT binary patch literal 11610 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 { return ( + - {children} + + {children} + + + ); } diff --git a/src/app/server/inner.tsx b/src/app/server/inner.tsx index 9f81857..5b26626 100644 --- a/src/app/server/inner.tsx +++ b/src/app/server/inner.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Preloaded, useMutation, usePreloadedQuery } from 'convex/react'; +import { type Preloaded, useMutation, usePreloadedQuery } from 'convex/react'; import { api } from '~/convex/_generated/api'; export default function Home({ diff --git a/src/components/ConvexClientProvider.tsx b/src/components/providers/ConvexClientProvider.tsx similarity index 80% rename from src/components/ConvexClientProvider.tsx rename to src/components/providers/ConvexClientProvider.tsx index a7bfb6c..f5672c4 100644 --- a/src/components/ConvexClientProvider.tsx +++ b/src/components/providers/ConvexClientProvider.tsx @@ -2,15 +2,15 @@ import { ConvexAuthNextjsProvider } from '@convex-dev/auth/nextjs'; import { ConvexReactClient } from 'convex/react'; -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); -export default function ConvexClientProvider({ +export const ConvexClientProvider = ({ children, }: { children: ReactNode; -}) { +}) => { return ( {children} diff --git a/src/components/providers/ThemeProvider.tsx b/src/components/providers/ThemeProvider.tsx new file mode 100644 index 0000000..ebd12ea --- /dev/null +++ b/src/components/providers/ThemeProvider.tsx @@ -0,0 +1,69 @@ +'use client'; +import { useEffect, useState, type ComponentProps } from 'react'; +import { ThemeProvider as NextThemesProvider } from 'next-themes'; +import { Moon, Sun } from 'lucide-react'; +import { useTheme } from 'next-themes'; +import { Button } from '@/components/ui'; +import { cn } from '@/lib/utils'; + +const ThemeProvider = ({ + children, + ...props +}: ComponentProps) => { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) return null; + return {children}; +}; + +type ThemeToggleProps = { + size?: number; + buttonProps?: Omit, 'onClick'>; +}; + +const ThemeToggle = ({ size = 1, buttonProps }: ThemeToggleProps) => { + const { setTheme, resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return ( + + ); + } + + const toggleTheme = () => { + if (resolvedTheme === 'dark') setTheme('light'); + else setTheme('dark'); + }; + + return ( + + ); +}; + +export { ThemeProvider, ThemeToggle, type ThemeToggleProps }; diff --git a/src/components/providers/index.tsx b/src/components/providers/index.tsx new file mode 100644 index 0000000..8ab0dcf --- /dev/null +++ b/src/components/providers/index.tsx @@ -0,0 +1,2 @@ +export { ConvexClientProvider } from './ConvexClientProvider'; +export { ThemeProvider, ThemeToggle, type ThemeToggleProps } from './ThemeProvider'; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..a2df8dc --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/index.tsx b/src/components/ui/index.tsx new file mode 100644 index 0000000..3e6a69e --- /dev/null +++ b/src/components/ui/index.tsx @@ -0,0 +1 @@ +export { Button, buttonVariants } from './button'; diff --git a/src/lib/metadata.ts b/src/lib/metadata.ts new file mode 100644 index 0000000..cf98cfb --- /dev/null +++ b/src/lib/metadata.ts @@ -0,0 +1,369 @@ +import type { Metadata } from 'next'; +import * as Sentry from '@sentry/nextjs'; + +export const generateMetadata = (): Metadata => { + return { + title: { + template: '%s | Tech Tracker', + default: 'Tech Tracker', + }, + description: + 'App used by COG IT employees to \ + update their status throughout the day.', + applicationName: 'Tech Tracker', + keywords: + 'Tech Tracker, City of Gulfport, Information Technology, T3 Template, ' + + 'Next.js, Supabase, Tailwind, TypeScript, React, T3, Gib', + authors: [{ name: 'Gib', url: 'https://gbrown.org' }], + creator: 'Gib Brown', + publisher: 'Gib Brown', + formatDetection: { + email: false, + address: false, + telephone: false, + }, + robots: { + index: true, + follow: true, + nocache: false, + googleBot: { + index: true, + follow: true, + noimageindex: false, + 'max-video-preview': -1, + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, + icons: { + icon: [ + { url: '/favicon.ico', type: 'image/x-icon', sizes: 'any' }, + { + url: '/favicon-16.png', + type: 'image/png', + sizes: '16x16', + }, + { + url: '/favicon-32.png', + type: 'image/png', + sizes: '32x32', + }, + { url: '/favicon.png', type: 'image/png', sizes: '96x96' }, + { + url: '/favicon.ico', + type: 'image/x-icon', + sizes: 'any', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/favicon-16.png', + type: 'image/png', + sizes: '16x16', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/favicon-32.png', + type: 'image/png', + sizes: '32x32', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/favicon.png', + type: 'image/png', + sizes: '96x96', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-36.png', + type: 'image/png', + sizes: '36x36', + }, + { + url: '/appicon/icon-48.png', + type: 'image/png', + sizes: '48x48', + }, + { + url: '/appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + }, + { + url: '/appicon/icon-96.png', + type: 'image/png', + sizes: '96x96', + }, + { + url: '/appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + }, + { + url: '/appicon/icon.png', + type: 'image/png', + sizes: '192x192', + }, + { + url: '/appicon/icon-36.png', + type: 'image/png', + sizes: '36x36', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-48.png', + type: 'image/png', + sizes: '48x48', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-96.png', + type: 'image/png', + sizes: '96x96', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon.png', + type: 'image/png', + sizes: '192x192', + media: '(prefers-color-scheme: dark)', + }, + ], + shortcut: [ + { + url: '/appicon/icon-36.png', + type: 'image/png', + sizes: '36x36', + }, + { + url: '/appicon/icon-48.png', + type: 'image/png', + sizes: '48x48', + }, + { + url: '/appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + }, + { + url: '/appicon/icon-96.png', + type: 'image/png', + sizes: '96x96', + }, + { + url: '/appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + }, + { + url: '/appicon/icon.png', + type: 'image/png', + sizes: '192x192', + }, + { + url: '/appicon/icon-36.png', + type: 'image/png', + sizes: '36x36', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-48.png', + type: 'image/png', + sizes: '48x48', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-96.png', + type: 'image/png', + sizes: '96x96', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/appicon/icon.png', + type: 'image/png', + sizes: '192x192', + media: '(prefers-color-scheme: dark)', + }, + ], + apple: [ + { + url: 'appicon/icon-57.png', + type: 'image/png', + sizes: '57x57', + }, + { + url: 'appicon/icon-60.png', + type: 'image/png', + sizes: '60x60', + }, + { + url: 'appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + }, + { + url: 'appicon/icon-76.png', + type: 'image/png', + sizes: '76x76', + }, + { + url: 'appicon/icon-114.png', + type: 'image/png', + sizes: '114x114', + }, + { + url: 'appicon/icon-120.png', + type: 'image/png', + sizes: '120x120', + }, + { + url: 'appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + }, + { + url: 'appicon/icon-152.png', + type: 'image/png', + sizes: '152x152', + }, + { + url: 'appicon/icon-180.png', + type: 'image/png', + sizes: '180x180', + }, + { + url: 'appicon/icon.png', + type: 'image/png', + sizes: '192x192', + }, + { + url: 'appicon/icon-57.png', + type: 'image/png', + sizes: '57x57', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-60.png', + type: 'image/png', + sizes: '60x60', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-72.png', + type: 'image/png', + sizes: '72x72', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-76.png', + type: 'image/png', + sizes: '76x76', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-114.png', + type: 'image/png', + sizes: '114x114', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-120.png', + type: 'image/png', + sizes: '120x120', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-144.png', + type: 'image/png', + sizes: '144x144', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-152.png', + type: 'image/png', + sizes: '152x152', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon-180.png', + type: 'image/png', + sizes: '180x180', + media: '(prefers-color-scheme: dark)', + }, + { + url: 'appicon/icon.png', + type: 'image/png', + sizes: '192x192', + media: '(prefers-color-scheme: dark)', + }, + ], + other: [ + { + rel: 'apple-touch-icon-precomposed', + url: '/appicon/icon-precomposed.png', + type: 'image/png', + sizes: '180x180', + }, + ], + }, + other: { + ...Sentry.getTraceData(), + }, + appleWebApp: { + title: 'Tech Tracker', + statusBarStyle: 'black-translucent', + startupImage: [ + '/icons/apple/splash-768x1004.png', + { + url: '/icons/apple/splash-1536x2008.png', + media: '(device-width: 768px) and (device-height: 1024px)', + }, + ], + }, + verification: { + google: 'google', + yandex: 'yandex', + yahoo: 'yahoo', + }, + category: 'technology', + /* + appLinks: { + ios: { + url: 'https://techtracker.gbrown.org/ios', + app_store_id: 'com.gbrown.techtracker', + }, + android: { + package: 'https://techtracker.gbrown.org/android', + app_name: 'app_t3_template', + }, + web: { + url: 'https://techtracker.gbrown.org', + should_fallback: true, + }, + }, + */ + }; +}; diff --git a/src/lib/middleware/ban-suspicious-ips.ts b/src/lib/middleware/ban-suspicious-ips.ts new file mode 100644 index 0000000..6e60c32 --- /dev/null +++ b/src/lib/middleware/ban-suspicious-ips.ts @@ -0,0 +1,201 @@ +import { type NextRequest, NextResponse } from 'next/server'; + +// In-memory stores for tracking IPs (use Redis in production) +const ipAttempts = new Map(); +const ip404Attempts = new Map(); +const bannedIPs = new Set(); + +// Ban Arctic Wolf Explicitly +bannedIPs.add('::ffff:10.0.1.49'); + +// Suspicious patterns that indicate malicious activity +const MALICIOUS_PATTERNS = [ + // Your existing patterns + /web-inf/i, + /\.jsp/i, + /\.php/i, + /puttest/i, + /WEB-INF/i, + /\.xml$/i, + /perl/i, + /xampp/i, + /phpwebgallery/i, + /FileManager/i, + /standalonemanager/i, + /h2console/i, + /WebAdmin/i, + /login_form\.php/i, + /%2e/i, + /%u002e/i, + /\.%00/i, + /\.\./, + /lcgi/i, + + // New patterns from your logs + /\/appliance\//i, + /bomgar/i, + /netburner-logo/i, + /\/ui\/images\//i, + /logon_merge/i, + /logon_t\.gif/i, + /login_top\.gif/i, + /theme1\/images/i, + /\.well-known\/acme-challenge\/.*\.jpg$/i, + /\.well-known\/pki-validation\/.*\.jpg$/i, + + // Path traversal and system file access patterns + /\/etc\/passwd/i, + /\/etc%2fpasswd/i, + /\/etc%5cpasswd/i, + /\/\/+etc/i, + /\\\\+.*etc/i, + /%2f%2f/i, + /%5c%5c/i, + /\/\/+/, + /\\\\+/, + /%00/i, + /%23/i, + + // Encoded path traversal attempts + /%2e%2e/i, + /%252e/i, + /%c0%ae/i, + /%c1%9c/i, +]; + +// Suspicious HTTP methods +const SUSPICIOUS_METHODS = ['TRACE', 'PUT', 'DELETE', 'PATCH']; + +const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute +const MAX_ATTEMPTS = 10; // Max suspicious requests per window +const BAN_DURATION = 30 * 60 * 1000; // 30 minutes + +// 404 rate limiting settings +const RATE_404_WINDOW = 2 * 60 * 1000; // 2 minutes +const MAX_404_ATTEMPTS = 10; // Max 404s before ban + +const getClientIP = (request: NextRequest): string => { + const forwarded = request.headers.get('x-forwarded-for'); + const realIP = request.headers.get('x-real-ip'); + const cfConnectingIP = request.headers.get('cf-connecting-ip'); + + if (forwarded) return (forwarded.split(',')[0] ?? '').trim(); + if (realIP) return realIP; + if (cfConnectingIP) return cfConnectingIP; + return request.headers.get('host') ?? 'unknown'; +}; + +const isPathSuspicious = (pathname: string): boolean => { + return MALICIOUS_PATTERNS.some((pattern) => pattern.test(pathname)); +}; + +const isMethodSuspicious = (method: string): boolean => { + return SUSPICIOUS_METHODS.includes(method); +}; + +const updateIPAttempts = (ip: string): boolean => { + const now = Date.now(); + const attempts = ipAttempts.get(ip); + + if (!attempts || now - attempts.lastAttempt > RATE_LIMIT_WINDOW) { + ipAttempts.set(ip, { count: 1, lastAttempt: now }); + return false; + } + + attempts.count++; + attempts.lastAttempt = now; + + if (attempts.count > MAX_ATTEMPTS) { + bannedIPs.add(ip); + ipAttempts.delete(ip); + + setTimeout(() => { + bannedIPs.delete(ip); + }, BAN_DURATION); + + return true; + } + + return false; +}; + +const update404Attempts = (ip: string): boolean => { + const now = Date.now(); + const attempts = ip404Attempts.get(ip); + + if (!attempts || now - attempts.lastAttempt > RATE_404_WINDOW) { + ip404Attempts.set(ip, { count: 1, lastAttempt: now }); + return false; + } + + attempts.count++; + attempts.lastAttempt = now; + + if (attempts.count > MAX_404_ATTEMPTS) { + bannedIPs.add(ip); + ip404Attempts.delete(ip); + + console.log( + `🔨 IP ${ip} banned for excessive 404 requests (${attempts.count} in ${RATE_404_WINDOW / 1000}s)`, + ); + + setTimeout(() => { + bannedIPs.delete(ip); + }, BAN_DURATION); + + return true; + } + + return false; +}; + +export const banSuspiciousIPs = (request: NextRequest): NextResponse | null => { + const { pathname } = request.nextUrl; + const method = request.method; + const ip = getClientIP(request); + + // Check if IP is already banned + if (bannedIPs.has(ip)) { + return new NextResponse('Access denied.', { status: 403 }); + } + + const isSuspiciousPath = isPathSuspicious(pathname); + const isSuspiciousMethod = isMethodSuspicious(method); + + // Handle suspicious activity + if (isSuspiciousPath || isSuspiciousMethod) { + const shouldBan = updateIPAttempts(ip); + + if (shouldBan) { + console.log(`🔨 IP ${ip} has been banned for suspicious activity`); + return new NextResponse('Access denied - IP banned. Please fuck off.', { + status: 403, + }); + } + + return new NextResponse('Not Found', { status: 404 }); + } + + return null; +}; + +// Call this function when you detect a 404 response +export const handle404Response = ( + request: NextRequest, +): NextResponse | null => { + const ip = getClientIP(request); + + if (bannedIPs.has(ip)) { + return new NextResponse('Access denied.', { status: 403 }); + } + + const shouldBan = update404Attempts(ip); + + if (shouldBan) { + return new NextResponse('Access denied - IP banned for excessive 404s.', { + status: 403, + }); + } + + return null; +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..e0376b2 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,20 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +}; + +export const ccn = ({ + context, + className, + on = '', + off = '', +}: { + context: boolean; + className: string; + on: string; + off: string; +}) => { + return twMerge(className, context ? on : off); +}; diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 0000000..b72aecb --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,167 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme { + --font-sans: var(--font-sans), ui-sans-serif, system-ui, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +@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-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); + --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); + + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); + --font-serif: var(--font-serif); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --shadow-2xs: var(--shadow-2xs); + --shadow-xs: var(--shadow-xs); + --shadow-sm: var(--shadow-sm); + --shadow: var(--shadow); + --shadow-md: var(--shadow-md); + --shadow-lg: var(--shadow-lg); + --shadow-xl: var(--shadow-xl); + --shadow-2xl: var(--shadow-2xl); +} + +:root { + --background: oklch(0.9227 0.0011 17.1793); + --foreground: oklch(0.2840 0.0220 262.4967); + --card: oklch(0.9699 0.0013 106.4238); + --card-foreground: oklch(0.2840 0.0220 262.4967); + --popover: oklch(0.9699 0.0013 106.4238); + --popover-foreground: oklch(0.2840 0.0220 262.4967); + --primary: oklch(0.6378 0.1247 281.2150); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.8682 0.0026 48.7143); + --secondary-foreground: oklch(0.4507 0.0152 255.5845); + --muted: oklch(0.9227 0.0011 17.1793); + --muted-foreground: oklch(0.5551 0.0147 266.6154); + --accent: oklch(0.9409 0.0164 322.6966); + --accent-foreground: oklch(0.3774 0.0189 260.6754); + --destructive: oklch(0.6322 0.1310 21.4751); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0.8682 0.0026 48.7143); + --input: oklch(0.8682 0.0026 48.7143); + --ring: oklch(0.6378 0.1247 281.2150); + --chart-1: oklch(0.6378 0.1247 281.2150); + --chart-2: oklch(0.5608 0.1433 283.1275); + --chart-3: oklch(0.5008 0.1358 283.9499); + --chart-4: oklch(0.4372 0.1108 283.4322); + --chart-5: oklch(0.3928 0.0817 282.8932); + --sidebar: oklch(0.8682 0.0026 48.7143); + --sidebar-foreground: oklch(0.2840 0.0220 262.4967); + --sidebar-primary: oklch(0.6378 0.1247 281.2150); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.9409 0.0164 322.6966); + --sidebar-accent-foreground: oklch(0.3774 0.0189 260.6754); + --sidebar-border: oklch(0.8682 0.0026 48.7143); + --sidebar-ring: oklch(0.6378 0.1247 281.2150); + --font-sans: Inter, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 1.0rem; + --shadow-2xs: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.09); + --shadow-xs: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.09); + --shadow-sm: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 1px 2px 3px hsl(240 1.9608% 60% / 0.18); + --shadow: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 1px 2px 3px hsl(240 1.9608% 60% / 0.18); + --shadow-md: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 2px 4px 3px hsl(240 1.9608% 60% / 0.18); + --shadow-lg: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 4px 6px 3px hsl(240 1.9608% 60% / 0.18); + --shadow-xl: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.18), 2px 8px 10px 3px hsl(240 1.9608% 60% / 0.18); + --shadow-2xl: 2px 2px 10px 4px hsl(240 1.9608% 60% / 0.45); + --tracking-normal: 0em; + --spacing: 0.25rem; +} + +.dark { + --background: oklch(0.2236 0.0049 67.5717); + --foreground: oklch(0.9301 0.0075 260.7315); + --card: oklch(0.2793 0.0057 56.1503); + --card-foreground: oklch(0.9301 0.0075 260.7315); + --popover: oklch(0.2793 0.0057 56.1503); + --popover-foreground: oklch(0.9301 0.0075 260.7315); + --primary: oklch(0.7223 0.0946 279.6746); + --primary-foreground: oklch(0.2236 0.0049 67.5717); + --secondary: oklch(0.3352 0.0055 56.2080); + --secondary-foreground: oklch(0.8726 0.0059 264.5296); + --muted: oklch(0.2793 0.0057 56.1503); + --muted-foreground: oklch(0.7176 0.0111 261.7826); + --accent: oklch(0.3889 0.0053 56.2463); + --accent-foreground: oklch(0.8726 0.0059 264.5296); + --destructive: oklch(0.6322 0.1310 21.4751); + --destructive-foreground: oklch(0.2236 0.0049 67.5717); + --border: oklch(0.3352 0.0055 56.2080); + --input: oklch(0.3352 0.0055 56.2080); + --ring: oklch(0.7223 0.0946 279.6746); + --chart-1: oklch(0.7223 0.0946 279.6746); + --chart-2: oklch(0.6378 0.1247 281.2150); + --chart-3: oklch(0.5608 0.1433 283.1275); + --chart-4: oklch(0.5008 0.1358 283.9499); + --chart-5: oklch(0.4372 0.1108 283.4322); + --sidebar: oklch(0.3352 0.0055 56.2080); + --sidebar-foreground: oklch(0.9301 0.0075 260.7315); + --sidebar-primary: oklch(0.7223 0.0946 279.6746); + --sidebar-primary-foreground: oklch(0.2236 0.0049 67.5717); + --sidebar-accent: oklch(0.3889 0.0053 56.2463); + --sidebar-accent-foreground: oklch(0.8726 0.0059 264.5296); + --sidebar-border: oklch(0.3352 0.0055 56.2080); + --sidebar-ring: oklch(0.7223 0.0946 279.6746); + --font-sans: Inter, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 1.0rem; + --shadow-2xs: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.09); + --shadow-xs: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.09); + --shadow-sm: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 1px 2px 3px hsl(0 0% 10.1961% / 0.18); + --shadow: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 1px 2px 3px hsl(0 0% 10.1961% / 0.18); + --shadow-md: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 2px 4px 3px hsl(0 0% 10.1961% / 0.18); + --shadow-lg: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 4px 6px 3px hsl(0 0% 10.1961% / 0.18); + --shadow-xl: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.18), 2px 8px 10px 3px hsl(0 0% 10.1961% / 0.18); + --shadow-2xl: 2px 2px 10px 4px hsl(0 0% 10.1961% / 0.45); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +}