From c31ad639557ddeb3899f557243f1d5d12c96a3ed Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Sat, 9 May 2026 06:05:03 +0100 Subject: [PATCH] chore: ai, public course and other ui improvements --- .../plans/ai_assistant_prd_5a6f948c.plan.md | 8 +- .cursor/plans/labs_prd.md | 942 ++++++++++++++ AGENTS.md | 25 +- PRD-TRACKER.md | 26 +- README.md | 11 +- apps/api/.env.example | 1 + .../src/__tests__/ai-credits-usage.test.ts | 55 + .../api/src/__tests__/question-update.test.ts | 32 + apps/api/src/app.ts | 18 +- apps/api/src/constants/index.ts | 1 - apps/api/src/routes/agent/agent.ts | 462 +++++++ apps/api/src/routes/agent/chat.ts | 1097 ----------------- apps/api/src/routes/agent/credits.ts | 35 - apps/api/src/routes/agent/history.ts | 41 +- apps/api/src/routes/agent/index.ts | 7 +- apps/api/src/routes/agent/status.ts | 58 - apps/api/src/routes/agent/upload.ts | 91 -- apps/api/src/routes/agent/usage.ts | 21 - apps/api/src/routes/course/exercise.ts | 72 +- apps/api/src/routes/org-site/index.ts | 1 + apps/api/src/routes/org-site/public-course.ts | 55 + .../src/services/agent/agent-tool-schemas.ts | 239 ++++ apps/api/src/services/agent/chat-context.ts | 160 +++ apps/api/src/services/agent/chat-tools.ts | 655 ++++++++++ apps/api/src/services/agent/context-window.ts | 46 +- .../api/src/services/agent/credit-purchase.ts | 21 + apps/api/src/services/agent/document.ts | 10 +- .../api/src/services/agent/question-update.ts | 11 +- .../src/services/agent/title-generation.ts | 71 +- apps/api/src/services/agent/usage.ts | 232 ++-- .../services/course-import/course-import.ts | 23 +- apps/api/src/services/course/clone.ts | 54 +- apps/api/src/services/course/completion.ts | 16 +- apps/api/src/services/course/content.ts | 203 +-- apps/api/src/services/course/course.ts | 63 +- apps/api/src/services/course/invite.ts | 151 +-- apps/api/src/services/course/landing-page.ts | 24 +- .../services/course/public-course-guard.ts | 61 + apps/api/src/services/course/public-course.ts | 151 +++ apps/api/src/services/course/slug.ts | 42 + apps/api/src/services/exercise/exercise.ts | 394 +++++- apps/api/src/services/exercise/utils.ts | 9 + .../src/services/exercise/video-recording.ts | 217 ++++ apps/api/src/services/lesson/lesson.ts | 31 +- apps/api/src/services/organization/invite.ts | 243 ++-- apps/api/src/services/program/program.ts | 58 +- .../api/src/services/submission/submission.ts | 91 +- apps/dashboard/.env.example | 5 + apps/dashboard/package.json | 3 +- .../components/Navigation/CustomLinks.svelte | 1 - .../agent/api/credit-purchase.server.ts | 25 + .../ai-assistant/ai-course-chat.svelte | 282 +++-- .../ai-assistant/api/ai-assistant.svelte.ts | 17 + .../features/ai-assistant/chat-header.svelte | 24 +- .../features/ai-assistant/chat-input.svelte | 41 +- .../ai-assistant/message-bubble.svelte | 16 +- .../features/ai-assistant/model-picker.svelte | 49 - .../features/ai-assistant/plan-view.svelte | 20 +- .../ai-assistant/utils/course-plan.ts | 18 + .../lib/features/ai-assistant/utils/store.ts | 21 + .../ai-assistant/utils/tool-labels.ts | 6 + .../features/ai-assistant/utils/tool-parts.ts | 12 +- .../lib/features/ai-assistant/utils/types.ts | 18 + .../lib/features/course/api/course.svelte.ts | 16 +- .../features/course/api/exercise.svelte.ts | 94 +- .../lib/features/course/api/lesson.svelte.ts | 15 +- .../course/components/card-dropdown.svelte | 112 +- .../features/course/components/card.svelte | 22 +- .../course/components/course-header.svelte | 41 +- .../course/components/course-list-row.svelte | 72 +- .../components/course-public-badge.svelte | 19 + .../exercise/delete-confirmation.svelte | 19 +- .../components/exercise/edit-mode.svelte | 458 +++++-- .../exercise/exercise-settings-tab.svelte | 27 +- .../course/components/exercise/functions.ts | 1 + .../course/components/exercise/preview.svelte | 66 +- .../components/exercise/question-labels.ts | 105 ++ .../exercise/question-type-select.svelte | 66 + .../exercise/question-type-utils.ts | 17 + .../components/exercise/section-editor.svelte | 184 +++ .../course/components/exercise/store.ts | 320 ++++- .../exercise/submissions/individual.svelte | 75 +- .../exercise/submissions/summary.svelte | 77 +- .../components/exercise/view-mode.svelte | 543 ++++++-- .../lesson/lesson-page-edit-header.svelte | 31 +- .../course/components/new-course-modal.svelte | 23 +- .../components/question-container.svelte | 3 + .../lib/features/course/pages/exercise.svelte | 213 +++- .../lib/features/course/pages/lesson.svelte | 13 +- .../lib/features/course/pages/lessons.svelte | 35 + .../lib/features/course/pages/newsfeed.svelte | 6 +- .../lib/features/course/pages/settings.svelte | 135 +- .../src/lib/features/course/store.ts | 2 + .../src/lib/features/course/types.ts | 1 + .../features/course/utils/course-preview.ts | 39 +- .../course/utils/exercise-page-utils.ts | 61 +- .../course/utils/exercise-section-utils.ts | 15 + .../course/utils/public-course-mappers.ts | 121 ++ .../features/course/utils/settings-store.ts | 9 +- .../src/lib/features/course/utils/types.ts | 22 + .../features/license/api/license.svelte.ts | 2 +- .../lib/features/lms/pages/dashboard.svelte | 118 +- .../settings/api/ai-credits.svelte.ts | 51 + .../ai-credits/buy-tokens-card.svelte | 95 ++ .../ai-credits/team-leaderboard.svelte | 84 ++ .../components/ai-credits/usage-chart.svelte | 59 + .../ai-credits/usage-summary-cards.svelte | 88 ++ .../features/settings/pages/ai-credits.svelte | 40 + .../features/settings/pages/billing.svelte | 74 -- .../src/lib/features/settings/pages/index.ts | 1 + .../src/lib/features/settings/utils/types.ts | 14 + .../course-landing-page.svelte | 235 ++-- .../features/ui/navigation/org-navigation.ts | 26 +- .../src/lib/features/ui/question/constants.ts | 7 +- .../sidebar/org-sidebar/org-switcher.svelte | 4 +- .../src/lib/utils/constants/routes.ts | 4 +- .../src/lib/utils/services/org/quiz.ts | 8 +- .../src/lib/utils/translations/da.json | 198 ++- .../src/lib/utils/translations/de.json | 198 ++- .../src/lib/utils/translations/en.json | 217 +++- .../src/lib/utils/translations/es.json | 198 ++- .../src/lib/utils/translations/fr.json | 198 ++- .../src/lib/utils/translations/hi.json | 198 ++- .../src/lib/utils/translations/pl.json | 198 ++- .../src/lib/utils/translations/pt.json | 198 ++- .../src/lib/utils/translations/ru.json | 198 ++- .../src/lib/utils/translations/vi.json | 198 ++- apps/dashboard/src/lib/utils/types/org.ts | 10 + apps/dashboard/src/lib/utils/types/polar.ts | 34 +- .../routes/(app)/courses/[id]/+layout.svelte | 8 +- .../[id]/exercises/[exerciseId]/+page.svelte | 2 +- .../routes/(app)/org/[slug]/+layout.server.ts | 2 +- .../routes/(app)/org/[slug]/+page.server.ts | 61 +- .../src/routes/(app)/org/[slug]/+page.svelte | 401 +++--- .../(app)/org/[slug]/dash/+page.server.ts | 62 + .../routes/(app)/org/[slug]/dash/+page.svelte | 236 ++++ .../[slug]/settings/ai-credits/+page.svelte | 21 + .../(app)/programs/[id]/courses/+page.svelte | 7 +- .../(app)/programs/[id]/newsfeed/+page.svelte | 6 +- .../(org-site)/course/[slug]/+page.server.ts | 25 +- .../(org-site)/course/[slug]/+page.svelte | 9 +- .../course/[slug]/enroll/+page.server.ts | 4 +- .../course/[slug]/enroll/+page.svelte | 8 +- .../[slug]/lesson/[itemSlug]/+page.server.ts | 66 + .../[slug]/lesson/[itemSlug]/+page.svelte | 98 ++ .../routes/api/polar/buy-tokens/+server.ts | 64 + .../src/routes/api/polar/webhook/+server.ts | 128 +- .../webhook/__tests__/order-paid.fixture.json | 18 + apps/dashboard/static/sticky-test.css | 2 - apps/dashboard/static/sticky-test.html | 103 -- apps/dashboard/vite.config.ts | 4 + apps/website/src/app.css | 2 - apps/website/src/app.html | 5 + .../src/lib/components/ai-section.svelte | 4 +- apps/website/src/lib/components/footer.svelte | 4 +- .../src/lib/components/navigation.svelte | 333 +++-- .../src/lib/components/page-header.svelte | 14 +- .../routes/(marketing)/bootcamps/+page.svelte | 6 +- .../customer-education/+page.svelte | 4 +- .../employee-training/+page.svelte | 6 +- apps/website/src/routes/pricing/+page.svelte | 3 + package.json | 8 +- packages/ai-assistant/package.json | 2 + packages/ai-assistant/src/prompt/teacher.ts | 27 +- packages/ai-assistant/src/providers/index.ts | 13 +- packages/ai-assistant/src/tools/index.ts | 6 + packages/ai-assistant/src/tools/teacher.ts | 74 +- packages/ai-assistant/src/types.ts | 5 +- .../src/migrations/0004_ai_chat_document.sql | 21 - .../db/src/queries/agent/chat-document.ts | 4 +- .../db/src/queries/agent/chat-resource.ts | 54 + .../db/src/queries/agent/credit-purchase.ts | 136 ++ packages/db/src/queries/agent/index.ts | 4 + packages/db/src/queries/agent/token-usage.ts | 144 +++ .../src/queries/agent/usage-aggregations.ts | 68 + .../db/src/queries/course/content-batch.ts | 186 +++ packages/db/src/queries/course/course.ts | 48 + packages/db/src/queries/course/index.ts | 3 +- packages/db/src/queries/course/invite.ts | 98 +- .../db/src/queries/course/public-course.ts | 534 ++++++++ .../src/queries/exercise/exercise-section.ts | 136 ++ packages/db/src/queries/exercise/exercise.ts | 2 - packages/db/src/queries/exercise/index.ts | 2 +- packages/db/src/queries/group/group.ts | 33 + packages/db/src/queries/index.ts | 1 + .../db/src/queries/organization/invite.ts | 176 ++- .../src/queries/organization/organization.ts | 44 + packages/db/src/queries/program/program.ts | 6 +- packages/db/src/queries/query-errors.ts | 15 + packages/db/src/schema.ts | 102 +- packages/db/src/types.ts | 3 + packages/db/src/utils/seed/questionType.ts | 6 + packages/question-types/src/answer-codecs.ts | 63 +- packages/question-types/src/answer-data.ts | 17 +- .../src/exercise-section-types.ts | 21 + packages/question-types/src/exercise-types.ts | 51 +- packages/question-types/src/index.ts | 2 + .../src/question-answer-payload.ts | 17 +- .../src/question-type-capabilities.ts | 11 + .../question-types/src/question-type-keys.ts | 3 +- .../src/question-type-registry.ts | 9 + .../question-types/src/render-contract.ts | 3 + .../src/video-recording-settings.ts | 14 + .../tests/question-type-registry.test.ts | 23 + .../animation/animation.stories.svelte | 25 + .../course-creator.stories.svelte | 61 + .../exercise-question.stories.svelte | 155 +++ .../exercise-question/question-fixtures.ts | 56 + .../exercise-question/question-labels.ts | 35 + .../question-type-modes.svelte | 22 +- .../model-picker/model-picker.stories.svelte | 45 + .../newsfeed-reactions.stories.svelte | 2 +- .../src/molecules/public-course/fixtures.ts | 187 +++ .../public-course.stories.svelte | 121 ++ packages/ui/README.md | 1 + packages/ui/src/base/card/card.svelte | 2 +- packages/ui/src/base/number-badge/index.ts | 1 + .../src/base/number-badge/number-badge.svelte | 36 + packages/ui/src/base/page/page-header.svelte | 2 +- packages/ui/src/base/textarea/textarea.svelte | 2 +- .../toggle-group/toggle-group-item.svelte | 2 +- .../custom/animation/blurfade/blurfade.svelte | 2 +- .../animation/dot-field/dot-field.svelte | 287 +++++ .../src/custom/animation/dot-field/index.ts | 1 + .../gradual-spacing/gradual-spacing.svelte | 2 +- packages/ui/src/custom/animation/index.ts | 1 + .../src/custom/course-card/course-card.svelte | 35 + .../course-creator/course-creator.svelte | 126 ++ .../ui/src/custom/course-creator/index.ts | 4 + .../editor/extensions/FindAndReplace.ts | 36 +- .../ui/src/custom/exercise-question/index.ts | 8 + .../exercise-question/question-list.svelte | 7 +- .../question-renderer.svelte | 25 +- .../exercise-question/renderer-contract.ts | 11 + .../renderers/checkbox/edit.svelte | 4 +- .../renderers/checkbox/take.svelte | 2 + .../renderers/radio/edit.svelte | 4 +- .../renderers/radio/take.svelte | 2 + .../renderers/video-recording/edit.svelte | 68 + .../renderers/video-recording/format.ts | 15 + .../renderers/video-recording/preview.svelte | 25 + .../video-recording/submission.svelte | 70 ++ .../renderers/video-recording/take.svelte | 481 ++++++++ .../section-after-behavior.svelte | 71 ++ .../exercise-question/section-header.svelte | 69 ++ .../section-navigation-sidebar.svelte | 48 + .../exercise-question/section-overview.svelte | 93 ++ packages/ui/src/custom/model-picker/index.ts | 4 + .../custom/model-picker/model-picker.svelte | 117 ++ .../custom/org-landing-page/bold/hero.svelte | 1 + .../org-landing-page/classic/hero.svelte | 7 +- .../org-landing-page/minimal/hero.svelte | 7 +- .../ui/src/custom/org-landing-page/types.ts | 2 +- .../ui/src/custom/password/password.svelte | 4 +- .../custom/public-course/bottom-nav.svelte | 81 ++ .../src/custom/public-course/callout.svelte | 62 + .../custom/public-course/exercise-view.svelte | 422 +++++++ .../custom/public-course/footer-nav.svelte | 96 ++ packages/ui/src/custom/public-course/index.ts | 29 + .../custom/public-course/lesson-view.svelte | 85 ++ .../custom/public-course/mobile-sheet.svelte | 73 ++ .../custom/public-course/powered-by.svelte | 53 + .../public-exercise-attempts-storage.ts | 71 ++ .../ui/src/custom/public-course/shell.svelte | 243 ++++ .../custom/public-course/sidebar-row.svelte | 59 + .../src/custom/public-course/sidebar.svelte | 72 ++ packages/ui/src/custom/public-course/types.ts | 75 ++ .../question-type-picker/exercise-labels.ts | 35 + .../question-type-picker/picker-data.ts | 16 + .../question-type-picker.svelte | 41 +- .../src/custom/widget-layouts/carousel.svelte | 3 +- packages/ui/src/index.ts | 2 + packages/ui/src/output.css | 2 +- packages/utils/src/agent-models/index.ts | 43 +- packages/utils/src/constants/error-codes.ts | 3 + packages/utils/src/plans/data.ts | 4 +- packages/utils/src/plans/index.ts | 1 + packages/utils/src/plans/token-packs.ts | 13 + packages/utils/src/validation/agent/agent.ts | 33 +- .../utils/src/validation/course/callout.ts | 20 + .../utils/src/validation/course/course.ts | 8 +- packages/utils/src/validation/course/index.ts | 2 + .../src/validation/course/public-course.ts | 19 + .../validation/exercise/exercise-section.ts | 23 + .../utils/src/validation/exercise/exercise.ts | 34 +- .../utils/src/validation/exercise/index.ts | 2 + .../validation/exercise/video-recording.ts | 38 + packages/utils/src/validation/index.ts | 1 + .../utils/src/validation/lesson/lesson.ts | 6 +- packages/utils/src/validation/shared/index.ts | 1 + packages/utils/src/validation/shared/slug.ts | 57 + pnpm-lock.yaml | 216 ++-- .../README.md | 4 + .../implementation-plan.md | 0 .../README.md | 2 +- .../UX.md | 0 .../README.md | 6 +- .../implementation-plan.md | 0 .../README.md | 2 +- prd/exercise-question-types [DONE]/README.md | 2 +- .../README.md | 258 ++-- .../implementation-plan.md | 264 ++-- .../README.md | 28 +- .../consumption-spec.md | 0 .../implementation-plan.md | 2 +- .../README.md | 4 + .../implementation-plan.md | 0 prd/{programs => programs [DONE]}/README.md | 4 + .../README.md | 28 +- prd/public-courses/README.md | 673 ++++------ .../design/lesson-page-full.png | Bin 0 -> 10782 bytes .../design/lesson-page-reference.png | Bin 0 -> 73267 bytes .../design/sidebar-active-state.png | Bin 0 -> 11711 bytes prd/public-courses/implementation-plan.md | 436 +++---- prd/study-from-source/README.md | 914 ++++++++++++++ prd/study-from-source/implementation-plan.md | 795 ++++++++++++ .../README.md | 759 ++++++++++++ prd/webhooks/README.md | 2 +- 318 files changed, 20668 insertions(+), 4788 deletions(-) create mode 100644 .cursor/plans/labs_prd.md create mode 100644 apps/api/src/__tests__/ai-credits-usage.test.ts create mode 100644 apps/api/src/routes/agent/agent.ts delete mode 100644 apps/api/src/routes/agent/chat.ts delete mode 100644 apps/api/src/routes/agent/credits.ts delete mode 100644 apps/api/src/routes/agent/status.ts delete mode 100644 apps/api/src/routes/agent/upload.ts delete mode 100644 apps/api/src/routes/agent/usage.ts create mode 100644 apps/api/src/routes/org-site/index.ts create mode 100644 apps/api/src/routes/org-site/public-course.ts create mode 100644 apps/api/src/services/agent/agent-tool-schemas.ts create mode 100644 apps/api/src/services/agent/chat-context.ts create mode 100644 apps/api/src/services/agent/chat-tools.ts create mode 100644 apps/api/src/services/agent/credit-purchase.ts create mode 100644 apps/api/src/services/course/public-course-guard.ts create mode 100644 apps/api/src/services/course/public-course.ts create mode 100644 apps/api/src/services/course/slug.ts create mode 100644 apps/api/src/services/exercise/video-recording.ts create mode 100644 apps/dashboard/src/lib/features/agent/api/credit-purchase.server.ts delete mode 100644 apps/dashboard/src/lib/features/ai-assistant/model-picker.svelte create mode 100644 apps/dashboard/src/lib/features/ai-assistant/utils/course-plan.ts create mode 100644 apps/dashboard/src/lib/features/course/components/course-public-badge.svelte create mode 100644 apps/dashboard/src/lib/features/course/components/exercise/question-type-select.svelte create mode 100644 apps/dashboard/src/lib/features/course/components/exercise/section-editor.svelte create mode 100644 apps/dashboard/src/lib/features/course/utils/exercise-section-utils.ts create mode 100644 apps/dashboard/src/lib/features/course/utils/public-course-mappers.ts create mode 100644 apps/dashboard/src/lib/features/settings/api/ai-credits.svelte.ts create mode 100644 apps/dashboard/src/lib/features/settings/components/ai-credits/buy-tokens-card.svelte create mode 100644 apps/dashboard/src/lib/features/settings/components/ai-credits/team-leaderboard.svelte create mode 100644 apps/dashboard/src/lib/features/settings/components/ai-credits/usage-chart.svelte create mode 100644 apps/dashboard/src/lib/features/settings/components/ai-credits/usage-summary-cards.svelte create mode 100644 apps/dashboard/src/lib/features/settings/pages/ai-credits.svelte create mode 100644 apps/dashboard/src/lib/features/settings/utils/types.ts create mode 100644 apps/dashboard/src/routes/(app)/org/[slug]/dash/+page.server.ts create mode 100644 apps/dashboard/src/routes/(app)/org/[slug]/dash/+page.svelte create mode 100644 apps/dashboard/src/routes/(app)/org/[slug]/settings/ai-credits/+page.svelte create mode 100644 apps/dashboard/src/routes/(org-site)/course/[slug]/lesson/[itemSlug]/+page.server.ts create mode 100644 apps/dashboard/src/routes/(org-site)/course/[slug]/lesson/[itemSlug]/+page.svelte create mode 100644 apps/dashboard/src/routes/api/polar/buy-tokens/+server.ts create mode 100644 apps/dashboard/src/routes/api/polar/webhook/__tests__/order-paid.fixture.json delete mode 100644 apps/dashboard/static/sticky-test.css delete mode 100644 apps/dashboard/static/sticky-test.html delete mode 100644 packages/db/src/migrations/0004_ai_chat_document.sql create mode 100644 packages/db/src/queries/agent/chat-resource.ts create mode 100644 packages/db/src/queries/agent/credit-purchase.ts create mode 100644 packages/db/src/queries/agent/token-usage.ts create mode 100644 packages/db/src/queries/agent/usage-aggregations.ts create mode 100644 packages/db/src/queries/course/content-batch.ts create mode 100644 packages/db/src/queries/course/public-course.ts create mode 100644 packages/db/src/queries/exercise/exercise-section.ts create mode 100644 packages/db/src/queries/query-errors.ts create mode 100644 packages/question-types/src/exercise-section-types.ts create mode 100644 packages/question-types/src/video-recording-settings.ts create mode 100644 packages/storybook/src/molecules/course-creator/course-creator.stories.svelte create mode 100644 packages/storybook/src/molecules/model-picker/model-picker.stories.svelte create mode 100644 packages/storybook/src/molecules/public-course/fixtures.ts create mode 100644 packages/storybook/src/molecules/public-course/public-course.stories.svelte create mode 100644 packages/ui/src/base/number-badge/index.ts create mode 100644 packages/ui/src/base/number-badge/number-badge.svelte create mode 100644 packages/ui/src/custom/animation/dot-field/dot-field.svelte create mode 100644 packages/ui/src/custom/animation/dot-field/index.ts create mode 100644 packages/ui/src/custom/course-creator/course-creator.svelte create mode 100644 packages/ui/src/custom/course-creator/index.ts create mode 100644 packages/ui/src/custom/exercise-question/renderers/video-recording/edit.svelte create mode 100644 packages/ui/src/custom/exercise-question/renderers/video-recording/format.ts create mode 100644 packages/ui/src/custom/exercise-question/renderers/video-recording/preview.svelte create mode 100644 packages/ui/src/custom/exercise-question/renderers/video-recording/submission.svelte create mode 100644 packages/ui/src/custom/exercise-question/renderers/video-recording/take.svelte create mode 100644 packages/ui/src/custom/exercise-question/section-after-behavior.svelte create mode 100644 packages/ui/src/custom/exercise-question/section-header.svelte create mode 100644 packages/ui/src/custom/exercise-question/section-navigation-sidebar.svelte create mode 100644 packages/ui/src/custom/exercise-question/section-overview.svelte create mode 100644 packages/ui/src/custom/model-picker/index.ts create mode 100644 packages/ui/src/custom/model-picker/model-picker.svelte create mode 100644 packages/ui/src/custom/public-course/bottom-nav.svelte create mode 100644 packages/ui/src/custom/public-course/callout.svelte create mode 100644 packages/ui/src/custom/public-course/exercise-view.svelte create mode 100644 packages/ui/src/custom/public-course/footer-nav.svelte create mode 100644 packages/ui/src/custom/public-course/index.ts create mode 100644 packages/ui/src/custom/public-course/lesson-view.svelte create mode 100644 packages/ui/src/custom/public-course/mobile-sheet.svelte create mode 100644 packages/ui/src/custom/public-course/powered-by.svelte create mode 100644 packages/ui/src/custom/public-course/public-exercise-attempts-storage.ts create mode 100644 packages/ui/src/custom/public-course/shell.svelte create mode 100644 packages/ui/src/custom/public-course/sidebar-row.svelte create mode 100644 packages/ui/src/custom/public-course/sidebar.svelte create mode 100644 packages/ui/src/custom/public-course/types.ts create mode 100644 packages/utils/src/plans/token-packs.ts create mode 100644 packages/utils/src/validation/course/callout.ts create mode 100644 packages/utils/src/validation/course/public-course.ts create mode 100644 packages/utils/src/validation/exercise/exercise-section.ts create mode 100644 packages/utils/src/validation/exercise/video-recording.ts create mode 100644 packages/utils/src/validation/shared/index.ts create mode 100644 packages/utils/src/validation/shared/slug.ts rename prd/{ai-course-assistant => ai-course-assistant [DONE]}/README.md (99%) rename prd/{ai-course-assistant => ai-course-assistant [DONE]}/implementation-plan.md (100%) rename prd/{compliance-training-platform => compliance-training-platform [DONE]}/README.md (99%) rename prd/{compliance-training-platform => compliance-training-platform [DONE]}/UX.md (100%) rename prd/{course-widget-embed => course-widget-embed [DONE]}/README.md (99%) rename prd/{course-widget-embed => course-widget-embed [DONE]}/implementation-plan.md (100%) rename prd/{custom-domain-cloudflare => custom-domain-cloudflare [DONE]}/README.md (99%) rename prd/{exercise-sections => exercise-sections [DONE]}/README.md (77%) rename prd/{exercise-sections => exercise-sections [DONE]}/implementation-plan.md (79%) rename prd/{mcp-course-authoring => mcp-course-authoring [DONE]}/README.md (97%) rename prd/{mcp-course-authoring => mcp-course-authoring [DONE]}/consumption-spec.md (100%) rename prd/{mcp-course-authoring => mcp-course-authoring [DONE]}/implementation-plan.md (97%) rename prd/{org-landing-page-theme-picker => org-landing-page-theme-picker [DONE]}/README.md (99%) rename prd/{org-landing-page-theme-picker => org-landing-page-theme-picker [DONE]}/implementation-plan.md (100%) rename prd/{programs => programs [DONE]}/README.md (99%) rename prd/{public-api => public-api [DONE]}/README.md (98%) create mode 100644 prd/public-courses/design/lesson-page-full.png create mode 100644 prd/public-courses/design/lesson-page-reference.png create mode 100644 prd/public-courses/design/sidebar-active-state.png create mode 100644 prd/study-from-source/README.md create mode 100644 prd/study-from-source/implementation-plan.md create mode 100644 prd/video-recording-question-type [DONE]/README.md diff --git a/.cursor/plans/ai_assistant_prd_5a6f948c.plan.md b/.cursor/plans/ai_assistant_prd_5a6f948c.plan.md index 7ed04a7e..948af3ea 100644 --- a/.cursor/plans/ai_assistant_prd_5a6f948c.plan.md +++ b/.cursor/plans/ai_assistant_prd_5a6f948c.plan.md @@ -1,9 +1,9 @@ --- name: AI assistant PRD -overview: Write a new PRD from scratch for the AI Course Assistant feature, replacing the existing placeholder at prd/ai-course-assistant/README.md. The PRD will document a hybrid execution model, configurable LLM providers, streaming chat, and the full package/API/UI architecture. +overview: Write a new PRD from scratch for the AI Course Assistant feature, replacing the existing placeholder at prd/ai-course-assistant [DONE]/README.md. The PRD will document a hybrid execution model, configurable LLM providers, streaming chat, and the full package/API/UI architecture. todos: - id: write-prd - content: "Write the full PRD markdown file at prd/ai-course-assistant/README.md with all sections: purpose, architecture, execution model, package design, tools, safeguards, UI spec, self-host gating, streaming, API routes, and implementation phases" + content: "Write the full PRD markdown file at prd/ai-course-assistant [DONE]/README.md with all sections: purpose, architecture, execution model, package design, tools, safeguards, UI spec, self-host gating, streaming, API routes, and implementation phases" status: pending isProject: false --- @@ -12,7 +12,7 @@ isProject: false ## What We're Writing -Replace [prd/ai-course-assistant/README.md](prd/ai-course-assistant/README.md) with a comprehensive PRD for an AI assistant embedded in the course UI. The PRD will be a single markdown file covering architecture, UI, safeguards, package design, and implementation phases. +Replace [prd/ai-course-assistant [DONE]/README.md](prd/ai-course-assistant [DONE]/README.md) with a comprehensive PRD for an AI assistant embedded in the course UI. The PRD will be a single markdown file covering architecture, UI, safeguards, package design, and implementation phases. ## Key Architectural Decisions @@ -612,7 +612,7 @@ Registered in [apps/api/src/app.ts](apps/api/src/app.ts) as `.route('/agent', ag **New files:** -- `prd/ai-course-assistant/README.md` — this PRD (replace existing) +- `prd/ai-course-assistant [DONE]/README.md` — this PRD (replace existing) **Files referenced (not modified in PRD phase):** diff --git a/.cursor/plans/labs_prd.md b/.cursor/plans/labs_prd.md new file mode 100644 index 00000000..e8223f58 --- /dev/null +++ b/.cursor/plans/labs_prd.md @@ -0,0 +1,942 @@ +# PRD: ClassroomIO Labs + +**Status:** Draft +**Author:** Rotimi Best +**Date:** 2026-05-03 +**Version:** 1.0 + +--- + +## 1. Overview + +Labs is a new lesson content block type that allows teachers to embed interactive, sandboxed mini-applications directly inside a lesson. Unlike quizzes — which test recall through structured questions — Labs are experiential: students practice, simulate, explore, or make decisions inside a purpose-built interactive environment. + +Each Lab is a single compiled Svelte application hosted as a standalone HTML page and embedded via ` + {:else} + + {/if} + + {/if} + + {#if lesson.sectionTitle} +
+ {lesson.sectionTitle} +
+ {/if} + +

+ {lesson.title} +

