From f916f058fc53b975fcf1a5c98aecc04938d573cf Mon Sep 17 00:00:00 2001
From: asowneryt
Date: Wed, 30 Sep 2026 21:25:07 +1300
Subject: [PATCH] Swap to shadcn components and polish the design
Components (added via the shadcn MCP/CLI):
- Navbar: NavigationMenu on desktop, Sheet on mobile; drops
Navbar.module.css and the hand-rolled slide-down menu
- Buttons: shadcn Button via a shared ContactButtons component
(replaces Button.js and its CSS module)
- Card for price cards, testimonials, the business block and plan;
Table for prices; Input/Textarea/Label/NativeSelect/RadioGroup and
Alert in the quote form; Separator in the footer
- Components import cn from @/lib/utils (not the new "cn" package);
accordion moved to the unified radix-ui package
Design tweaks:
- Geist Sans is actually applied (font variables were on , but
Tailwind reads them on , so the site fell back to system fonts)
- Theme primary set to the existing brand blue; slightly smaller radius
- Balanced, tighter heading tracking; muted secondary text
- 44px contact buttons; 16px FAQ text; tighter homepage spacing
- Full-width selects with 16px text on phones (no iOS zoom)
Co-Authored-By: Claude Opus 5.5
---
package-lock.json | 1479 ++++++++++++++++++++-
package.json | 2 +-
src/app/about/page.js | 2 +-
src/app/areas/[suburb]/page.js | 23 +-
src/app/business/page.js | 81 +-
src/app/components/Button.js | 16 -
src/app/components/Button.module.css | 37 -
src/app/components/ContactButtons.js | 56 +
src/app/components/ContactForm.js | 199 ++-
src/app/components/ContactForm.module.css | 43 -
src/app/components/CtaBand.js | 33 +-
src/app/components/Footer.js | 10 +-
src/app/components/MobileCallBar.js | 4 +-
src/app/components/Navbar.js | 344 ++---
src/app/components/Navbar.module.css | 229 ----
src/app/components/PriceTable.js | 18 +
src/app/components/ServicePage.js | 56 +-
src/app/contact/page.js | 31 +-
src/app/globals.css | 16 +-
src/app/layout.js | 6 +-
src/app/page.js | 93 +-
src/app/pricing/page.js | 16 +-
src/app/windows-10/page.js | 20 +-
src/components/ui/accordion.jsx | 6 +-
src/components/ui/alert.jsx | 68 +
src/components/ui/button.jsx | 60 +
src/components/ui/card.jsx | 112 ++
src/components/ui/input.jsx | 24 +
src/components/ui/label.jsx | 23 +
src/components/ui/native-select.jsx | 65 +
src/components/ui/navigation-menu.jsx | 165 +++
src/components/ui/radio-group.jsx | 44 +
src/components/ui/separator.jsx | 27 +
src/components/ui/sheet.jsx | 147 ++
src/components/ui/table.jsx | 136 ++
src/components/ui/textarea.jsx | 20 +
36 files changed, 2888 insertions(+), 823 deletions(-)
delete mode 100644 src/app/components/Button.js
delete mode 100644 src/app/components/Button.module.css
create mode 100644 src/app/components/ContactButtons.js
delete mode 100644 src/app/components/ContactForm.module.css
delete mode 100644 src/app/components/Navbar.module.css
create mode 100644 src/app/components/PriceTable.js
create mode 100644 src/components/ui/alert.jsx
create mode 100644 src/components/ui/button.jsx
create mode 100644 src/components/ui/card.jsx
create mode 100644 src/components/ui/input.jsx
create mode 100644 src/components/ui/label.jsx
create mode 100644 src/components/ui/native-select.jsx
create mode 100644 src/components/ui/navigation-menu.jsx
create mode 100644 src/components/ui/radio-group.jsx
create mode 100644 src/components/ui/separator.jsx
create mode 100644 src/components/ui/sheet.jsx
create mode 100644 src/components/ui/table.jsx
create mode 100644 src/components/ui/textarea.jsx
diff --git a/package-lock.json b/package-lock.json
index 4e1af69..7f9dc72 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,11 @@
"name": "computer-repair",
"version": "0.1.0",
"dependencies": {
- "@radix-ui/react-accordion": "^1.2.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.49.0",
"next": "^16.3.7",
+ "radix-ui": "^1.6.7",
"react": "^19.3.0",
"react-dom": "^19.3.0",
"sharp": "^0.35.5",
@@ -525,6 +525,44 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
+ "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.12"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz",
+ "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.8.0",
+ "@floating-ui/utils": "^0.2.12"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz",
+ "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.8.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz",
+ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
@@ -1394,12 +1432,41 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@radix-ui/number": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz",
+ "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==",
+ "license": "MIT"
+ },
"node_modules/@radix-ui/primitive": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz",
"integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==",
"license": "MIT"
},
+ "node_modules/@radix-ui/react-accessible-icon": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.15.tgz",
+ "integrity": "sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-visually-hidden": "1.2.11"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-accordion": {
"version": "1.2.20",
"resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.20.tgz",
@@ -1431,6 +1498,136 @@
}
}
},
+ "node_modules/@radix-ui/react-alert-dialog": {
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz",
+ "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dialog": "1.1.23",
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz",
+ "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-aspect-ratio": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.15.tgz",
+ "integrity": "sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-avatar": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz",
+ "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-is-hydrated": "0.1.3",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-checkbox": {
+ "version": "1.3.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz",
+ "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-size": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz",
@@ -1517,6 +1714,70 @@
}
}
},
+ "node_modules/@radix-ui/react-context-menu": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.7.tgz",
+ "integrity": "sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-menu": "2.1.24",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog": {
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz",
+ "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.7.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-direction": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz",
@@ -1532,6 +1793,161 @@
}
}
},
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz",
+ "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-effect-event": "0.0.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dropdown-menu": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz",
+ "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-menu": "2.1.24",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz",
+ "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz",
+ "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-form": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.16.tgz",
+ "integrity": "sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-label": "2.1.15",
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-hover-card": {
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.23.tgz",
+ "integrity": "sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-id": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz",
@@ -1550,6 +1966,294 @@
}
}
},
+ "node_modules/@radix-ui/react-label": {
+ "version": "2.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz",
+ "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-menu": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz",
+ "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.7.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-menubar": {
+ "version": "1.1.24",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.24.tgz",
+ "integrity": "sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-menu": "2.1.24",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-navigation-menu": {
+ "version": "1.2.22",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.22.tgz",
+ "integrity": "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-previous": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-one-time-password-field": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.16.tgz",
+ "integrity": "sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-effect-event": "0.0.5",
+ "@radix-ui/react-use-is-hydrated": "0.1.3",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-password-toggle-field": {
+ "version": "0.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.11.tgz",
+ "integrity": "sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-effect-event": "0.0.5",
+ "@radix-ui/react-use-is-hydrated": "0.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover": {
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz",
+ "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.7.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz",
+ "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-rect": "1.1.4",
+ "@radix-ui/react-use-size": "1.1.4",
+ "@radix-ui/rect": "1.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.1.17",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz",
+ "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-presence": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz",
@@ -1596,6 +2300,225 @@
}
}
},
+ "node_modules/@radix-ui/react-progress": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.16.tgz",
+ "integrity": "sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-radio-group": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.7.tgz",
+ "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-size": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-roving-focus": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz",
+ "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-is-hydrated": "0.1.3",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-scroll-area": {
+ "version": "1.2.18",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz",
+ "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz",
+ "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-previous": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.7.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-separator": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz",
+ "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.7.tgz",
+ "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-previous": "1.1.4",
+ "@radix-ui/react-use-size": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-slot": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
@@ -1614,6 +2537,231 @@
}
}
},
+ "node_modules/@radix-ui/react-switch": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz",
+ "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-size": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz",
+ "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toast": {
+ "version": "1.2.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.23.tgz",
+ "integrity": "sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toggle": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.18.tgz",
+ "integrity": "sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toggle-group": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.19.tgz",
+ "integrity": "sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-toggle": "1.1.18",
+ "@radix-ui/react-use-controllable-state": "1.2.6"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-toolbar": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.19.tgz",
+ "integrity": "sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-separator": "1.1.15",
+ "@radix-ui/react-toggle-group": "1.1.19"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip": {
+ "version": "1.2.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz",
+ "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz",
+ "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz",
@@ -1652,6 +2800,39 @@
}
}
},
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.5.tgz",
+ "integrity": "sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-is-hydrated": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz",
+ "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz",
@@ -1667,6 +2848,86 @@
}
}
},
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz",
+ "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz",
+ "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/rect": "1.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz",
+ "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.4"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz",
+ "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz",
+ "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==",
+ "license": "MIT"
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2749,6 +4010,18 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
@@ -3365,6 +4638,12 @@
"node": ">=8"
}
},
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
+ "license": "MIT"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -4340,6 +5619,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@@ -6112,6 +7400,83 @@
],
"license": "MIT"
},
+ "node_modules/radix-ui": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.7.tgz",
+ "integrity": "sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-accessible-icon": "1.1.15",
+ "@radix-ui/react-accordion": "1.2.20",
+ "@radix-ui/react-alert-dialog": "1.1.23",
+ "@radix-ui/react-arrow": "1.1.15",
+ "@radix-ui/react-aspect-ratio": "1.1.15",
+ "@radix-ui/react-avatar": "1.2.6",
+ "@radix-ui/react-checkbox": "1.3.11",
+ "@radix-ui/react-collapsible": "1.1.20",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-context-menu": "2.3.7",
+ "@radix-ui/react-dialog": "1.1.23",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-dropdown-menu": "2.1.24",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-form": "0.1.16",
+ "@radix-ui/react-hover-card": "1.1.23",
+ "@radix-ui/react-label": "2.1.15",
+ "@radix-ui/react-menu": "2.1.24",
+ "@radix-ui/react-menubar": "1.1.24",
+ "@radix-ui/react-navigation-menu": "1.2.22",
+ "@radix-ui/react-one-time-password-field": "0.1.16",
+ "@radix-ui/react-password-toggle-field": "0.1.11",
+ "@radix-ui/react-popover": "1.1.23",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-progress": "1.1.16",
+ "@radix-ui/react-radio-group": "1.4.7",
+ "@radix-ui/react-roving-focus": "1.1.19",
+ "@radix-ui/react-scroll-area": "1.2.18",
+ "@radix-ui/react-select": "2.3.7",
+ "@radix-ui/react-separator": "1.1.15",
+ "@radix-ui/react-slider": "1.4.7",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-switch": "1.3.7",
+ "@radix-ui/react-tabs": "1.1.21",
+ "@radix-ui/react-toast": "1.2.23",
+ "@radix-ui/react-toggle": "1.1.18",
+ "@radix-ui/react-toggle-group": "1.1.19",
+ "@radix-ui/react-toolbar": "1.1.19",
+ "@radix-ui/react-tooltip": "1.2.16",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-effect-event": "0.0.5",
+ "@radix-ui/react-use-escape-keydown": "1.1.5",
+ "@radix-ui/react-use-is-hydrated": "0.1.3",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-size": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react": {
"version": "19.3.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
@@ -6140,6 +7505,75 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-remove-scroll": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.7",
+ "react-style-singleton": "^2.2.3",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.3",
+ "use-sidecar": "^1.1.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-style-singleton": "^2.2.2",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -7119,6 +8553,49 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-callback-ref": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 01b0bb1..2a50abd 100644
--- a/package.json
+++ b/package.json
@@ -9,11 +9,11 @@
"lint": "eslint ."
},
"dependencies": {
- "@radix-ui/react-accordion": "^1.2.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.49.0",
"next": "^16.3.7",
+ "radix-ui": "^1.6.7",
"react": "^19.3.0",
"react-dom": "^19.3.0",
"sharp": "^0.35.5",
diff --git a/src/app/about/page.js b/src/app/about/page.js
index bc570f9..c3d4728 100644
--- a/src/app/about/page.js
+++ b/src/app/about/page.js
@@ -68,7 +68,7 @@ export default function AboutPage() {
{areaSlugs[name] ? (
{name}
diff --git a/src/app/areas/[suburb]/page.js b/src/app/areas/[suburb]/page.js
index c9f341a..3aefba1 100644
--- a/src/app/areas/[suburb]/page.js
+++ b/src/app/areas/[suburb]/page.js
@@ -1,14 +1,14 @@
import Link from "next/link";
import { notFound } from "next/navigation";
-import { ArrowRight, MessageSquare, Phone } from "lucide-react";
+import { ArrowRight } from "lucide-react";
import HeaderSm from "../../components/HeaderSm";
-import Button from "../../components/Button";
+import ContactButtons from "../../components/ContactButtons";
import CtaBand from "../../components/CtaBand";
import FaqList from "../../components/FaqList";
import JsonLd from "../../components/JsonLd";
import { areas, getArea } from "@/lib/areas";
import { services } from "@/lib/services";
-import { business, links, pageMetadata, serviceSchema } from "@/lib/site";
+import { business, pageMetadata, serviceSchema } from "@/lib/site";
export const dynamicParams = false;
@@ -52,16 +52,7 @@ export default async function AreaPage({ params }) {
{area.intro}
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
+
@@ -85,7 +76,7 @@ export default async function AreaPage({ params }) {
{service.name}
@@ -95,7 +86,7 @@ export default async function AreaPage({ params }) {
See all prices on the{" "}
-
+
pricing page
. I don't repair Apple devices.
@@ -118,7 +109,7 @@ export default async function AreaPage({ params }) {
{name}
diff --git a/src/app/business/page.js b/src/app/business/page.js
index eaae0f6..f59ba4a 100644
--- a/src/app/business/page.js
+++ b/src/app/business/page.js
@@ -1,14 +1,20 @@
import Link from "next/link";
-import { Check, MessageSquare, Phone } from "lucide-react";
+import { Check } from "lucide-react";
import HeaderSm from "../components/HeaderSm";
-import Button from "../components/Button";
+import ContactButtons from "../components/ContactButtons";
import CtaBand from "../components/CtaBand";
import FaqList from "../components/FaqList";
import JsonLd from "../components/JsonLd";
import {
- business,
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
businessPlan,
- links,
pageMetadata,
serviceSchema,
} from "@/lib/site";
@@ -77,40 +83,39 @@ export default function BusinessPage() {
I support small businesses around Te Atatū and West Auckland, onsite
or remotely. You deal with me directly, every time.
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
+
-
-
Business IT plan
-
-
- {businessPlan.price}
- {" "}
- a month
-
-
- {businessPlan.includes.map((item) => (
-
-
- {item}
-
- ))}
-
-
{businessPlan.terms}
-
+
+
+
+ Business IT plan
+
+
+
+ {businessPlan.price}
+ {" "}
+ a month
+
+
+
+
+ {businessPlan.includes.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+ {businessPlan.terms}
+
+
Prefer to pay as you go? Support is $35 an hour, onsite or remote.
@@ -123,7 +128,7 @@ export default function BusinessPage() {
{item}
@@ -133,14 +138,14 @@ export default function BusinessPage() {
Hardware repairs and upgrades are covered too. Parts are extra. See{" "}
IT support
{" "}
and{" "}
computer repairs
diff --git a/src/app/components/Button.js b/src/app/components/Button.js
deleted file mode 100644
index 2b88e66..0000000
--- a/src/app/components/Button.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import Link from "next/link";
-import styles from "./Button.module.css";
-
-// Renders a link when given href (internal paths use next/link), otherwise a button.
-export default function Button({ children, onClick, href, variant }) {
- const className = `${styles.button} ${variant ? styles[variant] : ""}`;
- if (href?.startsWith("/")) {
- return {children};
- }
- if (href) {
- return {children} ;
- }
- return (
- {children}
- );
-}
diff --git a/src/app/components/Button.module.css b/src/app/components/Button.module.css
deleted file mode 100644
index 523e7c8..0000000
--- a/src/app/components/Button.module.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.button {
- background-color: white;
- border: none;
- color: black;
- padding: 1em 2em;
- text-align: center;
- text-decoration: none;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 0.5em;
- font-size: 1em;
- cursor: pointer;
- border-radius: 5px;
-}
-
-.button svg {
- width: 1em;
- height: 1em;
-}
-
-.outline {
- background-color: transparent;
- color: white;
- box-shadow: inset 0 0 0 2px white;
-}
-
-.primary {
- background-color: var(--color-sky-800);
- color: white;
-}
-
-.outlineDark {
- background-color: transparent;
- color: var(--color-sky-800);
- box-shadow: inset 0 0 0 2px var(--color-sky-800);
-}
diff --git a/src/app/components/ContactButtons.js b/src/app/components/ContactButtons.js
new file mode 100644
index 0000000..cff41dc
--- /dev/null
+++ b/src/app/components/ContactButtons.js
@@ -0,0 +1,56 @@
+import Link from "next/link";
+import { MessageCircle, MessageSquare, Phone } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
+import { business, links } from "@/lib/site";
+
+// Call / Text (and optionally WhatsApp and Get a quote) buttons.
+// Use onDark over photos or the blue band, where the default styles lack contrast.
+export default function ContactButtons({
+ onDark = false,
+ textLabel = "Text me",
+ whatsapp = false,
+ quoteHref,
+ className,
+}) {
+ // Larger than shadcn's lg size: these are the main tap targets on phones.
+ const base = "h-11 px-6 text-base";
+ const secondary = onDark
+ ? "border-white bg-transparent text-white hover:bg-white/10 hover:text-white"
+ : "border-primary text-primary hover:text-primary";
+
+ return (
+
+ );
+}
diff --git a/src/app/components/ContactForm.js b/src/app/components/ContactForm.js
index 470a4a0..23f6feb 100644
--- a/src/app/components/ContactForm.js
+++ b/src/app/components/ContactForm.js
@@ -1,7 +1,17 @@
"use client";
import { useState } from "react";
-import styles from "./ContactForm.module.css";
+import { CircleAlert, CircleCheck } from "lucide-react";
+import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/components/ui/native-select";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import { Textarea } from "@/components/ui/textarea";
import { business, links } from "@/lib/site";
import { trackEvent } from "@/lib/analytics";
@@ -21,6 +31,10 @@ const problems = [
];
const contactMethods = ["Call", "Text", "Email"];
+function Field({ children }) {
+ return {children}
;
+}
+
// Submits to form.taxi with JavaScript for an inline success message; without
// JavaScript the form still posts normally.
export default function ContactForm() {
@@ -46,90 +60,122 @@ export default function ContactForm() {
if (status === "sent") {
return (
-
-
Thanks, I've got it
-
- I'll usually reply the same day during business hours (Monday
- to Friday, 9am to 6pm). If it's urgent, call or text{" "}
-
- {business.phone}
-
- .
-
-
+
+
+ Thanks, I've got it
+
+
+ I'll usually reply the same day during business hours (Monday
+ to Friday, 9am to 6pm). If it's urgent, call or text{" "}
+
+ {business.phone}
+
+ .
+
+
+
);
}
return (
Get a quote
-
+
Tell me what's going on and I'll get back to you, usually the
same day during business hours.
);
diff --git a/src/app/components/ContactForm.module.css b/src/app/components/ContactForm.module.css
deleted file mode 100644
index 7ef2501..0000000
--- a/src/app/components/ContactForm.module.css
+++ /dev/null
@@ -1,43 +0,0 @@
-.form input[type="text"],
-.form input[type="email"],
-.form input[type="tel"],
-.form select,
-.form textarea {
- width: 100%; /* Full width */
- padding: 1em;
- border: 1px solid #ccc; /* Border */
- border-radius: 0.5em; /* Rounded corners */
- box-sizing: border-box; /* Make sure that padding and width stays in place */
- margin-top: 6px;
- margin-bottom: 16px;
- resize: vertical; /* Allow the user to vertically resize the textarea (not horizontally) */
-}
-
-/* Style the submit button */
-.form input[type="submit"] {
- padding: 1em 1.75em;
- border: none;
- border-radius: 4px;
- cursor: pointer;
-}
-
-.sm {
- font-size: 0.75em;
- font-style: italic;
-}
-
-.fieldset {
- margin-bottom: 16px;
-}
-
-.radios {
- display: flex;
- gap: 1.5em;
- margin-top: 6px;
-}
-
-.radios label {
- display: flex;
- align-items: center;
- gap: 0.35em;
-}
diff --git a/src/app/components/CtaBand.js b/src/app/components/CtaBand.js
index 19301f2..d2fbc2f 100644
--- a/src/app/components/CtaBand.js
+++ b/src/app/components/CtaBand.js
@@ -1,33 +1,22 @@
-import { MessageCircle, MessageSquare, Phone } from "lucide-react";
-import Button from "./Button";
-import { business, links } from "@/lib/site";
+import ContactButtons from "./ContactButtons";
export default function CtaBand({ quoteHref = "/contact" }) {
return (
-
Get your computer sorted
-
+
+ Get your computer sorted
+
+
Call or text now, or send the details and I'll get back to you
with a quote.
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
-
- WhatsApp
-
-
- Get a quote
-
-
+
);
diff --git a/src/app/components/Footer.js b/src/app/components/Footer.js
index d6f4849..222b610 100644
--- a/src/app/components/Footer.js
+++ b/src/app/components/Footer.js
@@ -1,5 +1,6 @@
import Link from "next/link";
import { Mail, MapPin, Phone } from "lucide-react";
+import { Separator } from "@/components/ui/separator";
import { business, links } from "@/lib/site";
import { services } from "@/lib/services";
import { areas } from "@/lib/areas";
@@ -86,9 +87,12 @@ export default function Footer() {
-
- Copyright © {business.name}, {new Date().getFullYear()}
-
+
+
+
+ Copyright © {business.name}, {new Date().getFullYear()}
+
+
);
}
diff --git a/src/app/components/MobileCallBar.js b/src/app/components/MobileCallBar.js
index 0a9c659..b248eb5 100644
--- a/src/app/components/MobileCallBar.js
+++ b/src/app/components/MobileCallBar.js
@@ -7,14 +7,14 @@ export default function MobileCallBar() {
Call
Text
diff --git a/src/app/components/Navbar.js b/src/app/components/Navbar.js
index 8a15beb..c920e7d 100644
--- a/src/app/components/Navbar.js
+++ b/src/app/components/Navbar.js
@@ -1,213 +1,163 @@
"use client";
-import { useState, useRef, useEffect } from "react";
import Image from "next/image";
import Link from "next/link";
-import styles from "./Navbar.module.css";
-import logo from "../assets/logo.svg";
-import menu from "../assets/menu.svg";
-import close from "../assets/close.svg";
import { usePathname } from "next/navigation";
-import { ChevronDownIcon } from "lucide-react";
+import { MenuIcon, Phone } from "lucide-react";
+import logo from "../assets/logo.svg";
+import { Button } from "@/components/ui/button";
+import {
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ navigationMenuTriggerStyle,
+} from "@/components/ui/navigation-menu";
+import {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/components/ui/sheet";
+import { cn } from "@/lib/utils";
+import { business } from "@/lib/site";
+import { services } from "@/lib/services";
+
+const serviceLinks = [
+ ...Object.entries(services).map(([slug, { name }]) => ({
+ href: `/services/${slug}`,
+ label: name,
+ })),
+ { href: "/business", label: "Business IT plans" },
+ { href: "/windows-10", label: "Windows 10 help" },
+];
+
+const pageLinks = [
+ { href: "/pricing", label: "Pricing" },
+ { href: "/about", label: "About" },
+ { href: "/#faq", label: "FAQ" },
+];
export default function Navbar() {
const pathname = usePathname();
- const [isActive, setActive] = useState("false");
- const [isDropdownActive, setDropdownActive] = useState(false);
-
- function handleToggle() {
- setActive(!isActive);
- }
-
- function handleDropdownToggle() {
- setDropdownActive(!isDropdownActive);
- }
-
- const wrapperRef = useRef(null);
-
- useEffect(() => {
- /**
- * Close the menu if clicked on outside of element
- */
- function handleClickOutside(event) {
- if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
- setActive(true);
- }
- }
- // Bind the event listener
- document.addEventListener("mousedown", handleClickOutside);
- return () => {
- // Unbind the event listener on clean up
- document.removeEventListener("mousedown", handleClickOutside);
- };
- }, []);
+ const isActive = (href) => pathname === href;
+ const inServices = serviceLinks.some(({ href }) => isActive(href));
return (
-
-
-
-
-
-
-
Te Atatu Computers
-
-
-
-
-
-
-
-
-
- Home
-
-
- setDropdownActive(true)}
- onMouseLeave={() => setDropdownActive(false)}
- >
-
-
+
+
+
+ {business.name}
+
+
+ {/* Desktop */}
+
+
+
+
+ Home
+
+
+
+
Services
-
-
-
-
-
-
+
+
+ {serviceLinks.map(({ href, label }) => (
+
+
+ {label}
+
+
+ ))}
+
+
+
+ {pageLinks.map(({ href, label }) => (
+
+
- Computer & Laptop Repairs
-
-
-
-
- Computer Upgrades & Speedup
-
-
-
-
- IT Support
-
-
-
-
- Virus Removal
-
-
-
-
- Business IT Plans
-
-
-
-
- Windows 10 Help
-
-
-
-
-
-
- Pricing
-
-
-
-
- About
-
-
-
-
- FAQ
-
-
-
-
-
-
-
- Contact Us
-
-
-
+ {label}
+
+
+ ))}
+
+
+
+
+
+ Contact
+
+
+
+ {/* Mobile */}
+
+
+
+
+ Menu
+
+
+
+
+ Menu
+
+
+
+
+
+
+
+
+
+ Contact
+
+
+
+
+
+
-
+
+ );
+}
+
+function MobileList({ links, isActive }) {
+ return (
+
+ {links.map(({ href, label }) => (
+
+
+
+ {label}
+
+
+
+ ))}
+
);
}
diff --git a/src/app/components/Navbar.module.css b/src/app/components/Navbar.module.css
deleted file mode 100644
index 69a0194..0000000
--- a/src/app/components/Navbar.module.css
+++ /dev/null
@@ -1,229 +0,0 @@
-.navbar {
- /* Make navbar stay at top of screen (static) */
- position: sticky;
- top: 0;
- left: 0;
- right: 0;
- z-index: 9999;
- color: black;
- background-color: rgba(255, 255, 255, 0.9);
- backdrop-filter: blur(2rem);
-}
-
-/* Reset default styling on lists */
-.navbar .container ul {
- list-style-type: none;
- margin-left: 0;
- margin-bottom: 0;
-}
-
-/* Position nav items correctly */
-.navbar .container li {
- padding-left: 0.75em;
- padding-right: 0.75em;
-}
-
-/* Reset default styling on links */
-.navbar .container li a {
- color: inherit;
- text-decoration: none;
-}
-
-/* Underline links on hover */
-.navbar .container li a:hover {
- text-decoration: underline;
-}
-
-/* Make active link bold */
-.navbar .container li a.active {
- font-weight: bold;
-}
-
-.navbar .container li a svg {
- width: 1em;
- height: 1em;
-}
-
-.navbar .container li a {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 0.25em;
-}
-
-.highlight {
- color: white;
- background-color: var(--accent);
-}
-
-.menuIcon {
- font-size: 1.5rem;
- cursor: pointer;
- border: none;
- background: none;
- color: inherit;
-}
-
-/* Add styles for mobile view */
-@media (max-width: 899px) {
- .navbar {
- background-color: white;
- }
-
- .links {
- display: flex;
- flex-direction: column;
- border-radius: 0;
-
- /* Hidden by sliding up its own height, so it works for any number of links. */
- position: absolute;
- top: 4rem;
- transform: translateY(-100%);
- left: 0;
- right: 0;
- z-index: -2;
-
- font-size: 1rem;
- background-color: white;
- padding-top: 0.5em;
- padding-bottom: 0.5em;
-
- transition-property: transform, border-radius;
- transition-duration: 0.4s;
- transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
- }
-
- .links {
- padding-left: 20px;
- padding-right: 20px;
- }
-
- .links li,
- .dropdownTitle {
- padding-top: 1rem;
- padding-bottom: 1rem;
- margin-bottom: 0.5rem;
- border-radius: 0.5em;
- }
-
- .links.active {
- transform: translateY(0);
- border-radius: 0 0 0.5em 0.5em;
- }
-
- .header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-top: 1rem;
- padding-bottom: 1rem;
- background-color: #fff;
- }
-
- .dropdown {
- padding: 0 !important;
- }
-
- .dropdownMenu {
- max-height: 0.5em;
- overflow: hidden;
- transition: max-height 0.5s cubic-bezier(0.215, 0.61, 0.355, 1);
- }
-
- .dropdownActive .dropdownMenu {
- max-height: 30em;
- transition: max-height 0.5s cubic-bezier(0.215, 0.61, 0.355, 1);
- }
-}
-
-/* Styles for desktop view */
-@media (min-width: 900px) {
- .links {
- display: flex;
- flex-direction: row;
- }
-
- .links li {
- padding-top: 1rem;
- padding-bottom: 1rem;
- }
-
- .menuIcon {
- display: none;
- }
-
- .navbar .container {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- }
-
- .dropdownMenu {
- position: absolute;
- display: none;
- background-color: white;
- margin-top: 1rem;
- border-radius: 0.25rem;
- }
-
- .dropdown:hover .dropdownMenu {
- display: block;
- }
-}
-
-.header {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-}
-
-.logo {
- display: flex;
- gap: 1em;
- flex-flow: row nowrap;
- align-items: center;
-}
-
-.logo p {
- color: black;
- margin-bottom: 0;
- font-family: var(--font-geist-mono);
-}
-
-.dropdown a {
- cursor: pointer;
- margin-bottom: 0;
-}
-
-.dropdown li {
- margin-bottom: 0;
-}
-
-.dropdownMenu li:last-of-type {
- margin-bottom: 0;
-}
-
-.dropdown:not(.dropdownActive) {
- transition: all 0.3s;
-}
-
-.dropdown:not(.dropdownActive) li {
- margin: 0 !important;
-}
-
-.dropdownActive {
- background-color: #f1f1f1;
-}
-
-.dropdown:not(.dropdownActive) .dropdownTitle {
- margin-bottom: 0;
-}
-
-.dropdownTitle {
- cursor: pointer;
-}
-
-.dropdownTitle.active {
- font-weight: bold;
-}
diff --git a/src/app/components/PriceTable.js b/src/app/components/PriceTable.js
new file mode 100644
index 0000000..f30a0b1
--- /dev/null
+++ b/src/app/components/PriceTable.js
@@ -0,0 +1,18 @@
+import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
+
+export default function PriceTable({ items, className }) {
+ return (
+
+
+ {items.map(({ item, price }) => (
+
+ {item}
+
+ {price}
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/app/components/ServicePage.js b/src/app/components/ServicePage.js
index 108a32b..09cad77 100644
--- a/src/app/components/ServicePage.js
+++ b/src/app/components/ServicePage.js
@@ -1,11 +1,13 @@
import Link from "next/link";
-import { ArrowRight, Check, MessageSquare, Phone } from "lucide-react";
+import { ArrowRight, Check } from "lucide-react";
import HeaderSm from "./HeaderSm";
-import Button from "./Button";
+import ContactButtons from "./ContactButtons";
import CtaBand from "./CtaBand";
import FaqList from "./FaqList";
import JsonLd from "./JsonLd";
-import { business, links, pageMetadata, serviceSchema } from "@/lib/site";
+import PriceTable from "./PriceTable";
+import { Card, CardContent } from "@/components/ui/card";
+import { business, pageMetadata, serviceSchema } from "@/lib/site";
import { services } from "@/lib/services";
const promises = [
@@ -56,16 +58,7 @@ export default function ServicePage({ slug }) {
{p}
))}
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
+
@@ -75,7 +68,7 @@ export default function ServicePage({ slug }) {
{fix}
@@ -86,25 +79,14 @@ export default function ServicePage({ slug }) {
Pricing
-
-
- {service.prices.map(({ item, price }) => (
-
-
- {item}
-
- {price}
-
- ))}
-
-
+
The diagnosis is $35. If I can't fix it, you don't pay it.
Once I know what's wrong, I'll tell you what the repair will
cost before I start. If you decide not to go ahead, the $35 still
applies.
-
+
Labour is $35 an hour. Parts are extra.
@@ -144,12 +126,18 @@ export default function ServicePage({ slug }) {
))}
{testimonial && (
-
-
- “{testimonial.quote}”
-
- {testimonial.name}
-
+
+
+
+
+ “{testimonial.quote}”
+
+
+ {testimonial.name}
+
+
+
+
)}
@@ -168,7 +156,7 @@ export default function ServicePage({ slug }) {
{services[related].name}
diff --git a/src/app/contact/page.js b/src/app/contact/page.js
index d51a872..3aa9dc7 100644
--- a/src/app/contact/page.js
+++ b/src/app/contact/page.js
@@ -1,7 +1,7 @@
import Link from "next/link";
-import { Clock, Mail, MapPin, MessageCircle, MessageSquare, Phone } from "lucide-react";
+import { Clock, Mail, MapPin } from "lucide-react";
import HeaderSm from "../components/HeaderSm";
-import Button from "../components/Button";
+import ContactButtons from "../components/ContactButtons";
import ContactForm from "../components/ContactForm";
import MapEmbed from "../components/MapEmbed";
import { business, links, pageMetadata } from "@/lib/site";
@@ -24,34 +24,21 @@ export default function ContactPage() {
The quickest way to reach me is a call or text. I'm{" "}
{business.ownerName}, and I'll be the one fixing your computer.
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
-
- WhatsApp
-
-
+
-
+
Hours
Monday to Friday, 9am to 6pm. Weekends by appointment.
-
+
Drop-off by appointment
{address.street}, {address.suburb}, {address.city}{" "}
@@ -59,10 +46,10 @@ export default function ContactPage() {
-
+
Email
-
+
{business.email}
@@ -75,11 +62,11 @@ export default function ContactPage() {
See{" "}
-
+
prices
{" "}
and{" "}
-
+
common questions
.
diff --git a/src/app/globals.css b/src/app/globals.css
index 4055cb7..77a6937 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -30,14 +30,15 @@
---break---
*/
:root {
- --radius: 0.625rem;
+ --radius: 0.5rem;
--background: oklch(1 0 0);
--foreground: oklch(0.129 0.042 264.695);
--card: oklch(1 0 0);
--card-foreground: oklch(0.129 0.042 264.695);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.129 0.042 264.695);
- --primary: oklch(0.208 0.042 265.755);
+ /* Brand blue (Tailwind sky-800), matching the existing buttons and bands. */
+ --primary: oklch(0.443 0.11 240.79);
--primary-foreground: oklch(0.984 0.003 247.858);
--secondary: oklch(0.968 0.007 247.896);
--secondary-foreground: oklch(0.208 0.042 265.755);
@@ -48,7 +49,7 @@
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.929 0.013 255.508);
--input: oklch(0.929 0.013 255.508);
- --ring: oklch(0.704 0.04 256.788);
+ --ring: oklch(0.588 0.158 241.966);
--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);
@@ -103,6 +104,8 @@
---break---
*/
@theme inline {
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
@@ -147,7 +150,12 @@
@apply border-border outline-ring/50;
}
body {
- @apply bg-background text-foreground;
+ @apply bg-background text-foreground antialiased;
+ }
+ h1,
+ h2,
+ h3 {
+ @apply tracking-tight text-balance;
}
}
/* Keep the Chatwoot bubble clear of the mobile Call/Text bar. */
diff --git a/src/app/layout.js b/src/app/layout.js
index 75c4066..7f4ee95 100644
--- a/src/app/layout.js
+++ b/src/app/layout.js
@@ -27,10 +27,8 @@ export const metadata = {
export default function RootLayout({ children }) {
return (
-
-
+
+
diff --git a/src/app/page.js b/src/app/page.js
index 609f79c..635358d 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -2,8 +2,9 @@ import Image from "next/image";
import Link from "next/link";
import styles from "./page.module.css";
import bgimage from "./assets/header-bg.jpg";
-import Button from "./components/Button";
+import ContactButtons from "./components/ContactButtons";
import MapEmbed from "./components/MapEmbed";
+import { Card, CardContent } from "@/components/ui/card";
import ContactForm from "./components/ContactForm";
import FaqList from "./components/FaqList";
import CtaBand from "./components/CtaBand";
@@ -18,10 +19,8 @@ import {
HardDrive,
Laptop,
MapPin,
- MessageSquare,
Monitor,
MonitorCog,
- Phone,
Power,
ShieldAlert,
Star,
@@ -103,7 +102,7 @@ const steps = [
function SectionCta() {
return (
-
+
Call or text {business.phone}
@@ -138,16 +137,7 @@ export default function Home() {
tell you what it costs before I start. Local, honest help
for West Auckland.
-
-
-
- Call {business.phone}
-
-
-
- Text me
-
-
+
-
+
Common fixes & prices
-
+
Labour prices. Parts are extra, and I'll always quote before
starting.
{commonFixes.map(({ icon: Icon, title, price, href }) => (
-
-
-
- {title}
- {price}
-
+
+
+
+
+ {title}
+
+ {price}
+
+
+
))}
@@ -200,7 +191,7 @@ export default function Home() {
Still on Windows 10?
@@ -212,15 +203,15 @@ export default function Home() {
-
+
How it works
{steps.map(({ title, text }, i) => (
-
-
+
+
{i + 1}
{title}
@@ -232,7 +223,7 @@ export default function Home() {
-
+
Hi, I'm {business.ownerName}
@@ -249,22 +240,26 @@ export default function Home() {
-
+
-
+
Areas I serve
@@ -293,7 +288,7 @@ export default function Home() {
{page ? (
{area}
@@ -311,7 +306,7 @@ export default function Home() {
Call or text {business.phone}
@@ -322,11 +317,11 @@ export default function Home() {
-
-
+
+
@@ -339,16 +334,16 @@ export default function Home() {
Business IT plans
-
+
diff --git a/src/app/pricing/page.js b/src/app/pricing/page.js
index 5b0889a..7bc7689 100644
--- a/src/app/pricing/page.js
+++ b/src/app/pricing/page.js
@@ -1,6 +1,7 @@
import Link from "next/link";
import HeaderSm from "../components/HeaderSm";
import CtaBand from "../components/CtaBand";
+import PriceTable from "../components/PriceTable";
import { business, pageMetadata, priceList } from "@/lib/site";
export const metadata = pageMetadata({
@@ -27,18 +28,7 @@ export default function PricingPage() {
{priceList.map(({ group, items }) => (
{group}
-
-
- {items.map(({ item, price }) => (
-
-
- {item}
-
- {price}
-
- ))}
-
-
+
))}
@@ -63,7 +53,7 @@ export default function PricingPage() {
by appointment, has no travel fee. For onsite visits or pickup and
return, the travel fee depends on distance. Get in touch with your
suburb for a quote. Small businesses can also get{" "}
-
+
2 hours of support a month for $65
.
diff --git a/src/app/windows-10/page.js b/src/app/windows-10/page.js
index 4ac5391..fa0d544 100644
--- a/src/app/windows-10/page.js
+++ b/src/app/windows-10/page.js
@@ -1,10 +1,9 @@
import Link from "next/link";
-import { MessageSquare, Phone } from "lucide-react";
import HeaderSm from "../components/HeaderSm";
-import Button from "../components/Button";
+import ContactButtons from "../components/ContactButtons";
import CtaBand from "../components/CtaBand";
import FaqList from "../components/FaqList";
-import { business, links, pageMetadata } from "@/lib/site";
+import { pageMetadata } from "@/lib/site";
export const metadata = pageMetadata({
title: "Still on Windows 10? What to Do Now | Te Atatu Computers",
@@ -62,16 +61,7 @@ export default function Windows10Page() {
buy a new computer. Here are your options, and I'm happy to
check which one suits you for free.
-
-
-
- Call {business.phone}
-
-
-
- Text for a free check
-
-
+
@@ -79,7 +69,7 @@ export default function Windows10Page() {
{options.map(({ title, text }, i) => (
-
+
{i + 1}
@@ -92,7 +82,7 @@ export default function Windows10Page() {
Not sure? Get in touch and I'll give you a straight answer. See
all prices on the{" "}
-
+
pricing page
.
diff --git a/src/components/ui/accordion.jsx b/src/components/ui/accordion.jsx
index cf0f200..9240b64 100644
--- a/src/components/ui/accordion.jsx
+++ b/src/components/ui/accordion.jsx
@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
-import * as AccordionPrimitive from "@radix-ui/react-accordion"
+import { Accordion as AccordionPrimitive } from "radix-ui"
import { ChevronDownIcon } from "lucide-react"
import { cn } from "@/lib/utils"
@@ -34,7 +34,7 @@ function AccordionTrigger({
svg]:rotate-180",
+ "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-base font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}>
@@ -57,7 +57,7 @@ function AccordionContent({
{children}
diff --git a/src/components/ui/alert.jsx b/src/components/ui/alert.jsx
new file mode 100644
index 0000000..607c06c
--- /dev/null
+++ b/src/components/ui/alert.jsx
@@ -0,0 +1,68 @@
+import * as React from "react"
+import { cva } from "class-variance-authority";
+import { cn } from "@/lib/utils"
+
+const alertVariants = cva(
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
+ {
+ variants: {
+ variant: {
+ default: "bg-card text-card-foreground",
+ destructive:
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Alert({
+ className,
+ variant,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function AlertTitle({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/src/components/ui/button.jsx b/src/components/ui/button.jsx
new file mode 100644
index 0000000..e744b1c
--- /dev/null
+++ b/src/components/ui/button.jsx
@@ -0,0 +1,60 @@
+import * as React from "react"
+import { cva } from "class-variance-authority";
+import { cn } from "@/lib/utils"
+import { Slot } from "radix-ui"
+
+const buttonVariants = cva(
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+ {
+ variants: {
+ variant: {
+ 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:bg-destructive/60 dark:focus-visible:ring-destructive/40",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground 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",
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm": "size-8",
+ "icon-lg": "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ asChild = false,
+ ...props
+}) {
+ const Comp = asChild ? Slot.Root : "button"
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/src/components/ui/card.jsx b/src/components/ui/card.jsx
new file mode 100644
index 0000000..526c1b5
--- /dev/null
+++ b/src/components/ui/card.jsx
@@ -0,0 +1,112 @@
+import * as React from "react"
+import { cn } from "@/lib/utils"
+
+function Card({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardHeader({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardTitle({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardDescription({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardAction({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardContent({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function CardFooter({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/src/components/ui/input.jsx b/src/components/ui/input.jsx
new file mode 100644
index 0000000..292ed7d
--- /dev/null
+++ b/src/components/ui/input.jsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+import { cn } from "@/lib/utils"
+
+function Input({
+ className,
+ type,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/src/components/ui/label.jsx b/src/components/ui/label.jsx
new file mode 100644
index 0000000..abc2406
--- /dev/null
+++ b/src/components/ui/label.jsx
@@ -0,0 +1,23 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { Label as LabelPrimitive } from "radix-ui"
+
+function Label({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { Label }
diff --git a/src/components/ui/native-select.jsx b/src/components/ui/native-select.jsx
new file mode 100644
index 0000000..3b06434
--- /dev/null
+++ b/src/components/ui/native-select.jsx
@@ -0,0 +1,65 @@
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { ChevronDownIcon } from "lucide-react"
+
+function NativeSelect({
+ className,
+ wrapperClassName,
+ size = "default",
+ ...props
+}) {
+ return (
+
+
+
+
+ )
+}
+
+function NativeSelectOption({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function NativeSelectOptGroup({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { NativeSelect, NativeSelectOptGroup, NativeSelectOption }
diff --git a/src/components/ui/navigation-menu.jsx b/src/components/ui/navigation-menu.jsx
new file mode 100644
index 0000000..d8a6826
--- /dev/null
+++ b/src/components/ui/navigation-menu.jsx
@@ -0,0 +1,165 @@
+import * as React from "react"
+import { cva } from "class-variance-authority"
+import { cn } from "@/lib/utils"
+import { ChevronDownIcon } from "lucide-react"
+import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
+
+function NavigationMenu({
+ className,
+ children,
+ viewport = true,
+ ...props
+}) {
+ return (
+
+ {children}
+ {viewport && }
+
+ )
+}
+
+function NavigationMenuList({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function NavigationMenuItem({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+const navigationMenuTriggerStyle = cva(
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-[color,box-shadow] outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
+)
+
+function NavigationMenuTrigger({
+ className,
+ children,
+ ...props
+}) {
+ return (
+
+ {children}{" "}
+
+
+ )
+}
+
+function NavigationMenuContent({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function NavigationMenuViewport({
+ className,
+ ...props
+}) {
+ return (
+
+
+
+ )
+}
+
+function NavigationMenuLink({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function NavigationMenuIndicator({
+ className,
+ ...props
+}) {
+ return (
+
+
+
+ )
+}
+
+export {
+ NavigationMenu,
+ NavigationMenuList,
+ NavigationMenuItem,
+ NavigationMenuContent,
+ NavigationMenuTrigger,
+ NavigationMenuLink,
+ NavigationMenuIndicator,
+ NavigationMenuViewport,
+ navigationMenuTriggerStyle,
+}
diff --git a/src/components/ui/radio-group.jsx b/src/components/ui/radio-group.jsx
new file mode 100644
index 0000000..dfd1634
--- /dev/null
+++ b/src/components/ui/radio-group.jsx
@@ -0,0 +1,44 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { CircleIcon } from "lucide-react"
+import { RadioGroup as RadioGroupPrimitive } from "radix-ui"
+
+function RadioGroup({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function RadioGroupItem({
+ className,
+ ...props
+}) {
+ return (
+
+
+
+
+
+ )
+}
+
+export { RadioGroup, RadioGroupItem }
diff --git a/src/components/ui/separator.jsx b/src/components/ui/separator.jsx
new file mode 100644
index 0000000..8918101
--- /dev/null
+++ b/src/components/ui/separator.jsx
@@ -0,0 +1,27 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { Separator as SeparatorPrimitive } from "radix-ui"
+
+function Separator({
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { Separator }
diff --git a/src/components/ui/sheet.jsx b/src/components/ui/sheet.jsx
new file mode 100644
index 0000000..37ad9ab
--- /dev/null
+++ b/src/components/ui/sheet.jsx
@@ -0,0 +1,147 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { XIcon } from "lucide-react"
+import { Dialog as SheetPrimitive } from "radix-ui"
+
+function Sheet({
+ ...props
+}) {
+ return
+}
+
+function SheetTrigger({
+ ...props
+}) {
+ return
+}
+
+function SheetClose({
+ ...props
+}) {
+ return
+}
+
+function SheetPortal({
+ ...props
+}) {
+ return
+}
+
+function SheetOverlay({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function SheetContent({
+ className,
+ children,
+ side = "right",
+ showCloseButton = true,
+ ...props
+}) {
+ return (
+
+
+
+ {children}
+ {showCloseButton && (
+
+
+ Close
+
+ )}
+
+
+ )
+}
+
+function SheetHeader({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function SheetFooter({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function SheetTitle({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function SheetDescription({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export {
+ Sheet,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription,
+}
diff --git a/src/components/ui/table.jsx b/src/components/ui/table.jsx
new file mode 100644
index 0000000..afed64f
--- /dev/null
+++ b/src/components/ui/table.jsx
@@ -0,0 +1,136 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+
+function Table({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function TableHeader({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function TableBody({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function TableFooter({
+ className,
+ ...props
+}) {
+ return (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+function TableHead({
+ className,
+ ...props
+}) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCell({
+ className,
+ ...props
+}) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/src/components/ui/textarea.jsx b/src/components/ui/textarea.jsx
new file mode 100644
index 0000000..2dba2c2
--- /dev/null
+++ b/src/components/ui/textarea.jsx
@@ -0,0 +1,20 @@
+import * as React from "react"
+import { cn } from "@/lib/utils"
+
+function Textarea({
+ className,
+ ...props
+}) {
+ return (
+
+ )
+}
+
+export { Textarea }