+ +
+ {#if lesson.body} + + {:else} +

No content yet.

+ {/if} +
+ + + {/if} + diff --git a/packages/ui/src/custom/public-course/mobile-sheet.svelte b/packages/ui/src/custom/public-course/mobile-sheet.svelte new file mode 100644 index 00000000..fae5a929 --- /dev/null +++ b/packages/ui/src/custom/public-course/mobile-sheet.svelte @@ -0,0 +1,73 @@ + + + + + + + + + {title} + +
+ +
+
+
+
diff --git a/packages/ui/src/custom/public-course/powered-by.svelte b/packages/ui/src/custom/public-course/powered-by.svelte new file mode 100644 index 00000000..2ada2b31 --- /dev/null +++ b/packages/ui/src/custom/public-course/powered-by.svelte @@ -0,0 +1,53 @@ + + +
+ ClassroomIO + {label} + + {brand} + +
diff --git a/packages/ui/src/custom/public-course/public-exercise-attempts-storage.ts b/packages/ui/src/custom/public-course/public-exercise-attempts-storage.ts new file mode 100644 index 00000000..bb25b95e --- /dev/null +++ b/packages/ui/src/custom/public-course/public-exercise-attempts-storage.ts @@ -0,0 +1,71 @@ +import type { AnswerData } from '@cio/question-types'; + +/** Bump when persisted shape changes. */ +export const PUBLIC_EXERCISE_ATTEMPTS_STORE_VERSION = 1; + +export interface PublicExerciseStoredAttempt { + /** ISO timestamp when the learner submitted answers. */ + savedAt: string; + answersByKey: Record; + correctCount: number; + totalGradable: number; +} + +interface PersistedEnvelope { + v: number; + attempts: PublicExerciseStoredAttempt[]; +} + +/** Namespace localStorage keys — built by the consuming route (`courseSlug` + exercise `itemSlug`). */ +export function publicExerciseAttemptsStorageKey(courseSlug: string, exerciseSlug: string): string { + return `cio-public-exercise-attempts::v${PUBLIC_EXERCISE_ATTEMPTS_STORE_VERSION}::${courseSlug}::${exerciseSlug}`; +} + +function isStoredAttempt(value: unknown): value is PublicExerciseStoredAttempt { + if (!value || typeof value !== 'object') return false; + const row = value as Record; + return ( + typeof row.savedAt === 'string' && + typeof row.answersByKey === 'object' && + row.answersByKey !== null && + typeof row.correctCount === 'number' && + Number.isFinite(row.correctCount) && + typeof row.totalGradable === 'number' && + Number.isFinite(row.totalGradable) + ); +} + +/** Read persisted attempts — returns newest-last order. Empty if missing or malformed. */ +export function readPublicExerciseAttempts(storageKey: string | null): PublicExerciseStoredAttempt[] { + if (!storageKey || typeof localStorage === 'undefined') return []; + + try { + const raw = localStorage.getItem(storageKey); + if (!raw) return []; + + const parsed = JSON.parse(raw) as unknown; + const envelope = + parsed && typeof parsed === 'object' && 'attempts' in parsed ? (parsed as PersistedEnvelope) : null; + + if (!envelope || !Array.isArray(envelope.attempts)) return []; + + return envelope.attempts.filter(isStoredAttempt); + } catch (error) { + console.warn('readPublicExerciseAttempts: failed', error); + return []; + } +} + +export function writePublicExerciseAttempts(storageKey: string | null, attempts: PublicExerciseStoredAttempt[]): void { + if (!storageKey || typeof localStorage === 'undefined') return; + + try { + const envelope: PersistedEnvelope = { + v: PUBLIC_EXERCISE_ATTEMPTS_STORE_VERSION, + attempts + }; + localStorage.setItem(storageKey, JSON.stringify(envelope)); + } catch (error) { + console.warn('writePublicExerciseAttempts: failed', error); + } +} diff --git a/packages/ui/src/custom/public-course/shell.svelte b/packages/ui/src/custom/public-course/shell.svelte new file mode 100644 index 00000000..ade8eb0b --- /dev/null +++ b/packages/ui/src/custom/public-course/shell.svelte @@ -0,0 +1,243 @@ + + + + +
+ +
+
+ + {#if org?.avatarUrl} + + {:else} + + {/if} + + {org?.name ?? ''} + + + + + + + {courseTitle} + + +
+ {#if topRight} + {@render topRight()} + {:else} + + + {/if} +
+
+
+ + +
+ + +
+ {@render children()} + + {#if prevItem || nextItem} +
+ onItemClick?.(item)} + prevLabel={footerPrevLabel} + nextLabel={footerNextLabel} + /> +
+ {/if} +
+
+ + {#if activeItem} + onPrev?.()} + onNext={() => onNext?.()} + onOpenSheet={() => (sheetOpen = true)} + /> + {/if} + + { + onItemClick?.(item); + sheetOpen = false; + }} + /> +
diff --git a/packages/ui/src/custom/public-course/sidebar-row.svelte b/packages/ui/src/custom/public-course/sidebar-row.svelte new file mode 100644 index 00000000..2aba4c04 --- /dev/null +++ b/packages/ui/src/custom/public-course/sidebar-row.svelte @@ -0,0 +1,59 @@ + + +{#snippet body()} + + {title} +{/snippet} + +{#if href} + + {@render body()} + +{:else} + +{/if} diff --git a/packages/ui/src/custom/public-course/sidebar.svelte b/packages/ui/src/custom/public-course/sidebar.svelte new file mode 100644 index 00000000..28d52e2e --- /dev/null +++ b/packages/ui/src/custom/public-course/sidebar.svelte @@ -0,0 +1,72 @@ + + +
+ + + {#if showPoweredBy} + + {/if} +
diff --git a/packages/ui/src/custom/public-course/types.ts b/packages/ui/src/custom/public-course/types.ts new file mode 100644 index 00000000..24488b8e --- /dev/null +++ b/packages/ui/src/custom/public-course/types.ts @@ -0,0 +1,75 @@ +/** + * Shared types for the public course UI (sidebar, shell, lesson view, exercise view). + * Keep these intentionally narrow — the API and the consuming routes normalize into + * these shapes before rendering. + * + * For exercise questions we deliberately reuse `ExerciseQuestionModel` from + * `@cio/question-types` so the public exercise view drives the same renderer + * pipeline as the authenticated student view (`ExerciseQuestion.QuestionList`). + * That way, every supported question type — RADIO, CHECKBOX, TRUE_FALSE, + * SHORT_ANSWER, NUMERIC, FILL_BLANK, ORDERING, MATCHING, etc. — picks up the + * standard `take` and `review` renderers without us re-implementing them. + */ + +import type { ExerciseQuestionLabels, ExerciseQuestionModel } from '@cio/question-types'; + +export interface PublicCourseCalloutData { + title: string; + description: string; + buttonLabel: string; + buttonUrl: string; +} + +/** Org / creator info shown in the top header (logo, name) of public course pages. */ +export interface PublicCourseOrgData { + id: string; + name: string; + siteName: string | null; + avatarUrl: string | null; +} + +export interface PublicCourseSidebarItem { + kind: 'lesson' | 'exercise'; + id: string; + slug: string; + title: string; + isUnlocked: boolean; +} + +export interface PublicCourseSidebarSection { + id: string; + title: string; + items: PublicCourseSidebarItem[]; +} + +export type PublicCourseItemVariant = 'lesson' | 'exercise'; + +export interface PublicLessonViewData { + kind: 'lesson'; + title: string; + sectionTitle: string | null; + body: string; + video: { + type: 'youtube' | 'generic' | 'upload' | 'google_drive'; + link: string; + } | null; + isUnlocked: boolean; +} + +export interface PublicExerciseViewData { + kind: 'exercise'; + title: string; + description: string | null; + sectionTitle: string | null; + isUnlocked: boolean; + /** + * Questions in the standard `ExerciseQuestionModel` shape. Each question's + * `options[].isCorrect` and `settings` carry the answer keys used for + * client-side grading in PUBLIC courses. + */ + questions: ExerciseQuestionModel[]; +} + +export type { ExerciseQuestionLabels }; + +export type PublicItemViewData = PublicLessonViewData | PublicExerciseViewData; diff --git a/packages/ui/src/custom/question-type-picker/exercise-labels.ts b/packages/ui/src/custom/question-type-picker/exercise-labels.ts index e060e3c0..6650d8dc 100644 --- a/packages/ui/src/custom/question-type-picker/exercise-labels.ts +++ b/packages/ui/src/custom/question-type-picker/exercise-labels.ts @@ -104,6 +104,41 @@ export const QUESTION_TYPE_PICKER_EXERCISE_LABELS: ExerciseQuestionLabels = { 'file_upload.take.download': 'Download', 'file_upload.preview.accepted_types_label': 'Accepted types', 'file_upload.preview.max_size_label': 'Max size (MB)', + 'video_recording.edit.max_duration_label': 'Max duration', + 'video_recording.edit.max_duration_helper': 'Students record in the browser and can retake before submitting.', + 'video_recording.edit.max_duration_placeholder': '60', + 'video_recording.edit.manual_grading': 'Requires manual grading', + 'video_recording.take.unsupported': 'Video recording is not supported in this browser.', + 'video_recording.take.permission_prompt': 'Allow camera and microphone access to record your answer.', + 'video_recording.take.permission_denied': 'Camera or microphone access was blocked. Check browser permissions.', + 'video_recording.take.start_camera': 'Set up camera', + 'video_recording.take.camera_label': 'Camera', + 'video_recording.take.camera_placeholder': 'Select camera', + 'video_recording.take.play_preview': 'Play recording preview', + 'video_recording.take.pause_preview': 'Pause recording preview', + 'video_recording.take.start_recording': 'Start recording', + 'video_recording.take.stop_recording': 'Stop recording', + 'video_recording.take.elapsed': 'Elapsed', + 'video_recording.take.remaining': 'Remaining', + 'video_recording.take.max_duration': 'Max', + 'video_recording.take.too_short': 'Record for at least 1 second.', + 'video_recording.take.use_recording': 'Use recording', + 'video_recording.take.retake': 'Retake', + 'video_recording.take.delete': 'Delete recording', + 'video_recording.take.retry_upload': 'Retry upload', + 'video_recording.take.uploading': 'Uploading recording...', + 'video_recording.take.uploaded': 'Recording uploaded', + 'video_recording.take.upload_failed': 'Upload failed. Try again.', + 'video_recording.take.replaces_previous': 'A new accepted take replaces the previous answer after upload.', + 'video_recording.preview.max_duration_label': 'Max duration', + 'video_recording.preview.manual_grading': 'Learners record a short video response. Manual grading required.', + 'video_recording.submission.no_video': 'No video submitted.', + 'video_recording.submission.unavailable': 'Video unavailable.', + 'video_recording.submission.duration': 'Duration', + 'video_recording.submission.file_size': 'File size', + 'video_recording.submission.recorded_at': 'Recorded', + 'video_recording.submission.uploaded_at': 'Uploaded', + 'video_recording.submission.retake_count': 'Retakes', 'link.edit.instructions_placeholder': 'Optional instructions for learners', 'link.edit.helper': 'Learners can submit one or more valid URLs (http/https).', 'link.take.helper': 'Add one or more links. Only valid http/https URLs are accepted.', diff --git a/packages/ui/src/custom/question-type-picker/picker-data.ts b/packages/ui/src/custom/question-type-picker/picker-data.ts index 5af23f4d..eb720e34 100644 --- a/packages/ui/src/custom/question-type-picker/picker-data.ts +++ b/packages/ui/src/custom/question-type-picker/picker-data.ts @@ -296,6 +296,22 @@ const QUESTION_TYPE_PICKER_ENTRIES: QuestionTypePickerEntry[] = [ questionType: 'STAR', settings: { correctValue: 4, maxStars: 5 } } + }, + { + key: QUESTION_TYPE_KEY.VIDEO_RECORDING, + displayLabel: 'Video recording', + description: 'Learners record a short camera response — useful for reflection, speaking, and demos.', + demoQuestion: { + id: 'picker-video-recording', + key: 'picker-video-recording', + title: 'Record a short reflection explaining one idea from this module that you would apply at work.', + questionType: 'VIDEO_RECORDING', + settings: { + description: 'Keep it concise. You can retake before submitting.', + maxDurationSeconds: 120, + allowRetakes: true + } + } } ]; diff --git a/packages/ui/src/custom/question-type-picker/question-type-picker.svelte b/packages/ui/src/custom/question-type-picker/question-type-picker.svelte index 115ad42a..253674d7 100644 --- a/packages/ui/src/custom/question-type-picker/question-type-picker.svelte +++ b/packages/ui/src/custom/question-type-picker/question-type-picker.svelte @@ -1,5 +1,5 @@
{ answer = nextAnswer; diff --git a/packages/ui/src/custom/widget-layouts/carousel.svelte b/packages/ui/src/custom/widget-layouts/carousel.svelte index 373834bb..0442faf8 100644 --- a/packages/ui/src/custom/widget-layouts/carousel.svelte +++ b/packages/ui/src/custom/widget-layouts/carousel.svelte @@ -14,7 +14,6 @@ const maxIndex = $derived(Math.max(courses.length - visible, 0)); let position = $state(0); - let trackEl = $state(null); let timer: ReturnType | null = null; const cardBasis = $derived(`calc((100% - ${(visible - 1) * 12}px) / ${visible})`); @@ -68,7 +67,7 @@ aria-roledescription="carousel" >
-
+
{#each courses as course (course.id)} :not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.ui\:gap-x-6{column-gap:calc(var(--ui-spacing) * 6)}:where(.ui\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--ui-spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--ui-spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.ui\:gap-y-0\.5{row-gap:calc(var(--ui-spacing) * .5)}.ui\:gap-y-2{row-gap:calc(var(--ui-spacing) * 2)}.ui\:self-center{align-self:center}.ui\:self-start{align-self:flex-start}.ui\:self-stretch{align-self:stretch}.ui\:justify-self-end{justify-self:flex-end}.ui\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui\:overflow-auto{overflow:auto}.ui\:overflow-hidden{overflow:hidden}.ui\:overflow-visible{overflow:visible}.ui\:overflow-x-auto{overflow-x:auto}.ui\:overflow-x-hidden{overflow-x:hidden}.ui\:overflow-y-auto{overflow-y:auto}.ui\:overflow-y-hidden{overflow-y:hidden}.ui\:scroll-smooth{scroll-behavior:smooth}.ui\:rounded{border-radius:.25rem}.ui\:rounded-\(--radius\){border-radius:var(--radius)}.ui\:rounded-2xl{border-radius:var(--ui-radius-2xl)}.ui\:rounded-3xl{border-radius:var(--ui-radius-3xl)}.ui\:rounded-\[2px\]{border-radius:2px}.ui\:rounded-\[4px\]{border-radius:4px}.ui\:rounded-\[calc\(var\(--radius\)-5px\)\]{border-radius:calc(var(--radius) - 5px)}.ui\:rounded-full{border-radius:3.40282e38px}.ui\:rounded-lg{border-radius:var(--radius)}.ui\:rounded-md{border-radius:calc(var(--radius) - 2px)}.ui\:rounded-md\!{border-radius:calc(var(--radius) - 2px)!important}.ui\:rounded-none{border-radius:0}.ui\:rounded-sm{border-radius:calc(var(--radius) - 4px)}.ui\:rounded-xl{border-radius:calc(var(--radius) + 4px)}.ui\:rounded-xs{border-radius:var(--ui-radius-xs)}.ui\:border{border-style:var(--tw-border-style);border-width:1px}.ui\:border-0{border-style:var(--tw-border-style);border-width:0}.ui\:border-2{border-style:var(--tw-border-style);border-width:2px}.ui\:border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.ui\:border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.ui\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.ui\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.ui\:border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.ui\:border-dashed{--tw-border-style:dashed;border-style:dashed}.ui\:border-none{--tw-border-style:none;border-style:none}.ui\:border-solid{--tw-border-style:solid;border-style:solid}.ui\:border-\(--color-border\){border-color:var(--color-border)}.ui\:border-amber-200{border-color:var(--ui-color-amber-200)}.ui\:border-amber-400{border-color:var(--ui-color-amber-400)}.ui\:border-background\/40{border-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:border-background\/40{border-color:color-mix(in oklab, var(--background) 40%, transparent)}}.ui\:border-border,.ui\:border-border\/40{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/40{border-color:color-mix(in oklab, var(--border) 40%, transparent)}}.ui\:border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/50{border-color:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:border-border\/60{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/60{border-color:color-mix(in oklab, var(--border) 60%, transparent)}}.ui\:border-emerald-300{border-color:var(--ui-color-emerald-300)}.ui\:border-emerald-400{border-color:var(--ui-color-emerald-400)}.ui\:border-emerald-500{border-color:var(--ui-color-emerald-500)}.ui\:border-input{border-color:var(--input)}.ui\:border-muted{border-color:var(--muted)}.ui\:border-muted-foreground,.ui\:border-muted-foreground\/40{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:border-muted-foreground\/40{border-color:color-mix(in oklab, var(--muted-foreground) 40%, transparent)}}.ui\:border-neutral-800{border-color:var(--ui-color-neutral-800)}.ui\:border-primary,.ui\:border-primary\/30{border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:border-primary\/30{border-color:color-mix(in oklab, var(--primary) 30%, transparent)}}.ui\:border-red-500{border-color:var(--ui-color-red-500)}.ui\:border-sidebar-border{border-color:var(--sidebar-border)}.ui\:border-sky-200{border-color:var(--ui-color-sky-200)}.ui\:border-transparent{border-color:#0000}.ui\:border-white\/10{border-color:var(--ui-color-white)}@supports (color:color-mix(in lab, red, red)){.ui\:border-white\/10{border-color:color-mix(in oklab, var(--ui-color-white) 10%, transparent)}}.ui\:border-l-foreground{border-left-color:var(--foreground)}.ui\:border-l-transparent{border-left-color:#0000}.ui\:bg-\(--color-bg\){background-color:var(--color-bg)}.ui\:bg-accent{background-color:var(--accent)}.ui\:bg-amber-50,.ui\:bg-amber-50\/30{background-color:var(--ui-color-amber-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-amber-50\/30{background-color:color-mix(in oklab, var(--ui-color-amber-50) 30%, transparent)}}.ui\:bg-amber-600{background-color:var(--ui-color-amber-600)}.ui\:bg-background,.ui\:bg-background\/50{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-background\/50{background-color:color-mix(in oklab, var(--background) 50%, transparent)}}.ui\:bg-background\/90{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-background\/90{background-color:color-mix(in oklab, var(--background) 90%, transparent)}}.ui\:bg-black\/50{background-color:var(--ui-color-black)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-black\/50{background-color:color-mix(in oklab, var(--ui-color-black) 50%, transparent)}}.ui\:bg-border{background-color:var(--border)}.ui\:bg-card{background-color:var(--card)}.ui\:bg-destructive{background-color:var(--destructive)}.ui\:bg-emerald-50\/20{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/20{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 20%, transparent)}}.ui\:bg-emerald-50\/30{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/30{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 30%, transparent)}}.ui\:bg-emerald-50\/40{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/40{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 40%, transparent)}}.ui\:bg-emerald-600{background-color:var(--ui-color-emerald-600)}.ui\:bg-foreground{background-color:var(--foreground)}.ui\:bg-gray-200{background-color:var(--ui-color-gray-200)}.ui\:bg-green-500{background-color:var(--ui-color-green-500)}.ui\:bg-inherit{background-color:inherit}.ui\:bg-input{background-color:var(--input)}.ui\:bg-muted,.ui\:bg-muted\/10{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/10{background-color:color-mix(in oklab, var(--muted) 10%, transparent)}}.ui\:bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/30{background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.ui\:bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.ui\:bg-popover{background-color:var(--popover)}.ui\:bg-primary{background-color:var(--primary)}.ui\:bg-primary-foreground{background-color:var(--primary-foreground)}.ui\:bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/5{background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/10{background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.ui\:bg-primary\/20{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/20{background-color:color-mix(in oklab, var(--primary) 20%, transparent)}}.ui\:bg-primary\/60{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/60{background-color:color-mix(in oklab, var(--primary) 60%, transparent)}}.ui\:bg-red-50\/40{background-color:var(--ui-color-red-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-red-50\/40{background-color:color-mix(in oklab, var(--ui-color-red-50) 40%, transparent)}}.ui\:bg-red-500{background-color:var(--ui-color-red-500)}.ui\:bg-secondary,.ui\:bg-secondary\/50{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-secondary\/50{background-color:color-mix(in oklab, var(--secondary) 50%, transparent)}}.ui\:bg-sidebar{background-color:var(--sidebar)}.ui\:bg-sidebar-border{background-color:var(--sidebar-border)}.ui\:bg-sky-50{background-color:var(--ui-color-sky-50)}.ui\:bg-slate-500{background-color:var(--ui-color-slate-500)}.ui\:bg-transparent{background-color:#0000}.ui\:bg-white{background-color:var(--ui-color-white)}.ui\:bg-yellow-500{background-color:var(--ui-color-yellow-500)}.ui\:bg-linear-to-r{--tw-gradient-position:to right}@supports (background-image:linear-gradient(in lab, red, red)){.ui\:bg-linear-to-r{--tw-gradient-position:to right in oklab}}.ui\:bg-linear-to-r{background-image:linear-gradient(var(--tw-gradient-stops))}.ui\:bg-linear-to-t{--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab, red, red)){.ui\:bg-linear-to-t{--tw-gradient-position:to top in oklab}}.ui\:bg-linear-to-t{background-image:linear-gradient(var(--tw-gradient-stops))}.ui\:bg-\[linear-gradient\(110deg\,\#000103\,45\%\,\#1e2631\,55\%\,\#000103\)\]{background-image:linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)}.ui\:from-background{--tw-gradient-from:var(--background);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:from-pink-500{--tw-gradient-from:var(--ui-color-pink-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:from-slate-500{--tw-gradient-from:var(--ui-color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:via-blue-600\/30{--tw-gradient-via:var(--ui-color-blue-600)}@supports (color:color-mix(in lab, red, red)){.ui\:via-blue-600\/30{--tw-gradient-via:color-mix(in oklab, var(--ui-color-blue-600) 30%, transparent)}}.ui\:via-blue-600\/30{--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.ui\:to-orange-500{--tw-gradient-to:var(--ui-color-orange-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:bg-\[length\:200\%_100\%\]{background-size:200% 100%}.ui\:bg-clip-padding{background-clip:padding-box}.ui\:fill-amber-400{fill:var(--ui-color-amber-400)}.ui\:fill-current{fill:currentColor}.ui\:fill-none{fill:none}.ui\:fill-primary{fill:var(--primary)}.ui\:object-cover{object-fit:cover}.ui\:p-0{padding:calc(var(--ui-spacing) * 0)}.ui\:p-0\.5{padding:calc(var(--ui-spacing) * .5)}.ui\:p-1{padding:calc(var(--ui-spacing) * 1)}.ui\:p-1\!{padding:calc(var(--ui-spacing) * 1)!important}.ui\:p-2{padding:calc(var(--ui-spacing) * 2)}.ui\:p-3{padding:calc(var(--ui-spacing) * 3)}.ui\:p-3\!{padding:calc(var(--ui-spacing) * 3)!important}.ui\:p-4{padding:calc(var(--ui-spacing) * 4)}.ui\:p-6{padding:calc(var(--ui-spacing) * 6)}.ui\:p-8{padding:calc(var(--ui-spacing) * 8)}.ui\:p-\[3px\]{padding:3px}.ui\:px-1{padding-inline:calc(var(--ui-spacing) * 1)}.ui\:px-1\.5{padding-inline:calc(var(--ui-spacing) * 1.5)}.ui\:px-2{padding-inline:calc(var(--ui-spacing) * 2)}.ui\:px-2\.5{padding-inline:calc(var(--ui-spacing) * 2.5)}.ui\:px-3{padding-inline:calc(var(--ui-spacing) * 3)}.ui\:px-4{padding-inline:calc(var(--ui-spacing) * 4)}.ui\:px-6{padding-inline:calc(var(--ui-spacing) * 6)}.ui\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:py-0\.5{padding-block:calc(var(--ui-spacing) * .5)}.ui\:py-1{padding-block:calc(var(--ui-spacing) * 1)}.ui\:py-1\.5{padding-block:calc(var(--ui-spacing) * 1.5)}.ui\:py-2{padding-block:calc(var(--ui-spacing) * 2)}.ui\:py-3{padding-block:calc(var(--ui-spacing) * 3)}.ui\:py-4{padding-block:calc(var(--ui-spacing) * 4)}.ui\:py-5{padding-block:calc(var(--ui-spacing) * 5)}.ui\:py-6{padding-block:calc(var(--ui-spacing) * 6)}.ui\:py-10{padding-block:calc(var(--ui-spacing) * 10)}.ui\:py-12{padding-block:calc(var(--ui-spacing) * 12)}.ui\:py-16{padding-block:calc(var(--ui-spacing) * 16)}.ui\:py-20{padding-block:calc(var(--ui-spacing) * 20)}.ui\:py-24{padding-block:calc(var(--ui-spacing) * 24)}.ui\:ps-3{padding-inline-start:calc(var(--ui-spacing) * 3)}.ui\:pe-3{padding-inline-end:calc(var(--ui-spacing) * 3)}.ui\:pt-0{padding-top:calc(var(--ui-spacing) * 0)}.ui\:pt-1\.5{padding-top:calc(var(--ui-spacing) * 1.5)}.ui\:pt-3{padding-top:calc(var(--ui-spacing) * 3)}.ui\:pt-3\!{padding-top:calc(var(--ui-spacing) * 3)!important}.ui\:pt-4{padding-top:calc(var(--ui-spacing) * 4)}.ui\:pr-1{padding-right:calc(var(--ui-spacing) * 1)}.ui\:pr-2{padding-right:calc(var(--ui-spacing) * 2)}.ui\:pr-8{padding-right:calc(var(--ui-spacing) * 8)}.ui\:pr-9{padding-right:calc(var(--ui-spacing) * 9)}.ui\:pr-18{padding-right:calc(var(--ui-spacing) * 18)}.ui\:pb-2{padding-bottom:calc(var(--ui-spacing) * 2)}.ui\:pb-3{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:pb-4{padding-bottom:calc(var(--ui-spacing) * 4)}.ui\:pb-16{padding-bottom:calc(var(--ui-spacing) * 16)}.ui\:pl-2{padding-left:calc(var(--ui-spacing) * 2)}.ui\:pl-3{padding-left:calc(var(--ui-spacing) * 3)}.ui\:pl-8{padding-left:calc(var(--ui-spacing) * 8)}.ui\:text-center{text-align:center}.ui\:text-left{text-align:left}.ui\:text-right{text-align:right}.ui\:align-middle{vertical-align:middle}.ui\:font-mono{font-family:var(--ui-font-mono)}.ui\:font-sans{font-family:var(--ui-font-sans)}.ui\:text-2xl{font-size:var(--ui-text-2xl);line-height:var(--tw-leading,var(--ui-text-2xl--line-height))}.ui\:text-3xl{font-size:var(--ui-text-3xl);line-height:var(--tw-leading,var(--ui-text-3xl--line-height))}.ui\:text-4xl{font-size:var(--ui-text-4xl);line-height:var(--tw-leading,var(--ui-text-4xl--line-height))}.ui\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:text-base{font-size:var(--ui-text-base);line-height:var(--tw-leading,var(--ui-text-base--line-height))}.ui\:text-base\!{font-size:var(--ui-text-base)!important;line-height:var(--tw-leading,var(--ui-text-base--line-height))!important}.ui\:text-lg{font-size:var(--ui-text-lg);line-height:var(--tw-leading,var(--ui-text-lg--line-height))}.ui\:text-sm{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:text-sm\/relaxed{font-size:var(--ui-text-sm);line-height:var(--ui-leading-relaxed)}.ui\:text-xl{font-size:var(--ui-text-xl);line-height:var(--tw-leading,var(--ui-text-xl--line-height))}.ui\:text-xs{font-size:var(--ui-text-xs);line-height:var(--tw-leading,var(--ui-text-xs--line-height))}.ui\:text-\[10px\]{font-size:10px}.ui\:text-\[13px\]{font-size:13px}.ui\:leading-7{--tw-leading:calc(var(--ui-spacing) * 7);line-height:calc(var(--ui-spacing) * 7)}.ui\:leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.ui\:leading-none{--tw-leading:1;line-height:1}.ui\:leading-normal{--tw-leading:var(--ui-leading-normal);line-height:var(--ui-leading-normal)}.ui\:leading-relaxed{--tw-leading:var(--ui-leading-relaxed);line-height:var(--ui-leading-relaxed)}.ui\:leading-snug{--tw-leading:var(--ui-leading-snug);line-height:var(--ui-leading-snug)}.ui\:leading-tight{--tw-leading:var(--ui-leading-tight);line-height:var(--ui-leading-tight)}.ui\:font-black{--tw-font-weight:var(--ui-font-weight-black);font-weight:var(--ui-font-weight-black)}.ui\:font-bold{--tw-font-weight:var(--ui-font-weight-bold);font-weight:var(--ui-font-weight-bold)}.ui\:font-light{--tw-font-weight:var(--ui-font-weight-light);font-weight:var(--ui-font-weight-light)}.ui\:font-medium{--tw-font-weight:var(--ui-font-weight-medium);font-weight:var(--ui-font-weight-medium)}.ui\:font-normal{--tw-font-weight:var(--ui-font-weight-normal);font-weight:var(--ui-font-weight-normal)}.ui\:font-semibold{--tw-font-weight:var(--ui-font-weight-semibold);font-weight:var(--ui-font-weight-semibold)}.ui\:tracking-tight{--tw-tracking:var(--ui-tracking-tight);letter-spacing:var(--ui-tracking-tight)}.ui\:tracking-tighter{--tw-tracking:var(--ui-tracking-tighter);letter-spacing:var(--ui-tracking-tighter)}.ui\:tracking-wide{--tw-tracking:var(--ui-tracking-wide);letter-spacing:var(--ui-tracking-wide)}.ui\:tracking-widest{--tw-tracking:var(--ui-tracking-widest);letter-spacing:var(--ui-tracking-widest)}.ui\:text-balance{text-wrap:balance}.ui\:break-words{overflow-wrap:break-word}.ui\:break-all{word-break:break-all}.ui\:text-ellipsis{text-overflow:ellipsis}.ui\:whitespace-nowrap{white-space:nowrap}.ui\:text-accent-foreground{color:var(--accent-foreground)}.ui\:text-amber-200{color:var(--ui-color-amber-200)}.ui\:text-amber-500{color:var(--ui-color-amber-500)}.ui\:text-amber-600{color:var(--ui-color-amber-600)}.ui\:text-amber-900{color:var(--ui-color-amber-900)}.ui\:text-background,.ui\:text-background\/60{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/60{color:color-mix(in oklab, var(--background) 60%, transparent)}}.ui\:text-background\/70{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/70{color:color-mix(in oklab, var(--background) 70%, transparent)}}.ui\:text-background\/80{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/80{color:color-mix(in oklab, var(--background) 80%, transparent)}}.ui\:text-card-foreground{color:var(--card-foreground)}.ui\:text-current{color:currentColor}.ui\:text-destructive{color:var(--destructive)}.ui\:text-emerald-600{color:var(--ui-color-emerald-600)}.ui\:text-foreground,.ui\:text-foreground\/70{color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-foreground\/70{color:color-mix(in oklab, var(--foreground) 70%, transparent)}}.ui\:text-foreground\/72{color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-foreground\/72{color:color-mix(in oklab, var(--foreground) 72%, transparent)}}.ui\:text-green-500{color:var(--ui-color-green-500)}.ui\:text-muted-foreground,.ui\:text-muted-foreground\/75{color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-muted-foreground\/75{color:color-mix(in oklab, var(--muted-foreground) 75%, transparent)}}.ui\:text-popover-foreground{color:var(--popover-foreground)}.ui\:text-primary{color:var(--primary)}.ui\:text-primary-foreground{color:var(--primary-foreground)}.ui\:text-red-600{color:var(--ui-color-red-600)}.ui\:text-red-700{color:var(--ui-color-red-700)}.ui\:text-secondary-foreground{color:var(--secondary-foreground)}.ui\:text-sidebar-foreground,.ui\:text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-sidebar-foreground\/70{color:color-mix(in oklab, var(--sidebar-foreground) 70%, transparent)}}.ui\:text-sky-900{color:var(--ui-color-sky-900)}.ui\:text-white{color:var(--ui-color-white)}.ui\:text-white\!{color:var(--ui-color-white)!important}.ui\:capitalize{text-transform:capitalize}.ui\:uppercase{text-transform:uppercase}.ui\:no-underline{text-decoration-line:none}.ui\:underline{text-decoration-line:underline}.ui\:underline-offset-4{text-underline-offset:4px}.ui\:opacity-0{opacity:0}.ui\:opacity-10{opacity:.1}.ui\:opacity-40{opacity:.4}.ui\:opacity-50{opacity:.5}.ui\:opacity-60{opacity:.6}.ui\:opacity-70{opacity:.7}.ui\:opacity-90{opacity:.9}.ui\:opacity-100{opacity:1}.ui\:opacity-\[0\.06\]{opacity:.06}.ui\:opacity-\[0\.08\]{opacity:.08}.ui\:opacity-\[0\.15\]{opacity:.15}.ui\:shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-2xs{--tw-shadow:0 1px var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[0_0_0_1px_\#ffffff10\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#ffffff10);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[0_0_0_1px_var\(--sidebar-border\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-border));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[inset_0_-8px_10px_\#ffffff1f\]{--tw-shadow:inset 0 -8px 10px var(--tw-shadow-color,#ffffff1f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-3{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-primary\/20{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:shadow-primary\/20{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--primary) 20%, transparent) var(--tw-shadow-alpha), transparent)}}.ui\:ring-accent{--tw-ring-color:var(--accent)}.ui\:ring-background{--tw-ring-color:var(--background)}.ui\:ring-border\/50{--tw-ring-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:ring-border\/50{--tw-ring-color:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ui\:ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:ring-offset-background{--tw-ring-offset-color:var(--background)}.ui\:outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:outline-hidden{outline-offset:2px;outline:2px solid #0000}}.ui\:blur-\[2px\]{--tw-blur:blur(2px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:brightness-200{--tw-brightness:brightness(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:drop-shadow-none{--tw-drop-shadow: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:grayscale{--tw-grayscale:grayscale(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:backdrop-blur-sm{--tw-backdrop-blur:blur(var(--ui-blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.ui\:backdrop-blur-xl{--tw-backdrop-blur:blur(var(--ui-blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.ui\:transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-none{transition-property:none}.ui\:duration-100{--tw-duration:.1s;transition-duration:.1s}.ui\:duration-200{--tw-duration:.2s;transition-duration:.2s}.ui\:duration-300{--tw-duration:.3s;transition-duration:.3s}.ui\:duration-500{--tw-duration:.5s;transition-duration:.5s}.ui\:ease-in-out{--tw-ease:var(--ui-ease-in-out);transition-timing-function:var(--ui-ease-in-out)}.ui\:ease-linear{--tw-ease:linear;transition-timing-function:linear}.ui\:fade-in-0{--tw-enter-opacity:0}.ui\:outline-none{--tw-outline-style:none;outline-style:none}.ui\:select-none{-webkit-user-select:none;user-select:none}.ui\:zoom-in-95{--tw-enter-scale:.95}.ui\:\[background\:conic-gradient\(from_calc\(270deg-\(var\(--spread\)\*0\.5\)\)\,transparent_0\,var\(--shimmer-color\)_var\(--spread\)\,transparent_var\(--spread\)\)\]{background:conic-gradient(from calc(270deg - (var(--spread) * .5)),transparent 0,var(--shimmer-color) var(--spread),transparent var(--spread))}.ui\:\[background\:var\(--bg\)\]{background:var(--bg)}.ui\:\[mask\:none\]{-webkit-mask:none;mask:none}.ui\:ring-inset{--tw-ring-inset:inset}:is(.ui\:\*\:w-full>*){width:100%}.ui\:group-focus-within\/menu-item\:opacity-100:is(:where(.ui\:group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.ui\:group-hover\:scale-\[1\.02\]:is(:where(.ui\:group):hover *){scale:1.02}.ui\:group-hover\:bg-primary:is(:where(.ui\:group):hover *){background-color:var(--primary)}.ui\:group-hover\:text-primary-foreground:is(:where(.ui\:group):hover *){color:var(--primary-foreground)}.ui\:group-hover\:opacity-100:is(:where(.ui\:group):hover *){opacity:1}.ui\:group-hover\:shadow-\[inset_0_-6px_10px_\#ffffff3f\]:is(:where(.ui\:group):hover *){--tw-shadow:inset 0 -6px 10px var(--tw-shadow-color,#ffffff3f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-hover\/menu-item\:opacity-100:is(:where(.ui\:group\/menu-item):hover *){opacity:1}}.ui\:group-active\:shadow-\[inset_0_-10px_10px_\#ffffff3f\]:is(:where(.ui\:group):active *){--tw-shadow:inset 0 -10px 10px var(--tw-shadow-color,#ffffff3f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-has-data-\[orientation\=horizontal\]\/field\:text-balance:is(:where(.ui\:group\/field):has([data-orientation=horizontal]) *){text-wrap:balance}.ui\:group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.ui\:group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--ui-spacing) * 8)}.ui\:group-has-\[\[data-slot\=item-description\]\]\/item\:translate-y-0\.5:is(:where(.ui\:group\/item):has([data-slot=item-description]) *){--tw-translate-y:calc(var(--ui-spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:group-has-\[\[data-slot\=item-description\]\]\/item\:self-start:is(:where(.ui\:group\/item):has([data-slot=item-description]) *){align-self:flex-start}.ui\:group-has-\[\>input\]\/input-group\:pt-2\.5:is(:where(.ui\:group\/input-group):has(>input) *){padding-top:calc(var(--ui-spacing) * 2.5)}.ui\:group-has-\[\>input\]\/input-group\:pb-2\.5:is(:where(.ui\:group\/input-group):has(>input) *){padding-bottom:calc(var(--ui-spacing) * 2.5)}.ui\:group-data-\[collapsible\=icon\]\:pointer-events-none:is(:where(.ui\:group)[data-collapsible=icon] *){pointer-events:none}.ui\:group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.ui\:group)[data-collapsible=icon] *){margin-top:calc(var(--ui-spacing) * -8)}.ui\:group-data-\[collapsible\=icon\]\:hidden:is(:where(.ui\:group)[data-collapsible=icon] *){display:none}.ui\:group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.ui\:group)[data-collapsible=icon] *){width:calc(var(--ui-spacing) * 8)!important;height:calc(var(--ui-spacing) * 8)!important}.ui\:group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.ui\:group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.ui\:group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.ui\:group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--ui-spacing) * 4)) + 2px)}.ui\:group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.ui\:group)[data-collapsible=icon] *){overflow:hidden}.ui\:group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.ui\:group)[data-collapsible=icon] *){padding:calc(var(--ui-spacing) * 0)!important}.ui\:group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.ui\:group)[data-collapsible=icon] *){padding:calc(var(--ui-spacing) * 2)!important}.ui\:group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.ui\:group)[data-collapsible=icon] *){opacity:0}.ui\:group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.ui\:group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width) * -1)}.ui\:group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.ui\:group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width) * -1)}.ui\:group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.ui\:group)[data-collapsible=offcanvas] *){width:calc(var(--ui-spacing) * 0)}.ui\:group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.ui\:group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--ui-spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.ui\:group)[data-disabled=true] *){pointer-events:none}.ui\:group-data-\[disabled\=true\]\:opacity-50:is(:where(.ui\:group)[data-disabled=true] *),.ui\:group-data-\[disabled\=true\]\/field\:opacity-50:is(:where(.ui\:group\/field)[data-disabled=true] *),.ui\:group-data-\[disabled\=true\]\/input-group\:opacity-50:is(:where(.ui\:group\/input-group)[data-disabled=true] *){opacity:.5}.ui\:group-data-\[side\=left\]\:-right-4:is(:where(.ui\:group)[data-side=left] *){right:calc(var(--ui-spacing) * -4)}.ui\:group-data-\[side\=left\]\:border-r:is(:where(.ui\:group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:group-data-\[side\=right\]\:left-0:is(:where(.ui\:group)[data-side=right] *){left:calc(var(--ui-spacing) * 0)}.ui\:group-data-\[side\=right\]\:rotate-180:is(:where(.ui\:group)[data-side=right] *){rotate:180deg}.ui\:group-data-\[side\=right\]\:border-l:is(:where(.ui\:group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.ui\:group-data-\[variant\=floating\]\:rounded-lg:is(:where(.ui\:group)[data-variant=floating] *){border-radius:var(--radius)}.ui\:group-data-\[variant\=floating\]\:border:is(:where(.ui\:group)[data-variant=floating] *){border-style:var(--tw-border-style);border-width:1px}.ui\:group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.ui\:group)[data-variant=floating] *){border-color:var(--sidebar-border)}.ui\:group-data-\[variant\=floating\]\:shadow-sm:is(:where(.ui\:group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-data-\[variant\=outline\]\/field-group\:-mb-2:is(:where(.ui\:group\/field-group)[data-variant=outline] *){margin-bottom:calc(var(--ui-spacing) * -2)}.ui\:group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:block:is(:where(.ui\:group\/drawer-content)[data-vaul-drawer-direction=bottom] *){display:block}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:top-full:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){top:100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:mt-1\.5:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){margin-top:calc(var(--ui-spacing) * 1.5)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:overflow-hidden:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){overflow:hidden}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:rounded-md:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){border-radius:calc(var(--radius) - 2px)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:border:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){border-style:var(--tw-border-style);border-width:1px}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:bg-popover:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){background-color:var(--popover)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:text-popover-foreground:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){color:var(--popover-foreground)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:shadow:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:duration-200:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){--tw-duration:.2s;transition-duration:.2s}@media (hover:hover){.ui\:peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.ui\:peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.ui\:peer-disabled\:cursor-not-allowed:is(:where(.ui\:peer):disabled~*){cursor:not-allowed}.ui\:peer-disabled\:opacity-50:is(:where(.ui\:peer):disabled~*){opacity:.5}.ui\:peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.ui\:peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.ui\:peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.ui\:peer\/menu-button)[data-size=default]~*){top:calc(var(--ui-spacing) * 1.5)}.ui\:peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.ui\:peer\/menu-button)[data-size=lg]~*){top:calc(var(--ui-spacing) * 2.5)}.ui\:peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.ui\:peer\/menu-button)[data-size=sm]~*){top:calc(var(--ui-spacing) * 1)}.ui\:selection\:bg-primary ::selection{background-color:var(--primary)}.ui\:selection\:bg-primary::selection{background-color:var(--primary)}.ui\:selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.ui\:selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.ui\:placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.ui\:after\:absolute:after{content:var(--tw-content);position:absolute}.ui\:after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--ui-spacing) * -2)}.ui\:after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--ui-spacing) * 0)}.ui\:after\:left-\[calc\(1\/2\*100\%-1px\)\]:after{content:var(--tw-content);left:calc(50% - 1px)}.ui\:after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.ui\:group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.ui\:group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.ui\:last\:mt-0:last-child{margin-top:calc(var(--ui-spacing) * 0)}.ui\:last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:focus-within\:border-ring:focus-within{border-color:var(--ring)}.ui\:focus-within\:ring-\[3px\]:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-within\:ring-ring\/50:focus-within{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-within\:ring-ring\/50:focus-within{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}@media (hover:hover){.ui\:hover\:-translate-y-1:hover{--tw-translate-y:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:hover\:cursor-pointer:hover{cursor:pointer}.ui\:hover\:border:hover{border-style:var(--tw-border-style);border-width:1px}.ui\:hover\:border-background\/60:hover{border-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:border-background\/60:hover{border-color:color-mix(in oklab, var(--background) 60%, transparent)}}.ui\:hover\:border-border:hover{border-color:var(--border)}.ui\:hover\:border-primary:hover{border-color:var(--primary)}.ui\:hover\:bg-accent:hover,.ui\:hover\:bg-accent\/25:hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-accent\/25:hover{background-color:color-mix(in oklab, var(--accent) 25%, transparent)}}.ui\:hover\:bg-amber-500\/10:hover{background-color:var(--ui-color-amber-500)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-amber-500\/10:hover{background-color:color-mix(in oklab, var(--ui-color-amber-500) 10%, transparent)}}.ui\:hover\:bg-background\/10:hover{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-background\/10:hover{background-color:color-mix(in oklab, var(--background) 10%, transparent)}}.ui\:hover\:bg-muted:hover,.ui\:hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.ui\:hover\:bg-primary:hover,.ui\:hover\:bg-primary\/5:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-primary\/5:hover{background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.ui\:hover\:bg-transparent\!:hover{background-color:#0000!important}.ui\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.ui\:hover\:text-background:hover{color:var(--background)}.ui\:hover\:text-foreground:hover{color:var(--foreground)}.ui\:hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.ui\:hover\:text-primary:hover{color:var(--primary)}.ui\:hover\:text-primary-foreground:hover{color:var(--primary-foreground)}.ui\:hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.ui\:hover\:underline:hover{text-decoration-line:underline}.ui\:hover\:opacity-100:hover{opacity:1}.ui\:hover\:shadow-2xs:hover{--tw-shadow:0 1px var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-\[0_0_0_1px_var\(--sidebar-accent\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-accent));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-primary\/5:hover{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:shadow-primary\/5:hover{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--primary) 5%, transparent) var(--tw-shadow-alpha), transparent)}}.ui\:hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.ui\:group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.ui\:hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.ui\:focus\:bg-accent:focus{background-color:var(--accent)}.ui\:focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.ui\:focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.ui\:focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.ui\:focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.ui\:focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.ui\:focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:focus-visible\:ring-ring:focus-visible,.ui\:focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.ui\:focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:focus-visible\:outline-hidden:focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:focus-visible\:outline-hidden:focus-visible{outline-offset:2px;outline:2px solid #0000}}.ui\:focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.ui\:focus-visible\:outline-ring:focus-visible{outline-color:var(--ring)}.ui\:active\:translate-y-px:active{--tw-translate-y:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.ui\:active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.ui\:disabled\:pointer-events-none:disabled{pointer-events:none}.ui\:disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.ui\:disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .ui\:in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .ui\:in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:bg-background\/20{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:bg-background\/20{background-color:color-mix(in oklab, var(--background) 20%, transparent)}}:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:text-background{color:var(--background)}.ui\:has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.ui\:has-data-\[state\=checked\]\:border-primary:has([data-state=checked]){border-color:var(--primary)}.ui\:has-data-\[state\=checked\]\:bg-primary\/5:has([data-state=checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:has-data-\[state\=checked\]\:bg-primary\/5:has([data-state=checked]){background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has([data-slot=input-group-control]:focus-visible){border-color:var(--ring)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-\[3px\]:has([data-slot=input-group-control]:focus-visible){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has([data-slot][aria-invalid=true]){border-color:var(--destructive)}.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:has-\[\>\[data-align\=block-end\]\]\:h-auto:has(>[data-align=block-end]){height:auto}.ui\:has-\[\>\[data-align\=block-end\]\]\:flex-col:has(>[data-align=block-end]){flex-direction:column}.ui\:has-\[\>\[data-align\=block-start\]\]\:h-auto:has(>[data-align=block-start]){height:auto}.ui\:has-\[\>\[data-align\=block-start\]\]\:flex-col:has(>[data-align=block-start]){flex-direction:column}.ui\:has-\[\>\[data-slot\=button-group\]\]\:gap-2:has(>[data-slot=button-group]){gap:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has(>[data-slot=checkbox-group]){gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}.ui\:has-\[\>\[data-slot\=field\]\]\:w-full:has(>[data-slot=field]){width:100%}.ui\:has-\[\>\[data-slot\=field\]\]\:flex-col:has(>[data-slot=field]){flex-direction:column}.ui\:has-\[\>\[data-slot\=field\]\]\:rounded-md:has(>[data-slot=field]){border-radius:calc(var(--radius) - 2px)}.ui\:has-\[\>\[data-slot\=field\]\]\:border:has(>[data-slot=field]){border-style:var(--tw-border-style);border-width:1px}.ui\:has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has(>[data-slot=radio-group]){gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>button\]\:ms-\[-0\.45rem\]:has(>button){margin-inline-start:-.45rem}.ui\:has-\[\>button\]\:me-\[-0\.45rem\]:has(>button){margin-inline-end:-.45rem}.ui\:has-\[\>kbd\]\:ms-\[-0\.35rem\]:has(>kbd){margin-inline-start:-.35rem}.ui\:has-\[\>kbd\]\:me-\[-0\.35rem\]:has(>kbd){margin-inline-end:-.35rem}.ui\:has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing) * 4) 1fr}.ui\:has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>svg\]\:p-0:has(>svg){padding:calc(var(--ui-spacing) * 0)}.ui\:has-\[\>svg\]\:px-2:has(>svg){padding-inline:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--ui-spacing) * 2.5)}.ui\:has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--ui-spacing) * 4)}.ui\:has-\[\>textarea\]\:h-auto:has(>textarea){height:auto}.ui\:aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.ui\:aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.ui\:aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.ui\:aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:aria-selected\:bg-accent[aria-selected=true]{background-color:var(--accent)}.ui\:aria-selected\:text-accent-foreground[aria-selected=true]{color:var(--accent-foreground)}:is(.ui\:\*\*\:data-command-group\:px-2 *)[data-command-group]{padding-inline:calc(var(--ui-spacing) * 2)}:is(.ui\:\*\*\:data-command-input\:h-12 *)[data-command-input]{height:calc(var(--ui-spacing) * 12)}:is(.ui\:\*\*\:data-command-item\:px-2 *)[data-command-item]{padding-inline:calc(var(--ui-spacing) * 2)}:is(.ui\:\*\*\:data-command-item\:py-3 *)[data-command-item]{padding-block:calc(var(--ui-spacing) * 3)}.ui\:data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.ui\:data-disabled\:cursor-not-allowed[data-disabled]{cursor:not-allowed}.ui\:data-disabled\:opacity-50[data-disabled]{opacity:.5}.ui\:data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.ui\:data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.ui\:data-inset\:pl-8[data-inset]{padding-left:calc(var(--ui-spacing) * 8)}.ui\:data-placeholder\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.ui\:data-\[active\=true\]\:bg-accent\/50[data-active=true]{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[active\=true\]\:bg-accent\/50[data-active=true]{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}.ui\:data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.ui\:data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--ui-font-weight-medium);font-weight:var(--ui-font-weight-medium)}.ui\:data-\[active\=true\]\:text-accent-foreground[data-active=true]{color:var(--accent-foreground)}.ui\:data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}@media (hover:hover){.ui\:data-\[active\=true\]\:hover\:bg-accent[data-active=true]:hover{background-color:var(--accent)}}.ui\:data-\[active\=true\]\:focus\:bg-accent[data-active=true]:focus{background-color:var(--accent)}.ui\:data-\[current\=true\]\:bg-muted[data-current=true]{background-color:var(--muted)}.ui\:data-\[disabled\=true\]\:pointer-events-none[data-disabled=true]{pointer-events:none}.ui\:data-\[disabled\=true\]\:opacity-50[data-disabled=true]{opacity:.5}.ui\:data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--ui-spacing) * 8)}.ui\:data-\[invalid\=true\]\:text-destructive[data-invalid=true]{color:var(--destructive)}.ui\:data-\[motion\=from-end\]\:slide-in-from-end-52[data-motion=from-end]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=from-end\]\:slide-in-from-end-52[data-motion=from-end]:where(:dir(rtl),[dir=rtl]),.ui\:data-\[motion\=from-start\]\:slide-in-from-start-52[data-motion=from-start]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(52*var(--spacing)*-1)}.ui\:data-\[motion\=from-start\]\:slide-in-from-start-52[data-motion=from-start]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=to-end\]\:slide-out-to-end-52[data-motion=to-end]:where(:dir(ltr),[dir=ltr]){--tw-exit-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=to-end\]\:slide-out-to-end-52[data-motion=to-end]:where(:dir(rtl),[dir=rtl]),.ui\:data-\[motion\=to-start\]\:slide-out-to-start-52[data-motion=to-start]:where(:dir(ltr),[dir=ltr]){--tw-exit-translate-x:calc(52*var(--spacing)*-1)}.ui\:data-\[motion\=to-start\]\:slide-out-to-start-52[data-motion=to-start]:where(:dir(rtl),[dir=rtl]){--tw-exit-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\^\=from-\]\:animate-in[data-motion^=from-]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[motion\^\=from-\]\:fade-in[data-motion^=from-]{--tw-enter-opacity:0}.ui\:data-\[motion\^\=to-\]\:animate-out[data-motion^=to-]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[motion\^\=to-\]\:fade-out[data-motion^=to-]{--tw-exit-opacity:0}.ui\:data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.ui\:data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.ui\:data-\[orientation\=vertical\]\:h-auto[data-orientation=vertical]{height:auto}.ui\:data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.ui\:data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.ui\:data-\[side\=bottom\]\:-translate-x-1\/2[data-side=bottom]{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:-translate-y-\[calc\(-50\%\+1px\)\][data-side=bottom]{--tw-translate-y:calc(calc(-50% + 1px) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--ui-spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=left\]\:-translate-y-\[calc\(50\%-3px\)\][data-side=left]{--tw-translate-y:calc(calc(50% - 3px) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--ui-spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:translate-x-\[calc\(50\%\+2px\)\][data-side=right]{--tw-translate-x:calc(50% + 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:translate-y-1\/2[data-side=right]{--tw-translate-y:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=top\]\:translate-x-1\/2[data-side=top]{--tw-translate-x:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:translate-y-\[calc\(-50\%\+2px\)\][data-side=top]{--tw-translate-y:calc(-50% + 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.ui\:data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--ui-spacing) * 9)}.ui\:data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--ui-spacing) * 8)}.ui\:data-\[slot\=checkbox-group\]\:gap-3[data-slot=checkbox-group]{gap:calc(var(--ui-spacing) * 3)}:is(.ui\:\*\:data-\[slot\=field\]\:p-4>*)[data-slot=field]{padding:calc(var(--ui-spacing) * 4)}:is(.ui\:\*\:data-\[slot\=field-group\]\:gap-4>*)[data-slot=field-group]{gap:calc(var(--ui-spacing) * 4)}:is(.ui\:\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:ring-0 *)[data-slot=navigation-menu-link]:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.ui\:\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:outline-none *)[data-slot=navigation-menu-link]:focus{--tw-outline-style:none;outline-style:none}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:line-clamp-1 *)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:flex *)[data-slot=select-value]{display:flex}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:items-center *)[data-slot=select-value]{align-items:center}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:gap-2 *)[data-slot=select-value]{gap:calc(var(--ui-spacing) * 2)}.ui\:data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.ui\:data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state=checked]{--tw-translate-x:calc(100% - 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.ui\:data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.ui\:data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.ui\:data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.ui\:data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.ui\:data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.ui\:data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.ui\:data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.ui\:data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.ui\:data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:animate-out:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:fade-out-0:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{--tw-exit-opacity:0}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:zoom-out-95:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{--tw-exit-scale:.95}.ui\:data-\[state\=hidden\]\:animate-out[data-state=hidden]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=hidden\]\:fade-out[data-state=hidden]{--tw-exit-opacity:0}.ui\:data-\[state\=off\]\:text-muted-foreground[data-state=off]{color:var(--muted-foreground)}@media (hover:hover){.ui\:hover\:data-\[state\=off\]\:text-accent-foreground:hover[data-state=off]{color:var(--accent-foreground)}}.ui\:data-\[state\=on\]\:bg-accent[data-state=on]{background-color:var(--accent)}.ui\:data-\[state\=on\]\:bg-transparent[data-state=on]{background-color:#0000}.ui\:data-\[state\=on\]\:text-accent-foreground[data-state=on]{color:var(--accent-foreground)}.ui\:data-\[state\=on\]\:text-muted-foreground[data-state=on]{color:var(--muted-foreground)}@media (hover:hover){.ui\:hover\:data-\[state\=on\]\:text-accent-foreground:hover[data-state=on]{color:var(--accent-foreground)}}.ui\:data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.ui\:data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.ui\:data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.ui\:data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.ui\:data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.ui\:data-\[state\=open\]\:zoom-in-90[data-state=open]{--tw-enter-scale:.9}.ui\:data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.ui\:data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.ui\:data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.ui\:data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.ui\:data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:animate-in:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:fade-in-0:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{--tw-enter-opacity:0}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:zoom-in-95:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{--tw-enter-scale:.95}@media (hover:hover){.ui\:data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.ui\:data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.ui\:data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.ui\:data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--ui-spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.ui\:data-\[state\=visible\]\:animate-in[data-state=visible]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=visible\]\:fade-in[data-state=visible]{--tw-enter-opacity:0}.ui\:data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.ui\:data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.ui\:data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.ui\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.ui\:data-\[variant\=destructive\]\:data-highlighted\:text-destructive[data-variant=destructive][data-highlighted]{color:var(--destructive)}.ui\:data-\[variant\=label\]\:text-sm[data-variant=label]{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:data-\[variant\=legend\]\:text-base[data-variant=legend]{font-size:var(--ui-text-base);line-height:var(--tw-leading,var(--ui-text-base--line-height))}.ui\:data-\[vaul-drawer-direction\=bottom\]\:inset-x-0[data-vaul-drawer-direction=bottom]{inset-inline:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:bottom-0[data-vaul-drawer-direction=bottom]{bottom:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:mt-24[data-vaul-drawer-direction=bottom]{margin-top:calc(var(--ui-spacing) * 24)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:max-h-\[80vh\][data-vaul-drawer-direction=bottom]{max-height:80vh}.ui\:data-\[vaul-drawer-direction\=bottom\]\:rounded-t-lg[data-vaul-drawer-direction=bottom]{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:border-t[data-vaul-drawer-direction=bottom]{border-top-style:var(--tw-border-style);border-top-width:1px}.ui\:data-\[vaul-drawer-direction\=left\]\:inset-y-0[data-vaul-drawer-direction=left]{inset-block:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=left\]\:start-0[data-vaul-drawer-direction=left]{inset-inline-start:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=left\]\:w-3\/4[data-vaul-drawer-direction=left]{width:75%}.ui\:data-\[vaul-drawer-direction\=left\]\:border-e[data-vaul-drawer-direction=left]{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.ui\:data-\[vaul-drawer-direction\=right\]\:inset-y-0[data-vaul-drawer-direction=right]{inset-block:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=right\]\:end-0[data-vaul-drawer-direction=right]{inset-inline-end:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=right\]\:w-3\/4[data-vaul-drawer-direction=right]{width:75%}.ui\:data-\[vaul-drawer-direction\=right\]\:border-s[data-vaul-drawer-direction=right]{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.ui\:data-\[vaul-drawer-direction\=top\]\:inset-x-0[data-vaul-drawer-direction=top]{inset-inline:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=top\]\:top-0[data-vaul-drawer-direction=top]{top:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=top\]\:mb-24[data-vaul-drawer-direction=top]{margin-bottom:calc(var(--ui-spacing) * 24)}.ui\:data-\[vaul-drawer-direction\=top\]\:max-h-\[80vh\][data-vaul-drawer-direction=top]{max-height:80vh}.ui\:data-\[vaul-drawer-direction\=top\]\:rounded-b-lg[data-vaul-drawer-direction=top]{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.ui\:data-\[vaul-drawer-direction\=top\]\:border-b[data-vaul-drawer-direction=top]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:nth-last-2\:-mt-1:nth-last-child(2){margin-top:calc(var(--ui-spacing) * -1)}@media (min-width:40rem){.ui\:sm\:flex{display:flex}.ui\:sm\:w-48{width:calc(var(--ui-spacing) * 48)}.ui\:sm\:max-w-lg{max-width:var(--ui-container-lg)}.ui\:sm\:max-w-sm{max-width:var(--ui-container-sm)}.ui\:sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:sm\:flex-row{flex-direction:row}.ui\:sm\:justify-end{justify-content:flex-end}.ui\:sm\:gap-2\.5{gap:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:p-6{padding:calc(var(--ui-spacing) * 6)}.ui\:sm\:py-32{padding-block:calc(var(--ui-spacing) * 32)}.ui\:sm\:pr-2\.5{padding-right:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:pl-2\.5{padding-left:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:sm\:text-xl{font-size:var(--ui-text-xl);line-height:var(--tw-leading,var(--ui-text-xl--line-height))}.ui\:data-\[vaul-drawer-direction\=left\]\:sm\:max-w-sm[data-vaul-drawer-direction=left],.ui\:data-\[vaul-drawer-direction\=right\]\:sm\:max-w-sm[data-vaul-drawer-direction=right]{max-width:var(--ui-container-sm)}}@media (min-width:48rem){.ui\:md\:block{display:block}.ui\:md\:flex{display:flex}.ui\:md\:w-52{width:calc(var(--ui-spacing) * 52)}.ui\:md\:w-full{width:100%}.ui\:md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:md\:flex-col{flex-direction:column}.ui\:md\:flex-row{flex-direction:row}.ui\:md\:overflow-x-visible{overflow-x:visible}.ui\:md\:overflow-y-visible{overflow-y:visible}.ui\:md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:md\:p-12{padding:calc(var(--ui-spacing) * 12)}.ui\:md\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:md\:pb-24{padding-bottom:calc(var(--ui-spacing) * 24)}.ui\:md\:text-6xl{font-size:var(--ui-text-6xl);line-height:var(--tw-leading,var(--ui-text-6xl--line-height))}.ui\:md\:text-sm{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:md\:opacity-0{opacity:0}.ui\:md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.ui\:peer)[data-variant=inset]~*){margin:calc(var(--ui-spacing) * 2)}.ui\:md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.ui\:peer)[data-variant=inset]~*){margin-left:calc(var(--ui-spacing) * 0)}.ui\:md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.ui\:peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.ui\:md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.ui\:peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.ui\:peer)[data-variant=inset]~*):is(:where(.ui\:peer)[data-state=collapsed]~*){margin-left:calc(var(--ui-spacing) * 2)}.ui\:md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (min-width:64rem){.ui\:lg\:block{display:block}.ui\:lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.ui\:lg\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:lg\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:lg\:text-6xl{font-size:var(--ui-text-6xl);line-height:var(--tw-leading,var(--ui-text-6xl--line-height))}.ui\:lg\:text-7xl{font-size:var(--ui-text-7xl);line-height:var(--tw-leading,var(--ui-text-7xl--line-height))}}.ui\:dark\:scale-0:where(.dark,.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:dark\:scale-100:where(.dark,.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:dark\:-rotate-90:where(.dark,.dark *){rotate:-90deg}.ui\:dark\:rotate-0:where(.dark,.dark *){rotate:none}.ui\:dark\:border-amber-900:where(.dark,.dark *){border-color:var(--ui-color-amber-900)}.ui\:dark\:border-input:where(.dark,.dark *){border-color:var(--input)}.ui\:dark\:border-sky-900:where(.dark,.dark *){border-color:var(--ui-color-sky-900)}.ui\:dark\:border-slate-800:where(.dark,.dark *){border-color:var(--ui-color-slate-800)}.ui\:dark\:bg-amber-500:where(.dark,.dark *){background-color:var(--ui-color-amber-500)}.ui\:dark\:bg-amber-950:where(.dark,.dark *){background-color:var(--ui-color-amber-950)}.ui\:dark\:bg-destructive\/60:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-destructive\/60:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 60%, transparent)}}.ui\:dark\:bg-destructive\/70:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-destructive\/70:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 70%, transparent)}}.ui\:dark\:bg-emerald-500:where(.dark,.dark *){background-color:var(--ui-color-emerald-500)}.ui\:dark\:bg-input\/30:where(.dark,.dark *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-input\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.ui\:dark\:bg-sky-950:where(.dark,.dark *){background-color:var(--ui-color-sky-950)}.ui\:dark\:bg-slate-950:where(.dark,.dark *){background-color:var(--ui-color-slate-950)}.ui\:dark\:bg-transparent:where(.dark,.dark *){background-color:#0000}.ui\:dark\:fill-amber-400:where(.dark,.dark *){fill:var(--ui-color-amber-400)}.ui\:dark\:text-amber-50:where(.dark,.dark *){color:var(--ui-color-amber-50)}.ui\:dark\:text-amber-300:where(.dark,.dark *){color:var(--ui-color-amber-300)}.ui\:dark\:text-amber-400:where(.dark,.dark *){color:var(--ui-color-amber-400)}.ui\:dark\:text-amber-500\/40:where(.dark,.dark *){color:var(--ui-color-amber-500)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:text-amber-500\/40:where(.dark,.dark *){color:color-mix(in oklab, var(--ui-color-amber-500) 40%, transparent)}}.ui\:dark\:text-amber-950:where(.dark,.dark *){color:var(--ui-color-amber-950)}.ui\:dark\:text-emerald-950:where(.dark,.dark *){color:var(--ui-color-emerald-950)}.ui\:dark\:text-muted-foreground:where(.dark,.dark *){color:var(--muted-foreground)}.ui\:dark\:text-sky-50:where(.dark,.dark *){color:var(--ui-color-sky-50)}.ui\:dark\:text-white:where(.dark,.dark *){color:var(--ui-color-white)}@media (hover:hover){.ui\:dark\:hover\:border-input:where(.dark,.dark *):hover{border-color:var(--input)}.ui\:dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}}.ui\:dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}:where([data-slot=tooltip-content]) .ui\:dark\:in-data-\[slot\=tooltip-content\]\:bg-background\/10:where(.dark,.dark *){background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){:where([data-slot=tooltip-content]) .ui\:dark\:in-data-\[slot\=tooltip-content\]\:bg-background\/10:where(.dark,.dark *){background-color:color-mix(in oklab, var(--background) 10%, transparent)}}.ui\:dark\:has-data-\[state\=checked\]\:bg-primary\/10:where(.dark,.dark *):has([data-state=checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:has-data-\[state\=checked\]\:bg-primary\/10:where(.dark,.dark *):has([data-state=checked]){background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.ui\:dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.ui\:dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.ui\:dark\:data-\[state\=active\]\:border-input:where(.dark,.dark *)[data-state=active]{border-color:var(--input)}.ui\:dark\:data-\[state\=active\]\:bg-input\/30:where(.dark,.dark *)[data-state=active]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:data-\[state\=active\]\:bg-input\/30:where(.dark,.dark *)[data-state=active]{background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.ui\:dark\:data-\[state\=active\]\:text-foreground:where(.dark,.dark *)[data-state=active]{color:var(--foreground)}.ui\:dark\:data-\[state\=checked\]\:bg-primary:where(.dark,.dark *)[data-state=checked]{background-color:var(--primary)}.ui\:dark\:data-\[state\=checked\]\:bg-primary-foreground:where(.dark,.dark *)[data-state=checked]{background-color:var(--primary-foreground)}.ui\:dark\:data-\[state\=unchecked\]\:bg-foreground:where(.dark,.dark *)[data-state=unchecked]{background-color:var(--foreground)}.ui\:dark\:data-\[state\=unchecked\]\:bg-input\/80:where(.dark,.dark *)[data-state=unchecked]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:data-\[state\=unchecked\]\:bg-input\/80:where(.dark,.dark *)[data-state=unchecked]{background-color:color-mix(in oklab, var(--input) 80%, transparent)}}.ui\:\[\&_\.lc-area-path\]\:opacity-100 .lc-area-path{opacity:1}.ui\:\[\&_\.lc-axis-tick\]\:stroke-0 .lc-axis-tick{stroke-width:0}.ui\:\[\&_\.lc-axis-tick-label\]\:fill-muted-foreground .lc-axis-tick-label{fill:var(--muted-foreground)}.ui\:\[\&_\.lc-axis-tick-label\]\:font-normal .lc-axis-tick-label{--tw-font-weight:var(--ui-font-weight-normal);font-weight:var(--ui-font-weight-normal)}.ui\:\[\&_\.lc-grid-x-radial-circle\]\:stroke-border .lc-grid-x-radial-circle,.ui\:\[\&_\.lc-grid-x-radial-line\]\:stroke-border .lc-grid-x-radial-line,.ui\:\[\&_\.lc-grid-y-radial-circle\]\:stroke-border .lc-grid-y-radial-circle,.ui\:\[\&_\.lc-grid-y-radial-line\]\:stroke-border .lc-grid-y-radial-line{stroke:var(--border)}.ui\:\[\&_\.lc-highlight-line\]\:stroke-0 .lc-highlight-line{stroke-width:0}.ui\:\[\&_\.lc-highlight-line\]\:opacity-100 .lc-highlight-line,.ui\:\[\&_\.lc-highlight-point\]\:opacity-100 .lc-highlight-point{opacity:1}.ui\:\[\&_\.lc-labels-text\:not\(\[fill\]\)\]\:fill-foreground .lc-labels-text:not([fill]){fill:var(--foreground)}.ui\:\[\&_\.lc-layout-svg-g\]\:fill-transparent .lc-layout-svg-g{fill:#0000}.ui\:\[\&_\.lc-legend-swatch\]\:size-2\.5 .lc-legend-swatch{width:calc(var(--ui-spacing) * 2.5);height:calc(var(--ui-spacing) * 2.5)}.ui\:\[\&_\.lc-legend-swatch\]\:rounded-\[2px\] .lc-legend-swatch{border-radius:2px}.ui\:\[\&_\.lc-legend-swatch-button\]\:items-center .lc-legend-swatch-button{align-items:center}.ui\:\[\&_\.lc-legend-swatch-button\]\:gap-1\.5 .lc-legend-swatch-button{gap:calc(var(--ui-spacing) * 1.5)}.ui\:\[\&_\.lc-legend-swatch-group\]\:items-center .lc-legend-swatch-group{align-items:center}.ui\:\[\&_\.lc-legend-swatch-group\]\:gap-4 .lc-legend-swatch-group{gap:calc(var(--ui-spacing) * 4)}.ui\:\[\&_\.lc-line\]\:stroke-border\/50 .lc-line{stroke:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:\[\&_\.lc-line\]\:stroke-border\/50 .lc-line{stroke:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:\[\&_\.lc-root-container\]\:w-full .lc-root-container{width:100%}.ui\:\[\&_\.lc-rule-x-line\:not\(\.lc-grid-x-rule\)\]\:stroke-0 .lc-rule-x-line:not(.lc-grid-x-rule),.ui\:\[\&_\.lc-rule-y-line\:not\(\.lc-grid-y-rule\)\]\:stroke-0 .lc-rule-y-line:not(.lc-grid-y-rule){stroke-width:0}.ui\:\[\&_\.lc-spline-path\]\:opacity-100 .lc-spline-path{opacity:1}.ui\:\[\&_\.lc-text\]\:text-xs .lc-text{font-size:var(--ui-text-xs);line-height:var(--tw-leading,var(--ui-text-xs--line-height))}.ui\:\[\&_\.lc-text-svg\]\:overflow-visible .lc-text-svg{overflow:visible}.ui\:\[\&_\.lc-tooltip-rects-g\]\:fill-transparent .lc-tooltip-rects-g{fill:#0000}.ui\:\[\&_\.stroke-white\]\:stroke-transparent .stroke-white{stroke:#0000}.ui\:\[\&_\[data-command-group\]\:not\(\[hidden\]\)_\~\[data-command-group\]\]\:pt-0 [data-command-group]:not([hidden])~[data-command-group]{padding-top:calc(var(--ui-spacing) * 0)}.ui\:\[\&_\[data-command-input-wrapper\]_svg\]\:h-5 [data-command-input-wrapper] svg{height:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-input-wrapper\]_svg\]\:w-5 [data-command-input-wrapper] svg{width:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-item\]_svg\]\:h-5 [data-command-item] svg{height:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-item\]_svg\]\:w-5 [data-command-item] svg{width:calc(var(--ui-spacing) * 5)}.ui\:\[\&_img\]\:size-full img{width:100%;height:100%}.ui\:\[\&_img\]\:object-cover img{object-fit:cover}.ui\:\[\&_p\]\:leading-relaxed p{--tw-leading:var(--ui-leading-relaxed);line-height:var(--ui-leading-relaxed)}.ui\:\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.ui\:\[\&_svg\]\:size-8\! svg{width:calc(var(--ui-spacing) * 8)!important;height:calc(var(--ui-spacing) * 8)!important}.ui\:\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.ui\:\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--ui-spacing) * 3);height:calc(var(--ui-spacing) * 3)}.ui\:\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.ui\:\[\&_svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-4 svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&_svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-6 svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 6);height:calc(var(--ui-spacing) * 6)}.ui\:\[\&_text\]\:stroke-transparent text{stroke:#0000}.ui\:\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.ui\:\[\&\+\[data-slot\=item-content\]\]\:flex-none+[data-slot=item-content]{flex:none}.ui\:\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--ui-spacing) * 0)}.ui\:\[\.border-b\]\:pb-3.border-b{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--ui-spacing) * 6)}.ui\:\[\.border-t\]\:pt-3.border-t{padding-top:calc(var(--ui-spacing) * 3)}.ui\:\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--ui-spacing) * 6)}.ui\:\[a\]\:transition-colors:is(a){transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}@media (hover:hover){.ui\:\[a\]\:hover\:bg-accent\/50:is(a):hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:\[a\]\:hover\:bg-accent\/50:is(a):hover{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}}:is(.ui\:\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.ui\:\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.ui\:\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--ui-spacing) * 2)}:is(.ui\:data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive\![data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.ui\:\[\&\>\*\]\:focus-visible\:relative>:focus-visible{position:relative}.ui\:\[\&\>\*\]\:focus-visible\:z-10>:focus-visible{z-index:10}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:rounded-s-none>:not(:first-child){border-start-start-radius:0;border-end-start-radius:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:rounded-t-none>:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:border-s-0>:not(:first-child){border-inline-start-style:var(--tw-border-style);border-inline-start-width:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:border-t-0>:not(:first-child){border-top-style:var(--tw-border-style);border-top-width:0}.ui\:\[\&\>\*\:not\(\:last-child\)\]\:rounded-e-none>:not(:last-child){border-start-end-radius:0;border-end-end-radius:0}.ui\:\[\&\>\*\:not\(\:last-child\)\]\:rounded-b-none>:not(:last-child){border-bottom-right-radius:0;border-bottom-left-radius:0}.ui\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}.ui\:has-\[select\[aria-hidden\=true\]\:last-child\]\:\[\&\>\[data-slot\=select-trigger\]\:last-of-type\]\:rounded-e-md:has(:is(select[aria-hidden=true]:last-child))>[data-slot=select-trigger]:last-of-type{border-start-end-radius:calc(var(--radius) - 2px);border-end-end-radius:calc(var(--radius) - 2px)}.ui\:\[\&\>\[data-slot\=select-trigger\]\:not\(\[class\*\=\'ui\:w-\'\]\)\]\:w-fit>[data-slot=select-trigger]:not([class*=ui\:w-]){width:fit-content}.ui\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.ui\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}.ui\:\[\&\>a\]\:underline>a{text-decoration-line:underline}.ui\:\[\&\>a\]\:underline-offset-4>a{text-underline-offset:4px}.ui\:\[\&\>a\:hover\]\:text-primary>a:hover{color:var(--primary)}.ui\:\[\&\>button\]\:hidden>button{display:none}.ui\:\[\&\>input\]\:flex-1>input{flex:1}.ui\:has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has(>[data-align=block-end])>input{padding-top:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has(>[data-align=block-start])>input{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pe-2:has(>[data-align=inline-end])>input{padding-inline-end:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:ps-2:has(>[data-align=inline-start])>input{padding-inline-start:calc(var(--ui-spacing) * 2)}.ui\:\[\&\>kbd\]\:rounded-\[calc\(var\(--radius\)-5px\)\]>kbd{border-radius:calc(var(--radius) - 5px)}.ui\:\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui\:\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.ui\:\[\&\>svg\]\:size-2\.5>svg{width:calc(var(--ui-spacing) * 2.5);height:calc(var(--ui-spacing) * 2.5)}.ui\:\[\&\>svg\]\:size-3>svg{width:calc(var(--ui-spacing) * 3);height:calc(var(--ui-spacing) * 3)}.ui\:\[\&\>svg\]\:size-3\.5>svg{width:calc(var(--ui-spacing) * 3.5);height:calc(var(--ui-spacing) * 3.5)}.ui\:\[\&\>svg\]\:size-4>svg{width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.ui\:\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--ui-spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:\[\&\>svg\]\:text-current>svg{color:currentColor}.ui\:\[\&\>svg\]\:text-muted-foreground>svg{color:var(--muted-foreground)}.ui\:\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.ui\:\[\&\>svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-3\.5>svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 3.5);height:calc(var(--ui-spacing) * 3.5)}.ui\:\[\&\>svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-4>svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}@media (hover:hover){:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}}.ui\:\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}[data-side=left][data-collapsible=offcanvas] .ui\:\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--ui-spacing) * -2)}[data-side=left][data-state=collapsed] .ui\:\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .ui\:\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--ui-spacing) * -2)}[data-side=right][data-state=collapsed] .ui\:\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-variant=legend]+.ui\:\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5{margin-top:calc(var(--ui-spacing) * -1.5)}@media (hover:hover){a.ui\:\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.ui\:\[a\&\]\:hover\:bg-amber-600\/90:hover{background-color:var(--ui-color-amber-600)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-amber-600\/90:hover{background-color:color-mix(in oklab, var(--ui-color-amber-600) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--destructive) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-emerald-600\/90:hover{background-color:var(--ui-color-emerald-600)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-emerald-600\/90:hover{background-color:color-mix(in oklab, var(--ui-color-emerald-600) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-primary\/30:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-primary\/30:hover{background-color:color-mix(in oklab, var(--primary) 30%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--primary) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab, var(--secondary) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}a.ui\:\[a\&\]\:hover\:text-primary:hover{color:var(--primary)}a.ui\:\[a\&\]\:hover\:underline:hover{text-decoration-line:underline}a.ui\:dark\:\[a\&\]\:hover\:bg-accent\/50:where(.dark,.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){a.ui\:dark\:\[a\&\]\:hover\:bg-accent\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}@keyframes border-beam{to{offset-distance:100%}}@keyframes meteor{0%{opacity:1;transform:rotate(215deg)translate(0)}70%{opacity:1}to{opacity:0;transform:rotate(215deg)translate(-500px)}}@keyframes shimmer-marketing{0%,90%,to{background-position:calc(-100% - var(--shimmer-width)) 0}30%,60%{background-position:calc(100% + var(--shimmer-width)) 0}}@keyframes spin-around{0%{transform:translateZ(0)rotate(0)}15%,35%{transform:translateZ(0)rotate(90deg)}65%,85%{transform:translateZ(0)rotate(270deg)}to{transform:translateZ(0)rotate(360deg)}}@keyframes magicslide{to{transform:translate(calc(100cqw - 100%))}}:root{--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(48.8% .243 264.376);--primary-foreground:oklch(97% .014 254.604);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(80.9% .105 251.813);--chart-2:oklch(62.3% .214 259.815);--chart-3:oklch(54.6% .245 262.881);--chart-4:oklch(48.8% .243 264.376);--chart-5:oklch(42.4% .199 265.638);--radius:.625rem;--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(54.6% .245 262.881);--sidebar-primary-foreground:oklch(97% .014 254.604);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(42.4% .199 265.638);--primary-foreground:oklch(97% .014 254.604);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(80.9% .105 251.813);--chart-2:oklch(62.3% .214 259.815);--chart-3:oklch(54.6% .245 262.881);--chart-4:oklch(48.8% .243 264.376);--chart-5:oklch(42.4% .199 265.638);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(62.3% .214 259.815);--sidebar-primary-foreground:oklch(97% .014 254.604);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}@property --border-angle{syntax:"";inherits:false;initial-value:0deg}@keyframes border{to{--border-angle:360deg}}@keyframes flip{to{transform:rotate(360deg)}}@keyframes kitrotate{to{transform:rotate(90deg)}}@keyframes shine{0%{background-position:0 0}to{background-position:-200% 0}}.animate-gradient-border{background:linear-gradient(45deg, white, white) padding-box, conic-gradient(from var(--border-angle), #94a3b84d 75%, var(--primary,#1d4ee2) 82%, var(--primary-foreground,white) 88%, var(--primary,#1d4ee2) 92%, #94a3b84d 100%) border-box;border:1px solid #0000;transition:--border-angle 3s ease-in-out}.animate-gradient-border:hover{animation:4s ease-in-out infinite border}.dark.animate-gradient-border{background:linear-gradient(45deg, #000, #000) padding-box, conic-gradient(from var(--border-angle), #4755697a 75%, var(--primary,#1d4ee2) 82%, var(--primary-foreground,black) 88%, var(--primary,#1d4ee2) 92%, #4755697a 100%) border-box}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}to{height:0}} \ No newline at end of file +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-outline-style:solid;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--border-angle:0deg}}}@layer theme{:root,:host{--ui-font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--ui-font-serif:ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;--ui-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--ui-color-red-50:oklch(97.1% .013 17.38);--ui-color-red-500:oklch(63.7% .237 25.331);--ui-color-red-600:oklch(57.7% .245 27.325);--ui-color-red-700:oklch(50.5% .213 27.518);--ui-color-orange-500:oklch(70.5% .213 47.604);--ui-color-amber-50:oklch(98.7% .022 95.277);--ui-color-amber-200:oklch(92.4% .12 95.746);--ui-color-amber-300:oklch(87.9% .169 91.605);--ui-color-amber-400:oklch(82.8% .189 84.429);--ui-color-amber-500:oklch(76.9% .188 70.08);--ui-color-amber-600:oklch(66.6% .179 58.318);--ui-color-amber-900:oklch(41.4% .112 45.904);--ui-color-amber-950:oklch(27.9% .077 45.635);--ui-color-yellow-500:oklch(79.5% .184 86.047);--ui-color-green-500:oklch(72.3% .219 149.579);--ui-color-emerald-50:oklch(97.9% .021 166.113);--ui-color-emerald-300:oklch(84.5% .143 164.978);--ui-color-emerald-400:oklch(76.5% .177 163.223);--ui-color-emerald-500:oklch(69.6% .17 162.48);--ui-color-emerald-600:oklch(59.6% .145 163.225);--ui-color-emerald-950:oklch(26.2% .051 172.552);--ui-color-sky-50:oklch(97.7% .013 236.62);--ui-color-sky-200:oklch(90.1% .058 230.902);--ui-color-sky-900:oklch(39.1% .09 240.876);--ui-color-sky-950:oklch(29.3% .066 243.157);--ui-color-blue-600:oklch(54.6% .245 262.881);--ui-color-violet-500:oklch(60.6% .25 292.717);--ui-color-pink-500:oklch(65.6% .241 354.308);--ui-color-rose-500:oklch(64.5% .246 16.439);--ui-color-slate-500:oklch(55.4% .046 257.417);--ui-color-slate-800:oklch(27.9% .041 260.031);--ui-color-slate-950:oklch(12.9% .042 264.695);--ui-color-gray-200:oklch(92.8% .006 264.531);--ui-color-neutral-800:oklch(26.9% 0 0);--ui-color-black:#000;--ui-color-white:#fff;--ui-spacing:.25rem;--ui-container-xs:20rem;--ui-container-sm:24rem;--ui-container-md:28rem;--ui-container-lg:32rem;--ui-container-xl:36rem;--ui-container-2xl:42rem;--ui-container-3xl:48rem;--ui-container-4xl:56rem;--ui-container-5xl:64rem;--ui-container-7xl:80rem;--ui-text-xs:.75rem;--ui-text-xs--line-height:calc(1 / .75);--ui-text-sm:.875rem;--ui-text-sm--line-height:calc(1.25 / .875);--ui-text-base:1rem;--ui-text-base--line-height:calc(1.5 / 1);--ui-text-lg:1.125rem;--ui-text-lg--line-height:calc(1.75 / 1.125);--ui-text-xl:1.25rem;--ui-text-xl--line-height:calc(1.75 / 1.25);--ui-text-2xl:1.5rem;--ui-text-2xl--line-height:calc(2 / 1.5);--ui-text-3xl:1.875rem;--ui-text-3xl--line-height:calc(2.25 / 1.875);--ui-text-4xl:2.25rem;--ui-text-4xl--line-height:calc(2.5 / 2.25);--ui-text-5xl:3rem;--ui-text-5xl--line-height:1;--ui-text-6xl:3.75rem;--ui-text-6xl--line-height:1;--ui-text-7xl:4.5rem;--ui-text-7xl--line-height:1;--ui-font-weight-light:300;--ui-font-weight-normal:400;--ui-font-weight-medium:500;--ui-font-weight-semibold:600;--ui-font-weight-bold:700;--ui-font-weight-black:900;--ui-tracking-tighter:-.05em;--ui-tracking-tight:-.025em;--ui-tracking-wide:.025em;--ui-tracking-wider:.05em;--ui-tracking-widest:.1em;--ui-leading-tight:1.25;--ui-leading-snug:1.375;--ui-leading-normal:1.5;--ui-leading-relaxed:1.625;--ui-radius-xs:.125rem;--ui-radius-2xl:1rem;--ui-radius-3xl:1.5rem;--ui-ease-in-out:cubic-bezier(.4, 0, .2, 1);--ui-animate-spin:spin 1s linear infinite;--ui-animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--ui-blur-sm:8px;--ui-blur-xl:24px;--ui-aspect-video:16 / 9;--ui-default-transition-duration:.15s;--ui-default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--ui-default-mono-font-family:var(--ui-font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--font-cio,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--ui-default-font-feature-settings,normal);font-variation-settings:var(--ui-default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--ui-default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--ui-default-mono-font-feature-settings,normal);font-variation-settings:var(--ui-default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){*{outline-color:color-mix(in oklab, var(--ring) 50%, transparent)}}body{background-color:var(--background);font-family:var(--font-sans);color:var(--foreground)}button,input,select,optgroup,textarea{font-family:var(--font-sans)}::file-selector-button{font-family:var(--font-sans)}.toaster{--normal-bg:var(--color-popover);--normal-text:var(--color-popover-foreground);--normal-border:var(--color-border)}}@layer components{.cio-marketing-border-beam{pointer-events:none;border-radius:inherit;border:calc(var(--border-width) * 1px) solid transparent;-webkit-mask-composite:source-in;-webkit-mask-source-type:auto,auto;position:absolute;inset:0;-webkit-mask-image:linear-gradient(#0000,#0000),linear-gradient(#fff,#fff);mask-image:linear-gradient(#0000,#0000),linear-gradient(#fff,#fff);-webkit-mask-position:0 0,0 0;mask-position:0 0,0 0;-webkit-mask-size:auto,auto;mask-size:auto,auto;-webkit-mask-repeat:repeat,repeat;mask-repeat:repeat,repeat;-webkit-mask-clip:padding-box,border-box;mask-clip:padding-box,border-box;-webkit-mask-origin:border-box,border-box;mask-origin:border-box,border-box;-webkit-mask-composite:source-in;mask-composite:intersect;-webkit-mask-source-type:auto,auto;mask-mode:match-source,match-source}.cio-marketing-border-beam:after{content:"";aspect-ratio:1;width:calc(var(--size) * 1px);animation-delay:var(--delay);background:linear-gradient(to left, var(--color-from), var(--color-to), transparent);offset-anchor:calc(var(--anchor) * 1%) 50%;offset-path:rect(0 auto auto 0 round calc(var(--size) * 1px));animation:border-beam calc(var(--duration) * 1s) infinite linear;position:absolute}}@layer utilities{.ui\:\@container\/field-group{container:field-group/inline-size}.ui\:\@container-\[size\]{container-type:size}.ui\:pointer-events-none{pointer-events:none}.ui\:sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.ui\:absolute{position:absolute}.ui\:fixed{position:fixed}.ui\:relative{position:relative}.ui\:sticky{position:sticky}.ui\:-inset-full{inset:-100%}.ui\:inset-\(--cut\){inset:var(--cut)}.ui\:inset-0{inset:calc(var(--ui-spacing) * 0)}.ui\:inset-x-0{inset-inline:calc(var(--ui-spacing) * 0)}.ui\:inset-y-0{inset-block:calc(var(--ui-spacing) * 0)}.ui\:start-0{inset-inline-start:calc(var(--ui-spacing) * 0)}.ui\:end-4{inset-inline-end:calc(var(--ui-spacing) * 4)}.ui\:-top-2{top:calc(var(--ui-spacing) * -2)}.ui\:-top-3{top:calc(var(--ui-spacing) * -3)}.ui\:top-0{top:calc(var(--ui-spacing) * 0)}.ui\:top-1\.5{top:calc(var(--ui-spacing) * 1.5)}.ui\:top-1\/2{top:50%}.ui\:top-2{top:calc(var(--ui-spacing) * 2)}.ui\:top-3\.5{top:calc(var(--ui-spacing) * 3.5)}.ui\:top-4{top:calc(var(--ui-spacing) * 4)}.ui\:top-12{top:calc(var(--ui-spacing) * 12)}.ui\:top-14{top:calc(var(--ui-spacing) * 14)}.ui\:top-\[-12px\]{top:-12px}.ui\:top-\[50\%\]{top:50%}.ui\:top-\[60\%\]{top:60%}.ui\:top-full{top:100%}.ui\:top-px{top:1px}.ui\:-right-4{right:calc(var(--ui-spacing) * -4)}.ui\:right-0{right:calc(var(--ui-spacing) * 0)}.ui\:right-1{right:calc(var(--ui-spacing) * 1)}.ui\:right-2{right:calc(var(--ui-spacing) * 2)}.ui\:right-3{right:calc(var(--ui-spacing) * 3)}.ui\:right-4{right:calc(var(--ui-spacing) * 4)}.ui\:right-9{right:calc(var(--ui-spacing) * 9)}.ui\:right-\[-12px\]{right:-12px}.ui\:-bottom-px{bottom:-1px}.ui\:bottom-0{bottom:calc(var(--ui-spacing) * 0)}.ui\:bottom-2{bottom:calc(var(--ui-spacing) * 2)}.ui\:bottom-full{bottom:100%}.ui\:left-0{left:calc(var(--ui-spacing) * 0)}.ui\:left-1\/2{left:50%}.ui\:left-2{left:calc(var(--ui-spacing) * 2)}.ui\:left-\[50\%\]{left:50%}.ui\:left-\[calc\(50\%-3rem\)\]{left:calc(50% - 3rem)}.ui\:left-auto{left:auto}.ui\:isolate{isolation:isolate}.ui\:-z-10{z-index:calc(10 * -1)}.ui\:-z-20{z-index:calc(20 * -1)}.ui\:-z-30{z-index:calc(30 * -1)}.ui\:z-0{z-index:0}.ui\:z-1{z-index:1}.ui\:z-10{z-index:10}.ui\:z-20{z-index:20}.ui\:z-30{z-index:30}.ui\:z-40{z-index:40}.ui\:z-50{z-index:50}.ui\:z-100{z-index:100}.ui\:z-200{z-index:200}.ui\:z-999{z-index:999}.ui\:z-\[1\]{z-index:1}.ui\:z-\[110\]{z-index:110}.ui\:order-first{order:-9999}.ui\:order-last{order:9999}.ui\:col-start-2{grid-column-start:2}.ui\:row-span-2{grid-row:span 2/span 2}.ui\:row-start-1{grid-row-start:1}.ui\:m-0{margin:calc(var(--ui-spacing) * 0)}.ui\:m-0\!{margin:calc(var(--ui-spacing) * 0)!important}.ui\:-mx-1{margin-inline:calc(var(--ui-spacing) * -1)}.ui\:mx-0\.5{margin-inline:calc(var(--ui-spacing) * .5)}.ui\:mx-auto{margin-inline:auto}.ui\:my-0{margin-block:calc(var(--ui-spacing) * 0)}.ui\:my-0\.5{margin-block:calc(var(--ui-spacing) * .5)}.ui\:my-1{margin-block:calc(var(--ui-spacing) * 1)}.ui\:my-2{margin-block:calc(var(--ui-spacing) * 2)}.ui\:my-3{margin-block:calc(var(--ui-spacing) * 3)}.ui\:my-4{margin-block:calc(var(--ui-spacing) * 4)}.ui\:my-10{margin-block:calc(var(--ui-spacing) * 10)}.ui\:ms-1{margin-inline-start:calc(var(--ui-spacing) * 1)}.ui\:ms-4{margin-inline-start:calc(var(--ui-spacing) * 4)}.ui\:mt-0\.5{margin-top:calc(var(--ui-spacing) * .5)}.ui\:mt-1{margin-top:calc(var(--ui-spacing) * 1)}.ui\:mt-1\.5{margin-top:calc(var(--ui-spacing) * 1.5)}.ui\:mt-2{margin-top:calc(var(--ui-spacing) * 2)}.ui\:mt-3{margin-top:calc(var(--ui-spacing) * 3)}.ui\:mt-4{margin-top:calc(var(--ui-spacing) * 4)}.ui\:mt-6{margin-top:calc(var(--ui-spacing) * 6)}.ui\:mt-8{margin-top:calc(var(--ui-spacing) * 8)}.ui\:mt-9{margin-top:calc(var(--ui-spacing) * 9)}.ui\:mt-10{margin-top:calc(var(--ui-spacing) * 10)}.ui\:mt-12{margin-top:calc(var(--ui-spacing) * 12)}.ui\:mt-auto{margin-top:auto}.ui\:mr-1{margin-right:calc(var(--ui-spacing) * 1)}.ui\:mr-2{margin-right:calc(var(--ui-spacing) * 2)}.ui\:mb-1{margin-bottom:calc(var(--ui-spacing) * 1)}.ui\:mb-2{margin-bottom:calc(var(--ui-spacing) * 2)}.ui\:mb-3{margin-bottom:calc(var(--ui-spacing) * 3)}.ui\:mb-4{margin-bottom:calc(var(--ui-spacing) * 4)}.ui\:mb-6{margin-bottom:calc(var(--ui-spacing) * 6)}.ui\:mb-8{margin-bottom:calc(var(--ui-spacing) * 8)}.ui\:mb-10{margin-bottom:calc(var(--ui-spacing) * 10)}.ui\:mb-12{margin-bottom:calc(var(--ui-spacing) * 12)}.ui\:mb-16{margin-bottom:calc(var(--ui-spacing) * 16)}.ui\:ml-1{margin-left:calc(var(--ui-spacing) * 1)}.ui\:ml-2{margin-left:calc(var(--ui-spacing) * 2)}.ui\:ml-2\.5{margin-left:calc(var(--ui-spacing) * 2.5)}.ui\:ml-auto{margin-left:auto}.ui\:box-border{box-sizing:border-box}.ui\:line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.ui\:line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.ui\:line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.ui\:block{display:block}.ui\:flex{display:flex}.ui\:grid{display:grid}.ui\:hidden{display:none}.ui\:inline-block{display:inline-block}.ui\:inline-flex{display:inline-flex}.ui\:field-sizing-content{field-sizing:content}.ui\:aspect-\[1\],.ui\:aspect-square{aspect-ratio:1}.ui\:aspect-video{aspect-ratio:var(--ui-aspect-video)}.ui\:size-2{width:calc(var(--ui-spacing) * 2);height:calc(var(--ui-spacing) * 2)}.ui\:size-2\.5{width:calc(var(--ui-spacing) * 2.5);height:calc(var(--ui-spacing) * 2.5)}.ui\:size-3{width:calc(var(--ui-spacing) * 3);height:calc(var(--ui-spacing) * 3)}.ui\:size-3\.5{width:calc(var(--ui-spacing) * 3.5);height:calc(var(--ui-spacing) * 3.5)}.ui\:size-4{width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:size-5{width:calc(var(--ui-spacing) * 5);height:calc(var(--ui-spacing) * 5)}.ui\:size-6{width:calc(var(--ui-spacing) * 6);height:calc(var(--ui-spacing) * 6)}.ui\:size-7{width:calc(var(--ui-spacing) * 7);height:calc(var(--ui-spacing) * 7)}.ui\:size-8{width:calc(var(--ui-spacing) * 8);height:calc(var(--ui-spacing) * 8)}.ui\:size-9{width:calc(var(--ui-spacing) * 9);height:calc(var(--ui-spacing) * 9)}.ui\:size-10{width:calc(var(--ui-spacing) * 10);height:calc(var(--ui-spacing) * 10)}.ui\:size-14{width:calc(var(--ui-spacing) * 14);height:calc(var(--ui-spacing) * 14)}.ui\:size-14\!{width:calc(var(--ui-spacing) * 14)!important;height:calc(var(--ui-spacing) * 14)!important}.ui\:size-20{width:calc(var(--ui-spacing) * 20);height:calc(var(--ui-spacing) * 20)}.ui\:size-24{width:calc(var(--ui-spacing) * 24);height:calc(var(--ui-spacing) * 24)}.ui\:size-\[2\.4px\]{width:2.4px;height:2.4px}.ui\:size-fit{width:fit-content;height:fit-content}.ui\:size-full{width:100%;height:100%}.ui\:h-\(--bits-navigation-menu-viewport-height\){height:var(--bits-navigation-menu-viewport-height)}.ui\:h-\(--bits-select-anchor-height\){height:var(--bits-select-anchor-height)}.ui\:h-0{height:calc(var(--ui-spacing) * 0)}.ui\:h-0\.5{height:calc(var(--ui-spacing) * .5)}.ui\:h-0\.5\!{height:calc(var(--ui-spacing) * .5)!important}.ui\:h-1{height:calc(var(--ui-spacing) * 1)}.ui\:h-1\.5{height:calc(var(--ui-spacing) * 1.5)}.ui\:h-2{height:calc(var(--ui-spacing) * 2)}.ui\:h-2\.5{height:calc(var(--ui-spacing) * 2.5)}.ui\:h-4{height:calc(var(--ui-spacing) * 4)}.ui\:h-5{height:calc(var(--ui-spacing) * 5)}.ui\:h-6{height:calc(var(--ui-spacing) * 6)}.ui\:h-7{height:calc(var(--ui-spacing) * 7)}.ui\:h-8{height:calc(var(--ui-spacing) * 8)}.ui\:h-9{height:calc(var(--ui-spacing) * 9)}.ui\:h-10{height:calc(var(--ui-spacing) * 10)}.ui\:h-12{height:calc(var(--ui-spacing) * 12)}.ui\:h-20{height:calc(var(--ui-spacing) * 20)}.ui\:h-24{height:calc(var(--ui-spacing) * 24)}.ui\:h-28{height:calc(var(--ui-spacing) * 28)}.ui\:h-40{height:calc(var(--ui-spacing) * 40)}.ui\:h-48{height:calc(var(--ui-spacing) * 48)}.ui\:h-50\!{height:calc(var(--ui-spacing) * 50)!important}.ui\:h-96{height:calc(var(--ui-spacing) * 96)}.ui\:h-128{height:calc(var(--ui-spacing) * 128)}.ui\:h-\[1\.2rem\]{height:1.2rem}.ui\:h-\[1\.15rem\]{height:1.15rem}.ui\:h-\[6px\]{height:6px}.ui\:h-\[70px\]{height:70px}.ui\:h-\[100cqh\]{height:100cqh}.ui\:h-\[280px\]{height:280px}.ui\:h-\[500px\]{height:500px}.ui\:h-\[calc\(100\%-1px\)\]{height:calc(100% - 1px)}.ui\:h-\[calc\(100\%-3px\)\]{height:calc(100% - 3px)}.ui\:h-\[calc\(100dvh-3rem\)\]{height:calc(100dvh - 3rem)}.ui\:h-auto{height:auto}.ui\:h-fit{height:fit-content}.ui\:h-fit\!{height:fit-content!important}.ui\:h-full{height:100%}.ui\:h-px{height:1px}.ui\:h-screen{height:100vh}.ui\:max-h-\(--bits-dropdown-menu-content-available-height\){max-height:var(--bits-dropdown-menu-content-available-height)}.ui\:max-h-\(--bits-select-content-available-height\){max-height:var(--bits-select-content-available-height)}.ui\:max-h-32{max-height:calc(var(--ui-spacing) * 32)}.ui\:max-h-52{max-height:calc(var(--ui-spacing) * 52)}.ui\:max-h-60{max-height:calc(var(--ui-spacing) * 60)}.ui\:max-h-64{max-height:calc(var(--ui-spacing) * 64)}.ui\:max-h-72{max-height:calc(var(--ui-spacing) * 72)}.ui\:max-h-80{max-height:calc(var(--ui-spacing) * 80)}.ui\:max-h-90{max-height:calc(var(--ui-spacing) * 90)}.ui\:max-h-96{max-height:calc(var(--ui-spacing) * 96)}.ui\:max-h-\[85vh\]{max-height:85vh}.ui\:max-h-\[300px\]{max-height:300px}.ui\:max-h-svh{max-height:100svh}.ui\:min-h-0{min-height:calc(var(--ui-spacing) * 0)}.ui\:min-h-4{min-height:calc(var(--ui-spacing) * 4)}.ui\:min-h-8{min-height:calc(var(--ui-spacing) * 8)}.ui\:min-h-14{min-height:calc(var(--ui-spacing) * 14)}.ui\:min-h-16{min-height:calc(var(--ui-spacing) * 16)}.ui\:min-h-40{min-height:calc(var(--ui-spacing) * 40)}.ui\:min-h-96{min-height:calc(var(--ui-spacing) * 96)}.ui\:min-h-\[32rem\]{min-height:32rem}.ui\:min-h-\[44px\]{min-height:44px}.ui\:min-h-\[63px\]{min-height:63px}.ui\:min-h-\[240px\]{min-height:240px}.ui\:min-h-\[320px\]{min-height:320px}.ui\:min-h-\[calc\(100dvh-3rem\)\]{min-height:calc(100dvh - 3rem)}.ui\:min-h-\[calc\(100vh-48px\)\]{min-height:calc(100vh - 48px)}.ui\:min-h-dvh{min-height:100dvh}.ui\:min-h-screen{min-height:100vh}.ui\:w-\(--sidebar-width\){width:var(--sidebar-width)}.ui\:w-0{width:calc(var(--ui-spacing) * 0)}.ui\:w-1{width:calc(var(--ui-spacing) * 1)}.ui\:w-1\.5{width:calc(var(--ui-spacing) * 1.5)}.ui\:w-1\/4{width:25%}.ui\:w-2{width:calc(var(--ui-spacing) * 2)}.ui\:w-2\.5{width:calc(var(--ui-spacing) * 2.5)}.ui\:w-2\/4{width:50%}.ui\:w-3\/4{width:75%}.ui\:w-4{width:calc(var(--ui-spacing) * 4)}.ui\:w-5{width:calc(var(--ui-spacing) * 5)}.ui\:w-6{width:calc(var(--ui-spacing) * 6)}.ui\:w-7{width:calc(var(--ui-spacing) * 7)}.ui\:w-8{width:calc(var(--ui-spacing) * 8)}.ui\:w-10{width:calc(var(--ui-spacing) * 10)}.ui\:w-14{width:calc(var(--ui-spacing) * 14)}.ui\:w-24{width:calc(var(--ui-spacing) * 24)}.ui\:w-28{width:calc(var(--ui-spacing) * 28)}.ui\:w-40{width:calc(var(--ui-spacing) * 40)}.ui\:w-52{width:calc(var(--ui-spacing) * 52)}.ui\:w-64{width:calc(var(--ui-spacing) * 64)}.ui\:w-72{width:calc(var(--ui-spacing) * 72)}.ui\:w-88{width:calc(var(--ui-spacing) * 88)}.ui\:w-\[1\.2rem\]{width:1.2rem}.ui\:w-\[25px\]{width:25px}.ui\:w-\[50px\]{width:50px}.ui\:w-\[80\%\]{width:80%}.ui\:w-\[100px\]{width:100px}.ui\:w-\[260px\]{width:260px}.ui\:w-auto{width:auto}.ui\:w-fit{width:fit-content}.ui\:w-fit\!{width:fit-content!important}.ui\:w-full{width:100%}.ui\:w-full\!{width:100%!important}.ui\:w-max{width:max-content}.ui\:w-screen{width:100vw}.ui\:max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.ui\:max-w-2xl{max-width:var(--ui-container-2xl)}.ui\:max-w-3xl{max-width:var(--ui-container-3xl)}.ui\:max-w-4xl{max-width:var(--ui-container-4xl)}.ui\:max-w-5xl{max-width:var(--ui-container-5xl)}.ui\:max-w-6{max-width:calc(var(--ui-spacing) * 6)}.ui\:max-w-7xl{max-width:var(--ui-container-7xl)}.ui\:max-w-40{max-width:calc(var(--ui-spacing) * 40)}.ui\:max-w-80{max-width:calc(var(--ui-spacing) * 80)}.ui\:max-w-\[16rem\]{max-width:16rem}.ui\:max-w-\[55vw\]{max-width:55vw}.ui\:max-w-\[60\%\]{max-width:60%}.ui\:max-w-\[200px\]{max-width:200px}.ui\:max-w-\[300px\]{max-width:300px}.ui\:max-w-\[320px\]{max-width:320px}.ui\:max-w-\[1200px\]{max-width:1200px}.ui\:max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.ui\:max-w-full{max-width:100%}.ui\:max-w-max{max-width:max-content}.ui\:max-w-md{max-width:var(--ui-container-md)}.ui\:max-w-none{max-width:none}.ui\:max-w-sm{max-width:var(--ui-container-sm)}.ui\:max-w-xl{max-width:var(--ui-container-xl)}.ui\:max-w-xs{max-width:var(--ui-container-xs)}.ui\:min-w-\(--bits-select-anchor-width\){min-width:var(--bits-select-anchor-width)}.ui\:min-w-0{min-width:calc(var(--ui-spacing) * 0)}.ui\:min-w-5{min-width:calc(var(--ui-spacing) * 5)}.ui\:min-w-7{min-width:calc(var(--ui-spacing) * 7)}.ui\:min-w-8{min-width:calc(var(--ui-spacing) * 8)}.ui\:min-w-9{min-width:calc(var(--ui-spacing) * 9)}.ui\:min-w-10{min-width:calc(var(--ui-spacing) * 10)}.ui\:min-w-18{min-width:calc(var(--ui-spacing) * 18)}.ui\:min-w-32{min-width:calc(var(--ui-spacing) * 32)}.ui\:min-w-48{min-width:calc(var(--ui-spacing) * 48)}.ui\:min-w-\[5rem\]{min-width:5rem}.ui\:min-w-\[9rem\]{min-width:9rem}.ui\:min-w-\[min\(24rem\,calc\(100vw-5rem\)\)\]{min-width:min(24rem,100vw - 5rem)}.ui\:min-w-full{min-width:100%}.ui\:flex-1{flex:1}.ui\:shrink{flex-shrink:1}.ui\:shrink-0{flex-shrink:0}.ui\:basis-full{flex-basis:100%}.ui\:origin-\(--bits-dropdown-menu-content-transform-origin\){transform-origin:var(--bits-dropdown-menu-content-transform-origin)}.ui\:origin-\(--bits-menubar-content-transform-origin\){transform-origin:var(--bits-menubar-content-transform-origin)}.ui\:origin-\(--bits-popover-content-transform-origin\){transform-origin:var(--bits-popover-content-transform-origin)}.ui\:origin-\(--bits-select-content-transform-origin\){transform-origin:var(--bits-select-content-transform-origin)}.ui\:origin-\(--bits-tooltip-content-transform-origin\){transform-origin:var(--bits-tooltip-content-transform-origin)}.ui\:origin-center{transform-origin:50%}.ui\:-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:-translate-x-px{--tw-translate-x:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-x-4{--tw-translate-x:calc(var(--ui-spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-x-px{--tw-translate-x:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-y-0\.5{--tw-translate-y:calc(var(--ui-spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-y-4{--tw-translate-y:calc(var(--ui-spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:\[translate\:0_0\]{translate:0}.ui\:scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:rotate-0{rotate:none}.ui\:rotate-5{rotate:5deg}.ui\:rotate-45{rotate:45deg}.ui\:rotate-90{rotate:90deg}.ui\:\[transform\:scaleX\(-1\)\]{transform:scaleX(-1)}.ui\:transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.ui\:transform-gpu{transform:translateZ(0) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.ui\:animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:animate-magicslide{animation:magicslide var(--speed) ease-in-out infinite alternate}.ui\:animate-meteor{animation:5s linear infinite meteor}.ui\:animate-pulse{animation:var(--ui-animate-pulse)}.ui\:animate-shine{animation:2s linear infinite shine}.ui\:animate-spin{animation:var(--ui-animate-spin)}.ui\:animate-spin-around{animation:spin-around calc(var(--speed) * 2) infinite linear}.ui\:cursor-auto{cursor:auto}.ui\:cursor-col-resize{cursor:col-resize}.ui\:cursor-default{cursor:default}.ui\:cursor-default\!{cursor:default!important}.ui\:cursor-grab{cursor:grab}.ui\:cursor-not-allowed{cursor:not-allowed}.ui\:cursor-pointer{cursor:pointer}.ui\:cursor-text{cursor:text}.ui\:resize-none{resize:none}.ui\:scroll-my-1{scroll-margin-block:calc(var(--ui-spacing) * 1)}.ui\:scroll-py-1{scroll-padding-block:calc(var(--ui-spacing) * 1)}.ui\:list-disc{list-style-type:disc}.ui\:list-none{list-style-type:none}.ui\:auto-rows-min{grid-auto-rows:min-content}.ui\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.ui\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.ui\:grid-cols-\[0_1fr\]{grid-template-columns:0 1fr}.ui\:grid-cols-\[6px_6px\]{grid-template-columns:6px 6px}.ui\:grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.ui\:flex-col{flex-direction:column}.ui\:flex-col-reverse{flex-direction:column-reverse}.ui\:flex-row{flex-direction:row}.ui\:flex-nowrap\!{flex-wrap:nowrap!important}.ui\:flex-wrap{flex-wrap:wrap}.ui\:place-content-center{place-content:center}.ui\:place-items-center{place-items:center}.ui\:items-baseline{align-items:baseline}.ui\:items-center{align-items:center}.ui\:items-end{align-items:flex-end}.ui\:items-start{align-items:flex-start}.ui\:items-stretch{align-items:stretch}.ui\:justify-between{justify-content:space-between}.ui\:justify-center{justify-content:center}.ui\:justify-end{justify-content:flex-end}.ui\:justify-start{justify-content:flex-start}.ui\:justify-start\!{justify-content:flex-start!important}.ui\:justify-items-center{justify-items:center}.ui\:justify-items-start{justify-items:start}.ui\:gap-0{gap:calc(var(--ui-spacing) * 0)}.ui\:gap-0\.5{gap:calc(var(--ui-spacing) * .5)}.ui\:gap-1{gap:calc(var(--ui-spacing) * 1)}.ui\:gap-1\.5{gap:calc(var(--ui-spacing) * 1.5)}.ui\:gap-2{gap:calc(var(--ui-spacing) * 2)}.ui\:gap-2\.5{gap:calc(var(--ui-spacing) * 2.5)}.ui\:gap-3{gap:calc(var(--ui-spacing) * 3)}.ui\:gap-4{gap:calc(var(--ui-spacing) * 4)}.ui\:gap-5{gap:calc(var(--ui-spacing) * 5)}.ui\:gap-6{gap:calc(var(--ui-spacing) * 6)}.ui\:gap-7{gap:calc(var(--ui-spacing) * 7)}.ui\:gap-8{gap:calc(var(--ui-spacing) * 8)}.ui\:gap-16{gap:calc(var(--ui-spacing) * 16)}:where(.ui\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.ui\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--ui-spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--ui-spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.ui\:gap-x-4{column-gap:calc(var(--ui-spacing) * 4)}.ui\:gap-x-6{column-gap:calc(var(--ui-spacing) * 6)}:where(.ui\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--ui-spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--ui-spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.ui\:gap-y-0\.5{row-gap:calc(var(--ui-spacing) * .5)}.ui\:gap-y-1{row-gap:calc(var(--ui-spacing) * 1)}.ui\:gap-y-2{row-gap:calc(var(--ui-spacing) * 2)}.ui\:self-center{align-self:center}.ui\:self-start{align-self:flex-start}.ui\:self-stretch{align-self:stretch}.ui\:justify-self-end{justify-self:flex-end}.ui\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui\:overflow-auto{overflow:auto}.ui\:overflow-hidden{overflow:hidden}.ui\:overflow-visible{overflow:visible}.ui\:overflow-x-auto{overflow-x:auto}.ui\:overflow-x-hidden{overflow-x:hidden}.ui\:overflow-y-auto{overflow-y:auto}.ui\:overflow-y-hidden{overflow-y:hidden}.ui\:scroll-smooth{scroll-behavior:smooth}.ui\:rounded{border-radius:.25rem}.ui\:rounded-\(--radius\){border-radius:var(--radius)}.ui\:rounded-2xl{border-radius:var(--ui-radius-2xl)}.ui\:rounded-3xl{border-radius:var(--ui-radius-3xl)}.ui\:rounded-\[2px\]{border-radius:2px}.ui\:rounded-\[4px\]{border-radius:4px}.ui\:rounded-\[calc\(var\(--radius\)-5px\)\]{border-radius:calc(var(--radius) - 5px)}.ui\:rounded-full{border-radius:3.40282e38px}.ui\:rounded-lg{border-radius:var(--radius)}.ui\:rounded-md{border-radius:calc(var(--radius) - 2px)}.ui\:rounded-md\!{border-radius:calc(var(--radius) - 2px)!important}.ui\:rounded-none{border-radius:0}.ui\:rounded-sm{border-radius:calc(var(--radius) - 4px)}.ui\:rounded-xl{border-radius:calc(var(--radius) + 4px)}.ui\:rounded-xs{border-radius:var(--ui-radius-xs)}.ui\:rounded-t-xl{border-top-left-radius:calc(var(--radius) + 4px);border-top-right-radius:calc(var(--radius) + 4px)}.ui\:border{border-style:var(--tw-border-style);border-width:1px}.ui\:border-0{border-style:var(--tw-border-style);border-width:0}.ui\:border-0\!{border-style:var(--tw-border-style)!important;border-width:0!important}.ui\:border-2{border-style:var(--tw-border-style);border-width:2px}.ui\:border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.ui\:border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.ui\:border-y-\[10px\]{border-block-style:var(--tw-border-style);border-block-width:10px}.ui\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.ui\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.ui\:border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.ui\:border-l-\[16px\]{border-left-style:var(--tw-border-style);border-left-width:16px}.ui\:border-dashed{--tw-border-style:dashed;border-style:dashed}.ui\:border-none{--tw-border-style:none;border-style:none}.ui\:border-solid{--tw-border-style:solid;border-style:solid}.ui\:border-\(--color-border\){border-color:var(--color-border)}.ui\:border-amber-200{border-color:var(--ui-color-amber-200)}.ui\:border-amber-400{border-color:var(--ui-color-amber-400)}.ui\:border-background\/40{border-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:border-background\/40{border-color:color-mix(in oklab, var(--background) 40%, transparent)}}.ui\:border-border,.ui\:border-border\/40{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/40{border-color:color-mix(in oklab, var(--border) 40%, transparent)}}.ui\:border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/50{border-color:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:border-border\/60{border-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:border-border\/60{border-color:color-mix(in oklab, var(--border) 60%, transparent)}}.ui\:border-emerald-300{border-color:var(--ui-color-emerald-300)}.ui\:border-emerald-400{border-color:var(--ui-color-emerald-400)}.ui\:border-emerald-500{border-color:var(--ui-color-emerald-500)}.ui\:border-input{border-color:var(--input)}.ui\:border-muted{border-color:var(--muted)}.ui\:border-muted-foreground,.ui\:border-muted-foreground\/40{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:border-muted-foreground\/40{border-color:color-mix(in oklab, var(--muted-foreground) 40%, transparent)}}.ui\:border-neutral-800{border-color:var(--ui-color-neutral-800)}.ui\:border-primary,.ui\:border-primary\/30{border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:border-primary\/30{border-color:color-mix(in oklab, var(--primary) 30%, transparent)}}.ui\:border-primary\/40{border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:border-primary\/40{border-color:color-mix(in oklab, var(--primary) 40%, transparent)}}.ui\:border-red-500{border-color:var(--ui-color-red-500)}.ui\:border-sidebar-border{border-color:var(--sidebar-border)}.ui\:border-sky-200{border-color:var(--ui-color-sky-200)}.ui\:border-transparent{border-color:#0000}.ui\:border-white\/10{border-color:var(--ui-color-white)}@supports (color:color-mix(in lab, red, red)){.ui\:border-white\/10{border-color:color-mix(in oklab, var(--ui-color-white) 10%, transparent)}}.ui\:border-y-transparent{border-block-color:#0000}.ui\:border-l-current{border-left-color:currentColor}.ui\:border-l-foreground{border-left-color:var(--foreground)}.ui\:border-l-transparent{border-left-color:#0000}.ui\:bg-\(--color-bg\){background-color:var(--color-bg)}.ui\:bg-accent{background-color:var(--accent)}.ui\:bg-amber-50,.ui\:bg-amber-50\/30{background-color:var(--ui-color-amber-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-amber-50\/30{background-color:color-mix(in oklab, var(--ui-color-amber-50) 30%, transparent)}}.ui\:bg-amber-500{background-color:var(--ui-color-amber-500)}.ui\:bg-amber-500\!{background-color:var(--ui-color-amber-500)!important}.ui\:bg-amber-600{background-color:var(--ui-color-amber-600)}.ui\:bg-background,.ui\:bg-background\/50{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-background\/50{background-color:color-mix(in oklab, var(--background) 50%, transparent)}}.ui\:bg-background\/90{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-background\/90{background-color:color-mix(in oklab, var(--background) 90%, transparent)}}.ui\:bg-background\/95{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-background\/95{background-color:color-mix(in oklab, var(--background) 95%, transparent)}}.ui\:bg-black,.ui\:bg-black\/10{background-color:var(--ui-color-black)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-black\/10{background-color:color-mix(in oklab, var(--ui-color-black) 10%, transparent)}}.ui\:bg-black\/40{background-color:var(--ui-color-black)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-black\/40{background-color:color-mix(in oklab, var(--ui-color-black) 40%, transparent)}}.ui\:bg-black\/50{background-color:var(--ui-color-black)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-black\/50{background-color:color-mix(in oklab, var(--ui-color-black) 50%, transparent)}}.ui\:bg-blue-600{background-color:var(--ui-color-blue-600)}.ui\:bg-blue-600\!{background-color:var(--ui-color-blue-600)!important}.ui\:bg-border{background-color:var(--border)}.ui\:bg-card{background-color:var(--card)}.ui\:bg-current{background-color:currentColor}.ui\:bg-destructive{background-color:var(--destructive)}.ui\:bg-emerald-50\/20{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/20{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 20%, transparent)}}.ui\:bg-emerald-50\/30{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/30{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 30%, transparent)}}.ui\:bg-emerald-50\/40{background-color:var(--ui-color-emerald-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-emerald-50\/40{background-color:color-mix(in oklab, var(--ui-color-emerald-50) 40%, transparent)}}.ui\:bg-emerald-500{background-color:var(--ui-color-emerald-500)}.ui\:bg-emerald-500\!{background-color:var(--ui-color-emerald-500)!important}.ui\:bg-emerald-600{background-color:var(--ui-color-emerald-600)}.ui\:bg-foreground{background-color:var(--foreground)}.ui\:bg-gray-200{background-color:var(--ui-color-gray-200)}.ui\:bg-green-500{background-color:var(--ui-color-green-500)}.ui\:bg-inherit{background-color:inherit}.ui\:bg-input{background-color:var(--input)}.ui\:bg-muted{background-color:var(--muted)}.ui\:bg-muted-foreground\/30{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted-foreground\/30{background-color:color-mix(in oklab, var(--muted-foreground) 30%, transparent)}}.ui\:bg-muted\/10{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/10{background-color:color-mix(in oklab, var(--muted) 10%, transparent)}}.ui\:bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/30{background-color:color-mix(in oklab, var(--muted) 30%, transparent)}}.ui\:bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-muted\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.ui\:bg-popover{background-color:var(--popover)}.ui\:bg-primary{background-color:var(--primary)}.ui\:bg-primary-foreground{background-color:var(--primary-foreground)}.ui\:bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/5{background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/10{background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.ui\:bg-primary\/20{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/20{background-color:color-mix(in oklab, var(--primary) 20%, transparent)}}.ui\:bg-primary\/60{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-primary\/60{background-color:color-mix(in oklab, var(--primary) 60%, transparent)}}.ui\:bg-red-50\/40{background-color:var(--ui-color-red-50)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-red-50\/40{background-color:color-mix(in oklab, var(--ui-color-red-50) 40%, transparent)}}.ui\:bg-red-500{background-color:var(--ui-color-red-500)}.ui\:bg-rose-500{background-color:var(--ui-color-rose-500)}.ui\:bg-rose-500\!{background-color:var(--ui-color-rose-500)!important}.ui\:bg-secondary,.ui\:bg-secondary\/50{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.ui\:bg-secondary\/50{background-color:color-mix(in oklab, var(--secondary) 50%, transparent)}}.ui\:bg-sidebar{background-color:var(--sidebar)}.ui\:bg-sidebar-border{background-color:var(--sidebar-border)}.ui\:bg-sky-50{background-color:var(--ui-color-sky-50)}.ui\:bg-slate-500{background-color:var(--ui-color-slate-500)}.ui\:bg-slate-500\!{background-color:var(--ui-color-slate-500)!important}.ui\:bg-transparent{background-color:#0000}.ui\:bg-violet-500{background-color:var(--ui-color-violet-500)}.ui\:bg-violet-500\!{background-color:var(--ui-color-violet-500)!important}.ui\:bg-white{background-color:var(--ui-color-white)}.ui\:bg-yellow-500{background-color:var(--ui-color-yellow-500)}.ui\:bg-linear-to-r{--tw-gradient-position:to right}@supports (background-image:linear-gradient(in lab, red, red)){.ui\:bg-linear-to-r{--tw-gradient-position:to right in oklab}}.ui\:bg-linear-to-r{background-image:linear-gradient(var(--tw-gradient-stops))}.ui\:bg-linear-to-t{--tw-gradient-position:to top}@supports (background-image:linear-gradient(in lab, red, red)){.ui\:bg-linear-to-t{--tw-gradient-position:to top in oklab}}.ui\:bg-linear-to-t{background-image:linear-gradient(var(--tw-gradient-stops))}.ui\:bg-\[linear-gradient\(110deg\,\#000103\,45\%\,\#1e2631\,55\%\,\#000103\)\]{background-image:linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)}.ui\:from-background{--tw-gradient-from:var(--background);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:from-pink-500{--tw-gradient-from:var(--ui-color-pink-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:from-slate-500{--tw-gradient-from:var(--ui-color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:via-blue-600\/30{--tw-gradient-via:var(--ui-color-blue-600)}@supports (color:color-mix(in lab, red, red)){.ui\:via-blue-600\/30{--tw-gradient-via:color-mix(in oklab, var(--ui-color-blue-600) 30%, transparent)}}.ui\:via-blue-600\/30{--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.ui\:to-orange-500{--tw-gradient-to:var(--ui-color-orange-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.ui\:bg-\[length\:200\%_100\%\]{background-size:200% 100%}.ui\:bg-clip-padding{background-clip:padding-box}.ui\:fill-amber-400{fill:var(--ui-color-amber-400)}.ui\:fill-current{fill:currentColor}.ui\:fill-none{fill:none}.ui\:fill-primary{fill:var(--primary)}.ui\:object-cover{object-fit:cover}.ui\:p-0{padding:calc(var(--ui-spacing) * 0)}.ui\:p-0\!{padding:calc(var(--ui-spacing) * 0)!important}.ui\:p-0\.5{padding:calc(var(--ui-spacing) * .5)}.ui\:p-1{padding:calc(var(--ui-spacing) * 1)}.ui\:p-1\!{padding:calc(var(--ui-spacing) * 1)!important}.ui\:p-1\.5{padding:calc(var(--ui-spacing) * 1.5)}.ui\:p-2{padding:calc(var(--ui-spacing) * 2)}.ui\:p-3{padding:calc(var(--ui-spacing) * 3)}.ui\:p-3\!{padding:calc(var(--ui-spacing) * 3)!important}.ui\:p-4{padding:calc(var(--ui-spacing) * 4)}.ui\:p-6{padding:calc(var(--ui-spacing) * 6)}.ui\:p-8{padding:calc(var(--ui-spacing) * 8)}.ui\:p-\[3px\]{padding:3px}.ui\:px-1{padding-inline:calc(var(--ui-spacing) * 1)}.ui\:px-1\.5{padding-inline:calc(var(--ui-spacing) * 1.5)}.ui\:px-2{padding-inline:calc(var(--ui-spacing) * 2)}.ui\:px-2\.5{padding-inline:calc(var(--ui-spacing) * 2.5)}.ui\:px-3{padding-inline:calc(var(--ui-spacing) * 3)}.ui\:px-4{padding-inline:calc(var(--ui-spacing) * 4)}.ui\:px-5{padding-inline:calc(var(--ui-spacing) * 5)}.ui\:px-6{padding-inline:calc(var(--ui-spacing) * 6)}.ui\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:py-0\.5{padding-block:calc(var(--ui-spacing) * .5)}.ui\:py-1{padding-block:calc(var(--ui-spacing) * 1)}.ui\:py-1\.5{padding-block:calc(var(--ui-spacing) * 1.5)}.ui\:py-2{padding-block:calc(var(--ui-spacing) * 2)}.ui\:py-3{padding-block:calc(var(--ui-spacing) * 3)}.ui\:py-4{padding-block:calc(var(--ui-spacing) * 4)}.ui\:py-5{padding-block:calc(var(--ui-spacing) * 5)}.ui\:py-6{padding-block:calc(var(--ui-spacing) * 6)}.ui\:py-8{padding-block:calc(var(--ui-spacing) * 8)}.ui\:py-10{padding-block:calc(var(--ui-spacing) * 10)}.ui\:py-12{padding-block:calc(var(--ui-spacing) * 12)}.ui\:py-16{padding-block:calc(var(--ui-spacing) * 16)}.ui\:py-20{padding-block:calc(var(--ui-spacing) * 20)}.ui\:py-24{padding-block:calc(var(--ui-spacing) * 24)}.ui\:ps-3{padding-inline-start:calc(var(--ui-spacing) * 3)}.ui\:pe-3{padding-inline-end:calc(var(--ui-spacing) * 3)}.ui\:pt-0{padding-top:calc(var(--ui-spacing) * 0)}.ui\:pt-1{padding-top:calc(var(--ui-spacing) * 1)}.ui\:pt-1\.5{padding-top:calc(var(--ui-spacing) * 1.5)}.ui\:pt-3{padding-top:calc(var(--ui-spacing) * 3)}.ui\:pt-3\!{padding-top:calc(var(--ui-spacing) * 3)!important}.ui\:pt-4{padding-top:calc(var(--ui-spacing) * 4)}.ui\:pt-6{padding-top:calc(var(--ui-spacing) * 6)}.ui\:pt-8{padding-top:calc(var(--ui-spacing) * 8)}.ui\:pr-1{padding-right:calc(var(--ui-spacing) * 1)}.ui\:pr-2{padding-right:calc(var(--ui-spacing) * 2)}.ui\:pr-4{padding-right:calc(var(--ui-spacing) * 4)}.ui\:pr-8{padding-right:calc(var(--ui-spacing) * 8)}.ui\:pr-9{padding-right:calc(var(--ui-spacing) * 9)}.ui\:pr-18{padding-right:calc(var(--ui-spacing) * 18)}.ui\:pb-2{padding-bottom:calc(var(--ui-spacing) * 2)}.ui\:pb-3{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:pb-4{padding-bottom:calc(var(--ui-spacing) * 4)}.ui\:pb-10{padding-bottom:calc(var(--ui-spacing) * 10)}.ui\:pb-16{padding-bottom:calc(var(--ui-spacing) * 16)}.ui\:pb-24{padding-bottom:calc(var(--ui-spacing) * 24)}.ui\:pb-\[max\(env\(safe-area-inset-bottom\)\,1rem\)\]{padding-bottom:max(env(safe-area-inset-bottom), 1rem)}.ui\:pl-2{padding-left:calc(var(--ui-spacing) * 2)}.ui\:pl-3{padding-left:calc(var(--ui-spacing) * 3)}.ui\:pl-8{padding-left:calc(var(--ui-spacing) * 8)}.ui\:text-center{text-align:center}.ui\:text-left{text-align:left}.ui\:text-right{text-align:right}.ui\:align-middle{vertical-align:middle}.ui\:font-mono{font-family:var(--ui-font-mono)}.ui\:font-sans{font-family:var(--ui-font-sans)}.ui\:font-serif{font-family:var(--ui-font-serif)}.ui\:text-2xl{font-size:var(--ui-text-2xl);line-height:var(--tw-leading,var(--ui-text-2xl--line-height))}.ui\:text-3xl{font-size:var(--ui-text-3xl);line-height:var(--tw-leading,var(--ui-text-3xl--line-height))}.ui\:text-4xl{font-size:var(--ui-text-4xl);line-height:var(--tw-leading,var(--ui-text-4xl--line-height))}.ui\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:text-base{font-size:var(--ui-text-base);line-height:var(--tw-leading,var(--ui-text-base--line-height))}.ui\:text-base\!{font-size:var(--ui-text-base)!important;line-height:var(--tw-leading,var(--ui-text-base--line-height))!important}.ui\:text-lg{font-size:var(--ui-text-lg);line-height:var(--tw-leading,var(--ui-text-lg--line-height))}.ui\:text-sm{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:text-sm\/relaxed{font-size:var(--ui-text-sm);line-height:var(--ui-leading-relaxed)}.ui\:text-xl{font-size:var(--ui-text-xl);line-height:var(--tw-leading,var(--ui-text-xl--line-height))}.ui\:text-xs{font-size:var(--ui-text-xs);line-height:var(--tw-leading,var(--ui-text-xs--line-height))}.ui\:text-\[0\.7rem\]{font-size:.7rem}.ui\:text-\[10px\]{font-size:10px}.ui\:text-\[11px\]{font-size:11px}.ui\:text-\[13px\]{font-size:13px}.ui\:leading-7{--tw-leading:calc(var(--ui-spacing) * 7);line-height:calc(var(--ui-spacing) * 7)}.ui\:leading-8{--tw-leading:calc(var(--ui-spacing) * 8);line-height:calc(var(--ui-spacing) * 8)}.ui\:leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.ui\:leading-none{--tw-leading:1;line-height:1}.ui\:leading-normal{--tw-leading:var(--ui-leading-normal);line-height:var(--ui-leading-normal)}.ui\:leading-relaxed{--tw-leading:var(--ui-leading-relaxed);line-height:var(--ui-leading-relaxed)}.ui\:leading-snug{--tw-leading:var(--ui-leading-snug);line-height:var(--ui-leading-snug)}.ui\:leading-tight{--tw-leading:var(--ui-leading-tight);line-height:var(--ui-leading-tight)}.ui\:font-black{--tw-font-weight:var(--ui-font-weight-black);font-weight:var(--ui-font-weight-black)}.ui\:font-bold{--tw-font-weight:var(--ui-font-weight-bold);font-weight:var(--ui-font-weight-bold)}.ui\:font-light{--tw-font-weight:var(--ui-font-weight-light);font-weight:var(--ui-font-weight-light)}.ui\:font-medium{--tw-font-weight:var(--ui-font-weight-medium);font-weight:var(--ui-font-weight-medium)}.ui\:font-normal{--tw-font-weight:var(--ui-font-weight-normal);font-weight:var(--ui-font-weight-normal)}.ui\:font-semibold{--tw-font-weight:var(--ui-font-weight-semibold);font-weight:var(--ui-font-weight-semibold)}.ui\:tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.ui\:tracking-tight{--tw-tracking:var(--ui-tracking-tight);letter-spacing:var(--ui-tracking-tight)}.ui\:tracking-tighter{--tw-tracking:var(--ui-tracking-tighter);letter-spacing:var(--ui-tracking-tighter)}.ui\:tracking-wide{--tw-tracking:var(--ui-tracking-wide);letter-spacing:var(--ui-tracking-wide)}.ui\:tracking-wider{--tw-tracking:var(--ui-tracking-wider);letter-spacing:var(--ui-tracking-wider)}.ui\:tracking-widest{--tw-tracking:var(--ui-tracking-widest);letter-spacing:var(--ui-tracking-widest)}.ui\:text-balance{text-wrap:balance}.ui\:text-wrap{text-wrap:wrap}.ui\:break-words{overflow-wrap:break-word}.ui\:break-all{word-break:break-all}.ui\:text-ellipsis{text-overflow:ellipsis}.ui\:whitespace-nowrap{white-space:nowrap}.ui\:text-accent-foreground{color:var(--accent-foreground)}.ui\:text-amber-200{color:var(--ui-color-amber-200)}.ui\:text-amber-500{color:var(--ui-color-amber-500)}.ui\:text-amber-600{color:var(--ui-color-amber-600)}.ui\:text-amber-900{color:var(--ui-color-amber-900)}.ui\:text-background,.ui\:text-background\/60{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/60{color:color-mix(in oklab, var(--background) 60%, transparent)}}.ui\:text-background\/70{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/70{color:color-mix(in oklab, var(--background) 70%, transparent)}}.ui\:text-background\/80{color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:text-background\/80{color:color-mix(in oklab, var(--background) 80%, transparent)}}.ui\:text-card-foreground{color:var(--card-foreground)}.ui\:text-current{color:currentColor}.ui\:text-destructive{color:var(--destructive)}.ui\:text-emerald-600{color:var(--ui-color-emerald-600)}.ui\:text-foreground,.ui\:text-foreground\/70{color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-foreground\/70{color:color-mix(in oklab, var(--foreground) 70%, transparent)}}.ui\:text-foreground\/72{color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-foreground\/72{color:color-mix(in oklab, var(--foreground) 72%, transparent)}}.ui\:text-green-500{color:var(--ui-color-green-500)}.ui\:text-muted-foreground,.ui\:text-muted-foreground\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-muted-foreground\/60{color:color-mix(in oklab, var(--muted-foreground) 60%, transparent)}}.ui\:text-muted-foreground\/75{color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-muted-foreground\/75{color:color-mix(in oklab, var(--muted-foreground) 75%, transparent)}}.ui\:text-popover-foreground{color:var(--popover-foreground)}.ui\:text-primary{color:var(--primary)}.ui\:text-primary\!{color:var(--primary)!important}.ui\:text-primary-foreground{color:var(--primary-foreground)}.ui\:text-red-500{color:var(--ui-color-red-500)}.ui\:text-red-600{color:var(--ui-color-red-600)}.ui\:text-red-700{color:var(--ui-color-red-700)}.ui\:text-secondary-foreground{color:var(--secondary-foreground)}.ui\:text-sidebar-foreground,.ui\:text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:text-sidebar-foreground\/70{color:color-mix(in oklab, var(--sidebar-foreground) 70%, transparent)}}.ui\:text-sky-900{color:var(--ui-color-sky-900)}.ui\:text-white{color:var(--ui-color-white)}.ui\:text-white\!{color:var(--ui-color-white)!important}.ui\:capitalize{text-transform:capitalize}.ui\:uppercase{text-transform:uppercase}.ui\:tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ui\:no-underline{text-decoration-line:none}.ui\:underline{text-decoration-line:underline}.ui\:underline-offset-2{text-underline-offset:2px}.ui\:underline-offset-4{text-underline-offset:4px}.ui\:opacity-0{opacity:0}.ui\:opacity-10{opacity:.1}.ui\:opacity-40{opacity:.4}.ui\:opacity-50{opacity:.5}.ui\:opacity-60{opacity:.6}.ui\:opacity-70{opacity:.7}.ui\:opacity-90{opacity:.9}.ui\:opacity-100{opacity:1}.ui\:opacity-\[0\.06\]{opacity:.06}.ui\:opacity-\[0\.08\]{opacity:.08}.ui\:opacity-\[0\.15\]{opacity:.15}.ui\:shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-2xs{--tw-shadow:0 1px var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[0_0_0_1px_\#ffffff10\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#ffffff10);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[0_0_0_1px_var\(--sidebar-border\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-border));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-\[inset_0_-8px_10px_\#ffffff1f\]{--tw-shadow:inset 0 -8px 10px var(--tw-shadow-color,#ffffff1f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:ring-3{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:shadow-primary\/20{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:shadow-primary\/20{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--primary) 20%, transparent) var(--tw-shadow-alpha), transparent)}}.ui\:ring-accent{--tw-ring-color:var(--accent)}.ui\:ring-background{--tw-ring-color:var(--background)}.ui\:ring-border,.ui\:ring-border\/50{--tw-ring-color:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:ring-border\/50{--tw-ring-color:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:ring-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ui\:ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:ring-offset-background{--tw-ring-offset-color:var(--background)}.ui\:outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:outline-hidden{outline-offset:2px;outline:2px solid #0000}}.ui\:blur-\[2px\]{--tw-blur:blur(2px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:brightness-200{--tw-brightness:brightness(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:drop-shadow-none{--tw-drop-shadow: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:grayscale{--tw-grayscale:grayscale(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.ui\:backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.ui\:backdrop-blur-sm{--tw-backdrop-blur:blur(var(--ui-blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.ui\:backdrop-blur-xl{--tw-backdrop-blur:blur(var(--ui-blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.ui\:transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}.ui\:transition-none{transition-property:none}.ui\:duration-100{--tw-duration:.1s;transition-duration:.1s}.ui\:duration-200{--tw-duration:.2s;transition-duration:.2s}.ui\:duration-300{--tw-duration:.3s;transition-duration:.3s}.ui\:duration-500{--tw-duration:.5s;transition-duration:.5s}.ui\:ease-in-out{--tw-ease:var(--ui-ease-in-out);transition-timing-function:var(--ui-ease-in-out)}.ui\:ease-linear{--tw-ease:linear;transition-timing-function:linear}.ui\:fade-in-0{--tw-enter-opacity:0}.ui\:outline-none{--tw-outline-style:none;outline-style:none}.ui\:select-none{-webkit-user-select:none;user-select:none}.ui\:zoom-in-95{--tw-enter-scale:.95}.ui\:\[background\:conic-gradient\(from_calc\(270deg-\(var\(--spread\)\*0\.5\)\)\,transparent_0\,var\(--shimmer-color\)_var\(--spread\)\,transparent_var\(--spread\)\)\]{background:conic-gradient(from calc(270deg - (var(--spread) * .5)),transparent 0,var(--shimmer-color) var(--spread),transparent var(--spread))}.ui\:\[background\:var\(--bg\)\]{background:var(--bg)}.ui\:\[mask\:none\]{-webkit-mask:none;mask:none}.ui\:ring-inset{--tw-ring-inset:inset}:is(.ui\:\*\:w-full>*){width:100%}.ui\:group-focus-within\/menu-item\:opacity-100:is(:where(.ui\:group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.ui\:group-hover\:scale-\[1\.02\]:is(:where(.ui\:group):hover *){scale:1.02}.ui\:group-hover\:bg-muted-foreground\/15:is(:where(.ui\:group):hover *){background-color:var(--muted-foreground)}@supports (color:color-mix(in lab, red, red)){.ui\:group-hover\:bg-muted-foreground\/15:is(:where(.ui\:group):hover *){background-color:color-mix(in oklab, var(--muted-foreground) 15%, transparent)}}.ui\:group-hover\:bg-primary:is(:where(.ui\:group):hover *){background-color:var(--primary)}.ui\:group-hover\:text-foreground:is(:where(.ui\:group):hover *){color:var(--foreground)}.ui\:group-hover\:text-primary:is(:where(.ui\:group):hover *){color:var(--primary)}.ui\:group-hover\:text-primary-foreground:is(:where(.ui\:group):hover *){color:var(--primary-foreground)}.ui\:group-hover\:opacity-100:is(:where(.ui\:group):hover *){opacity:1}.ui\:group-hover\:shadow-\[inset_0_-6px_10px_\#ffffff3f\]:is(:where(.ui\:group):hover *){--tw-shadow:inset 0 -6px 10px var(--tw-shadow-color,#ffffff3f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-hover\/menu-item\:opacity-100:is(:where(.ui\:group\/menu-item):hover *){opacity:1}}.ui\:group-focus-visible\:opacity-100:is(:where(.ui\:group):focus-visible *){opacity:1}.ui\:group-active\:shadow-\[inset_0_-10px_10px_\#ffffff3f\]:is(:where(.ui\:group):active *){--tw-shadow:inset 0 -10px 10px var(--tw-shadow-color,#ffffff3f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-has-data-\[orientation\=horizontal\]\/field\:text-balance:is(:where(.ui\:group\/field):has([data-orientation=horizontal]) *){text-wrap:balance}.ui\:group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.ui\:group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--ui-spacing) * 8)}.ui\:group-has-\[\[data-slot\=item-description\]\]\/item\:translate-y-0\.5:is(:where(.ui\:group\/item):has([data-slot=item-description]) *){--tw-translate-y:calc(var(--ui-spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:group-has-\[\[data-slot\=item-description\]\]\/item\:self-start:is(:where(.ui\:group\/item):has([data-slot=item-description]) *){align-self:flex-start}.ui\:group-has-\[\>input\]\/input-group\:pt-2\.5:is(:where(.ui\:group\/input-group):has(>input) *){padding-top:calc(var(--ui-spacing) * 2.5)}.ui\:group-has-\[\>input\]\/input-group\:pb-2\.5:is(:where(.ui\:group\/input-group):has(>input) *){padding-bottom:calc(var(--ui-spacing) * 2.5)}.ui\:group-data-\[collapsible\=icon\]\:pointer-events-none:is(:where(.ui\:group)[data-collapsible=icon] *){pointer-events:none}.ui\:group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.ui\:group)[data-collapsible=icon] *){margin-top:calc(var(--ui-spacing) * -8)}.ui\:group-data-\[collapsible\=icon\]\:hidden:is(:where(.ui\:group)[data-collapsible=icon] *){display:none}.ui\:group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.ui\:group)[data-collapsible=icon] *){width:calc(var(--ui-spacing) * 8)!important;height:calc(var(--ui-spacing) * 8)!important}.ui\:group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.ui\:group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.ui\:group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.ui\:group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--ui-spacing) * 4)) + 2px)}.ui\:group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.ui\:group)[data-collapsible=icon] *){overflow:hidden}.ui\:group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.ui\:group)[data-collapsible=icon] *){padding:calc(var(--ui-spacing) * 0)!important}.ui\:group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.ui\:group)[data-collapsible=icon] *){padding:calc(var(--ui-spacing) * 2)!important}.ui\:group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.ui\:group)[data-collapsible=icon] *){opacity:0}.ui\:group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.ui\:group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width) * -1)}.ui\:group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.ui\:group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width) * -1)}.ui\:group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.ui\:group)[data-collapsible=offcanvas] *){width:calc(var(--ui-spacing) * 0)}.ui\:group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.ui\:group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--ui-spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.ui\:group)[data-disabled=true] *){pointer-events:none}.ui\:group-data-\[disabled\=true\]\:opacity-50:is(:where(.ui\:group)[data-disabled=true] *),.ui\:group-data-\[disabled\=true\]\/field\:opacity-50:is(:where(.ui\:group\/field)[data-disabled=true] *),.ui\:group-data-\[disabled\=true\]\/input-group\:opacity-50:is(:where(.ui\:group\/input-group)[data-disabled=true] *){opacity:.5}.ui\:group-data-\[side\=left\]\:-right-4:is(:where(.ui\:group)[data-side=left] *){right:calc(var(--ui-spacing) * -4)}.ui\:group-data-\[side\=left\]\:border-r:is(:where(.ui\:group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:group-data-\[side\=right\]\:left-0:is(:where(.ui\:group)[data-side=right] *){left:calc(var(--ui-spacing) * 0)}.ui\:group-data-\[side\=right\]\:rotate-180:is(:where(.ui\:group)[data-side=right] *){rotate:180deg}.ui\:group-data-\[side\=right\]\:border-l:is(:where(.ui\:group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.ui\:group-data-\[variant\=floating\]\:rounded-lg:is(:where(.ui\:group)[data-variant=floating] *){border-radius:var(--radius)}.ui\:group-data-\[variant\=floating\]\:border:is(:where(.ui\:group)[data-variant=floating] *){border-style:var(--tw-border-style);border-width:1px}.ui\:group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.ui\:group)[data-variant=floating] *){border-color:var(--sidebar-border)}.ui\:group-data-\[variant\=floating\]\:shadow-sm:is(:where(.ui\:group)[data-variant=floating] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-data-\[variant\=outline\]\/field-group\:-mb-2:is(:where(.ui\:group\/field-group)[data-variant=outline] *){margin-bottom:calc(var(--ui-spacing) * -2)}.ui\:group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:block:is(:where(.ui\:group\/drawer-content)[data-vaul-drawer-direction=bottom] *){display:block}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:top-full:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){top:100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:mt-1\.5:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){margin-top:calc(var(--ui-spacing) * 1.5)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:overflow-hidden:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){overflow:hidden}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:rounded-md:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){border-radius:calc(var(--radius) - 2px)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:border:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){border-style:var(--tw-border-style);border-width:1px}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:bg-popover:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){background-color:var(--popover)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:text-popover-foreground:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){color:var(--popover-foreground)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:shadow:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:duration-200:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *){--tw-duration:.2s;transition-duration:.2s}@media (hover:hover){.ui\:peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.ui\:peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.ui\:peer-disabled\:cursor-not-allowed:is(:where(.ui\:peer):disabled~*){cursor:not-allowed}.ui\:peer-disabled\:opacity-50:is(:where(.ui\:peer):disabled~*){opacity:.5}.ui\:peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.ui\:peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.ui\:peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.ui\:peer\/menu-button)[data-size=default]~*){top:calc(var(--ui-spacing) * 1.5)}.ui\:peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.ui\:peer\/menu-button)[data-size=lg]~*){top:calc(var(--ui-spacing) * 2.5)}.ui\:peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.ui\:peer\/menu-button)[data-size=sm]~*){top:calc(var(--ui-spacing) * 1)}.ui\:selection\:bg-primary ::selection{background-color:var(--primary)}.ui\:selection\:bg-primary::selection{background-color:var(--primary)}.ui\:selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.ui\:selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.ui\:placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.ui\:after\:absolute:after{content:var(--tw-content);position:absolute}.ui\:after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--ui-spacing) * -2)}.ui\:after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--ui-spacing) * 0)}.ui\:after\:left-\[calc\(1\/2\*100\%-1px\)\]:after{content:var(--tw-content);left:calc(50% - 1px)}.ui\:after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.ui\:group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.ui\:group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.ui\:last\:mt-0:last-child{margin-top:calc(var(--ui-spacing) * 0)}.ui\:last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:focus-within\:border-ring:focus-within{border-color:var(--ring)}.ui\:focus-within\:ring-\[3px\]:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-within\:ring-ring\/50:focus-within{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-within\:ring-ring\/50:focus-within{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}@media (hover:hover){.ui\:hover\:-translate-y-1:hover{--tw-translate-y:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:hover\:cursor-pointer:hover{cursor:pointer}.ui\:hover\:border:hover{border-style:var(--tw-border-style);border-width:1px}.ui\:hover\:border-background\/60:hover{border-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:border-background\/60:hover{border-color:color-mix(in oklab, var(--background) 60%, transparent)}}.ui\:hover\:border-border:hover{border-color:var(--border)}.ui\:hover\:border-primary:hover,.ui\:hover\:border-primary\/40:hover{border-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:border-primary\/40:hover{border-color:color-mix(in oklab, var(--primary) 40%, transparent)}}.ui\:hover\:bg-accent:hover,.ui\:hover\:bg-accent\/25:hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-accent\/25:hover{background-color:color-mix(in oklab, var(--accent) 25%, transparent)}}.ui\:hover\:bg-amber-500\/10:hover{background-color:var(--ui-color-amber-500)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-amber-500\/10:hover{background-color:color-mix(in oklab, var(--ui-color-amber-500) 10%, transparent)}}.ui\:hover\:bg-background\/10:hover{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-background\/10:hover{background-color:color-mix(in oklab, var(--background) 10%, transparent)}}.ui\:hover\:bg-black\/20:hover{background-color:var(--ui-color-black)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-black\/20:hover{background-color:color-mix(in oklab, var(--ui-color-black) 20%, transparent)}}.ui\:hover\:bg-muted:hover,.ui\:hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.ui\:hover\:bg-primary:hover,.ui\:hover\:bg-primary\/5:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-primary\/5:hover{background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--primary) 90%, transparent)}}.ui\:hover\:bg-sidebar-accent:hover{background-color:var(--sidebar-accent)}.ui\:hover\:bg-transparent\!:hover{background-color:#0000!important}.ui\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.ui\:hover\:text-background:hover{color:var(--background)}.ui\:hover\:text-foreground:hover{color:var(--foreground)}.ui\:hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.ui\:hover\:text-primary:hover{color:var(--primary)}.ui\:hover\:text-primary-foreground:hover{color:var(--primary-foreground)}.ui\:hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.ui\:hover\:underline:hover{text-decoration-line:underline}.ui\:hover\:opacity-100:hover{opacity:1}.ui\:hover\:shadow-2xs:hover{--tw-shadow:0 1px var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-\[0_0_0_1px_var\(--sidebar-accent\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,var(--sidebar-accent));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:hover\:shadow-primary\/5:hover{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:hover\:shadow-primary\/5:hover{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--primary) 5%, transparent) var(--tw-shadow-alpha), transparent)}}.ui\:hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.ui\:group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.ui\:hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.ui\:focus\:bg-accent:focus{background-color:var(--accent)}.ui\:focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.ui\:focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.ui\:focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.ui\:focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.ui\:focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.ui\:focus-visible\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-0\!:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.ui\:focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:focus-visible\:ring-ring:focus-visible,.ui\:focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.ui\:focus-visible\:ring-offset-0\!:focus-visible{--tw-ring-offset-width:0px!important;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important}.ui\:focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ui\:focus-visible\:outline-hidden:focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.ui\:focus-visible\:outline-hidden:focus-visible{outline-offset:2px;outline:2px solid #0000}}.ui\:focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.ui\:focus-visible\:outline-ring:focus-visible{outline-color:var(--ring)}.ui\:focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.ui\:active\:translate-y-px:active{--tw-translate-y:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.ui\:active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.ui\:disabled\:pointer-events-none:disabled{pointer-events:none}.ui\:disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.ui\:disabled\:opacity-30:disabled{opacity:.3}.ui\:disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .ui\:in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .ui\:in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:bg-background\/20{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:bg-background\/20{background-color:color-mix(in oklab, var(--background) 20%, transparent)}}:where([data-slot=tooltip-content]) .ui\:in-data-\[slot\=tooltip-content\]\:text-background{color:var(--background)}.ui\:has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.ui\:has-data-\[state\=checked\]\:border-primary:has([data-state=checked]){border-color:var(--primary)}.ui\:has-data-\[state\=checked\]\:bg-primary\/5:has([data-state=checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:has-data-\[state\=checked\]\:bg-primary\/5:has([data-state=checked]){background-color:color-mix(in oklab, var(--primary) 5%, transparent)}}.ui\:has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has([data-slot=input-group-control]:focus-visible){border-color:var(--ring)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-\[3px\]:has([data-slot=input-group-control]:focus-visible){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.ui\:has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has([data-slot=input-group-control]:focus-visible){--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has([data-slot][aria-invalid=true]){border-color:var(--destructive)}.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:has-\[\>\[data-align\=block-end\]\]\:h-auto:has(>[data-align=block-end]){height:auto}.ui\:has-\[\>\[data-align\=block-end\]\]\:flex-col:has(>[data-align=block-end]){flex-direction:column}.ui\:has-\[\>\[data-align\=block-start\]\]\:h-auto:has(>[data-align=block-start]){height:auto}.ui\:has-\[\>\[data-align\=block-start\]\]\:flex-col:has(>[data-align=block-start]){flex-direction:column}.ui\:has-\[\>\[data-slot\=button-group\]\]\:gap-2:has(>[data-slot=button-group]){gap:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has(>[data-slot=checkbox-group]){gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has(>[data-slot=field-content]){align-items:flex-start}.ui\:has-\[\>\[data-slot\=field\]\]\:w-full:has(>[data-slot=field]){width:100%}.ui\:has-\[\>\[data-slot\=field\]\]\:flex-col:has(>[data-slot=field]){flex-direction:column}.ui\:has-\[\>\[data-slot\=field\]\]\:rounded-md:has(>[data-slot=field]){border-radius:calc(var(--radius) - 2px)}.ui\:has-\[\>\[data-slot\=field\]\]\:border:has(>[data-slot=field]){border-style:var(--tw-border-style);border-width:1px}.ui\:has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has(>[data-slot=radio-group]){gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>button\]\:ms-\[-0\.45rem\]:has(>button){margin-inline-start:-.45rem}.ui\:has-\[\>button\]\:me-\[-0\.45rem\]:has(>button){margin-inline-end:-.45rem}.ui\:has-\[\>kbd\]\:ms-\[-0\.35rem\]:has(>kbd){margin-inline-start:-.35rem}.ui\:has-\[\>kbd\]\:me-\[-0\.35rem\]:has(>kbd){margin-inline-end:-.35rem}.ui\:has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing) * 4) 1fr}.ui\:has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>svg\]\:p-0:has(>svg){padding:calc(var(--ui-spacing) * 0)}.ui\:has-\[\>svg\]\:px-2:has(>svg){padding-inline:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--ui-spacing) * 2.5)}.ui\:has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--ui-spacing) * 4)}.ui\:has-\[\>textarea\]\:h-auto:has(>textarea){height:auto}.ui\:aria-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.ui\:aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.ui\:aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.ui\:aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.ui\:aria-selected\:bg-accent[aria-selected=true]{background-color:var(--accent)}.ui\:aria-selected\:text-accent-foreground[aria-selected=true]{color:var(--accent-foreground)}:is(.ui\:\*\*\:data-command-group\:px-2 *)[data-command-group]{padding-inline:calc(var(--ui-spacing) * 2)}:is(.ui\:\*\*\:data-command-input\:h-12 *)[data-command-input]{height:calc(var(--ui-spacing) * 12)}:is(.ui\:\*\*\:data-command-item\:px-2 *)[data-command-item]{padding-inline:calc(var(--ui-spacing) * 2)}:is(.ui\:\*\*\:data-command-item\:py-3 *)[data-command-item]{padding-block:calc(var(--ui-spacing) * 3)}.ui\:data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.ui\:data-disabled\:cursor-not-allowed[data-disabled]{cursor:not-allowed}.ui\:data-disabled\:opacity-50[data-disabled]{opacity:.5}.ui\:data-highlighted\:bg-accent[data-highlighted]{background-color:var(--accent)}.ui\:data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--accent-foreground)}.ui\:data-inset\:pl-8[data-inset]{padding-left:calc(var(--ui-spacing) * 8)}.ui\:data-placeholder\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.ui\:data-\[active\=true\]\:bg-accent\/50[data-active=true]{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[active\=true\]\:bg-accent\/50[data-active=true]{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}.ui\:data-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.ui\:data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--ui-font-weight-medium);font-weight:var(--ui-font-weight-medium)}.ui\:data-\[active\=true\]\:text-accent-foreground[data-active=true]{color:var(--accent-foreground)}.ui\:data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}@media (hover:hover){.ui\:data-\[active\=true\]\:hover\:bg-accent[data-active=true]:hover{background-color:var(--accent)}}.ui\:data-\[active\=true\]\:focus\:bg-accent[data-active=true]:focus{background-color:var(--accent)}.ui\:data-\[current\=true\]\:bg-muted[data-current=true]{background-color:var(--muted)}.ui\:data-\[disabled\=true\]\:pointer-events-none[data-disabled=true]{pointer-events:none}.ui\:data-\[disabled\=true\]\:opacity-50[data-disabled=true]{opacity:.5}.ui\:data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--ui-spacing) * 8)}.ui\:data-\[invalid\=true\]\:text-destructive[data-invalid=true]{color:var(--destructive)}.ui\:data-\[motion\=from-end\]\:slide-in-from-end-52[data-motion=from-end]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=from-end\]\:slide-in-from-end-52[data-motion=from-end]:where(:dir(rtl),[dir=rtl]),.ui\:data-\[motion\=from-start\]\:slide-in-from-start-52[data-motion=from-start]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(52*var(--spacing)*-1)}.ui\:data-\[motion\=from-start\]\:slide-in-from-start-52[data-motion=from-start]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=to-end\]\:slide-out-to-end-52[data-motion=to-end]:where(:dir(ltr),[dir=ltr]){--tw-exit-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\=to-end\]\:slide-out-to-end-52[data-motion=to-end]:where(:dir(rtl),[dir=rtl]),.ui\:data-\[motion\=to-start\]\:slide-out-to-start-52[data-motion=to-start]:where(:dir(ltr),[dir=ltr]){--tw-exit-translate-x:calc(52*var(--spacing)*-1)}.ui\:data-\[motion\=to-start\]\:slide-out-to-start-52[data-motion=to-start]:where(:dir(rtl),[dir=rtl]){--tw-exit-translate-x:calc(52*var(--spacing))}.ui\:data-\[motion\^\=from-\]\:animate-in[data-motion^=from-]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[motion\^\=from-\]\:fade-in[data-motion^=from-]{--tw-enter-opacity:0}.ui\:data-\[motion\^\=to-\]\:animate-out[data-motion^=to-]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[motion\^\=to-\]\:fade-out[data-motion^=to-]{--tw-exit-opacity:0}.ui\:data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.ui\:data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.ui\:data-\[orientation\=vertical\]\:h-auto[data-orientation=vertical]{height:auto}.ui\:data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.ui\:data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.ui\:data-\[side\=bottom\]\:-translate-x-1\/2[data-side=bottom]{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:-translate-y-\[calc\(-50\%\+1px\)\][data-side=bottom]{--tw-translate-y:calc(calc(-50% + 1px) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--ui-spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=left\]\:-translate-y-\[calc\(50\%-3px\)\][data-side=left]{--tw-translate-y:calc(calc(50% - 3px) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=left\]\:slide-in-from-end-2[data-side=left]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--ui-spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:translate-x-\[calc\(50\%\+2px\)\][data-side=right]{--tw-translate-x:calc(50% + 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:translate-y-1\/2[data-side=right]{--tw-translate-y:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(ltr),[dir=ltr]){--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=right\]\:slide-in-from-start-2[data-side=right]:where(:dir(rtl),[dir=rtl]){--tw-enter-translate-x:calc(2*var(--spacing))}.ui\:data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.ui\:data-\[side\=top\]\:translate-x-1\/2[data-side=top]{--tw-translate-x:calc(1 / 2 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--ui-spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:translate-y-\[calc\(-50\%\+2px\)\][data-side=top]{--tw-translate-y:calc(-50% + 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.ui\:data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--ui-spacing) * 9)}.ui\:data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--ui-spacing) * 8)}.ui\:data-\[slot\=checkbox-group\]\:gap-3[data-slot=checkbox-group]{gap:calc(var(--ui-spacing) * 3)}:is(.ui\:\*\:data-\[slot\=field\]\:p-4>*)[data-slot=field]{padding:calc(var(--ui-spacing) * 4)}:is(.ui\:\*\:data-\[slot\=field-group\]\:gap-4>*)[data-slot=field-group]{gap:calc(var(--ui-spacing) * 4)}:is(.ui\:\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:ring-0 *)[data-slot=navigation-menu-link]:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:is(.ui\:\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:outline-none *)[data-slot=navigation-menu-link]:focus{--tw-outline-style:none;outline-style:none}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:line-clamp-1 *)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:flex *)[data-slot=select-value]{display:flex}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:items-center *)[data-slot=select-value]{align-items:center}:is(.ui\:\*\*\:data-\[slot\=select-value\]\:gap-2 *)[data-slot=select-value]{gap:calc(var(--ui-spacing) * 2)}.ui\:data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.ui\:data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state=checked]{--tw-translate-x:calc(100% - 2px);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.ui\:data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.ui\:data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.ui\:data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.ui\:data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.ui\:data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.ui\:data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.ui\:data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.ui\:data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.ui\:data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:animate-out:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:fade-out-0:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{--tw-exit-opacity:0}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:zoom-out-95:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=closed]{--tw-exit-scale:.95}.ui\:data-\[state\=hidden\]\:animate-out[data-state=hidden]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=hidden\]\:fade-out[data-state=hidden]{--tw-exit-opacity:0}.ui\:data-\[state\=off\]\:text-muted-foreground[data-state=off]{color:var(--muted-foreground)}@media (hover:hover){.ui\:hover\:data-\[state\=off\]\:text-accent-foreground:hover[data-state=off]{color:var(--accent-foreground)}}.ui\:data-\[state\=on\]\:bg-accent[data-state=on]{background-color:var(--accent)}.ui\:data-\[state\=on\]\:bg-primary[data-state=on]{background-color:var(--primary)}.ui\:data-\[state\=on\]\:bg-transparent[data-state=on]{background-color:#0000}.ui\:data-\[state\=on\]\:text-accent-foreground[data-state=on]{color:var(--accent-foreground)}.ui\:data-\[state\=on\]\:text-muted-foreground[data-state=on]{color:var(--muted-foreground)}.ui\:data-\[state\=on\]\:text-primary-foreground[data-state=on]{color:var(--primary-foreground)}@media (hover:hover){.ui\:hover\:data-\[state\=on\]\:text-accent-foreground:hover[data-state=on]{color:var(--accent-foreground)}}.ui\:data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.ui\:data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.ui\:data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.ui\:data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.ui\:data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.ui\:data-\[state\=open\]\:zoom-in-90[data-state=open]{--tw-enter-scale:.9}.ui\:data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.ui\:data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.ui\:data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.ui\:data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.ui\:data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:animate-in:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:fade-in-0:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{--tw-enter-opacity:0}.ui\:group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:zoom-in-95:is(:where(.ui\:group\/navigation-menu)[data-viewport=false] *)[data-state=open]{--tw-enter-scale:.95}@media (hover:hover){.ui\:data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.ui\:data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.ui\:data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.ui\:data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--ui-spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.ui\:data-\[state\=visible\]\:animate-in[data-state=visible]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.ui\:data-\[state\=visible\]\:fade-in[data-state=visible]{--tw-enter-opacity:0}.ui\:data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.ui\:data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.ui\:data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.ui\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:data-\[variant\=destructive\]\:data-highlighted\:bg-destructive\/10[data-variant=destructive][data-highlighted]{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.ui\:data-\[variant\=destructive\]\:data-highlighted\:text-destructive[data-variant=destructive][data-highlighted]{color:var(--destructive)}.ui\:data-\[variant\=label\]\:text-sm[data-variant=label]{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:data-\[variant\=legend\]\:text-base[data-variant=legend]{font-size:var(--ui-text-base);line-height:var(--tw-leading,var(--ui-text-base--line-height))}.ui\:data-\[vaul-drawer-direction\=bottom\]\:inset-x-0[data-vaul-drawer-direction=bottom]{inset-inline:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:bottom-0[data-vaul-drawer-direction=bottom]{bottom:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:mt-24[data-vaul-drawer-direction=bottom]{margin-top:calc(var(--ui-spacing) * 24)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:max-h-\[80vh\][data-vaul-drawer-direction=bottom]{max-height:80vh}.ui\:data-\[vaul-drawer-direction\=bottom\]\:rounded-t-lg[data-vaul-drawer-direction=bottom]{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.ui\:data-\[vaul-drawer-direction\=bottom\]\:border-t[data-vaul-drawer-direction=bottom]{border-top-style:var(--tw-border-style);border-top-width:1px}.ui\:data-\[vaul-drawer-direction\=left\]\:inset-y-0[data-vaul-drawer-direction=left]{inset-block:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=left\]\:start-0[data-vaul-drawer-direction=left]{inset-inline-start:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=left\]\:w-3\/4[data-vaul-drawer-direction=left]{width:75%}.ui\:data-\[vaul-drawer-direction\=left\]\:border-e[data-vaul-drawer-direction=left]{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.ui\:data-\[vaul-drawer-direction\=right\]\:inset-y-0[data-vaul-drawer-direction=right]{inset-block:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=right\]\:end-0[data-vaul-drawer-direction=right]{inset-inline-end:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=right\]\:w-3\/4[data-vaul-drawer-direction=right]{width:75%}.ui\:data-\[vaul-drawer-direction\=right\]\:border-s[data-vaul-drawer-direction=right]{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.ui\:data-\[vaul-drawer-direction\=top\]\:inset-x-0[data-vaul-drawer-direction=top]{inset-inline:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=top\]\:top-0[data-vaul-drawer-direction=top]{top:calc(var(--ui-spacing) * 0)}.ui\:data-\[vaul-drawer-direction\=top\]\:mb-24[data-vaul-drawer-direction=top]{margin-bottom:calc(var(--ui-spacing) * 24)}.ui\:data-\[vaul-drawer-direction\=top\]\:max-h-\[80vh\][data-vaul-drawer-direction=top]{max-height:80vh}.ui\:data-\[vaul-drawer-direction\=top\]\:rounded-b-lg[data-vaul-drawer-direction=top]{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.ui\:data-\[vaul-drawer-direction\=top\]\:border-b[data-vaul-drawer-direction=top]{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:nth-last-2\:-mt-1:nth-last-child(2){margin-top:calc(var(--ui-spacing) * -1)}@supports ((-webkit-backdrop-filter:var(--tw)) or (backdrop-filter:var(--tw))){.ui\:supports-\[backdrop-filter\]\:bg-background\/80{background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){.ui\:supports-\[backdrop-filter\]\:bg-background\/80{background-color:color-mix(in oklab, var(--background) 80%, transparent)}}}@media (min-width:40rem){.ui\:sm\:flex{display:flex}.ui\:sm\:inline-flex{display:inline-flex}.ui\:sm\:w-48{width:calc(var(--ui-spacing) * 48)}.ui\:sm\:w-64{width:calc(var(--ui-spacing) * 64)}.ui\:sm\:max-w-lg{max-width:var(--ui-container-lg)}.ui\:sm\:max-w-sm{max-width:var(--ui-container-sm)}.ui\:sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:sm\:flex-row{flex-direction:row}.ui\:sm\:items-end{align-items:flex-end}.ui\:sm\:justify-end{justify-content:flex-end}.ui\:sm\:gap-2\.5{gap:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:p-6{padding:calc(var(--ui-spacing) * 6)}.ui\:sm\:px-6{padding-inline:calc(var(--ui-spacing) * 6)}.ui\:sm\:py-32{padding-block:calc(var(--ui-spacing) * 32)}.ui\:sm\:pr-2\.5{padding-right:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:pl-2\.5{padding-left:calc(var(--ui-spacing) * 2.5)}.ui\:sm\:text-3xl{font-size:var(--ui-text-3xl);line-height:var(--tw-leading,var(--ui-text-3xl--line-height))}.ui\:sm\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:sm\:text-xl{font-size:var(--ui-text-xl);line-height:var(--tw-leading,var(--ui-text-xl--line-height))}.ui\:data-\[vaul-drawer-direction\=left\]\:sm\:max-w-sm[data-vaul-drawer-direction=left],.ui\:data-\[vaul-drawer-direction\=right\]\:sm\:max-w-sm[data-vaul-drawer-direction=right]{max-width:var(--ui-container-sm)}}@media (min-width:48rem){.ui\:md\:block{display:block}.ui\:md\:flex{display:flex}.ui\:md\:w-52{width:calc(var(--ui-spacing) * 52)}.ui\:md\:w-full{width:100%}.ui\:md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:md\:grid-cols-\[minmax\(0\,220px\)_1fr\]{grid-template-columns:minmax(0,220px) 1fr}.ui\:md\:flex-col{flex-direction:column}.ui\:md\:flex-row{flex-direction:row}.ui\:md\:items-start{align-items:flex-start}.ui\:md\:overflow-x-visible{overflow-x:visible}.ui\:md\:overflow-y-visible{overflow-y:visible}.ui\:md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.ui\:md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:md\:p-12{padding:calc(var(--ui-spacing) * 12)}.ui\:md\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:md\:pb-24{padding-bottom:calc(var(--ui-spacing) * 24)}.ui\:md\:text-6xl{font-size:var(--ui-text-6xl);line-height:var(--tw-leading,var(--ui-text-6xl--line-height))}.ui\:md\:text-sm{font-size:var(--ui-text-sm);line-height:var(--tw-leading,var(--ui-text-sm--line-height))}.ui\:md\:opacity-0{opacity:0}.ui\:md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.ui\:peer)[data-variant=inset]~*){margin:calc(var(--ui-spacing) * 2)}.ui\:md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.ui\:peer)[data-variant=inset]~*){margin-left:calc(var(--ui-spacing) * 0)}.ui\:md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.ui\:peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.ui\:md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.ui\:peer)[data-variant=inset]~*){--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ui\:md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.ui\:peer)[data-variant=inset]~*):is(:where(.ui\:peer)[data-state=collapsed]~*){margin-left:calc(var(--ui-spacing) * 2)}.ui\:md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (min-width:64rem){.ui\:lg\:block{display:block}.ui\:lg\:hidden{display:none}.ui\:lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ui\:lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.ui\:lg\:px-6{padding-inline:calc(var(--ui-spacing) * 6)}.ui\:lg\:px-8{padding-inline:calc(var(--ui-spacing) * 8)}.ui\:lg\:py-10{padding-block:calc(var(--ui-spacing) * 10)}.ui\:lg\:pb-12{padding-bottom:calc(var(--ui-spacing) * 12)}.ui\:lg\:text-5xl{font-size:var(--ui-text-5xl);line-height:var(--tw-leading,var(--ui-text-5xl--line-height))}.ui\:lg\:text-6xl{font-size:var(--ui-text-6xl);line-height:var(--tw-leading,var(--ui-text-6xl--line-height))}.ui\:lg\:text-7xl{font-size:var(--ui-text-7xl);line-height:var(--tw-leading,var(--ui-text-7xl--line-height))}}.ui\:dark\:scale-0:where(.dark,.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:dark\:scale-100:where(.dark,.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.ui\:dark\:-rotate-90:where(.dark,.dark *){rotate:-90deg}.ui\:dark\:rotate-0:where(.dark,.dark *){rotate:none}.ui\:dark\:border-amber-900:where(.dark,.dark *){border-color:var(--ui-color-amber-900)}.ui\:dark\:border-input:where(.dark,.dark *){border-color:var(--input)}.ui\:dark\:border-sky-900:where(.dark,.dark *){border-color:var(--ui-color-sky-900)}.ui\:dark\:border-slate-800:where(.dark,.dark *){border-color:var(--ui-color-slate-800)}.ui\:dark\:bg-amber-500:where(.dark,.dark *){background-color:var(--ui-color-amber-500)}.ui\:dark\:bg-amber-950:where(.dark,.dark *){background-color:var(--ui-color-amber-950)}.ui\:dark\:bg-destructive\/60:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-destructive\/60:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 60%, transparent)}}.ui\:dark\:bg-destructive\/70:where(.dark,.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-destructive\/70:where(.dark,.dark *){background-color:color-mix(in oklab, var(--destructive) 70%, transparent)}}.ui\:dark\:bg-emerald-500:where(.dark,.dark *){background-color:var(--ui-color-emerald-500)}.ui\:dark\:bg-input\/30:where(.dark,.dark *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:bg-input\/30:where(.dark,.dark *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.ui\:dark\:bg-sky-950:where(.dark,.dark *){background-color:var(--ui-color-sky-950)}.ui\:dark\:bg-slate-950:where(.dark,.dark *){background-color:var(--ui-color-slate-950)}.ui\:dark\:bg-transparent:where(.dark,.dark *){background-color:#0000}.ui\:dark\:fill-amber-400:where(.dark,.dark *){fill:var(--ui-color-amber-400)}.ui\:dark\:text-amber-50:where(.dark,.dark *){color:var(--ui-color-amber-50)}.ui\:dark\:text-amber-300:where(.dark,.dark *){color:var(--ui-color-amber-300)}.ui\:dark\:text-amber-400:where(.dark,.dark *){color:var(--ui-color-amber-400)}.ui\:dark\:text-amber-500\/40:where(.dark,.dark *){color:var(--ui-color-amber-500)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:text-amber-500\/40:where(.dark,.dark *){color:color-mix(in oklab, var(--ui-color-amber-500) 40%, transparent)}}.ui\:dark\:text-amber-950:where(.dark,.dark *){color:var(--ui-color-amber-950)}.ui\:dark\:text-emerald-950:where(.dark,.dark *){color:var(--ui-color-emerald-950)}.ui\:dark\:text-muted-foreground:where(.dark,.dark *){color:var(--muted-foreground)}.ui\:dark\:text-sky-50:where(.dark,.dark *){color:var(--ui-color-sky-50)}.ui\:dark\:text-white:where(.dark,.dark *){color:var(--ui-color-white)}@media (hover:hover){.ui\:dark\:hover\:border-input:where(.dark,.dark *):hover{border-color:var(--input)}.ui\:dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:hover\:bg-input\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}}.ui\:dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:focus-visible\:ring-destructive\/40:where(.dark,.dark *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}:where([data-slot=tooltip-content]) .ui\:dark\:in-data-\[slot\=tooltip-content\]\:bg-background\/10:where(.dark,.dark *){background-color:var(--background)}@supports (color:color-mix(in lab, red, red)){:where([data-slot=tooltip-content]) .ui\:dark\:in-data-\[slot\=tooltip-content\]\:bg-background\/10:where(.dark,.dark *){background-color:color-mix(in oklab, var(--background) 10%, transparent)}}.ui\:dark\:has-data-\[state\=checked\]\:bg-primary\/10:where(.dark,.dark *):has([data-state=checked]){background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:has-data-\[state\=checked\]\:bg-primary\/10:where(.dark,.dark *):has([data-state=checked]){background-color:color-mix(in oklab, var(--primary) 10%, transparent)}}.ui\:dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:where(.dark,.dark *):has([data-slot][aria-invalid=true]){--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.ui\:dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:aria-invalid\:ring-destructive\/40:where(.dark,.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.ui\:dark\:data-\[state\=active\]\:border-input:where(.dark,.dark *)[data-state=active]{border-color:var(--input)}.ui\:dark\:data-\[state\=active\]\:bg-input\/30:where(.dark,.dark *)[data-state=active]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:data-\[state\=active\]\:bg-input\/30:where(.dark,.dark *)[data-state=active]{background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.ui\:dark\:data-\[state\=active\]\:text-foreground:where(.dark,.dark *)[data-state=active]{color:var(--foreground)}.ui\:dark\:data-\[state\=checked\]\:bg-primary:where(.dark,.dark *)[data-state=checked]{background-color:var(--primary)}.ui\:dark\:data-\[state\=checked\]\:bg-primary-foreground:where(.dark,.dark *)[data-state=checked]{background-color:var(--primary-foreground)}.ui\:dark\:data-\[state\=unchecked\]\:bg-foreground:where(.dark,.dark *)[data-state=unchecked]{background-color:var(--foreground)}.ui\:dark\:data-\[state\=unchecked\]\:bg-input\/80:where(.dark,.dark *)[data-state=unchecked]{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.ui\:dark\:data-\[state\=unchecked\]\:bg-input\/80:where(.dark,.dark *)[data-state=unchecked]{background-color:color-mix(in oklab, var(--input) 80%, transparent)}}.ui\:\[\&_\.lc-area-path\]\:opacity-100 .lc-area-path{opacity:1}.ui\:\[\&_\.lc-axis-tick\]\:stroke-0 .lc-axis-tick{stroke-width:0}.ui\:\[\&_\.lc-axis-tick-label\]\:fill-muted-foreground .lc-axis-tick-label{fill:var(--muted-foreground)}.ui\:\[\&_\.lc-axis-tick-label\]\:font-normal .lc-axis-tick-label{--tw-font-weight:var(--ui-font-weight-normal);font-weight:var(--ui-font-weight-normal)}.ui\:\[\&_\.lc-grid-x-radial-circle\]\:stroke-border .lc-grid-x-radial-circle,.ui\:\[\&_\.lc-grid-x-radial-line\]\:stroke-border .lc-grid-x-radial-line,.ui\:\[\&_\.lc-grid-y-radial-circle\]\:stroke-border .lc-grid-y-radial-circle,.ui\:\[\&_\.lc-grid-y-radial-line\]\:stroke-border .lc-grid-y-radial-line{stroke:var(--border)}.ui\:\[\&_\.lc-highlight-line\]\:stroke-0 .lc-highlight-line{stroke-width:0}.ui\:\[\&_\.lc-highlight-line\]\:opacity-100 .lc-highlight-line,.ui\:\[\&_\.lc-highlight-point\]\:opacity-100 .lc-highlight-point{opacity:1}.ui\:\[\&_\.lc-labels-text\:not\(\[fill\]\)\]\:fill-foreground .lc-labels-text:not([fill]){fill:var(--foreground)}.ui\:\[\&_\.lc-layout-svg-g\]\:fill-transparent .lc-layout-svg-g{fill:#0000}.ui\:\[\&_\.lc-legend-swatch\]\:size-2\.5 .lc-legend-swatch{width:calc(var(--ui-spacing) * 2.5);height:calc(var(--ui-spacing) * 2.5)}.ui\:\[\&_\.lc-legend-swatch\]\:rounded-\[2px\] .lc-legend-swatch{border-radius:2px}.ui\:\[\&_\.lc-legend-swatch-button\]\:items-center .lc-legend-swatch-button{align-items:center}.ui\:\[\&_\.lc-legend-swatch-button\]\:gap-1\.5 .lc-legend-swatch-button{gap:calc(var(--ui-spacing) * 1.5)}.ui\:\[\&_\.lc-legend-swatch-group\]\:items-center .lc-legend-swatch-group{align-items:center}.ui\:\[\&_\.lc-legend-swatch-group\]\:gap-4 .lc-legend-swatch-group{gap:calc(var(--ui-spacing) * 4)}.ui\:\[\&_\.lc-line\]\:stroke-border\/50 .lc-line{stroke:var(--border)}@supports (color:color-mix(in lab, red, red)){.ui\:\[\&_\.lc-line\]\:stroke-border\/50 .lc-line{stroke:color-mix(in oklab, var(--border) 50%, transparent)}}.ui\:\[\&_\.lc-root-container\]\:w-full .lc-root-container{width:100%}.ui\:\[\&_\.lc-rule-x-line\:not\(\.lc-grid-x-rule\)\]\:stroke-0 .lc-rule-x-line:not(.lc-grid-x-rule),.ui\:\[\&_\.lc-rule-y-line\:not\(\.lc-grid-y-rule\)\]\:stroke-0 .lc-rule-y-line:not(.lc-grid-y-rule){stroke-width:0}.ui\:\[\&_\.lc-spline-path\]\:opacity-100 .lc-spline-path{opacity:1}.ui\:\[\&_\.lc-text\]\:text-xs .lc-text{font-size:var(--ui-text-xs);line-height:var(--tw-leading,var(--ui-text-xs--line-height))}.ui\:\[\&_\.lc-text-svg\]\:overflow-visible .lc-text-svg{overflow:visible}.ui\:\[\&_\.lc-tooltip-rects-g\]\:fill-transparent .lc-tooltip-rects-g{fill:#0000}.ui\:\[\&_\.stroke-white\]\:stroke-transparent .stroke-white{stroke:#0000}.ui\:\[\&_\[data-command-group\]\:not\(\[hidden\]\)_\~\[data-command-group\]\]\:pt-0 [data-command-group]:not([hidden])~[data-command-group]{padding-top:calc(var(--ui-spacing) * 0)}.ui\:\[\&_\[data-command-input-wrapper\]_svg\]\:h-5 [data-command-input-wrapper] svg{height:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-input-wrapper\]_svg\]\:w-5 [data-command-input-wrapper] svg{width:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-item\]_svg\]\:h-5 [data-command-item] svg{height:calc(var(--ui-spacing) * 5)}.ui\:\[\&_\[data-command-item\]_svg\]\:w-5 [data-command-item] svg{width:calc(var(--ui-spacing) * 5)}.ui\:\[\&_img\]\:size-full img{width:100%;height:100%}.ui\:\[\&_img\]\:object-cover img{object-fit:cover}.ui\:\[\&_p\]\:leading-relaxed p{--tw-leading:var(--ui-leading-relaxed);line-height:var(--ui-leading-relaxed)}.ui\:\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.ui\:\[\&_svg\]\:size-8\! svg{width:calc(var(--ui-spacing) * 8)!important;height:calc(var(--ui-spacing) * 8)!important}.ui\:\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.ui\:\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--ui-spacing) * 3);height:calc(var(--ui-spacing) * 3)}.ui\:\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.ui\:\[\&_svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-4 svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&_svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-6 svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 6);height:calc(var(--ui-spacing) * 6)}.ui\:\[\&_text\]\:stroke-transparent text{stroke:#0000}.ui\:\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.ui\:\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.ui\:\[\&\+\[data-slot\=item-content\]\]\:flex-none+[data-slot=item-content]{flex:none}.ui\:\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--ui-spacing) * 0)}.ui\:\[\.border-b\]\:pb-3.border-b{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--ui-spacing) * 6)}.ui\:\[\.border-t\]\:pt-3.border-t{padding-top:calc(var(--ui-spacing) * 3)}.ui\:\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--ui-spacing) * 6)}.ui\:\[a\]\:transition-colors:is(a){transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--ui-default-transition-timing-function));transition-duration:var(--tw-duration,var(--ui-default-transition-duration))}@media (hover:hover){.ui\:\[a\]\:hover\:bg-accent\/50:is(a):hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui\:\[a\]\:hover\:bg-accent\/50:is(a):hover{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}}:is(.ui\:\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.ui\:\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.ui\:\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--ui-spacing) * 2)}:is(.ui\:data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive\![data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.ui\:\[\&\>\*\]\:focus-visible\:relative>:focus-visible{position:relative}.ui\:\[\&\>\*\]\:focus-visible\:z-10>:focus-visible{z-index:10}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:rounded-s-none>:not(:first-child){border-start-start-radius:0;border-end-start-radius:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:rounded-t-none>:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:border-s-0>:not(:first-child){border-inline-start-style:var(--tw-border-style);border-inline-start-width:0}.ui\:\[\&\>\*\:not\(\:first-child\)\]\:border-t-0>:not(:first-child){border-top-style:var(--tw-border-style);border-top-width:0}.ui\:\[\&\>\*\:not\(\:last-child\)\]\:rounded-e-none>:not(:last-child){border-start-end-radius:0;border-end-end-radius:0}.ui\:\[\&\>\*\:not\(\:last-child\)\]\:rounded-b-none>:not(:last-child){border-bottom-right-radius:0;border-bottom-left-radius:0}.ui\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto>[data-slot=field-label]{flex:auto}.ui\:has-\[select\[aria-hidden\=true\]\:last-child\]\:\[\&\>\[data-slot\=select-trigger\]\:last-of-type\]\:rounded-e-md:has(:is(select[aria-hidden=true]:last-child))>[data-slot=select-trigger]:last-of-type{border-start-end-radius:calc(var(--radius) - 2px);border-end-end-radius:calc(var(--radius) - 2px)}.ui\:\[\&\>\[data-slot\=select-trigger\]\:not\(\[class\*\=\'ui\:w-\'\]\)\]\:w-fit>[data-slot=select-trigger]:not([class*=ui\:w-]){width:fit-content}.ui\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content])>[role=checkbox],.ui\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has(>[data-slot=field-content]) [role=radio]{margin-top:1px}.ui\:\[\&\>a\]\:underline>a{text-decoration-line:underline}.ui\:\[\&\>a\]\:underline-offset-4>a{text-underline-offset:4px}.ui\:\[\&\>a\:hover\]\:text-primary>a:hover{color:var(--primary)}.ui\:\[\&\>button\]\:hidden>button{display:none}.ui\:\[\&\>input\]\:flex-1>input{flex:1}.ui\:has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has(>[data-align=block-end])>input{padding-top:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has(>[data-align=block-start])>input{padding-bottom:calc(var(--ui-spacing) * 3)}.ui\:has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pe-2:has(>[data-align=inline-end])>input{padding-inline-end:calc(var(--ui-spacing) * 2)}.ui\:has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:ps-2:has(>[data-align=inline-start])>input{padding-inline-start:calc(var(--ui-spacing) * 2)}.ui\:\[\&\>kbd\]\:rounded-\[calc\(var\(--radius\)-5px\)\]>kbd{border-radius:calc(var(--radius) - 5px)}.ui\:\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui\:\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.ui\:\[\&\>svg\]\:size-2\.5>svg{width:calc(var(--ui-spacing) * 2.5);height:calc(var(--ui-spacing) * 2.5)}.ui\:\[\&\>svg\]\:size-3>svg{width:calc(var(--ui-spacing) * 3);height:calc(var(--ui-spacing) * 3)}.ui\:\[\&\>svg\]\:size-3\.5>svg{width:calc(var(--ui-spacing) * 3.5);height:calc(var(--ui-spacing) * 3.5)}.ui\:\[\&\>svg\]\:size-4>svg{width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}.ui\:\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.ui\:\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--ui-spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.ui\:\[\&\>svg\]\:text-current>svg{color:currentColor}.ui\:\[\&\>svg\]\:text-muted-foreground>svg{color:var(--muted-foreground)}.ui\:\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.ui\:\[\&\>svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-3\.5>svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 3.5);height:calc(var(--ui-spacing) * 3.5)}.ui\:\[\&\>svg\:not\(\[class\*\=\'ui\:size-\'\]\)\]\:size-4>svg:not([class*=ui\:size-]){width:calc(var(--ui-spacing) * 4);height:calc(var(--ui-spacing) * 4)}@media (hover:hover){:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper)>th,:is(.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover,.ui\:hover\:\[\&\,\&\>svelte-css-wrapper\]\:\[\&\>th\,td\]\:bg-muted\/50:hover>svelte-css-wrapper) td{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}}.ui\:\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.ui\:\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}[data-side=left][data-collapsible=offcanvas] .ui\:\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--ui-spacing) * -2)}[data-side=left][data-state=collapsed] .ui\:\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .ui\:\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--ui-spacing) * -2)}[data-side=right][data-state=collapsed] .ui\:\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}[data-variant=legend]+.ui\:\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5{margin-top:calc(var(--ui-spacing) * -1.5)}@media (hover:hover){a.ui\:\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.ui\:\[a\&\]\:hover\:bg-amber-600\/90:hover{background-color:var(--ui-color-amber-600)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-amber-600\/90:hover{background-color:color-mix(in oklab, var(--ui-color-amber-600) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--destructive) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-emerald-600\/90:hover{background-color:var(--ui-color-emerald-600)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-emerald-600\/90:hover{background-color:color-mix(in oklab, var(--ui-color-emerald-600) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-primary\/30:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-primary\/30:hover{background-color:color-mix(in oklab, var(--primary) 30%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--primary) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}a.ui\:\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){a.ui\:\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab, var(--secondary) 90%, transparent)}}a.ui\:\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}a.ui\:\[a\&\]\:hover\:text-primary:hover{color:var(--primary)}a.ui\:\[a\&\]\:hover\:underline:hover{text-decoration-line:underline}a.ui\:dark\:\[a\&\]\:hover\:bg-accent\/50:where(.dark,.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){a.ui\:dark\:\[a\&\]\:hover\:bg-accent\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab, var(--accent) 50%, transparent)}}}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}@keyframes border-beam{to{offset-distance:100%}}@keyframes meteor{0%{opacity:1;transform:rotate(215deg)translate(0)}70%{opacity:1}to{opacity:0;transform:rotate(215deg)translate(-500px)}}@keyframes shimmer-marketing{0%,90%,to{background-position:calc(-100% - var(--shimmer-width)) 0}30%,60%{background-position:calc(100% + var(--shimmer-width)) 0}}@keyframes spin-around{0%{transform:translateZ(0)rotate(0)}15%,35%{transform:translateZ(0)rotate(90deg)}65%,85%{transform:translateZ(0)rotate(270deg)}to{transform:translateZ(0)rotate(360deg)}}@keyframes magicslide{to{transform:translate(calc(100cqw - 100%))}}:root{--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(48.8% .243 264.376);--primary-foreground:oklch(97% .014 254.604);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(80.9% .105 251.813);--chart-2:oklch(62.3% .214 259.815);--chart-3:oklch(54.6% .245 262.881);--chart-4:oklch(48.8% .243 264.376);--chart-5:oklch(42.4% .199 265.638);--radius:.625rem;--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(54.6% .245 262.881);--sidebar-primary-foreground:oklch(97% .014 254.604);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(42.4% .199 265.638);--primary-foreground:oklch(97% .014 254.604);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(80.9% .105 251.813);--chart-2:oklch(62.3% .214 259.815);--chart-3:oklch(54.6% .245 262.881);--chart-4:oklch(48.8% .243 264.376);--chart-5:oklch(42.4% .199 265.638);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(62.3% .214 259.815);--sidebar-primary-foreground:oklch(97% .014 254.604);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}@property --border-angle{syntax:"";inherits:false;initial-value:0deg}@keyframes border{to{--border-angle:360deg}}@keyframes flip{to{transform:rotate(360deg)}}@keyframes kitrotate{to{transform:rotate(90deg)}}@keyframes shine{0%{background-position:0 0}to{background-position:-200% 0}}.animate-gradient-border{background:linear-gradient(45deg, white, white) padding-box, conic-gradient(from var(--border-angle), #94a3b84d 75%, var(--primary,#1d4ee2) 82%, var(--primary-foreground,white) 88%, var(--primary,#1d4ee2) 92%, #94a3b84d 100%) border-box;border:1px solid #0000;transition:--border-angle 3s ease-in-out}.animate-gradient-border:hover{animation:4s ease-in-out infinite border}.dark.animate-gradient-border{background:linear-gradient(45deg, #000, #000) padding-box, conic-gradient(from var(--border-angle), #4755697a 75%, var(--primary,#1d4ee2) 82%, var(--primary-foreground,black) 88%, var(--primary,#1d4ee2) 92%, #4755697a 100%) border-box}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto)))))}to{height:0}} \ No newline at end of file diff --git a/packages/utils/src/agent-models/index.ts b/packages/utils/src/agent-models/index.ts index acac0161..5204dde0 100644 --- a/packages/utils/src/agent-models/index.ts +++ b/packages/utils/src/agent-models/index.ts @@ -7,36 +7,59 @@ * even though it isn't currently exposed to users. */ -export const AGENT_MODEL_IDS = ['gemini-2.5-flash', 'gpt-4o', 'claude-sonnet-4-5'] as const; +export const AGENT_MODEL_IDS = ['gemini-2.5-flash', 'gpt-5.4-mini', 'claude-sonnet-3-5', 'kimi-k2.6'] as const; export type AgentModelId = (typeof AGENT_MODEL_IDS)[number]; -export type AgentModelProvider = 'google' | 'openai' | 'anthropic'; +export type AgentModelProvider = 'google' | 'openai' | 'anthropic' | 'moonshot'; +export type AgentModelCostTier = 'low' | 'high'; export interface AgentModelDescriptor { provider: AgentModelProvider; label: string; /** The exact model id passed to the provider SDK. */ backendModelId: string; + /** Whether this model is available on the free plan. */ + isFree: boolean; + /** Cost tier shown in the model picker — 'low' ($) or 'high' ($$$). */ + costTier: AgentModelCostTier; } export const AGENT_MODELS: Record = { 'gemini-2.5-flash': { provider: 'google', label: 'Gemini 2.5 Flash', - backendModelId: 'gemini-2.5-flash' + backendModelId: 'gemini-2.5-flash', + isFree: true, + costTier: 'low' }, - 'gpt-4o': { + 'gpt-5.4-mini': { provider: 'openai', - label: 'GPT-4o', - backendModelId: 'gpt-4o' + label: 'GPT-5.4 Mini', + backendModelId: 'gpt-5.4-mini', + isFree: false, + costTier: 'low' }, - 'claude-sonnet-4-5': { + 'claude-sonnet-3-5': { provider: 'anthropic', - label: 'Claude Sonnet 4.5', - backendModelId: 'claude-sonnet-4-5-20250929' + label: 'Claude Sonnet 4.6', + backendModelId: 'claude-sonnet-4-6', + isFree: false, + costTier: 'high' + }, + 'kimi-k2.6': { + provider: 'moonshot', + label: 'Kimi K2.6', + backendModelId: 'kimi-k2.6', + isFree: true, + costTier: 'low' } }; -export const UI_PICKER_MODEL_IDS = ['gemini-2.5-flash', 'gpt-4o'] as const satisfies readonly AgentModelId[]; +export const UI_PICKER_MODEL_IDS = [ + 'gemini-2.5-flash', + 'kimi-k2.6', + 'gpt-5.4-mini', + 'claude-sonnet-3-5' +] as const satisfies readonly AgentModelId[]; export const DEFAULT_PICKER_MODEL_ID: AgentModelId = 'gemini-2.5-flash'; diff --git a/packages/utils/src/constants/error-codes.ts b/packages/utils/src/constants/error-codes.ts index 7dcd01f8..b3077334 100644 --- a/packages/utils/src/constants/error-codes.ts +++ b/packages/utils/src/constants/error-codes.ts @@ -94,6 +94,9 @@ export const ErrorCodes = { COURSE_IMPORT_FETCH_FAILED: 'COURSE_IMPORT_FETCH_FAILED', COURSE_IMPORT_UPDATE_FAILED: 'COURSE_IMPORT_UPDATE_FAILED', COURSE_IMPORT_PUBLISH_FAILED: 'COURSE_IMPORT_PUBLISH_FAILED', + SLUG_IN_USE: 'SLUG_IN_USE', + QUESTION_TYPE_NOT_ALLOWED_IN_PUBLIC_COURSE: 'QUESTION_TYPE_NOT_ALLOWED_IN_PUBLIC_COURSE', + PUBLIC_COURSE_CONVERSION_BLOCKED: 'PUBLIC_COURSE_CONVERSION_BLOCKED', // Lesson errors LESSON_NOT_FOUND: 'LESSON_NOT_FOUND', diff --git a/packages/utils/src/plans/data.ts b/packages/utils/src/plans/data.ts index 97995f32..03de958d 100644 --- a/packages/utils/src/plans/data.ts +++ b/packages/utils/src/plans/data.ts @@ -14,7 +14,7 @@ export default { 'Unlimited Programs', '20 Students', 'Advanced Course Builder (no video upload)', - '50K AI credits / month', + '500K AI credits / month', 'MCP access with 20 automation credits / month', 'ClassroomIO branding' ], @@ -40,6 +40,7 @@ export default { 'Unlimited Collaborators', '10K Students', 'Course Builder with Video Upload & Certificate', + '3M AI credits / month', 'MCP access with 500 automation credits / month', 'Custom Branding', 'Custom Domain', @@ -68,6 +69,7 @@ export default { FEATURES: [ 'Everything in Early Adopters plus:', 'Unlimited students', + '15M AI credits / month', 'Unlimited API requests', 'SSO', '24/7 Support', diff --git a/packages/utils/src/plans/index.ts b/packages/utils/src/plans/index.ts index 88586621..4014e1a7 100644 --- a/packages/utils/src/plans/index.ts +++ b/packages/utils/src/plans/index.ts @@ -1,6 +1,7 @@ export * from './constants'; export * from './automation'; export * from './utils'; +export * from './token-packs'; import PLANS from './data'; diff --git a/packages/utils/src/plans/token-packs.ts b/packages/utils/src/plans/token-packs.ts new file mode 100644 index 00000000..68aabb54 --- /dev/null +++ b/packages/utils/src/plans/token-packs.ts @@ -0,0 +1,13 @@ +export const TOKEN_PACK = { + TOKENS_PER_UNIT: 5_000_000, + PRICE_USD_CENTS: 500, + CURRENCY: 'USD' +} as const; + +export type TokenPackMetadata = { + kind: 'token_pack'; + orgId: string; + orgSlug: string; + triggeredBy: string; + tokensPerUnit: number; +}; diff --git a/packages/utils/src/validation/agent/agent.ts b/packages/utils/src/validation/agent/agent.ts index 0ed8bf40..a47710a8 100644 --- a/packages/utils/src/validation/agent/agent.ts +++ b/packages/utils/src/validation/agent/agent.ts @@ -1,10 +1,12 @@ import { z } from 'zod'; import { AGENT_MODEL_IDS } from '../../agent-models'; +const ZAgentCourseId = z.string().min(1); + // ─── POST /agent/chat ──────────────────────────────────────────────────────── export const ZAgentChatBody = z.object({ - courseId: z.string().uuid(), + courseId: ZAgentCourseId, messages: z.array(z.any()), // UIMessage[] from Vercel AI SDK — validated by the SDK itself model: z.enum(AGENT_MODEL_IDS).optional(), context: z @@ -21,7 +23,7 @@ export type TAgentChatBody = z.infer; // ─── POST /agent/upload ────────────────────────────────────────────────────── export const ZAgentUploadQuery = z.object({ - courseId: z.string().uuid(), + courseId: ZAgentCourseId, conversationId: z.string().uuid() }); @@ -30,7 +32,7 @@ export type TAgentUploadQuery = z.infer; // ─── GET /agent/status ─────────────────────────────────────────────────────── export const ZAgentStatusQuery = z.object({ - courseId: z.string().uuid() + courseId: ZAgentCourseId }); export type TAgentStatusQuery = z.infer; @@ -43,6 +45,19 @@ export const ZAgentCreditsBody = z.object({ export type TAgentCreditsBody = z.infer; +export const ZAgentCreditPurchase = z.object({ + orgId: z.string().uuid(), + triggeredBy: z.string().uuid().optional(), + providerOrderId: z.string().min(1), + tokens: z.number().int().positive(), + quantity: z.number().int().positive(), + unitPriceCents: z.number().int().nonnegative(), + currency: z.string().default('USD'), + payload: z.record(z.string(), z.unknown()).optional() +}); + +export type TAgentCreditPurchase = z.infer; + // ─── Supported MIME types ──────────────────────────────────────────────────── export const SUPPORTED_UPLOAD_MIME_TYPES = [ @@ -56,7 +71,7 @@ export const MAX_AGENT_DOCUMENT_SIZE = 5 * 1024 * 1024; // 5MB // ─── GET /agent/history (list conversations) ──────────────────────────────── export const ZAgentHistoryQuery = z.object({ - courseId: z.string().uuid() + courseId: ZAgentCourseId }); export type TAgentHistoryQuery = z.infer; @@ -72,7 +87,7 @@ export type TAgentConversationParam = z.infer; // ─── POST /agent/history (create conversation) ────────────────────────────── export const ZAgentConversationCreateBody = z.object({ - courseId: z.string().uuid(), + courseId: ZAgentCourseId, title: z.string().optional() }); @@ -107,3 +122,11 @@ export const ZAgentGenerateTitleBody = z.object({ export type TAgentGenerateTitleParam = z.infer; export type TAgentGenerateTitleBody = z.infer; + +// ─── POST /agent/generate-course-title ─────────────────────────────────────── + +export const ZAgentGenerateCourseTitleBody = z.object({ + prompt: z.string().min(1).max(2000) +}); + +export type TAgentGenerateCourseTitleBody = z.infer; diff --git a/packages/utils/src/validation/course/callout.ts b/packages/utils/src/validation/course/callout.ts new file mode 100644 index 00000000..2e260b9f --- /dev/null +++ b/packages/utils/src/validation/course/callout.ts @@ -0,0 +1,20 @@ +import * as z from 'zod'; + +/** + * Creator-configured callout shown on public course lesson pages. + * Rendered inline at the bottom of every item body, and as the full replacement + * for locked items. + */ +export const ZCourseCallout = z.object({ + title: z.string().min(1).max(120), + description: z.string().min(1).max(500), + buttonLabel: z.string().min(1).max(40), + buttonUrl: z.string().url() +}); +export type TCourseCallout = z.infer; + +/** + * Accepts the callout object, or `null` to clear it. + */ +export const ZCourseCalloutInput = ZCourseCallout.nullable(); +export type TCourseCalloutInput = z.infer; diff --git a/packages/utils/src/validation/course/course.ts b/packages/utils/src/validation/course/course.ts index 3240dfe9..97313d65 100644 --- a/packages/utils/src/validation/course/course.ts +++ b/packages/utils/src/validation/course/course.ts @@ -1,6 +1,7 @@ import * as z from 'zod'; import { ALLOWED_CONTENT_TYPES, ALLOWED_DOCUMENT_TYPES } from '../constants'; +import { ZCourseCalloutInput } from './callout'; export const ZCourseClone = z.object({ title: z.string().min(1), @@ -227,7 +228,7 @@ export type TComplianceSettings = z.infer; export const ZCourseCreateBase = z.object({ title: z.string().min(1), description: z.string().min(1), - type: z.enum(['LIVE_CLASS', 'SELF_PACED', 'COMPLIANCE']), + type: z.enum(['LIVE_CLASS', 'SELF_PACED', 'COMPLIANCE', 'PUBLIC']), organizationId: z.string().min(1), compliance: ZComplianceSettings.optional() }); @@ -333,7 +334,7 @@ export type TCertificationSettings = z.infer; export const ZCourseUpdateBase = z.object({ title: z.string().min(1).optional(), description: z.string().min(1).optional(), - type: z.enum(['LIVE_CLASS', 'SELF_PACED', 'COMPLIANCE']).optional(), + type: z.enum(['LIVE_CLASS', 'SELF_PACED', 'COMPLIANCE', 'PUBLIC']).optional(), logo: z.string().optional(), slug: z.string().optional(), isPublished: z.boolean().optional(), @@ -343,7 +344,8 @@ export const ZCourseUpdateBase = z.object({ currency: z.enum(['NGN', 'USD']).optional(), certificate: ZCertificationSettings.optional(), tagIds: z.array(z.uuid()).max(100).optional(), - compliance: ZComplianceSettings.optional() + compliance: ZComplianceSettings.optional(), + callout: ZCourseCalloutInput.optional() }); export const ZCourseUpdate = ZCourseUpdateBase.refine( diff --git a/packages/utils/src/validation/course/index.ts b/packages/utils/src/validation/course/index.ts index 24c196f4..d31d0eaa 100644 --- a/packages/utils/src/validation/course/index.ts +++ b/packages/utils/src/validation/course/index.ts @@ -6,7 +6,9 @@ */ export * from './course'; +export * from './callout'; export * from './compliance'; +export * from './public-course'; export * from './section'; export * from './invite'; export { ZCourseGetParam, type TCourseGetParam } from './course'; diff --git a/packages/utils/src/validation/course/public-course.ts b/packages/utils/src/validation/course/public-course.ts new file mode 100644 index 00000000..a4d6a454 --- /dev/null +++ b/packages/utils/src/validation/course/public-course.ts @@ -0,0 +1,19 @@ +import * as z from 'zod'; + +import { ZSlug } from '../shared/slug'; + +/** + * Route params + query schemas for the anonymous, SEO-indexable public course + * surface under `(org-site)/course/[courseSlug]`. + */ + +export const ZPublicCourseBySlugParam = z.object({ + courseSlug: ZSlug +}); +export type TPublicCourseBySlugParam = z.infer; + +export const ZPublicCourseItemParam = z.object({ + courseSlug: ZSlug, + itemSlug: ZSlug +}); +export type TPublicCourseItemParam = z.infer; diff --git a/packages/utils/src/validation/exercise/exercise-section.ts b/packages/utils/src/validation/exercise/exercise-section.ts new file mode 100644 index 00000000..7a642743 --- /dev/null +++ b/packages/utils/src/validation/exercise/exercise-section.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; + +export const ZExerciseSectionAfterBehavior = z.discriminatedUnion('action', [ + z.object({ action: z.literal('continue') }), + z.object({ action: z.literal('go_to_section'), exerciseSectionId: z.string().uuid() }), + z.object({ action: z.literal('submit') }) +]); + +export const ZExerciseSection = z.object({ + id: z.string().uuid().optional(), + title: z.string().min(1, 'Section title is required'), + description: z.string().optional().nullable(), + order: z.number().int().min(0), + colorTheme: z.enum(['blue', 'green', 'amber', 'rose', 'violet', 'slate']).default('blue'), + afterBehavior: ZExerciseSectionAfterBehavior.default({ action: 'continue' }), + questionIds: z.array(z.union([z.string(), z.number().int()])).optional() +}); + +export const ZExerciseSectionCreate = ZExerciseSection.omit({ id: true }); +export const ZExerciseSectionUpdate = ZExerciseSection.partial().required({ id: true }); + +export type TExerciseSection = z.infer; +export type TExerciseSectionAfterBehavior = z.infer; diff --git a/packages/utils/src/validation/exercise/exercise.ts b/packages/utils/src/validation/exercise/exercise.ts index fb072fba..23ba4394 100644 --- a/packages/utils/src/validation/exercise/exercise.ts +++ b/packages/utils/src/validation/exercise/exercise.ts @@ -1,8 +1,14 @@ import * as z from 'zod'; -import { getStarRatingMaxFromSettings, isValidStarRatingValue } from '@cio/question-types'; +import { + VIDEO_RECORDING_PLATFORM_MAX_DURATION_SECONDS, + getStarRatingMaxFromSettings, + isValidStarRatingValue +} from '@cio/question-types'; import { QUESTION_TYPE } from '../constants'; +import { ZSlug } from '../shared/slug'; +import { ZExerciseSection } from './exercise-section'; const EXERCISE_QUESTION_TYPE_ID_LITERALS = [ z.literal(QUESTION_TYPE.RADIO), @@ -18,7 +24,8 @@ const EXERCISE_QUESTION_TYPE_ID_LITERALS = [ z.literal(QUESTION_TYPE.HOTSPOT), z.literal(QUESTION_TYPE.LINK), z.literal(QUESTION_TYPE.WORD_BANK), - z.literal(QUESTION_TYPE.STAR) + z.literal(QUESTION_TYPE.STAR), + z.literal(QUESTION_TYPE.VIDEO_RECORDING) ] as const; export const ZExerciseQuestionTypeId = z.union(EXERCISE_QUESTION_TYPE_ID_LITERALS); @@ -110,6 +117,22 @@ const QUESTION_VALIDATION_RULES: Record { + const settings = question.settings ?? {}; + const rawDuration = settings.maxDurationSeconds; + const maxDurationSeconds = + typeof rawDuration === 'number' ? rawDuration : rawDuration != null ? Number(rawDuration) : NaN; + if (!Number.isFinite(maxDurationSeconds) || maxDurationSeconds <= 0) { + return 'Set a recording duration greater than 0 seconds'; + } + if (maxDurationSeconds > VIDEO_RECORDING_PLATFORM_MAX_DURATION_SECONDS) { + return `Recording duration cannot exceed ${VIDEO_RECORDING_PLATFORM_MAX_DURATION_SECONDS} seconds`; + } + return null; } ] @@ -118,6 +141,7 @@ const QUESTION_VALIDATION_RULES: Record; @@ -196,7 +221,10 @@ export const ZExerciseUpdate = z.object({ isUnlocked: z.boolean().optional(), dueBy: z.string().optional(), // Changed from iso.datetime() to string to match frontend format allowMultipleAttempts: z.boolean().optional(), - questions: z.array(ZExerciseUpdateQuestion).optional() + slug: ZSlug.optional(), + questions: z.array(ZExerciseUpdateQuestion).optional(), + sections: z.array(ZExerciseSection).optional(), + sectionDisplayMode: z.enum(['one_question', 'all_questions']).optional() }); export type TExerciseUpdate = z.infer; diff --git a/packages/utils/src/validation/exercise/index.ts b/packages/utils/src/validation/exercise/index.ts index 0ccfe408..a41ca801 100644 --- a/packages/utils/src/validation/exercise/index.ts +++ b/packages/utils/src/validation/exercise/index.ts @@ -1 +1,3 @@ export * from './exercise'; +export * from './exercise-section'; +export * from './video-recording'; diff --git a/packages/utils/src/validation/exercise/video-recording.ts b/packages/utils/src/validation/exercise/video-recording.ts new file mode 100644 index 00000000..0434ee45 --- /dev/null +++ b/packages/utils/src/validation/exercise/video-recording.ts @@ -0,0 +1,38 @@ +import * as z from 'zod'; +import { VIDEO_RECORDING_PLATFORM_MAX_DURATION_SECONDS } from '@cio/question-types'; + +export const VIDEO_RECORDING_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime'] as const; + +export const ZExerciseVideoRecordingParam = z.object({ + exerciseId: z.string().min(1), + questionId: z.string().regex(/^\d+$/).transform(Number) +}); + +export const ZExerciseVideoRecordingPlaybackParam = ZExerciseVideoRecordingParam.extend({ + submissionId: z.string().uuid() +}); + +export const ZExerciseVideoRecordingUploadInit = z.object({ + fileName: z.string().min(1), + mimeType: z.enum(VIDEO_RECORDING_MIME_TYPES), + size: z.number().int().positive() +}); + +export const ZExerciseVideoRecordingUploadComplete = z.object({ + assetId: z.string().uuid(), + storageKey: z.string().min(1), + fileName: z.string().min(1), + mimeType: z.enum(VIDEO_RECORDING_MIME_TYPES), + size: z.number().int().positive(), + durationSeconds: z + .number() + .positive() + .max(VIDEO_RECORDING_PLATFORM_MAX_DURATION_SECONDS + 2), + recordedAt: z.string().datetime(), + retakeCount: z.number().int().min(0).optional() +}); + +export type TExerciseVideoRecordingParam = z.infer; +export type TExerciseVideoRecordingPlaybackParam = z.infer; +export type TExerciseVideoRecordingUploadInit = z.infer; +export type TExerciseVideoRecordingUploadComplete = z.infer; diff --git a/packages/utils/src/validation/index.ts b/packages/utils/src/validation/index.ts index 5e7550d6..cf0a1a61 100644 --- a/packages/utils/src/validation/index.ts +++ b/packages/utils/src/validation/index.ts @@ -1,4 +1,5 @@ export * from './constants'; +export * from './shared'; export * from './account'; export * from './agent'; diff --git a/packages/utils/src/validation/lesson/lesson.ts b/packages/utils/src/validation/lesson/lesson.ts index 0f28388e..2853c175 100644 --- a/packages/utils/src/validation/lesson/lesson.ts +++ b/packages/utils/src/validation/lesson/lesson.ts @@ -1,5 +1,7 @@ import * as z from 'zod'; +import { ZSlug } from '../shared/slug'; + // Lesson Schemas export const ZLessonCreate = z.object({ title: z.string().min(1), @@ -10,7 +12,8 @@ export const ZLessonCreate = z.object({ lessonAt: z.string().optional(), teacherId: z.string().optional(), isUnlocked: z.boolean().optional(), - public: z.boolean().optional() + public: z.boolean().optional(), + slug: ZSlug.optional() }); export type TLessonCreate = z.infer; @@ -24,6 +27,7 @@ export const ZLessonUpdate = z.object({ teacherId: z.string().optional(), isUnlocked: z.boolean().optional(), public: z.boolean().optional(), + slug: ZSlug.optional(), isComplete: z.boolean().optional(), videoUrl: z.url().optional(), slideUrl: z.url().optional(), diff --git a/packages/utils/src/validation/shared/index.ts b/packages/utils/src/validation/shared/index.ts new file mode 100644 index 00000000..79d92727 --- /dev/null +++ b/packages/utils/src/validation/shared/index.ts @@ -0,0 +1 @@ +export * from './slug'; diff --git a/packages/utils/src/validation/shared/slug.ts b/packages/utils/src/validation/shared/slug.ts new file mode 100644 index 00000000..2274e6f8 --- /dev/null +++ b/packages/utils/src/validation/shared/slug.ts @@ -0,0 +1,57 @@ +import * as z from 'zod'; + +/** + * Slug format used by public-course URLs (and shared by `lesson.slug` and `exercise.slug`). + * Kebab-case, 1–80 chars, lowercase alphanumerics with single `-` separators, no leading/trailing dash. + */ +export const SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + +export const ZSlug = z + .string() + .min(1, { message: 'Slug is required' }) + .max(80, { message: 'Slug must be 80 characters or fewer' }) + .regex(SLUG_REGEX, { + message: 'Slug must be lowercase letters, numbers, and single dashes' + }); +export type TSlug = z.infer; + +/** + * Generate a kebab-case slug from a free-form title. Does not enforce uniqueness; + * callers must resolve collisions against the target namespace (e.g. per-course). + */ +export function slugifyTitle(title: string): string { + const normalized = title + .normalize('NFKD') + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 80) + .replace(/-+$/g, ''); + + return normalized || 'untitled'; +} + +/** + * Resolve a slug against a list of already-taken slugs by appending `-2`, `-3`, ... + * until a free value is found. Respects the 80-char cap by trimming the base slug + * as needed to fit the suffix. + */ +export function resolveSlugCollision(baseSlug: string, takenSlugs: Iterable): string { + const taken = new Set(takenSlugs); + + if (!taken.has(baseSlug)) { + return baseSlug; + } + + let counter = 2; + while (true) { + const suffix = `-${counter}`; + const trimmedBase = baseSlug.slice(0, 80 - suffix.length).replace(/-+$/g, '') || 'untitled'; + const candidate = `${trimmedBase}${suffix}`; + if (!taken.has(candidate)) { + return candidate; + } + counter += 1; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 573dfe7f..c94402bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,10 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + zod: ^4.1.8 + miniflare>zod: 3.22.3 + patchedDependencies: layerchart@2.0.0-next.27: hash: 51dabc4b4462058655e01199047915e5582cfd0492e97d6ecd0c244dc17e5a3a @@ -289,9 +293,12 @@ importers: '@lucide/svelte': specifier: ^0.545.0 version: 0.545.0(svelte@5.46.0) + '@polar-sh/sdk': + specifier: ^0.47.1 + version: 0.47.1 '@polar-sh/sveltekit': - specifier: ^0.2.1 - version: 0.2.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(zod@4.2.1) + specifier: ^0.7.5 + version: 0.7.5(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1))) '@sveltejs/adapter-auto': specifier: ^3.0.0 version: 3.3.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1))) @@ -772,19 +779,25 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^3.0.69 - version: 3.0.69(zod@3.25.76) + version: 3.0.69(zod@4.3.6) '@ai-sdk/google': specifier: ^3.0.0 - version: 3.0.64(zod@3.25.76) + version: 3.0.64(zod@4.3.6) + '@ai-sdk/moonshotai': + specifier: ^2.0.21 + version: 2.0.21(zod@4.3.6) '@ai-sdk/openai': specifier: ^3.0.52 - version: 3.0.52(zod@3.25.76) + version: 3.0.52(zod@4.3.6) + '@cio/question-types': + specifier: workspace:* + version: link:../question-types ai: specifier: ^6.0.158 - version: 6.0.158(zod@3.25.76) + version: 6.0.158(zod@4.3.6) zod: - specifier: ^3.24.4 - version: 3.25.76 + specifier: ^4.1.8 + version: 4.3.6 devDependencies: '@cio/tsconfig': specifier: workspace:* @@ -1386,31 +1399,53 @@ packages: resolution: {integrity: sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 '@ai-sdk/gateway@3.0.95': resolution: {integrity: sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 '@ai-sdk/google@3.0.64': resolution: {integrity: sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 + + '@ai-sdk/moonshotai@2.0.21': + resolution: {integrity: sha512-wAfAE1VZ4muEldJLBDsKE2dVDGTNCCAXaUP8qxGAJG8e1U8qaR1UZQkTUNDvc3DdhTKPzff571rMKBAu3xOYEg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^4.1.8 + + '@ai-sdk/openai-compatible@2.0.46': + resolution: {integrity: sha512-23ExGdy3p0Grfz3BAjCbIOc74TjQc5nHu72e0+kx3hshvScp32a4nnQlzzG4VT1bDZxa9yPNNUNyb5nN6vJHcQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^4.1.8 '@ai-sdk/openai@3.0.52': resolution: {integrity: sha512-4Rr8NCGmfWTz6DCUvixn9UmyZcMatiHn0zWoMzI3JCUe9R1P/vsPOpCBALKoSzVYOjyJnhtnVIbfUKujcS39uw==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 '@ai-sdk/provider-utils@4.0.23': resolution: {integrity: sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 + + '@ai-sdk/provider-utils@4.0.26': + resolution: {integrity: sha512-CsKNLKsOpvPujRlIYvoz+Ybw+kGn7J4/fIZa/58+R7iWLLfwn6ifE2G6Yq8K9XvH/I/3bzaDAJ3NhRwEMsLBKQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^4.1.8 + + '@ai-sdk/provider@3.0.10': + resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==} + engines: {node: '>=18'} '@ai-sdk/provider@3.0.8': resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} @@ -3469,7 +3504,7 @@ packages: resolution: {integrity: sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw==} peerDependencies: hono: '>=3.9.0' - zod: ^3.25.0 || ^4.0.0 + zod: ^4.1.8 '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -3800,7 +3835,7 @@ packages: engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 + zod: ^4.1.8 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -4061,13 +4096,17 @@ packages: engines: {node: '>=18'} hasBin: true - '@polar-sh/sdk@0.20.2': - resolution: {integrity: sha512-Rrqt2jyg1VhO9Gfy3WpLF9bST5yxXGnNJ1a/vHw/N4CXWZNbafKjKVGI2f7t1emCosvzkT2BpIBf+b19CpUTiw==} - peerDependencies: - zod: '>= 3' + '@polar-sh/adapter-utils@0.4.5': + resolution: {integrity: sha512-ar9u2BMmF/6h0KQ2gRQBLPF/fKMcFyicZo1EpJU6awx84cmytVepw1LX5xQa7kMBxPgo8AIxUIXG5OPGeefnWQ==} - '@polar-sh/sveltekit@0.2.1': - resolution: {integrity: sha512-SYTy78YQnLaSYiz5BigDFzDSwcVCwFZgvSnNc50liiBKydAJu+ItHh70s7lQj+NOrlxmp2Ai0moQqCtrIuSM0w==} + '@polar-sh/sdk@0.46.7': + resolution: {integrity: sha512-g8/goRQFfnik9Ood2Ej5AsbEXv2rohzhVlmW7wjkCuwdc8i59FWhdBjolX2tUwcXXjugziXrhCZJLivY3W6ytQ==} + + '@polar-sh/sdk@0.47.1': + resolution: {integrity: sha512-fkz7wPLbqfuDmY9LxuXpE2uP2TAV6J0q/YN5hJ4UBxpjbkB0hKM6c4R35N89t83dfzMlG6EOlqOn+Rd1T6XrJQ==} + + '@polar-sh/sveltekit@0.7.5': + resolution: {integrity: sha512-1JtYxbLYIglwfzB9VygyfwYMleaXf8c5giHP4PjVqT3hH8+spQa9otFWAAJkvdxdodWSfuaAbo9HuosHQ0e6mA==} engines: {node: '>=16'} peerDependencies: '@sveltejs/kit': ^2.0.0 @@ -5033,7 +5072,7 @@ packages: sury: ^10.0.0 typebox: ^1.0.17 valibot: ^1.1.0 - zod: ^3.25.0 || ^4.0.0 + zod: ^4.1.8 zod-to-json-schema: ^3.24.5 peerDependenciesMeta: '@valibot/to-json-schema': @@ -5064,7 +5103,7 @@ packages: sury: ^10.0.0 typebox: ^1.0.0 valibot: ^1.1.0 - zod: ^3.25.0 || ^4.0.0 + zod: ^4.1.8 zod-openapi: ^4 peerDependenciesMeta: arktype: @@ -6569,7 +6608,7 @@ packages: resolution: {integrity: sha512-gLTp1UXFtMqKUi3XHs33K7UFglbvojkxF/aq337TxnLGOhHIW9+GyP2jwW4hYX87f1es+wId3VQoPRRu9zEStQ==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: ^4.1.8 ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} @@ -6874,7 +6913,7 @@ packages: better-call@1.1.8: resolution: {integrity: sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==} peerDependencies: - zod: ^4.0.0 + zod: ^4.1.8 peerDependenciesMeta: zod: optional: true @@ -8236,6 +8275,10 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} + eventsource-parser@3.0.8: + resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} + engines: {node: '>=18.0.0'} + eventsource@3.0.7: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} @@ -11363,7 +11406,7 @@ packages: peerDependencies: '@sveltejs/kit': ^2.21.0 svelte: ^5.7.0 - zod: ^4.1.0 + zod: ^4.1.8 peerDependenciesMeta: '@sveltejs/kit': optional: true @@ -13093,19 +13136,16 @@ packages: resolution: {integrity: sha512-DVLBNsnggh8k/Yq7qp+NeQCH6JuLIyGzUQWA1wN5+7DpQJSwC7WYR/2lE6uQDmGcS/X6l4tUyYFpeG4nk3nOpA==} engines: {node: '>=20'} peerDependencies: - zod: ^3.25.74 || ^4.0.0 + zod: ^4.1.8 zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: - zod: ^3.25 || ^4 + zod: ^4.1.8 zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.2.1: resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} @@ -13119,18 +13159,11 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@ai-sdk/anthropic@3.0.69(zod@3.25.76)': + '@ai-sdk/anthropic@3.0.69(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@3.25.76) - zod: 3.25.76 - - '@ai-sdk/gateway@3.0.95(zod@3.25.76)': - dependencies: - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@3.25.76) - '@vercel/oidc': 3.1.0 - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) + zod: 4.3.6 '@ai-sdk/gateway@3.0.95(zod@4.2.1)': dependencies: @@ -13139,24 +13172,37 @@ snapshots: '@vercel/oidc': 3.1.0 zod: 4.2.1 - '@ai-sdk/google@3.0.64(zod@3.25.76)': + '@ai-sdk/gateway@3.0.95(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) + '@vercel/oidc': 3.1.0 + zod: 4.3.6 - '@ai-sdk/openai@3.0.52(zod@3.25.76)': + '@ai-sdk/google@3.0.64(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) + zod: 4.3.6 - '@ai-sdk/provider-utils@4.0.23(zod@3.25.76)': + '@ai-sdk/moonshotai@2.0.21(zod@4.3.6)': + dependencies: + '@ai-sdk/openai-compatible': 2.0.46(zod@4.3.6) + '@ai-sdk/provider': 3.0.10 + '@ai-sdk/provider-utils': 4.0.26(zod@4.3.6) + zod: 4.3.6 + + '@ai-sdk/openai-compatible@2.0.46(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 3.0.10 + '@ai-sdk/provider-utils': 4.0.26(zod@4.3.6) + zod: 4.3.6 + + '@ai-sdk/openai@3.0.52(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) + zod: 4.3.6 '@ai-sdk/provider-utils@4.0.23(zod@4.2.1)': dependencies: @@ -13165,6 +13211,24 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.2.1 + '@ai-sdk/provider-utils@4.0.23(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 3.0.8 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 4.3.6 + + '@ai-sdk/provider-utils@4.0.26(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 3.0.10 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.8 + zod: 4.3.6 + + '@ai-sdk/provider@3.0.10': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/provider@3.0.8': dependencies: json-schema: 0.4.0 @@ -16152,17 +16216,25 @@ snapshots: dependencies: playwright: 1.57.0 - '@polar-sh/sdk@0.20.2(zod@4.2.1)': + '@polar-sh/adapter-utils@0.4.5': + dependencies: + '@polar-sh/sdk': 0.46.7 + + '@polar-sh/sdk@0.46.7': dependencies: standardwebhooks: 1.0.0 - zod: 4.2.1 + zod: 4.3.6 - '@polar-sh/sveltekit@0.2.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(zod@4.2.1)': + '@polar-sh/sdk@0.47.1': dependencies: - '@polar-sh/sdk': 0.20.2(zod@4.2.1) + standardwebhooks: 1.0.0 + zod: 4.3.6 + + '@polar-sh/sveltekit@0.7.5(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))': + dependencies: + '@polar-sh/adapter-utils': 0.4.5 + '@polar-sh/sdk': 0.46.7 '@sveltejs/kit': 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)))(svelte@5.46.0)(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)) - transitivePeerDependencies: - - zod '@polka/url@1.0.0-next.29': {} @@ -18254,7 +18326,7 @@ snapshots: recast: 0.23.11 source-map: 0.7.6 tsx: 4.21.0 - zod: 3.25.76 + zod: 4.3.6 transitivePeerDependencies: - supports-color optional: true @@ -18274,7 +18346,7 @@ snapshots: babel-dead-code-elimination: 1.0.12 chokidar: 3.6.0 unplugin: 2.3.11 - zod: 3.25.76 + zod: 4.3.6 optionalDependencies: '@tanstack/react-router': 1.143.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) vite: 5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1) @@ -18297,7 +18369,7 @@ snapshots: babel-dead-code-elimination: 1.0.12 chokidar: 3.6.0 unplugin: 2.3.11 - zod: 3.25.76 + zod: 4.3.6 optionalDependencies: '@tanstack/react-router': 1.143.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.1)(tsx@4.21.0)(yaml@2.8.2) @@ -18404,7 +18476,7 @@ snapshots: vite: 5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1) vitefu: 1.1.3(vite@5.4.21(@types/node@20.19.27)(lightningcss@1.32.0)(sass@1.97.1)) xmlbuilder2: 4.0.3 - zod: 3.25.76 + zod: 4.3.6 transitivePeerDependencies: - '@rsbuild/core' - '@tanstack/react-router' @@ -18437,7 +18509,7 @@ snapshots: vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.1)(tsx@4.21.0)(yaml@2.8.2) vitefu: 1.1.3(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.1)(tsx@4.21.0)(yaml@2.8.2)) xmlbuilder2: 4.0.3 - zod: 3.25.76 + zod: 4.3.6 transitivePeerDependencies: - '@rsbuild/core' - '@tanstack/react-router' @@ -19424,14 +19496,6 @@ snapshots: agent-base@7.1.4: {} - ai@6.0.158(zod@3.25.76): - dependencies: - '@ai-sdk/gateway': 3.0.95(zod@3.25.76) - '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@3.25.76) - '@opentelemetry/api': 1.9.0 - zod: 3.25.76 - ai@6.0.158(zod@4.2.1): dependencies: '@ai-sdk/gateway': 3.0.95(zod@4.2.1) @@ -19440,6 +19504,14 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.2.1 + ai@6.0.158(zod@4.3.6): + dependencies: + '@ai-sdk/gateway': 3.0.95(zod@4.3.6) + '@ai-sdk/provider': 3.0.8 + '@ai-sdk/provider-utils': 4.0.23(zod@4.3.6) + '@opentelemetry/api': 1.9.0 + zod: 4.3.6 + ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -21368,6 +21440,8 @@ snapshots: eventsource-parser@3.0.6: {} + eventsource-parser@3.0.8: {} + eventsource@3.0.7: dependencies: eventsource-parser: 3.0.6 @@ -27246,8 +27320,6 @@ snapshots: zod@3.22.3: {} - zod@3.25.76: {} - zod@4.2.1: {} zod@4.3.6: {} diff --git a/prd/ai-course-assistant/README.md b/prd/ai-course-assistant [DONE]/README.md similarity index 99% rename from prd/ai-course-assistant/README.md rename to prd/ai-course-assistant [DONE]/README.md index b10e915c..d56b6e23 100644 --- a/prd/ai-course-assistant/README.md +++ b/prd/ai-course-assistant [DONE]/README.md @@ -1,5 +1,9 @@ # AI Course Assistant +## Status + +- Done (shipped) + ## Purpose Deliver an AI assistant inside the course UI that teachers can use to: diff --git a/prd/ai-course-assistant/implementation-plan.md b/prd/ai-course-assistant [DONE]/implementation-plan.md similarity index 100% rename from prd/ai-course-assistant/implementation-plan.md rename to prd/ai-course-assistant [DONE]/implementation-plan.md diff --git a/prd/compliance-training-platform/README.md b/prd/compliance-training-platform [DONE]/README.md similarity index 99% rename from prd/compliance-training-platform/README.md rename to prd/compliance-training-platform [DONE]/README.md index 991c2f8a..c48b8a9e 100644 --- a/prd/compliance-training-platform/README.md +++ b/prd/compliance-training-platform [DONE]/README.md @@ -1,7 +1,7 @@ # Compliance Course Type PRD ## Status -- Draft +- Done (shipped) ## Date - February 21, 2026 (updated April 11, 2026) diff --git a/prd/compliance-training-platform/UX.md b/prd/compliance-training-platform [DONE]/UX.md similarity index 100% rename from prd/compliance-training-platform/UX.md rename to prd/compliance-training-platform [DONE]/UX.md diff --git a/prd/course-widget-embed/README.md b/prd/course-widget-embed [DONE]/README.md similarity index 99% rename from prd/course-widget-embed/README.md rename to prd/course-widget-embed [DONE]/README.md index ca6a1c51..f4368a09 100644 --- a/prd/course-widget-embed/README.md +++ b/prd/course-widget-embed [DONE]/README.md @@ -1,5 +1,9 @@ # Course Widget Embed PRD (Org Widgets) +## Status + +- Done (shipped) + ## Purpose Ship an organization-scoped widget system so admins can embed selected courses on external websites from `/org/[slug]/widgets`, with a visual editor and lightweight runtime. @@ -567,7 +571,7 @@ Parent (Dashboard) Iframe (Widget Runtime) ``` #### Documentation Output -- Spec file: `prd/course-widget-embed/iframe-protocol.md` (this section) +- Spec file: `prd/course-widget-embed [DONE]/iframe-protocol.md` (this section) - Implementation: `apps/embeds/src/widgets/course-widget/preview/iframe-bridge.ts` + `apps/dashboard/src/lib/features/widget/preview/iframe-parent.ts` ### TODO-3: Preview Data Freshness (Implementation Details) diff --git a/prd/course-widget-embed/implementation-plan.md b/prd/course-widget-embed [DONE]/implementation-plan.md similarity index 100% rename from prd/course-widget-embed/implementation-plan.md rename to prd/course-widget-embed [DONE]/implementation-plan.md diff --git a/prd/custom-domain-cloudflare/README.md b/prd/custom-domain-cloudflare [DONE]/README.md similarity index 99% rename from prd/custom-domain-cloudflare/README.md rename to prd/custom-domain-cloudflare [DONE]/README.md index 4fc75a24..360c44ad 100644 --- a/prd/custom-domain-cloudflare/README.md +++ b/prd/custom-domain-cloudflare [DONE]/README.md @@ -1,7 +1,7 @@ # Cloudflare Custom Subdomain Reconnect PRD ## Status -- Draft +- Done (shipped) ## Date - March 22, 2026 diff --git a/prd/exercise-question-types [DONE]/README.md b/prd/exercise-question-types [DONE]/README.md index 60a89064..35d80285 100644 --- a/prd/exercise-question-types [DONE]/README.md +++ b/prd/exercise-question-types [DONE]/README.md @@ -2,7 +2,7 @@ ## Status -- In progress +- In progress (remaining question-type work; exercise question sections and video recording are tracked as separate shipped PRDs — see PRD-TRACKER DONE) ## Date diff --git a/prd/exercise-sections/README.md b/prd/exercise-sections [DONE]/README.md similarity index 77% rename from prd/exercise-sections/README.md rename to prd/exercise-sections [DONE]/README.md index b7f3aff1..aae8e11e 100644 --- a/prd/exercise-sections/README.md +++ b/prd/exercise-sections [DONE]/README.md @@ -1,12 +1,12 @@ -# Exercise Sections PRD +# Exercise Question Sections PRD ## Status -- Draft +- Done (shipped) ## Date -- February 26, 2026 +- April 30, 2026 ## Implementation Plan @@ -14,7 +14,13 @@ ## Purpose -Allow instructors to group exercise questions into named sections, so exercises can be structured into logical parts (e.g., "Section A: Multiple Choice", "Section B: Written Responses") with per-section navigation, descriptions, and configurable post-section behavior. +Allow instructors to group questions inside a single exercise into named sections, so one assignment, quiz, or exam can be structured into logical parts (e.g., "Section A: Multiple Choice", "Section B: Written Responses") with per-section instructions, navigation, and review. + +## Terminology + +- **Exercise question section**: A grouping inside one exercise. This PRD is about this concept. +- **Course section**: The existing course content grouping that contains lessons and exercises. This PRD does not change course sections. +- The existing `exercise.sectionId` field points to a **course section**. New question-level grouping must use `question.exerciseSectionId` / DB `question.exercise_section_id` so the two concepts are not overloaded. ## Problem Statement @@ -29,8 +35,8 @@ Today, every exercise is a flat list of questions. Instructors cannot: | Area | Current State | Gap | | --- | --- | --- | -| `exercise` table | Has `title`, `description`, questions linked via `question.exercise_id` | No section concept | -| `question` table | Has `exercise_id`, `order`, `title`, `points`, `question_type_id` | No `section_id` or section ordering | +| `exercise` table | Has `title`, `description`, and `section_id` for the parent course section; questions link via `question.exercise_id` | No internal question-section concept | +| `question` table | Has `exercise_id`, `order`, `title`, `points`, `question_type_id` | No `exercise_section_id` or section ordering | | Exercise store | `questionnaire.questions` is a flat `Question[]` | No section grouping or section metadata | | Edit mode UI | Renders all questions in a single vertical list | No section headers, section CRUD, or drag-between-sections | | Take mode (view-mode) | Shows one question at a time via `currentQuestionIndex` on flat array | No section-at-a-time mode, no section overview screen | @@ -55,7 +61,7 @@ Today, every exercise is a flat list of questions. Instructors cannot: - `packages/ui/src/custom/exercise-question/question-navigation.svelte` - `packages/ui/src/custom/exercise-question/question-list.svelte` - `packages/question-types/src/exercise-types.ts` -- `prd/exercise-question-types/README.md` +- `prd/exercise-question-types [DONE]/README.md` ## Primary Users @@ -69,8 +75,9 @@ Today, every exercise is a flat list of questions. Instructors cannot: 2. Allow students to navigate exercises section-by-section with configurable question display (one-at-a-time or all-at-once within a section). 3. Show section-level subtotals alongside overall totals in grading. 4. Support configurable post-section behavior (continue, go to specific section, submit). -5. Maintain full backward compatibility — existing sectionless exercises work unchanged. -6. Support sections in exercise templates and cloning. +5. Let instructors assign lightweight visual color themes to sections for easier scanning while authoring. +6. Maintain full backward compatibility — existing sectionless exercises work unchanged. +7. Support sections in exercise templates and cloning. ## Non-Goals (v1) @@ -79,11 +86,12 @@ Today, every exercise is a flat list of questions. Instructors cannot: - Conditional/branching logic beyond post-section routing (no "if student scored < 50% on Section A, skip to Section C"). - Section-level randomization separate from exercise-level randomization. - Nested sections (sections within sections). +- Custom arbitrary color pickers. V1 uses a fixed set of color theme swatches. -## Confirmed Decisions +## Proposed Decisions / MVP Assumptions 1. An exercise is either **flat questions** (no sections) or **sections with questions** — not a mix of both. If an exercise has sections, every question must belong to a section. -2. A question belongs to exactly **one** section. No multi-section membership. +2. A question belongs to exactly **one exercise question section**. No multi-section membership. 3. Section scoring is the **sum of question points** within that section — no separate section-level weight multiplier. 4. Student display mode is **configurable per exercise**: either "one question at a time" (current behavior, but section-aware) or "one section at a time" (all questions in the current section shown together). 5. Students see a **section overview screen** before beginning each section (section title + description + question count + total points). @@ -94,6 +102,7 @@ Today, every exercise is a flat list of questions. Instructors cannot: 10. Existing exercises without sections continue to work exactly as they do today. Sections are **fully optional**. 11. Exercise templates support sections. Exercise cloning preserves sections. 12. Instructors can **drag questions between sections**, **reorder sections**, and **select which section** when adding a new question. +13. Section color themes are cosmetic only. They help authoring and review scanability and do not affect grading, routing, or student eligibility. --- @@ -112,7 +121,8 @@ Today, every exercise is a flat list of questions. Instructors cannot: - Instructor can edit the section **title** (required, min 1 character). - Instructor can edit the section **description** (optional, supports plain text). -- Instructor can configure the **after-section behavior** via a dropdown at the bottom of each section: +- Instructor can choose a section **color theme** from a fixed set of swatches. The selected theme appears as a dot in the section header and is persisted with the section. +- Instructor can configure the **after-section behavior** via a compact settings row near the section card when routing is in scope: - "Continue to next section" (default) - "Go to section [X]" (dropdown listing all other sections by title) - "Submit exercise" @@ -219,6 +229,7 @@ If the student finishes the last section and its after-behavior is "Continue to "title": "Section Title", "description": "Optional description", "order": 0, + "colorTheme": "blue", "afterBehavior": { "action": "continue" }, "questions": [{ "title": "...", ... }] } @@ -232,7 +243,7 @@ If the student finishes the last section and its after-behavior is "Continue to ### FR-6: Cloning - The clone service (`apps/api/src/services/course/clone.ts`) clones exercise sections alongside questions and options. -- Section ordering, titles, descriptions, and after-behavior settings are preserved. +- Section ordering, titles, descriptions, color themes, and after-behavior settings are preserved. - Question-to-section mappings are preserved via the section ID map. --- @@ -250,6 +261,7 @@ CREATE TABLE exercise_section ( title VARCHAR NOT NULL DEFAULT 'Untitled Section', description TEXT, "order" BIGINT NOT NULL DEFAULT 0, + color_theme VARCHAR NOT NULL DEFAULT 'blue', after_behavior JSONB NOT NULL DEFAULT '{"action": "continue"}', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() @@ -265,21 +277,21 @@ The `after_behavior` JSONB stores one of: { action: 'continue' } // Jump to a specific section -{ action: 'go_to_section', sectionId: '' } +{ action: 'go_to_section', exerciseSectionId: '' } // Submit the exercise immediately { action: 'submit' } ``` -#### 1.2: `question` Table — Add `section_id` +#### 1.2: `question` Table — Add `exercise_section_id` ```sql -ALTER TABLE question ADD COLUMN section_id UUID REFERENCES exercise_section(id) ON DELETE SET NULL; -CREATE INDEX idx_question_section_id ON question(section_id); +ALTER TABLE question ADD COLUMN exercise_section_id UUID REFERENCES exercise_section(id) ON DELETE SET NULL; +CREATE INDEX idx_question_exercise_section_id ON question(exercise_section_id); ``` -- `section_id` is **nullable**. When `NULL`, the question belongs to a sectionless exercise (backward compatible). -- When a question is part of a sectioned exercise, `section_id` is required (enforced at the application level, not DB constraint, to keep migration safe). +- `exercise_section_id` is **nullable**. When `NULL`, the question belongs to a sectionless exercise (backward compatible). +- When a question is part of a sectioned exercise, `exercise_section_id` is required (enforced at the application level, not DB constraint, to keep migration safe). #### 1.3: `exercise` Table — Add `section_display_mode` @@ -307,6 +319,7 @@ export const exerciseSection = pgTable( title: varchar().notNull().default('Untitled Section'), description: text(), order: bigint({ mode: 'number' }).notNull().default(0), + colorTheme: varchar('color_theme').notNull().default('blue'), afterBehavior: jsonb('after_behavior') .notNull() .default({ action: 'continue' }) @@ -328,16 +341,16 @@ export const exerciseSection = pgTable( Add to `question` table: ```typescript -sectionId: uuid('section_id'), +exerciseSectionId: uuid('exercise_section_id'), ``` Add foreign key in `question` table constraints: ```typescript foreignKey({ - columns: [table.sectionId], + columns: [table.exerciseSectionId], foreignColumns: [exerciseSection.id], - name: 'question_section_id_fkey' + name: 'question_exercise_section_id_fkey' }).onDelete('set null') ``` @@ -363,16 +376,18 @@ export type ExerciseSectionAfterAction = 'continue' | 'go_to_section' | 'submit' export interface ExerciseSectionAfterBehavior { action: ExerciseSectionAfterAction; - sectionId?: string; // only when action === 'go_to_section' + exerciseSectionId?: string; // only when action === 'go_to_section' } export type ExerciseSectionDisplayMode = 'one_question' | 'all_questions'; +export type ExerciseSectionColorTheme = 'blue' | 'green' | 'amber' | 'rose' | 'violet' | 'slate'; export interface ExerciseSectionModel { id?: string; title: string; description?: string | null; order: number; + colorTheme: ExerciseSectionColorTheme; afterBehavior: ExerciseSectionAfterBehavior; questions: ExerciseQuestionModel[]; } @@ -387,7 +402,7 @@ import { z } from 'zod'; export const ZExerciseSectionAfterBehavior = z.discriminatedUnion('action', [ z.object({ action: z.literal('continue') }), - z.object({ action: z.literal('go_to_section'), sectionId: z.string().uuid() }), + z.object({ action: z.literal('go_to_section'), exerciseSectionId: z.string().uuid() }), z.object({ action: z.literal('submit') }) ]); @@ -396,6 +411,7 @@ export const ZExerciseSection = z.object({ title: z.string().min(1, 'Section title is required'), description: z.string().optional().nullable(), order: z.number().int().min(0), + colorTheme: z.enum(['blue', 'green', 'amber', 'rose', 'violet', 'slate']).default('blue'), afterBehavior: ZExerciseSectionAfterBehavior.default({ action: 'continue' }) }); @@ -430,11 +446,11 @@ Required query functions: | --- | --- | | `getExerciseSectionsByExerciseId(exerciseId)` | Fetch all sections for an exercise, ordered by `order` | | `createExerciseSections(sections[])` | Bulk insert sections, return created rows | -| `updateExerciseSection(sectionId, data)` | Update a single section's title, description, order, or afterBehavior | -| `deleteExerciseSection(sectionId)` | Delete a section (questions cascade to `section_id = NULL`) | +| `updateExerciseSection(exerciseSectionId, data)` | Update a single section's title, description, colorTheme, order, or afterBehavior | +| `deleteExerciseSection(exerciseSectionId)` | Delete a section (questions cascade to `exercise_section_id = NULL`) | | `reorderExerciseSections(exerciseId, orderedIds[])` | Batch update `order` field for all sections | -| `moveQuestionToSection(questionId, sectionId)` | Update a question's `section_id` | -| `bulkMoveQuestionsToSection(questionIds[], sectionId)` | Move multiple questions to a section | +| `moveQuestionToSection(questionId, exerciseSectionId)` | Update a question's `exercise_section_id` | +| `bulkMoveQuestionsToSection(questionIds[], exerciseSectionId)` | Move multiple questions to a section | ### 4. API Layer Changes @@ -453,7 +469,8 @@ When fetching an exercise (`GET /course/:courseId/exercise/:exerciseId`), the re title: string; description: string | null; order: number; - afterBehavior: { action: string; sectionId?: string }; + colorTheme: 'blue' | 'green' | 'amber' | 'rose' | 'violet' | 'slate'; + afterBehavior: { action: string; exerciseSectionId?: string }; questions: Array<{ /* existing question shape */ }>; }>; questions: Array<{ /* questions without sections (backward compat) */ }>; @@ -478,7 +495,8 @@ The `PUT /course/:courseId/exercise/:exerciseId` endpoint accepts an optional `s title: string; description?: string; order: number; - afterBehavior: { action: string; sectionId?: string }; + colorTheme?: 'blue' | 'green' | 'amber' | 'rose' | 'violet' | 'slate'; + afterBehavior: { action: string; exerciseSectionId?: string }; questionIds: string[]; // ordered list of question IDs in this section }>; questions?: Array<{ /* existing question update shape */ }>; @@ -488,7 +506,7 @@ The `PUT /course/:courseId/exercise/:exerciseId` endpoint accepts an optional `s Service logic: 1. Upsert sections (create new, update existing, delete removed). -2. Update `question.section_id` for each question based on `questionIds` in each section. +2. Update `question.exercise_section_id` for each question based on `questionIds` in each section. 3. Update `question.order` within each section. 4. Validate that all `go_to_section` references point to valid section IDs within the same exercise. 5. Validate that if sections exist, every non-deleted question has a section assignment. @@ -504,7 +522,7 @@ When fetching submissions for grading, the API returns section groupings: ```typescript { sections: Array<{ - sectionId: string; + exerciseSectionId: string; sectionTitle: string; subtotal: number; maxPoints: number; @@ -526,7 +544,7 @@ Update `cloneExercises` to: 1. After creating new exercises, fetch sections for all old exercises. 2. Create a section ID map (old → new). 3. Bulk insert new sections with the new exercise IDs. -4. When cloning questions, include the mapped `sectionId`. +4. When cloning questions, include the mapped `exerciseSectionId`. ```typescript // Pseudocode addition to cloneExercises(): @@ -539,22 +557,23 @@ if (oldSections.length > 0) { title: section.title, description: section.description, order: section.order, + colorTheme: section.colorTheme, afterBehavior: section.afterBehavior, exerciseId: exerciseIdMap.get(section.exerciseId)! })) ); - const sectionIdMap = new Map(); + const exerciseSectionIdMap = new Map(); oldSections.forEach((oldSection, index) => { - sectionIdMap.set(oldSection.id, newSections[index].id); + exerciseSectionIdMap.set(oldSection.id, newSections[index].id); }); - // When cloning questions, include mapped sectionId: - // sectionId: question.sectionId ? sectionIdMap.get(question.sectionId) : null + // When cloning questions, include mapped exerciseSectionId: + // exerciseSectionId: question.exerciseSectionId ? exerciseSectionIdMap.get(question.exerciseSectionId) : null } ``` -Update `afterBehavior` references: if a section's `afterBehavior.action === 'go_to_section'`, remap the `sectionId` using the section ID map. +Update `afterBehavior` references: if a section's `afterBehavior.action === 'go_to_section'`, remap the `exerciseSectionId` using the section ID map. ### 6. Exercise Template Changes @@ -568,7 +587,8 @@ questionnaire: jsonb().default({}).$type<{ title: string; description?: string; order: number; - afterBehavior: { action: string; sectionId?: string }; + colorTheme?: string; + afterBehavior: { action: string; exerciseSectionId?: string }; questions: Array<{ title: string; name: string; @@ -594,6 +614,7 @@ export interface ExerciseSectionState { title: string; description: string | null; order: number; + colorTheme: ExerciseSectionColorTheme; afterBehavior: ExerciseSectionAfterBehavior; isDirty?: boolean; deletedAt?: string; @@ -626,13 +647,13 @@ export const questionnaire: Writable<{ #### 7.2: Extend Question Type -Add `sectionId` to the `Question` type: +Add `exerciseSectionId` to the `Question` type: ```typescript export type Question = Omit & { isDirty?: boolean; deletedAt?: string; - sectionId?: string | null; + exerciseSectionId?: string | null; options: QuestionOption[]; }; ``` @@ -667,6 +688,7 @@ export function handleAddSection() { title: 'Untitled Section', description: null, order: q.sections.length, + colorTheme: 'blue', afterBehavior: { action: 'continue' }, isDirty: true }; @@ -674,21 +696,21 @@ export function handleAddSection() { }); } -export function handleRemoveSection(sectionId: string, moveQuestionsToSectionId?: string) { +export function handleRemoveSection(exerciseSectionId: string, moveQuestionsToExerciseSectionId?: string) { questionnaire.update((q) => { - const section = q.sections.find((s) => s.id === sectionId); + const section = q.sections.find((s) => s.id === exerciseSectionId); if (!section) return q; let updatedQuestions = q.questions; - if (moveQuestionsToSectionId) { + if (moveQuestionsToExerciseSectionId) { updatedQuestions = q.questions.map((question) => - question.sectionId === sectionId - ? { ...question, sectionId: moveQuestionsToSectionId, isDirty: true } + question.exerciseSectionId === exerciseSectionId + ? { ...question, exerciseSectionId: moveQuestionsToExerciseSectionId, isDirty: true } : question ); } else { updatedQuestions = q.questions.map((question) => - question.sectionId === sectionId + question.exerciseSectionId === exerciseSectionId ? { ...question, deletedAt: new Date().toString() } : question ); @@ -697,32 +719,32 @@ export function handleRemoveSection(sectionId: string, moveQuestionsToSectionId? return { ...q, sections: q.sections.map((s) => - s.id === sectionId ? { ...s, deletedAt: new Date().toString() } : s + s.id === exerciseSectionId ? { ...s, deletedAt: new Date().toString() } : s ), questions: updatedQuestions }; }); } -export function handleMoveQuestionToSection(questionId: string, sectionId: string) { +export function handleMoveQuestionToSection(questionId: string, exerciseSectionId: string) { questionnaire.update((q) => ({ ...q, questions: q.questions.map((question) => question.id === questionId - ? { ...question, sectionId, isDirty: true } + ? { ...question, exerciseSectionId, isDirty: true } : question ) })); } export function handleUpdateSectionAfterBehavior( - sectionId: string, + exerciseSectionId: string, afterBehavior: ExerciseSectionAfterBehavior ) { questionnaire.update((q) => ({ ...q, sections: q.sections.map((section) => - section.id === sectionId + section.id === exerciseSectionId ? { ...section, afterBehavior, isDirty: true } : section ) @@ -731,10 +753,10 @@ export function handleUpdateSectionAfterBehavior( export function getQuestionsForSection( questions: Question[], - sectionId: string + exerciseSectionId: string ): Question[] { return questions - .filter((q) => q.sectionId === sectionId && !q.deletedAt) + .filter((q) => q.exerciseSectionId === exerciseSectionId && !q.deletedAt) .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); } @@ -745,73 +767,90 @@ export function hasSections(sections: ExerciseSectionState[]): boolean { ### 8. Dashboard UI Changes -#### 8.1: Edit Mode — Section Headers +#### 8.1: Edit Mode — Section Cards File: `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte` -When sections exist, render questions grouped under collapsible section headers: +When sections exist, render questions inside section cards with three distinct zones: ``` -┌─────────────────────────────────────────┐ -│ Section 1 of 2 ✕ ⋮ │ -│ [Section Title Input ] │ -│ [Description (optional) ] │ -├─────────────────────────────────────────┤ -│ ┌─ Question Card ──────────────────┐ │ -│ │ Q1: What is... [Radio ▼] │ │ -│ │ ... │ │ -│ └──────────────────────────────────┘ │ -│ ┌─ Question Card ──────────────────┐ │ -│ │ Q2: Explain... [Textarea ▼] │ │ -│ │ ... │ │ -│ └──────────────────────────────────┘ │ -│ │ -│ After section 1: [Continue ▼] │ -└─────────────────────────────────────────┘ - -┌─────────────────────────────────────────┐ -│ Section 2 of 2 ✕ ⋮ │ -│ ... │ -└─────────────────────────────────────────┘ +┌────────────────────────────────────────────────────────┐ +│ ⋮⋮ ● [Section title input ] [3 questions] × │ Header +│ ● ● ● ● ● │ Color swatches +├────────────────────────────────────────────────────────┤ +│ ┌─ Question sub-card ──────────────────────────────┐ │ +│ │ Q1: What is... [Radio ▼] │ │ +│ └──────────────────────────────────────────────────┘ │ +│ ┌─ Question sub-card ──────────────────────────────┐ │ Content +│ │ Q2: Explain... [Textarea ▼]│ │ +│ └──────────────────────────────────────────────────┘ │ +│ [ + Add question ] │ +├────────────────────────────────────────────────────────┤ +│ 3 required questions [↑] [↓] │ Footer +└────────────────────────────────────────────────────────┘ [ + Add Section ] ``` -- Each section has an inline title input and optional description textarea. -- The "After section N" dropdown appears at the bottom of each section, styled like the Google Forms pattern (see reference image). -- The three-dot menu (`⋮`) on each section header offers: "Move section up", "Move section down", "Delete section". -- The "Add question" button inside a section adds a question to that specific section. -- Questions can be dragged between sections. +Header: +- Sits on a secondary background to visually separate it from the white content area. +- Contains, from left to right: drag handle, color theme dot, editable title input, color swatches, question count badge, and remove button. +- The title input should visually blend into the header background until focused. +- Color swatches update the section's `colorTheme`; the dot reflects the selected theme. -#### 8.2: New Shared UI Component — `SectionHeader` +Content: +- Sits on the primary white background with comfortable padding. +- Renders question items stacked vertically. +- Each question item is its own bordered sub-card. +- A centered "Add question" button appears below the question list and appends a new question to that section. -File: `packages/ui/src/custom/exercise-question/section-header.svelte` (new) +Footer: +- Separated from content by a top border. +- Shows a small muted required-question count on the left. Until optional questions ship, this count equals the active question count. +- Shows small up/down arrow icon buttons on the right for moving the section within the exercise. + +If post-section routing remains in v1, render the "After section N" control as a compact settings row above the footer or in section settings. Do not overload the footer, which is reserved for required count and section reorder controls. +If section descriptions remain in v1, expose description editing in an expandable section settings area rather than in the compact header. +Questions can be dragged between sections, with context-menu movement as a fallback. + +#### 8.2: New Dashboard Component — `SectionEditor` + +File: `apps/dashboard/src/lib/features/course/components/exercise/section-editor.svelte` (new) ```typescript interface Props { + exerciseSectionId: string; title: string; - description?: string | null; sectionNumber: number; totalSections: number; + colorTheme: ExerciseSectionColorTheme; questionCount: number; - totalPoints: number; - mode: 'edit' | 'take' | 'preview' | 'view'; + requiredQuestionCount: number; + onTitleChange: (title: string) => void; + onColorThemeChange: (theme: ExerciseSectionColorTheme) => void; + onAddQuestion: () => void; + onDelete: () => void; + onMoveUp: () => void; + onMoveDown: () => void; + canMoveUp: boolean; + canMoveDown: boolean; } ``` -- In **edit** mode: editable title/description inputs. -- In **take** mode: read-only title, description, question count, points. -- In **preview/view** mode: read-only header. +- Renders the three-zone edit-mode section card described above. +- Reuses existing question editor markup in the content zone. +- Uses existing UI primitives for buttons, inputs, badges, and icon buttons. +- Keep take/preview/read-only section headers separate from this editor component so student-facing screens do not inherit authoring controls. #### 8.3: New Shared UI Component — `SectionAfterBehavior` File: `packages/ui/src/custom/exercise-question/section-after-behavior.svelte` (new) -A dropdown component that renders the "After section N" control: +A dropdown component that renders the "After section N" routing control. It should be visually adjacent to the section editor, but not inside the section editor footer. ```typescript interface Props { - sectionId: string; + exerciseSectionId: string; allSections: Array<{ id: string; title: string }>; afterBehavior: ExerciseSectionAfterBehavior; onChange: (behavior: ExerciseSectionAfterBehavior) => void; @@ -910,6 +949,7 @@ When sections exist, group questions under section headers: description={section.description} sectionNumber={sectionIndex + 1} totalSections={activeSections.length} + colorTheme={section.colorTheme} questionCount={getQuestionsForSection(questions, section.id).length} totalPoints={getSectionPoints(questions, section.id)} mode="preview" @@ -964,7 +1004,11 @@ Add under `course.navItem.lessons.exercises.all_exercises`: "section.delete_remove": "Delete questions", "section.move_up": "Move section up", "section.move_down": "Move section down", + "section.drag": "Drag section", "section.reorder": "Reorder Sections", + "section.question_count": "{count} questions", + "section.required_count": "{count} required questions", + "section.color_theme": "Section color", "section.after_behavior_prefix": "After section", "section.after_continue": "Continue to next section", "section.after_go_to": "Go to section", @@ -989,8 +1033,8 @@ Add under `course.navItem.lessons.exercises.all_exercises`: | Scenario | Behavior | | --- | --- | | Existing exercise, no sections | `sections` array is empty. All code paths fall through to existing flat-question logic. No migration needed. | -| Existing exercise, first section added | All questions auto-assigned to "Section 1". `sectionId` set on all questions. | -| Sectioned exercise, last section deleted | All questions become sectionless. `sectionId` set to `NULL`. Exercise reverts to flat mode. | +| Existing exercise, first section added | All questions auto-assigned to "Section 1". `exerciseSectionId` set on all questions. | +| Sectioned exercise, last section deleted | All questions become sectionless. `exerciseSectionId` set to `NULL`. Exercise reverts to flat mode. | | Old API clients that don't send `sections` | `sections` field is optional in update payload. Omitting it means "don't change sections." | | Exercise templates without sections | `sections` field is absent in JSONB. Template import uses `questions` array (current behavior). | @@ -1001,10 +1045,11 @@ Add under `course.navItem.lessons.exercises.all_exercises`: ### Database Migration 1. Create `exercise_section` table. -2. Add `section_id` column to `question` table (nullable, FK to `exercise_section`). +2. Add `exercise_section_id` column to `question` table (nullable, FK to `exercise_section`). 3. Add `section_display_mode` column to `exercise` table (default `'one_question'`). -4. Add index on `question.section_id`. -5. No data migration needed — existing exercises remain sectionless. +4. Add `color_theme` to `exercise_section` with default `'blue'`. +5. Add index on `question.exercise_section_id`. +6. No data migration needed — existing exercises remain sectionless. ### Rollout Strategy @@ -1026,7 +1071,7 @@ Add under `course.navItem.lessons.exercises.all_exercises`: | `packages/db/src/queries/exercise/exercise-section.ts` | CRUD queries for exercise sections | | `packages/utils/src/validation/exercise/exercise-section.ts` | Zod schemas for section validation | | `packages/question-types/src/exercise-section-types.ts` | TypeScript types for sections | -| `packages/ui/src/custom/exercise-question/section-header.svelte` | Section header UI component | +| `packages/ui/src/custom/exercise-question/section-header.svelte` | Read-only section header for take/preview/view modes | | `packages/ui/src/custom/exercise-question/section-after-behavior.svelte` | After-section behavior dropdown | | `packages/ui/src/custom/exercise-question/section-navigation-sidebar.svelte` | Section-grouped question nav | | `apps/dashboard/src/lib/features/course/components/exercise/section-editor.svelte` | Section editing card (edit mode) | @@ -1037,7 +1082,7 @@ Add under `course.navItem.lessons.exercises.all_exercises`: | File | Changes | | --- | --- | -| `packages/db/src/schema.ts` | Add `exerciseSection` table, `sectionId` to `question`, `sectionDisplayMode` to `exercise` | +| `packages/db/src/schema.ts` | Add `exerciseSection` table with `colorTheme`, `exerciseSectionId` to `question`, `sectionDisplayMode` to `exercise` | | `packages/db/src/queries/exercise/index.ts` | Export new section queries | | `packages/question-types/src/index.ts` | Export `exercise-section-types` | | `packages/ui/src/custom/exercise-question/index.ts` | Export new section components | @@ -1046,14 +1091,14 @@ Add under `course.navItem.lessons.exercises.all_exercises`: | `apps/api/src/routes/course/exercise.ts` | Accept/return sections in exercise endpoints | | `apps/api/src/services/exercise/exercise.ts` | Section upsert logic in exercise update | | `apps/api/src/services/course/clone.ts` | Clone sections, remap IDs and afterBehavior references | -| `apps/dashboard/src/lib/features/course/components/exercise/store.ts` | Add `sections`, `sectionDisplayMode`, section helpers | +| `apps/dashboard/src/lib/features/course/components/exercise/store.ts` | Add `sections`, `sectionDisplayMode`, `colorTheme`, section helpers | | `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte` | Render section-grouped editor | | `apps/dashboard/src/lib/features/course/components/exercise/view-mode.svelte` | Section-aware take flow | | `apps/dashboard/src/lib/features/course/components/exercise/preview.svelte` | Section-grouped preview | | `apps/dashboard/src/lib/features/course/components/exercise/submissions/individual.svelte` | Section-grouped grading | | `apps/dashboard/src/lib/features/course/components/exercise/submissions/summary.svelte` | Section-grouped analytics | | `apps/dashboard/src/lib/features/course/pages/exercise.svelte` | "Add Section" button, section display mode setting | -| `apps/dashboard/src/lib/features/course/types.ts` | Extend `Question` with `sectionId` | +| `apps/dashboard/src/lib/features/course/types.ts` | Extend `Question` with `exerciseSectionId` | | `apps/dashboard/src/lib/utils/translations/en.json` | Section-related translation keys | --- @@ -1066,7 +1111,7 @@ Add under `course.navItem.lessons.exercises.all_exercises`: - Exercise update with sections — questions correctly assigned to sections. - Exercise fetch returns sections with nested questions in correct order. - After-behavior validation — `go_to_section` must reference a valid section. -- Clone service preserves sections, question mappings, and remaps afterBehavior sectionIds. +- Clone service preserves sections, question mappings, and remaps afterBehavior exerciseSectionIds. - Backward compat: exercise without sections works identically to before. - Submission fetch returns section-grouped data when sections exist. @@ -1124,12 +1169,13 @@ pnpm --filter @cio/dashboard build | After-behavior "go to section" can create loops (A→B→A) | Validate at save time: warn if cycles detected, but allow (instructor may want loops intentionally for review) | | Drag-and-drop between sections may be unreliable on mobile | Provide "Move to section" context menu as a fallback; drag-drop is enhancement only | | Large exercises with many sections may slow down the editor | Lazy-render collapsed sections; only expand the active section | -| Clone service must remap `afterBehavior.sectionId` references | Explicit remap step in clone logic with validation | +| Clone service must remap `afterBehavior.exerciseSectionId` references | Explicit remap step in clone logic with validation | --- ## Open Questions -1. Should we support a "section shuffle" mode (randomize section order) independently of question shuffle? -2. Should the section navigation sidebar be collapsible or always visible during take mode? -3. Should after-behavior "Go to section X" carry over answered data, or should the target section reset? +1. Should v1 include Google Forms-style post-section routing, or should v1 only support linear sections with submit at the end? +2. Should we support a "section shuffle" mode (randomize section order) independently of question shuffle? +3. Should the section navigation sidebar be collapsible or always visible during take mode? +4. Should after-behavior "Go to section X" carry over answered data, or should the target section reset? diff --git a/prd/exercise-sections/implementation-plan.md b/prd/exercise-sections [DONE]/implementation-plan.md similarity index 79% rename from prd/exercise-sections/implementation-plan.md rename to prd/exercise-sections [DONE]/implementation-plan.md index 7b3b11c5..3a69ae17 100644 --- a/prd/exercise-sections/implementation-plan.md +++ b/prd/exercise-sections [DONE]/implementation-plan.md @@ -1,4 +1,6 @@ -# Exercise Sections — Implementation Plan +# Exercise Question Sections — Implementation Plan + +This plan implements sections inside a single exercise. It does not change course sections. The existing `exercise.sectionId` field continues to mean the course section that contains an exercise; the new question grouping uses `question.exerciseSectionId` / DB `question.exercise_section_id`. ## Delivery Phases @@ -32,6 +34,7 @@ export const exerciseSection = pgTable( title: varchar().notNull().default('Untitled Section'), description: text(), order: bigint({ mode: 'number' }).notNull().default(0), + colorTheme: varchar('color_theme').notNull().default('blue'), afterBehavior: jsonb('after_behavior') .notNull() .default({ action: 'continue' }), @@ -49,19 +52,19 @@ export const exerciseSection = pgTable( ); ``` -3. Add `sectionId` column to the existing `question` table definition. Find the `question` table object and add this column after `exerciseId`: +3. Add `exerciseSectionId` column to the existing `question` table definition. Find the `question` table object and add this column after `exerciseId`: ```typescript -sectionId: uuid('section_id'), +exerciseSectionId: uuid('exercise_section_id'), ``` 4. Add a foreign key constraint in the `question` table's constraint array: ```typescript foreignKey({ - columns: [table.sectionId], + columns: [table.exerciseSectionId], foreignColumns: [exerciseSection.id], - name: 'question_section_id_fkey' + name: 'question_exercise_section_id_fkey' }).onDelete('set null') ``` @@ -89,18 +92,19 @@ sectionDisplayMode: varchar('section_display_mode').default('one_question'), **What to do**: 1. Generate a Drizzle migration from the schema changes in Ticket 1.1. -2. The generated SQL should create the `exercise_section` table, add `section_id` to `question`, add `section_display_mode` to `exercise`, and create the index. +2. The generated SQL should create the `exercise_section` table, add `exercise_section_id` to `question`, add `section_display_mode` to `exercise`, and create the index. 3. Review the generated migration carefully to ensure: - - `section_id` on `question` is nullable (no `NOT NULL`). + - `exercise_section_id` on `question` is nullable (no `NOT NULL`). - `section_display_mode` on `exercise` has default `'one_question'`. + - `color_theme` on `exercise_section` has default `'blue'`. - The `exercise_section` table has `ON DELETE CASCADE` for `exercise_id`. - - The `question.section_id` FK has `ON DELETE SET NULL`. + - The `question.exercise_section_id` FK has `ON DELETE SET NULL`. **How to verify**: - Migration runs successfully against a local database. - Existing data is not affected. - `SELECT * FROM exercise_section` returns empty. -- `SELECT section_id FROM question LIMIT 1` returns `NULL`. +- `SELECT exercise_section_id FROM question LIMIT 1` returns `NULL`. --- @@ -120,16 +124,18 @@ export type ExerciseSectionAfterAction = 'continue' | 'go_to_section' | 'submit' export interface ExerciseSectionAfterBehavior { action: ExerciseSectionAfterAction; - sectionId?: string; + exerciseSectionId?: string; } export type ExerciseSectionDisplayMode = 'one_question' | 'all_questions'; +export type ExerciseSectionColorTheme = 'blue' | 'green' | 'amber' | 'rose' | 'violet' | 'slate'; export interface ExerciseSectionModel { id?: string; title: string; description?: string | null; order: number; + colorTheme: ExerciseSectionColorTheme; afterBehavior: ExerciseSectionAfterBehavior; questions: ExerciseQuestionModel[]; } @@ -169,7 +175,7 @@ import { z } from 'zod'; export const ZExerciseSectionAfterBehavior = z.discriminatedUnion('action', [ z.object({ action: z.literal('continue') }), - z.object({ action: z.literal('go_to_section'), sectionId: z.string().uuid() }), + z.object({ action: z.literal('go_to_section'), exerciseSectionId: z.string().uuid() }), z.object({ action: z.literal('submit') }) ]); @@ -178,6 +184,7 @@ export const ZExerciseSection = z.object({ title: z.string().min(1, 'Section title is required'), description: z.string().optional().nullable(), order: z.number().int().min(0), + colorTheme: z.enum(['blue', 'green', 'amber', 'rose', 'violet', 'slate']).default('blue'), afterBehavior: ZExerciseSectionAfterBehavior.default({ action: 'continue' }), questionIds: z.array(z.string()).optional() }); @@ -255,7 +262,8 @@ export async function createExerciseSections( title: string; description?: string | null; order: number; - afterBehavior?: { action: string; sectionId?: string }; + colorTheme?: string; + afterBehavior?: { action: string; exerciseSectionId?: string }; }> ) { if (sections.length === 0) return []; @@ -263,37 +271,38 @@ export async function createExerciseSections( } export async function updateExerciseSection( - sectionId: string, + exerciseSectionId: string, data: Partial<{ title: string; description: string | null; order: number; - afterBehavior: { action: string; sectionId?: string }; + colorTheme: string; + afterBehavior: { action: string; exerciseSectionId?: string }; }> ) { return db .update(exerciseSection) .set({ ...data, updatedAt: new Date().toISOString() }) - .where(eq(exerciseSection.id, sectionId)) + .where(eq(exerciseSection.id, exerciseSectionId)) .returning(); } -export async function deleteExerciseSection(sectionId: string) { - return db.delete(exerciseSection).where(eq(exerciseSection.id, sectionId)); +export async function deleteExerciseSection(exerciseSectionId: string) { + return db.delete(exerciseSection).where(eq(exerciseSection.id, exerciseSectionId)); } -export async function moveQuestionToSection(questionId: number, sectionId: string | null) { +export async function moveQuestionToSection(questionId: number, exerciseSectionId: string | null) { return db .update(question) - .set({ sectionId }) + .set({ exerciseSectionId }) .where(eq(question.id, questionId)); } -export async function bulkMoveQuestionsToSection(questionIds: number[], sectionId: string | null) { +export async function bulkMoveQuestionsToSection(questionIds: number[], exerciseSectionId: string | null) { if (questionIds.length === 0) return; return db .update(question) - .set({ sectionId }) + .set({ exerciseSectionId }) .where(inArray(question.id, questionIds)); } ``` @@ -335,9 +344,9 @@ export * from './exercise-section'; const sections = await getExerciseSectionsByExerciseId(exerciseId); ``` -3. Group questions by `sectionId`: - - Questions with a `sectionId` go into their respective section's `questions` array. - - Questions without a `sectionId` go into the top-level `questions` array. +3. Group questions by `exerciseSectionId`: + - Questions with an `exerciseSectionId` go into their respective section's `questions` array. + - Questions without an `exerciseSectionId` go into the top-level `questions` array. 4. Return the response shaped as: ```typescript @@ -349,6 +358,7 @@ const sections = await getExerciseSectionsByExerciseId(exerciseId); title: section.title, description: section.description, order: section.order, + colorTheme: section.colorTheme, afterBehavior: section.afterBehavior, questions: questionsInSection // sorted by order })), @@ -381,19 +391,19 @@ const sections = await getExerciseSectionsByExerciseId(exerciseId); ``` For each section in the payload: - - If section has `id` and exists in DB → update title, description, order, afterBehavior + - If section has `id` and exists in DB → update title, description, colorTheme, order, afterBehavior - If section has no `id` → create new section - Sections in DB that are NOT in the payload → delete them For each section: - Get `questionIds` from the section payload - - Update each question's `sectionId` to this section's ID + - Update each question's `exerciseSectionId` to this section's ID - Update each question's `order` based on its position in `questionIds` ``` 4. Validation rules: - If `sections` is provided and non-empty, every non-deleted question must be assigned to a section. - - If any section has `afterBehavior.action === 'go_to_section'`, the referenced `sectionId` must exist in the same exercise. + - If any section has `afterBehavior.action === 'go_to_section'`, the referenced `exerciseSectionId` must exist in the same exercise. - If `sections` is empty or omitted, don't touch existing sections. 5. Update `sectionDisplayMode` on the exercise if provided. @@ -433,7 +443,7 @@ For each section: // existing fields... sections: [ { - sectionId: 'uuid', + exerciseSectionId: 'uuid', sectionTitle: 'Part A', subtotal: 15, maxPoints: 20, @@ -471,7 +481,7 @@ For each section: const oldExerciseIds = oldExercises.map((ex) => ex.id); const oldSections = await getExerciseSectionsByExerciseIds(oldExerciseIds); -let sectionIdMap = new Map(); +let exerciseSectionIdMap = new Map(); if (oldSections.length > 0) { // 4b. Create new sections with mapped exercise IDs @@ -480,6 +490,7 @@ if (oldSections.length > 0) { title: section.title, description: section.description, order: section.order, + colorTheme: section.colorTheme, afterBehavior: section.afterBehavior, exerciseId: exerciseIdMap.get(section.exerciseId)! })) @@ -487,17 +498,17 @@ if (oldSections.length > 0) { // 4c. Build section ID map oldSections.forEach((oldSection, index) => { - sectionIdMap.set(oldSection.id, newSections[index].id); + exerciseSectionIdMap.set(oldSection.id, newSections[index].id); }); - // 4d. Remap afterBehavior.sectionId references + // 4d. Remap afterBehavior.exerciseSectionId references for (const newSection of newSections) { const behavior = newSection.afterBehavior as any; - if (behavior?.action === 'go_to_section' && behavior.sectionId) { - const remappedId = sectionIdMap.get(behavior.sectionId); + if (behavior?.action === 'go_to_section' && behavior.exerciseSectionId) { + const remappedId = exerciseSectionIdMap.get(behavior.exerciseSectionId); if (remappedId) { await updateExerciseSection(newSection.id, { - afterBehavior: { action: 'go_to_section', sectionId: remappedId } + afterBehavior: { action: 'go_to_section', exerciseSectionId: remappedId } }); } } @@ -505,10 +516,10 @@ if (oldSections.length > 0) { } ``` -3. When inserting cloned questions, include `sectionId`: +3. When inserting cloned questions, include `exerciseSectionId`: ```typescript -sectionId: question.sectionId ? sectionIdMap.get(question.sectionId) ?? null : null +exerciseSectionId: question.exerciseSectionId ? exerciseSectionIdMap.get(question.exerciseSectionId) ?? null : null ``` 4. Also copy `sectionDisplayMode` when creating the new exercise. @@ -519,8 +530,9 @@ sectionId: question.sectionId ? sectionIdMap.get(question.sectionId) ?? null : n **How to verify**: - Clone a course with sectioned exercises. - Verify cloned exercise has same number of sections with correct titles. +- Verify cloned section color themes are preserved. - Verify questions are assigned to correct cloned sections. -- Verify afterBehavior `sectionId` references point to the NEW section IDs (not old ones). +- Verify afterBehavior `exerciseSectionId` references point to the NEW section IDs (not old ones). - Verify `sectionDisplayMode` is preserved. --- @@ -542,6 +554,7 @@ sectionId: question.sectionId ? sectionIdMap.get(question.sectionId) ?? null : n ```typescript import type { ExerciseSectionAfterBehavior, + ExerciseSectionColorTheme, ExerciseSectionDisplayMode } from '@cio/question-types'; @@ -550,6 +563,7 @@ export interface ExerciseSectionState { title: string; description: string | null; order: number; + colorTheme: ExerciseSectionColorTheme; afterBehavior: ExerciseSectionAfterBehavior; isDirty?: boolean; deletedAt?: string; @@ -560,16 +574,17 @@ Add `sections` and `sectionDisplayMode` to the questionnaire writable's type and 3. Add helper functions (see PRD Section 7.4): - `handleAddSection()` - - `handleRemoveSection(sectionId, moveQuestionsToSectionId?)` - - `handleMoveQuestionToSection(questionId, sectionId)` - - `handleUpdateSectionAfterBehavior(sectionId, afterBehavior)` - - `getQuestionsForSection(questions, sectionId)` + - `handleRemoveSection(exerciseSectionId, moveQuestionsToExerciseSectionId?)` + - `handleMoveQuestionToSection(questionId, exerciseSectionId)` + - `handleUpdateSectionAfterBehavior(exerciseSectionId, afterBehavior)` + - `handleUpdateSectionColorTheme(exerciseSectionId, colorTheme)` + - `getQuestionsForSection(questions, exerciseSectionId)` - `hasSections(sections)` - `handleReorderSections(orderedSectionIds)` -4. Update `handleAddQuestion()` to accept an optional `sectionId` parameter. When sections exist, the new question must have a `sectionId`. +4. Update `handleAddQuestion()` to accept an optional `exerciseSectionId` parameter. When sections exist, the new question must have an `exerciseSectionId`. -5. Extend `Question` type in `apps/dashboard/src/lib/features/course/types.ts` to include `sectionId`. +5. Extend `Question` type in `apps/dashboard/src/lib/features/course/types.ts` to include `exerciseSectionId`. 6. Extend the metadata store to include `currentSectionIndex` and `sectionPhase`. @@ -580,31 +595,32 @@ Add `sections` and `sectionDisplayMode` to the questionnaire writable's type and **How to verify**: - Import and call `handleAddSection()` — store updates with a new section. - `getQuestionsForSection()` returns filtered questions. -- `handleMoveQuestionToSection()` updates the question's `sectionId`. +- `handleMoveQuestionToSection()` updates the question's `exerciseSectionId`. - `pnpm --filter @cio/dashboard build` succeeds. --- -### Ticket 3.2: Section Header UI Component +### Ticket 3.2: Section Editor UI Component **Priority**: P0 **Estimate**: 2–3 hours **What to do**: -1. Create `packages/ui/src/custom/exercise-question/section-header.svelte`: +1. Create `apps/dashboard/src/lib/features/course/components/exercise/section-editor.svelte`. This is the edit-mode section card that wraps existing question editor markup. ```typescript interface Props { + exerciseSectionId: string; title: string; - description?: string | null; sectionNumber: number; totalSections: number; - questionCount?: number; - totalPoints?: number; - mode: 'edit' | 'take' | 'preview' | 'view'; + colorTheme: ExerciseSectionColorTheme; + questionCount: number; + requiredQuestionCount: number; onTitleChange?: (title: string) => void; - onDescriptionChange?: (description: string) => void; + onColorThemeChange?: (colorTheme: ExerciseSectionColorTheme) => void; + onAddQuestion?: () => void; onDelete?: () => void; onMoveUp?: () => void; onMoveDown?: () => void; @@ -614,35 +630,32 @@ interface Props { } ``` -2. In **edit** mode, render: - - A badge showing "Section N of M" (styled similar to the Google Forms purple badge in the reference image). - - An editable title input (inline, not a dialog). - - An optional description textarea (placeholder: "Description (optional)"). - - A close button (✕) and a three-dot menu with "Move up", "Move down", "Delete section". +2. Render three distinct zones: + - Header: secondary background, drag handle on the far left, selected color dot, editable title input that blends into the background, small color swatches, question count badge, and remove button. + - Content: primary white background with padding, question items stacked vertically, each in a bordered sub-card, plus a centered "Add question" button below the list. + - Footer: top border, muted required-question count on the left, small up/down arrow icon buttons on the right. -3. In **take** mode, render: - - Section title (read-only, prominent). - - Section description (read-only). - - Question count and total points. - - No edit controls. +3. The color swatches use the supported `ExerciseSectionColorTheme` values. The swatch click updates the section state and marks it dirty. -4. In **preview/view** mode, render: - - Section title as a heading. - - Section description below it. - - No interactive controls. +4. Compute `requiredQuestionCount` from active questions where `required !== false`. Until optional questions are implemented, this equals the active question count. -5. Export from `packages/ui/src/custom/exercise-question/index.ts`. +5. Use icon buttons for drag/reorder/remove affordances. Use existing UI button/input primitives and `ui:` prefixes for UI theme color classes. + +6. If section descriptions remain in v1, expose description editing in an expandable section settings area instead of placing it in the compact header. **Files to create**: -- `packages/ui/src/custom/exercise-question/section-header.svelte` +- `apps/dashboard/src/lib/features/course/components/exercise/section-editor.svelte` **Files to change**: -- `packages/ui/src/custom/exercise-question/index.ts` +- `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte` **How to verify**: -- Component renders correctly in all modes. -- Edit mode: typing in title/description fires callbacks. -- `pnpm --filter @cio/ui build` succeeds. +- Header, content, and footer zones render with the expected backgrounds and borders. +- Typing in the title fires the title callback. +- Selecting a color swatch updates the section color dot and store state. +- Clicking the centered "Add question" button creates a question assigned to the section. +- Up/down arrow buttons reorder sections and respect disabled states at the first/last section. +- `pnpm --filter @cio/dashboard build` succeeds. --- @@ -670,15 +683,15 @@ interface Props { } ``` -2. Render a row at the bottom of each section card: +2. Render a compact routing settings row near each section card, outside the section editor footer: - Label: "After section {N}" (left side). - A `