@umituz/web-design-system 4.0.3 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -1
- package/src/__tests__/components.smoke.test.tsx +151 -1
- package/src/__tests__/styles.contract.test.ts +49 -0
- package/src/presentation/organisms/DataTable.tsx +286 -126
- package/src/presentation/organisms/KeyValueList.tsx +33 -0
- package/src/presentation/organisms/Toaster.tsx +104 -0
- package/src/presentation/organisms/index.ts +16 -2
- package/src/styles/bridge.css +74 -0
package/README.md
CHANGED
|
@@ -10,6 +10,9 @@ bundler compiles the components. Styling is available two ways:
|
|
|
10
10
|
|
|
11
11
|
- `@umituz/web-design-system/styles.css` — precompiled (`dist/styles.css`),
|
|
12
12
|
Tailwind included. For vanilla-CSS hosts.
|
|
13
|
+
- `@umituz/web-design-system/bridge.css` — the semantic palette bridge: maps
|
|
14
|
+
the `--color-*` contract onto the host's `--app-*` tokens. For vanilla-CSS
|
|
15
|
+
hosts that theme via their own token names (see below).
|
|
13
16
|
- `@umituz/web-design-system/theme.css` — the `@theme` layer alone. For
|
|
14
17
|
Tailwind v4 hosts that compile the component classes themselves.
|
|
15
18
|
|
|
@@ -51,6 +54,31 @@ Restyle by overriding the CSS variables — no rebuild needed:
|
|
|
51
54
|
Dark mode: add `data-theme="dark"` or `class="dark"` on any ancestor
|
|
52
55
|
(typically `<html>`). The package never switches themes on its own.
|
|
53
56
|
|
|
57
|
+
Hosts with their own token vocabulary (`--app-*`) use the bridge instead of
|
|
58
|
+
overriding `--color-*` one by one:
|
|
59
|
+
|
|
60
|
+
```css
|
|
61
|
+
/* app tokens css — imports in this order */
|
|
62
|
+
@import "@umituz/web-design-system/styles.css";
|
|
63
|
+
@import "@umituz/web-design-system/bridge.css";
|
|
64
|
+
|
|
65
|
+
:root {
|
|
66
|
+
--app-background: #fdfcf9; /* paper */
|
|
67
|
+
--app-foreground: #1c1917; /* ink */
|
|
68
|
+
--app-primary: #2e8b5f; /* brand */
|
|
69
|
+
--app-primary-foreground: #ffffff;
|
|
70
|
+
/* … the 19 required slots (missing slots break loudly). Optional:
|
|
71
|
+
--app-success / --app-warning / --app-info (+ -foreground),
|
|
72
|
+
--app-primary-soft, --app-font-sans fall back to DS defaults. */
|
|
73
|
+
}
|
|
74
|
+
[data-theme="dark"] {
|
|
75
|
+
--app-background: #0c0a09; /* re-define per theme */
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--radius-*` and `--font-mono` are deliberately not in the bridge — re-declare
|
|
80
|
+
them unlayered in the host css to override (name-collision doctrine).
|
|
81
|
+
|
|
54
82
|
### Tailwind v4 host
|
|
55
83
|
|
|
56
84
|
Compile the component classes as part of your own build — then utilities
|
|
@@ -107,9 +135,10 @@ is three variables:
|
|
|
107
135
|
```bash
|
|
108
136
|
@umituz/web-design-system/styles.css # precompiled stylesheet (vanilla hosts)
|
|
109
137
|
@umituz/web-design-system/theme.css # @theme layer (Tailwind v4 hosts)
|
|
138
|
+
@umituz/web-design-system/bridge.css # --color-* → --app-* palette bridge
|
|
110
139
|
@umituz/web-design-system/atoms # Button, Input, Badge, Spinner, …
|
|
111
140
|
@umituz/web-design-system/molecules # FormField, Select, Avatar, …
|
|
112
|
-
@umituz/web-design-system/organisms # Card, Dialog, Sheet, Table, DataTable, …
|
|
141
|
+
@umituz/web-design-system/organisms # Card, Dialog, Sheet, Table, DataTable, ToastProvider, KeyValueList, …
|
|
113
142
|
@umituz/web-design-system/templates # page-level layouts
|
|
114
143
|
@umituz/web-design-system/hooks # useMediaQuery, useDebounce, useTheme, …
|
|
115
144
|
@umituz/web-design-system/utils # cn, responsive helpers, clamp/pagination math
|
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@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-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial;--tw-space-x-reverse:0;--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}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:oklch(93.6% .032 17.717);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-green-100:oklch(96.2% .044 156.743);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-900:oklch(39.3% .095 152.535);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-600:oklch(60% .118 184.704);--color-cyan-500:oklch(71.5% .143 215.221);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-4xl:56rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--text-7xl:4.5rem;--text-7xl--line-height:1;--text-8xl:6rem;--text-8xl--line-height:1;--text-9xl:8rem;--text-9xl--line-height:1;--font-weight-thin:100;--font-weight-extralight:200;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--shadow-sm:0 1px 2px #0f172a0f;--shadow-md:0 4px 12px #0f172a14;--shadow-lg:0 12px 32px #0f172a24;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-accordion-down:accordion-down .2s ease-out;--animate-accordion-up:accordion-up .2s ease-out;--color-background:#f8fafc;--color-foreground:#0f172a;--color-card:#fff;--color-card-foreground:#0f172a;--color-popover:#fff;--color-popover-foreground:#0f172a;--color-primary:#2563eb;--color-primary-foreground:#fff;--color-secondary:#8b5cf6;--color-secondary-foreground:#fff;--color-muted:#f1f5f9;--color-muted-foreground:#64748b;--color-accent:#f1f5f9;--color-accent-foreground:#0f172a;--color-border:#e2e8f0;--color-input:#e2e8f0;--color-destructive:#dc2626;--color-destructive-foreground:#fff;--color-success:#16a34a;--color-success-foreground:#fff;--color-warning:#d97706;--color-warning-foreground:#fff;--color-info:#2563eb;--color-ring:#2563eb73}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){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}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-\[50\%\]{top:50%}.top-full{top:100%}.right-0{right:0}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-full{right:100%}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-2{left:calc(var(--spacing) * 2)}.left-3{left:calc(var(--spacing) * 3)}.left-\[50\%\]{left:50%}.left-full{left:100%}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-6{margin-block:calc(var(--spacing) * 6)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mr-1{margin-right:var(--spacing)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-square{aspect-ratio:1}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-24{height:calc(var(--spacing) * 24)}.h-28{height:calc(var(--spacing) * 28)}.h-\[1px\]{height:1px}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.min-h-\[80px\]{min-height:80px}.min-h-\[200px\]{min-height:200px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/2{width:50%}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-\[1px\]{width:1px}.w-full{width:100%}.w-px{width:1px}.max-w-4xl{max-width:var(--container-4xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:0}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-bounce{animation:var(--animate-bounce)}.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)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-\[3px\]{border-style:var(--tw-border-style);border-width:3px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-border{border-color:var(--color-border)}.border-destructive{border-color:var(--color-destructive)}.border-destructive\/50{border-color:#dc262680}@supports (color:color-mix(in lab, red, red)){.border-destructive\/50{border-color:color-mix(in oklab, var(--color-destructive) 50%, transparent)}}.border-input{border-color:var(--color-input)}.border-primary{border-color:var(--color-primary)}.border-secondary{border-color:var(--color-secondary)}.border-success{border-color:var(--color-success)}.border-transparent{border-color:#0000}.border-warning{border-color:var(--color-warning)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-background{background-color:var(--color-background)}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab, red, red)){.bg-black\/80{background-color:color-mix(in oklab, var(--color-black) 80%, transparent)}}.bg-border{background-color:var(--color-border)}.bg-card{background-color:var(--color-card)}.bg-destructive{background-color:var(--color-destructive)}.bg-foreground{background-color:var(--color-foreground)}.bg-green-100{background-color:var(--color-green-100)}.bg-muted{background-color:var(--color-muted)}.bg-muted\/50{background-color:#f1f5f980}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.bg-popover{background-color:var(--color-popover)}.bg-primary{background-color:var(--color-primary)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--color-secondary)}.bg-success{background-color:var(--color-success)}.bg-transparent{background-color:#0000}.bg-warning{background-color:var(--color-warning)}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-teal-500{--tw-gradient-from:var(--color-teal-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))}.from-white{--tw-gradient-from:var(--color-white);--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))}.to-cyan-500{--tw-gradient-to:var(--color-cyan-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))}.to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.fill-current{fill:currentColor}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-\[1px\]{padding:1px}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-0{padding-top:0}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-10{padding-right:calc(var(--spacing) * 10)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-9{padding-left:calc(var(--spacing) * 9)}.pl-10{padding-left:calc(var(--spacing) * 10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.text-8xl{font-size:var(--text-8xl);line-height:var(--tw-leading,var(--text-8xl--line-height))}.text-9xl{font-size:var(--text-9xl);line-height:var(--tw-leading,var(--text-9xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-extralight{--tw-font-weight:var(--font-weight-extralight);font-weight:var(--font-weight-extralight)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.font-thin{--tw-font-weight:var(--font-weight-thin);font-weight:var(--font-weight-thin)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.whitespace-nowrap{white-space:nowrap}.text-card-foreground{color:var(--color-card-foreground)}.text-destructive{color:var(--color-destructive)}.text-destructive-foreground{color:var(--color-destructive-foreground)}.text-foreground{color:var(--color-foreground)}.text-gray-900{color:var(--color-gray-900)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-muted-foreground\/50{color:#64748b80}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/50{color:color-mix(in oklab, var(--color-muted-foreground) 50%, transparent)}}.text-popover-foreground{color:var(--color-popover-foreground)}.text-primary{color:var(--color-primary)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-secondary{color:var(--color-secondary)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.text-success{color:var(--color-success)}.text-success-foreground{color:var(--color-success-foreground)}.text-teal-600{color:var(--color-teal-600)}.text-warning{color:var(--color-warning)}.text-warning-foreground{color:var(--color-warning-foreground)}.text-white{color:var(--color-white)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.accent-primary{accent-color:var(--color-primary)}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 12px 32px var(--tw-shadow-color,#0f172a24);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 12px var(--tw-shadow-color,#0f172a14);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px var(--tw-shadow-color,#0f172a0f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--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)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-offset-background{--tw-ring-offset-color:var(--color-background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.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(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.fade-out{--tw-exit-opacity:0}.running{animation-play-state:running}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-70:is(:where(.peer):disabled~*){opacity:.7}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.placeholder\:text-muted-foreground::placeholder{color:var(--color-muted-foreground)}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#2563eb80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary) 50%, transparent)}}.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-destructive\/90:hover{background-color:#dc2626e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--color-destructive) 90%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted)}.hover\:bg-muted\/30:hover{background-color:#f1f5f94d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/30:hover{background-color:color-mix(in oklab, var(--color-muted) 30%, transparent)}}.hover\:bg-muted\/50:hover{background-color:#f1f5f980}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#2563ebe6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#8b5cf6cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:text-foreground:hover{color:var(--color-foreground)}.hover\:text-muted-foreground:hover{color:var(--color-muted-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-lg:hover{--tw-shadow:0 12px 32px var(--tw-shadow-color,#0f172a24);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 12px var(--tw-shadow-color,#0f172a14);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:bg-accent:focus{background-color:var(--color-accent)}.focus\:text-accent-foreground:focus{color:var(--color-accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--color-ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.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)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:var(--color-background)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:scale-\[0\.98\]:active{scale:.98}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:translate-x-1[data-side=left]{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:translate-y-1[data-side=top]{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:translate-x-7[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 7);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--color-primary)}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:var(--animate-accordion-up)}.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)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x:calc(1 / 2*-100%)}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y:calc(48%*-1)}.data-\[state\=on\]\:bg-accent[data-state=on]{background-color:var(--color-accent)}.data-\[state\=on\]\:text-accent-foreground[data-state=on]{color:var(--color-accent-foreground)}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:var(--animate-accordion-down)}.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)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--color-accent)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--color-secondary)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--color-muted-foreground)}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x:calc(1 / 2*-100%)}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y:calc(48%*-1)}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--color-muted)}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--color-input)}@media (min-width:40rem){.sm\:mt-3{margin-top:calc(var(--spacing) * 3)}.sm\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.sm\:inline{display:inline}.sm\:h-2{height:calc(var(--spacing) * 2)}.sm\:h-2\.5{height:calc(var(--spacing) * 2.5)}.sm\:h-3{height:calc(var(--spacing) * 3)}.sm\:h-4{height:calc(var(--spacing) * 4)}.sm\:h-6{height:calc(var(--spacing) * 6)}.sm\:h-10{height:calc(var(--spacing) * 10)}.sm\:h-12{height:calc(var(--spacing) * 12)}.sm\:h-14{height:calc(var(--spacing) * 14)}.sm\:h-20{height:calc(var(--spacing) * 20)}.sm\:h-24{height:calc(var(--spacing) * 24)}.sm\:min-h-\[300px\]{min-height:300px}.sm\:w-1\/2{width:50%}.sm\:w-2{width:calc(var(--spacing) * 2)}.sm\:w-4{width:calc(var(--spacing) * 4)}.sm\:w-6{width:calc(var(--spacing) * 6)}.sm\:w-10{width:calc(var(--spacing) * 10)}.sm\:w-12{width:calc(var(--spacing) * 12)}.sm\:w-14{width:calc(var(--spacing) * 14)}.sm\:w-20{width:calc(var(--spacing) * 20)}.sm\:w-24{width:calc(var(--spacing) * 24)}.sm\:w-auto{width:auto}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:justify-start{justify-content:flex-start}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}:where(.sm\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}.sm\:self-auto{align-self:auto}.sm\:rounded-lg{border-radius:var(--radius-lg)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:text-left{text-align:left}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.sm\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:48rem){.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.md\:h-7{height:calc(var(--spacing) * 7)}.md\:w-7{width:calc(var(--spacing) * 7)}.md\:w-12{width:calc(var(--spacing) * 12)}.md\:gap-4{gap:calc(var(--spacing) * 4)}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.md\:p-5{padding:calc(var(--spacing) * 5)}.md\:py-12{padding-block:calc(var(--spacing) * 12)}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:64rem){.lg\:flex{display:flex}.lg\:h-2\.5{height:calc(var(--spacing) * 2.5)}.lg\:h-8{height:calc(var(--spacing) * 8)}.lg\:w-2\.5{width:calc(var(--spacing) * 2.5)}.lg\:w-8{width:calc(var(--spacing) * 8)}.lg\:gap-5{gap:calc(var(--spacing) * 5)}.lg\:p-6{padding:calc(var(--spacing) * 6)}.lg\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}}@media (min-width:80rem){.xl\:h-9{height:calc(var(--spacing) * 9)}.xl\:w-9{width:calc(var(--spacing) * 9)}.xl\:gap-5{gap:calc(var(--spacing) * 5)}.xl\:p-8{padding:calc(var(--spacing) * 8)}}@media (min-width:96rem){.\32 xl\:h-10{height:calc(var(--spacing) * 10)}.\32 xl\:w-10{width:calc(var(--spacing) * 10)}.\32 xl\:gap-6{gap:calc(var(--spacing) * 6)}.\32 xl\:p-10{padding:calc(var(--spacing) * 10)}}@media (prefers-color-scheme:dark){.dark\:border-destructive{border-color:var(--color-destructive)}.dark\:bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-green-900\/30{background-color:color-mix(in oklab, var(--color-green-900) 30%, transparent)}}.dark\:bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-red-900\/30{background-color:color-mix(in oklab, var(--color-red-900) 30%, transparent)}}.dark\:from-gray-800{--tw-gradient-from:var(--color-gray-800);--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))}.dark\:to-gray-900{--tw-gradient-to:var(--color-gray-900);--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))}.dark\:text-gray-100{color:var(--color-gray-100)}.dark\:text-green-300{color:var(--color-green-300)}.dark\:text-green-400{color:var(--color-green-400)}.dark\:text-red-300{color:var(--color-red-300)}.dark\:text-red-400{color:var(--color-red-400)}.dark\:text-teal-400{color:var(--color-teal-400)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>span\]\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:top-4>svg{top:calc(var(--spacing) * 4)}.\[\&\>svg\]\:left-4>svg{left:calc(var(--spacing) * 4)}.\[\&\>svg\]\:text-destructive>svg{color:var(--color-destructive)}.\[\&\>svg\]\:text-foreground>svg{color:var(--color-foreground)}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:calc(var(--spacing) * 7)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}}@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}[data-theme=dark],.dark{--color-background:#0b1120;--color-foreground:#f1f5f9;--color-card:#111827;--color-card-foreground:#f1f5f9;--color-popover:#111827;--color-popover-foreground:#f1f5f9;--color-primary:#3b82f6;--color-primary-foreground:#fff;--color-secondary:#a78bfa;--color-secondary-foreground:#fff;--color-muted:#1e293b;--color-muted-foreground:#94a3b8;--color-accent:#1e293b;--color-accent-foreground:#f1f5f9;--color-border:#1f2a3c;--color-input:#1f2a3c;--color-destructive:#ef4444;--color-destructive-foreground:#fff;--color-success:#22c55e;--color-success-foreground:#fff;--color-warning:#f59e0b;--color-warning-foreground:#fff;--color-info:#3b82f6;--color-info-foreground:#fff;--color-ring:#60a5fa80;--shadow-sm:0 1px 2px #0006;--shadow-md:0 4px 12px #00000073;--shadow-lg:0 12px 32px #0000008c}@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-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";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:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";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:"<percentage>";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:"<percentage>";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:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@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)}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height)}to{height:0}}
|
|
2
|
+
@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-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-space-x-reverse:0;--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}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:oklch(93.6% .032 17.717);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-green-100:oklch(96.2% .044 156.743);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-900:oklch(39.3% .095 152.535);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-600:oklch(60% .118 184.704);--color-cyan-500:oklch(71.5% .143 215.221);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-4xl:56rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--text-7xl:4.5rem;--text-7xl--line-height:1;--text-8xl:6rem;--text-8xl--line-height:1;--text-9xl:8rem;--text-9xl--line-height:1;--font-weight-thin:100;--font-weight-extralight:200;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-snug:1.375;--leading-relaxed:1.625;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--shadow-sm:0 1px 2px #0f172a0f;--shadow-md:0 4px 12px #0f172a14;--shadow-lg:0 12px 32px #0f172a24;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-accordion-down:accordion-down .2s ease-out;--animate-accordion-up:accordion-up .2s ease-out;--color-background:#f8fafc;--color-foreground:#0f172a;--color-card:#fff;--color-card-foreground:#0f172a;--color-popover:#fff;--color-popover-foreground:#0f172a;--color-primary:#2563eb;--color-primary-foreground:#fff;--color-secondary:#8b5cf6;--color-secondary-foreground:#fff;--color-muted:#f1f5f9;--color-muted-foreground:#64748b;--color-accent:#f1f5f9;--color-accent-foreground:#0f172a;--color-border:#e2e8f0;--color-input:#e2e8f0;--color-destructive:#dc2626;--color-destructive-foreground:#fff;--color-success:#16a34a;--color-success-foreground:#fff;--color-warning:#d97706;--color-warning-foreground:#fff;--color-info:#2563eb;--color-ring:#2563eb73}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){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}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:calc(var(--spacing) * 4)}.top-\[50\%\]{top:50%}.top-full{top:100%}.right-0{right:0}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-full{right:100%}.bottom-0{bottom:0}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-full{bottom:100%}.left-0{left:0}.left-2{left:calc(var(--spacing) * 2)}.left-3{left:calc(var(--spacing) * 3)}.left-\[50\%\]{left:50%}.left-full{left:100%}.z-50{z-index:50}.z-\[1\]{z-index:1}.z-\[100\]{z-index:100}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-6{margin-block:calc(var(--spacing) * 6)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-px{margin-top:1px}.mr-1{margin-right:var(--spacing)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-square{aspect-ratio:1}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-24{height:calc(var(--spacing) * 24)}.h-28{height:calc(var(--spacing) * 28)}.h-\[1px\]{height:1px}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.min-h-\[80px\]{min-height:80px}.min-h-\[200px\]{min-height:200px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/2{width:50%}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-3\/4{width:75%}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-\[1px\]{width:1px}.w-\[340px\]{width:340px}.w-full{width:100%}.w-px{width:1px}.max-w-4xl{max-width:var(--container-4xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:0}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.translate-x-\[-50\%\]{--tw-translate-x:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%;translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-bounce{animation:var(--animate-bounce)}.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)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-\[3px\]{border-style:var(--tw-border-style);border-width:3px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-\[3px\]{border-left-style:var(--tw-border-style);border-left-width:3px}.border-border{border-color:var(--color-border)}.border-destructive{border-color:var(--color-destructive)}.border-destructive\/50{border-color:#dc262680}@supports (color:color-mix(in lab, red, red)){.border-destructive\/50{border-color:color-mix(in oklab, var(--color-destructive) 50%, transparent)}}.border-input{border-color:var(--color-input)}.border-primary{border-color:var(--color-primary)}.border-secondary{border-color:var(--color-secondary)}.border-success{border-color:var(--color-success)}.border-transparent{border-color:#0000}.border-warning{border-color:var(--color-warning)}.border-t-transparent{border-top-color:#0000}.border-l-destructive{border-left-color:var(--color-destructive)}.border-l-info{border-left-color:var(--color-info)}.border-l-success{border-left-color:var(--color-success)}.border-l-transparent{border-left-color:#0000}.border-l-warning{border-left-color:var(--color-warning)}.bg-background{background-color:var(--color-background)}.bg-background\/95{background-color:#f8fafcf2}@supports (color:color-mix(in lab, red, red)){.bg-background\/95{background-color:color-mix(in oklab, var(--color-background) 95%, transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab, red, red)){.bg-black\/80{background-color:color-mix(in oklab, var(--color-black) 80%, transparent)}}.bg-border{background-color:var(--color-border)}.bg-card{background-color:var(--color-card)}.bg-destructive{background-color:var(--color-destructive)}.bg-foreground{background-color:var(--color-foreground)}.bg-green-100{background-color:var(--color-green-100)}.bg-muted{background-color:var(--color-muted)}.bg-muted\/50{background-color:#f1f5f980}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.bg-muted\/60{background-color:#f1f5f999}@supports (color:color-mix(in lab, red, red)){.bg-muted\/60{background-color:color-mix(in oklab, var(--color-muted) 60%, transparent)}}.bg-popover{background-color:var(--color-popover)}.bg-primary{background-color:var(--color-primary)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--color-secondary)}.bg-success{background-color:var(--color-success)}.bg-transparent{background-color:#0000}.bg-warning{background-color:var(--color-warning)}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-teal-500{--tw-gradient-from:var(--color-teal-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))}.from-white{--tw-gradient-from:var(--color-white);--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))}.to-cyan-500{--tw-gradient-to:var(--color-cyan-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))}.to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.fill-current{fill:currentColor}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-\[1px\]{padding:1px}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-0{padding-top:0}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-10{padding-right:calc(var(--spacing) * 10)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-9{padding-left:calc(var(--spacing) * 9)}.pl-10{padding-left:calc(var(--spacing) * 10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.text-8xl{font-size:var(--text-8xl);line-height:var(--tw-leading,var(--text-8xl--line-height))}.text-9xl{font-size:var(--text-9xl);line-height:var(--tw-leading,var(--text-9xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-extralight{--tw-font-weight:var(--font-weight-extralight);font-weight:var(--font-weight-extralight)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.font-thin{--tw-font-weight:var(--font-weight-thin);font-weight:var(--font-weight-thin)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.whitespace-nowrap{white-space:nowrap}.text-card-foreground{color:var(--color-card-foreground)}.text-destructive{color:var(--color-destructive)}.text-destructive-foreground{color:var(--color-destructive-foreground)}.text-foreground{color:var(--color-foreground)}.text-gray-900{color:var(--color-gray-900)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-info{color:var(--color-info)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-muted-foreground\/50{color:#64748b80}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/50{color:color-mix(in oklab, var(--color-muted-foreground) 50%, transparent)}}.text-popover-foreground{color:var(--color-popover-foreground)}.text-primary{color:var(--color-primary)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-secondary{color:var(--color-secondary)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.text-success{color:var(--color-success)}.text-success-foreground{color:var(--color-success-foreground)}.text-teal-600{color:var(--color-teal-600)}.text-warning{color:var(--color-warning)}.text-warning-foreground{color:var(--color-warning-foreground)}.text-white{color:var(--color-white)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.accent-foreground{accent-color:var(--color-foreground)}.accent-primary{accent-color:var(--color-primary)}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 12px 32px var(--tw-shadow-color,#0f172a24);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 12px var(--tw-shadow-color,#0f172a14);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px var(--tw-shadow-color,#0f172a0f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--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)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-offset-background{--tw-ring-offset-color:var(--color-background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--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,)}.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(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.fade-out{--tw-exit-opacity:0}.running{animation-play-state:running}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-70:is(:where(.peer):disabled~*){opacity:.7}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.placeholder\:text-muted-foreground::placeholder{color:var(--color-muted-foreground)}@media (hover:hover){.hover\:border-primary\/50:hover{border-color:#2563eb80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary) 50%, transparent)}}.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-destructive\/90:hover{background-color:#dc2626e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--color-destructive) 90%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted)}.hover\:bg-muted\/30:hover{background-color:#f1f5f94d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/30:hover{background-color:color-mix(in oklab, var(--color-muted) 30%, transparent)}}.hover\:bg-muted\/50:hover{background-color:#f1f5f980}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#2563ebe6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#8b5cf6cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:text-foreground:hover{color:var(--color-foreground)}.hover\:text-muted-foreground:hover{color:var(--color-muted-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-lg:hover{--tw-shadow:0 12px 32px var(--tw-shadow-color,#0f172a24);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 12px var(--tw-shadow-color,#0f172a14);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:bg-accent:focus{background-color:var(--color-accent)}.focus\:text-accent-foreground:focus{color:var(--color-accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--color-ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.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)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:var(--color-background)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:scale-\[0\.98\]:active{scale:.98}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:translate-x-1[data-side=left]{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:translate-y-1[data-side=top]{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:translate-x-7[data-state=checked]{--tw-translate-x:calc(var(--spacing) * 7);translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--color-primary)}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:var(--animate-accordion-up)}.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)}.data-\[state\=closed\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x:calc(1 / 2*-100%)}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y:calc(48%*-1)}.data-\[state\=on\]\:bg-accent[data-state=on]{background-color:var(--color-accent)}.data-\[state\=on\]\:text-accent-foreground[data-state=on]{color:var(--color-accent-foreground)}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:var(--animate-accordion-down)}.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)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--color-accent)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--color-secondary)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--color-muted-foreground)}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x:calc(1 / 2*-100%)}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y:calc(48%*-1)}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--color-muted)}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--color-input)}@media (min-width:40rem){.sm\:mt-3{margin-top:calc(var(--spacing) * 3)}.sm\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.sm\:inline{display:inline}.sm\:table-cell{display:table-cell}.sm\:h-2{height:calc(var(--spacing) * 2)}.sm\:h-2\.5{height:calc(var(--spacing) * 2.5)}.sm\:h-3{height:calc(var(--spacing) * 3)}.sm\:h-4{height:calc(var(--spacing) * 4)}.sm\:h-6{height:calc(var(--spacing) * 6)}.sm\:h-10{height:calc(var(--spacing) * 10)}.sm\:h-12{height:calc(var(--spacing) * 12)}.sm\:h-14{height:calc(var(--spacing) * 14)}.sm\:h-20{height:calc(var(--spacing) * 20)}.sm\:h-24{height:calc(var(--spacing) * 24)}.sm\:min-h-\[300px\]{min-height:300px}.sm\:w-1\/2{width:50%}.sm\:w-2{width:calc(var(--spacing) * 2)}.sm\:w-4{width:calc(var(--spacing) * 4)}.sm\:w-6{width:calc(var(--spacing) * 6)}.sm\:w-10{width:calc(var(--spacing) * 10)}.sm\:w-12{width:calc(var(--spacing) * 12)}.sm\:w-14{width:calc(var(--spacing) * 14)}.sm\:w-20{width:calc(var(--spacing) * 20)}.sm\:w-24{width:calc(var(--spacing) * 24)}.sm\:w-auto{width:auto}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:justify-start{justify-content:flex-start}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}:where(.sm\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}.sm\:self-auto{align-self:auto}.sm\:rounded-lg{border-radius:var(--radius-lg)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:text-left{text-align:left}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.sm\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:48rem){.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.md\:table-cell{display:table-cell}.md\:h-7{height:calc(var(--spacing) * 7)}.md\:w-7{width:calc(var(--spacing) * 7)}.md\:w-12{width:calc(var(--spacing) * 12)}.md\:gap-4{gap:calc(var(--spacing) * 4)}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.md\:p-5{padding:calc(var(--spacing) * 5)}.md\:py-12{padding-block:calc(var(--spacing) * 12)}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:64rem){.lg\:flex{display:flex}.lg\:table-cell{display:table-cell}.lg\:h-2\.5{height:calc(var(--spacing) * 2.5)}.lg\:h-8{height:calc(var(--spacing) * 8)}.lg\:w-2\.5{width:calc(var(--spacing) * 2.5)}.lg\:w-8{width:calc(var(--spacing) * 8)}.lg\:gap-5{gap:calc(var(--spacing) * 5)}.lg\:p-6{padding:calc(var(--spacing) * 6)}.lg\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}}@media (min-width:80rem){.xl\:table-cell{display:table-cell}.xl\:h-9{height:calc(var(--spacing) * 9)}.xl\:w-9{width:calc(var(--spacing) * 9)}.xl\:gap-5{gap:calc(var(--spacing) * 5)}.xl\:p-8{padding:calc(var(--spacing) * 8)}}@media (min-width:96rem){.\32 xl\:h-10{height:calc(var(--spacing) * 10)}.\32 xl\:w-10{width:calc(var(--spacing) * 10)}.\32 xl\:gap-6{gap:calc(var(--spacing) * 6)}.\32 xl\:p-10{padding:calc(var(--spacing) * 10)}}@media (prefers-color-scheme:dark){.dark\:border-destructive{border-color:var(--color-destructive)}.dark\:bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-green-900\/30{background-color:color-mix(in oklab, var(--color-green-900) 30%, transparent)}}.dark\:bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab, red, red)){.dark\:bg-red-900\/30{background-color:color-mix(in oklab, var(--color-red-900) 30%, transparent)}}.dark\:from-gray-800{--tw-gradient-from:var(--color-gray-800);--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))}.dark\:to-gray-900{--tw-gradient-to:var(--color-gray-900);--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))}.dark\:text-gray-100{color:var(--color-gray-100)}.dark\:text-green-300{color:var(--color-green-300)}.dark\:text-green-400{color:var(--color-green-400)}.dark\:text-red-300{color:var(--color-red-300)}.dark\:text-red-400{color:var(--color-red-400)}.dark\:text-teal-400{color:var(--color-teal-400)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>span\]\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:top-4>svg{top:calc(var(--spacing) * 4)}.\[\&\>svg\]\:left-4>svg{left:calc(var(--spacing) * 4)}.\[\&\>svg\]\:text-destructive>svg{color:var(--color-destructive)}.\[\&\>svg\]\:text-foreground>svg{color:var(--color-foreground)}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;translate:var(--tw-translate-x) var(--tw-translate-y)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:calc(var(--spacing) * 7)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}}@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}[data-theme=dark],.dark{--color-background:#0b1120;--color-foreground:#f1f5f9;--color-card:#111827;--color-card-foreground:#f1f5f9;--color-popover:#111827;--color-popover-foreground:#f1f5f9;--color-primary:#3b82f6;--color-primary-foreground:#fff;--color-secondary:#a78bfa;--color-secondary-foreground:#fff;--color-muted:#1e293b;--color-muted-foreground:#94a3b8;--color-accent:#1e293b;--color-accent-foreground:#f1f5f9;--color-border:#1f2a3c;--color-input:#1f2a3c;--color-destructive:#ef4444;--color-destructive-foreground:#fff;--color-success:#22c55e;--color-success-foreground:#fff;--color-warning:#f59e0b;--color-warning-foreground:#fff;--color-info:#3b82f6;--color-info-foreground:#fff;--color-ring:#60a5fa80;--shadow-sm:0 1px 2px #0006;--shadow-md:0 4px 12px #00000073;--shadow-lg:0 12px 32px #0000008c}@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-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";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:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";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:"<percentage>";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:"<percentage>";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:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";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-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@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)}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height)}to{height:0}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-design-system",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Web Design System - Tailwind v4 authored, precompiled CSS shipped. Atomic Design components (Atoms, Molecules, Organisms, Templates) for React applications.",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"./types": "./src/domain/types/index.ts",
|
|
24
24
|
"./styles.css": "./dist/styles.css",
|
|
25
25
|
"./theme.css": "./src/styles/theme.css",
|
|
26
|
+
"./bridge.css": "./src/styles/bridge.css",
|
|
26
27
|
"./package.json": "./package.json"
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { describe, expect, it } from 'vitest';
|
|
8
|
-
import { render, screen } from '@testing-library/react';
|
|
8
|
+
import { act, fireEvent, render, screen } from '@testing-library/react';
|
|
9
9
|
import { Button } from '../presentation/atoms/Button';
|
|
10
10
|
import { Badge } from '../presentation/atoms/Badge';
|
|
11
11
|
import { Spinner } from '../presentation/atoms/Spinner';
|
|
@@ -13,6 +13,10 @@ import { Alert, AlertTitle, AlertDescription } from '../presentation/organisms/A
|
|
|
13
13
|
import { Card, CardHeader, CardTitle, CardContent } from '../presentation/organisms/Card';
|
|
14
14
|
import { EmptyState } from '../presentation/organisms/EmptyState';
|
|
15
15
|
import { StatCard } from '../presentation/organisms/StatCard';
|
|
16
|
+
import { ToastProvider, useToast } from '../presentation/organisms/Toaster';
|
|
17
|
+
import type { ToastKind } from '../presentation/organisms/Toaster';
|
|
18
|
+
import { KeyValueList } from '../presentation/organisms/KeyValueList';
|
|
19
|
+
import { DataTable, TablePagination } from '../presentation/organisms/DataTable';
|
|
16
20
|
|
|
17
21
|
describe('atoms', () => {
|
|
18
22
|
it('Button renders label with default variant classes', () => {
|
|
@@ -75,4 +79,150 @@ describe('organisms', () => {
|
|
|
75
79
|
expect(screen.getByText('1200')).toBeTruthy();
|
|
76
80
|
expect(screen.getAllByText('+12.3%').length).toBeGreaterThan(0);
|
|
77
81
|
});
|
|
82
|
+
|
|
83
|
+
it('ToastProvider renders a fired toast with its kind accent', async () => {
|
|
84
|
+
function Harness({ kind }: { kind: ToastKind }) {
|
|
85
|
+
const { toast } = useToast();
|
|
86
|
+
return (
|
|
87
|
+
<button type="button" onClick={() => toast({ kind, title: 'Saved', description: 'Row updated' })}>
|
|
88
|
+
fire
|
|
89
|
+
</button>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
render(
|
|
93
|
+
<ToastProvider>
|
|
94
|
+
<Harness kind="success" />
|
|
95
|
+
</ToastProvider>,
|
|
96
|
+
);
|
|
97
|
+
await act(async () => {
|
|
98
|
+
fireEvent.click(screen.getByRole('button', { name: 'fire' }));
|
|
99
|
+
});
|
|
100
|
+
expect(screen.getByText('Saved')).toBeTruthy();
|
|
101
|
+
expect(screen.getByText('Row updated')).toBeTruthy();
|
|
102
|
+
expect(screen.getByText('Saved').closest('div')?.parentElement?.className).toContain('border-l-success');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('KeyValueList renders label/value pairs in a definition list', () => {
|
|
106
|
+
render(
|
|
107
|
+
<KeyValueList
|
|
108
|
+
items={[
|
|
109
|
+
{ label: 'E-posta', value: 'a@b.c' },
|
|
110
|
+
{ label: 'Rol', value: 'admin' },
|
|
111
|
+
]}
|
|
112
|
+
/>,
|
|
113
|
+
);
|
|
114
|
+
const dl = screen.getByText('E-posta').closest('dl');
|
|
115
|
+
expect(dl).toBeTruthy();
|
|
116
|
+
expect(dl?.querySelectorAll('dt').length).toBe(2);
|
|
117
|
+
expect(dl?.querySelectorAll('dd').length).toBe(2);
|
|
118
|
+
expect(screen.getByText('a@b.c').className).toContain('text-right');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('DataTable', () => {
|
|
123
|
+
interface Row {
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
qty: number;
|
|
127
|
+
}
|
|
128
|
+
const rows: Row[] = [
|
|
129
|
+
{ id: 'r2', name: 'bravo', qty: 2 },
|
|
130
|
+
{ id: 'r1', name: 'alpha', qty: 10 },
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
it('renders accessor columns and emptyState', () => {
|
|
134
|
+
render(
|
|
135
|
+
<DataTable<Row>
|
|
136
|
+
data={[]}
|
|
137
|
+
columns={[{ id: 'name', header: 'Name', accessor: 'name' }]}
|
|
138
|
+
emptyState={{ title: 'No rows' }}
|
|
139
|
+
/>,
|
|
140
|
+
);
|
|
141
|
+
expect(screen.getByText('No rows')).toBeTruthy();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('sorts internally by sortValue on header click', () => {
|
|
145
|
+
render(
|
|
146
|
+
<DataTable<Row>
|
|
147
|
+
data={rows}
|
|
148
|
+
sortable
|
|
149
|
+
columns={[
|
|
150
|
+
{ id: 'name', header: 'Name', accessor: 'name', sortable: true, sortValue: (r) => r.name },
|
|
151
|
+
]}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
const cellsBefore = screen.getAllByRole('row').map((tr) => tr.textContent);
|
|
155
|
+
expect(cellsBefore.join()).toContain('bravo');
|
|
156
|
+
fireEvent.click(screen.getByRole('columnheader', { name: /name/i }));
|
|
157
|
+
const firstDataRow = screen.getAllByRole('row')[1];
|
|
158
|
+
expect(firstDataRow.textContent).toContain('alpha');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('controlled sort delegates to onSortChange', () => {
|
|
162
|
+
let clicked = '';
|
|
163
|
+
render(
|
|
164
|
+
<DataTable<Row>
|
|
165
|
+
data={rows}
|
|
166
|
+
sortable
|
|
167
|
+
sort={{ id: 'qty', dir: 'desc' }}
|
|
168
|
+
onSortChange={(id) => {
|
|
169
|
+
clicked = id;
|
|
170
|
+
}}
|
|
171
|
+
columns={[{ id: 'qty', header: 'Qty', accessor: 'qty', sortable: true }]}
|
|
172
|
+
/>,
|
|
173
|
+
);
|
|
174
|
+
fireEvent.click(screen.getByRole('columnheader', { name: /qty/i }));
|
|
175
|
+
expect(clicked).toBe('qty');
|
|
176
|
+
expect(screen.getByRole('columnheader', { name: /qty/i }).getAttribute('aria-sort')).toBe('descending');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('applies align, hideBelow and sticky classes', () => {
|
|
180
|
+
const { container } = render(
|
|
181
|
+
<DataTable<Row>
|
|
182
|
+
data={rows}
|
|
183
|
+
sticky
|
|
184
|
+
columns={[
|
|
185
|
+
{ id: 'qty', header: 'Qty', accessor: 'qty', align: 'right', hideBelow: 'md' },
|
|
186
|
+
]}
|
|
187
|
+
/>,
|
|
188
|
+
);
|
|
189
|
+
const th = container.querySelector('thead th');
|
|
190
|
+
expect(th).toBeTruthy();
|
|
191
|
+
expect(th?.className).toContain('text-right');
|
|
192
|
+
expect(th?.className).toContain('hidden md:table-cell');
|
|
193
|
+
expect(th?.className).toContain('sticky top-0');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('renders skeleton rows while loading', () => {
|
|
197
|
+
render(
|
|
198
|
+
<DataTable<Row>
|
|
199
|
+
data={[]}
|
|
200
|
+
loading
|
|
201
|
+
columns={[
|
|
202
|
+
{ id: 'name', header: 'Name', accessor: 'name' },
|
|
203
|
+
{ id: 'qty', header: 'Qty', accessor: 'qty' },
|
|
204
|
+
]}
|
|
205
|
+
/>,
|
|
206
|
+
);
|
|
207
|
+
expect(screen.getAllByRole('status').length).toBeGreaterThan(0);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('renders the empty node when data is empty and empty is provided', () => {
|
|
211
|
+
render(
|
|
212
|
+
<DataTable<Row>
|
|
213
|
+
data={[]}
|
|
214
|
+
empty={<p>custom empty</p>}
|
|
215
|
+
columns={[{ id: 'name', header: 'Name', accessor: 'name' }]}
|
|
216
|
+
/>,
|
|
217
|
+
);
|
|
218
|
+
expect(screen.getByText('custom empty')).toBeTruthy();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('TablePagination disables boundary buttons and marks the active page', () => {
|
|
222
|
+
render(<TablePagination page={2} totalPages={3} onPage={() => {}} />);
|
|
223
|
+
expect(screen.getByRole('button', { name: 'First page' })).toBeTruthy();
|
|
224
|
+
expect(screen.getByLabelText('Next page')).toBeTruthy();
|
|
225
|
+
const active = screen.getByRole('button', { name: '2' });
|
|
226
|
+
expect(active.getAttribute('aria-current')).toBe('page');
|
|
227
|
+
});
|
|
78
228
|
});
|
|
@@ -12,6 +12,7 @@ import { resolve } from 'node:path';
|
|
|
12
12
|
import { describe, expect, it } from 'vitest';
|
|
13
13
|
|
|
14
14
|
const css = readFileSync(resolve(__dirname, '../../dist/styles.css'), 'utf8');
|
|
15
|
+
const bridge = readFileSync(resolve(__dirname, '../styles/bridge.css'), 'utf8');
|
|
15
16
|
|
|
16
17
|
describe('dist/styles.css contract', () => {
|
|
17
18
|
it('exists and is non-trivial', () => {
|
|
@@ -40,6 +41,10 @@ describe('dist/styles.css contract', () => {
|
|
|
40
41
|
// Radius scale
|
|
41
42
|
expect(css).toMatch(/\.rounded-md\{border-radius:var\(--radius-md\)\}/);
|
|
42
43
|
expect(css).toMatch(/\.rounded-lg\{border-radius:var\(--radius-lg\)\}/);
|
|
44
|
+
// Toaster viewport + kind accents
|
|
45
|
+
expect(css).toMatch(/\.border-l-success\{[^}]*var\(--color-success\)/);
|
|
46
|
+
expect(css).toMatch(/\.text-warning\{[^}]*var\(--color-warning\)/);
|
|
47
|
+
expect(css).toMatch(/\.bg-popover\{[^}]*var\(--color-popover\)/);
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
it('ships the dark palette under both dark selectors', () => {
|
|
@@ -58,3 +63,47 @@ describe('dist/styles.css contract', () => {
|
|
|
58
63
|
expect(css).toMatch(/@layer base/);
|
|
59
64
|
});
|
|
60
65
|
});
|
|
66
|
+
|
|
67
|
+
describe('bridge.css contract (published as-is)', () => {
|
|
68
|
+
const requiredSlots = [
|
|
69
|
+
'background',
|
|
70
|
+
'foreground',
|
|
71
|
+
'card',
|
|
72
|
+
'card-foreground',
|
|
73
|
+
'popover',
|
|
74
|
+
'popover-foreground',
|
|
75
|
+
'primary',
|
|
76
|
+
'primary-foreground',
|
|
77
|
+
'secondary',
|
|
78
|
+
'secondary-foreground',
|
|
79
|
+
'muted',
|
|
80
|
+
'muted-foreground',
|
|
81
|
+
'accent',
|
|
82
|
+
'accent-foreground',
|
|
83
|
+
'border',
|
|
84
|
+
'input',
|
|
85
|
+
'destructive',
|
|
86
|
+
'destructive-foreground',
|
|
87
|
+
'ring',
|
|
88
|
+
] as const;
|
|
89
|
+
|
|
90
|
+
it('maps every required slot to a bare --app-* token (loud failure)', () => {
|
|
91
|
+
for (const slot of requiredSlots) {
|
|
92
|
+
expect(bridge).toContain(`--color-${slot}: var(--app-${slot})`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('gives optional slots DS-light defaults as fallbacks', () => {
|
|
97
|
+
expect(bridge).toContain('--color-success: var(--app-success, #16a34a)');
|
|
98
|
+
expect(bridge).toContain('--color-warning: var(--app-warning, #d97706)');
|
|
99
|
+
expect(bridge).toContain('--color-info: var(--app-info, #2563eb)');
|
|
100
|
+
expect(bridge).toContain('--color-primary-soft: var(--app-primary-soft, rgba(37, 99, 235, 0.1))');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('never touches radius or mono font (name-collision doctrine)', () => {
|
|
104
|
+
// Strip doc comments first — the header documents the doctrine by name.
|
|
105
|
+
const declarations = bridge.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
106
|
+
expect(declarations).not.toContain('--radius-');
|
|
107
|
+
expect(declarations).not.toContain('--font-mono');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DataTable Component (Organism)
|
|
3
|
-
* @description Enhanced table component for displaying data with sorting and pagination
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { useState, useMemo } from 'react';
|
|
7
|
-
import {
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
ArrowDown,
|
|
5
|
+
ArrowUp,
|
|
6
|
+
ChevronLeft,
|
|
7
|
+
ChevronRight,
|
|
8
|
+
ChevronsLeft,
|
|
9
|
+
ChevronsRight,
|
|
10
|
+
ChevronsUpDown,
|
|
11
|
+
} from 'lucide-react';
|
|
8
12
|
import { cn } from '../../infrastructure/utils';
|
|
9
13
|
import {
|
|
10
14
|
Table,
|
|
@@ -17,6 +21,7 @@ import {
|
|
|
17
21
|
TableCaption,
|
|
18
22
|
} from './Table';
|
|
19
23
|
import { Button } from '../atoms/Button';
|
|
24
|
+
import { Skeleton } from '../atoms/Skeleton';
|
|
20
25
|
import {
|
|
21
26
|
computeTotalPages,
|
|
22
27
|
clampPageNumber,
|
|
@@ -25,21 +30,47 @@ import {
|
|
|
25
30
|
import type { BaseProps } from '../../domain/types';
|
|
26
31
|
import type { MediumSizes } from '../../infrastructure/constants';
|
|
27
32
|
|
|
33
|
+
export type ColumnAlign = 'left' | 'right' | 'center';
|
|
34
|
+
export type ColumnHideBelow = 'sm' | 'md' | 'lg' | 'xl';
|
|
35
|
+
export type SortDirection = 'asc' | 'desc';
|
|
36
|
+
|
|
37
|
+
export interface SortState {
|
|
38
|
+
id: string;
|
|
39
|
+
dir: SortDirection;
|
|
40
|
+
}
|
|
41
|
+
|
|
28
42
|
export interface Column<T> {
|
|
29
43
|
id: string;
|
|
30
|
-
header:
|
|
31
|
-
accessor: keyof T | ((row: T) =>
|
|
32
|
-
cell?: (row: T) =>
|
|
44
|
+
header: ReactNode;
|
|
45
|
+
accessor: keyof T | ((row: T) => ReactNode);
|
|
46
|
+
cell?: (row: T) => ReactNode;
|
|
47
|
+
sortValue?: (row: T) => string | number;
|
|
33
48
|
sortable?: boolean;
|
|
49
|
+
align?: ColumnAlign;
|
|
50
|
+
hideBelow?: ColumnHideBelow;
|
|
34
51
|
className?: string;
|
|
35
52
|
}
|
|
36
53
|
|
|
54
|
+
export interface TablePaginationProps extends BaseProps {
|
|
55
|
+
page: number;
|
|
56
|
+
totalPages: number;
|
|
57
|
+
onPage: (page: number) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
export interface DataTableProps<T> extends BaseProps {
|
|
38
61
|
data: T[];
|
|
39
62
|
columns: Column<T>[];
|
|
40
63
|
caption?: string;
|
|
41
64
|
size?: MediumSizes;
|
|
42
65
|
sortable?: boolean;
|
|
66
|
+
sort?: SortState;
|
|
67
|
+
onSortChange?: (id: string) => void;
|
|
68
|
+
rowKey?: (row: T) => string;
|
|
69
|
+
selectedId?: string;
|
|
70
|
+
dense?: boolean;
|
|
71
|
+
loading?: boolean;
|
|
72
|
+
empty?: ReactNode;
|
|
73
|
+
sticky?: boolean;
|
|
43
74
|
paginated?: boolean;
|
|
44
75
|
pageSize?: number;
|
|
45
76
|
emptyState?: {
|
|
@@ -49,6 +80,109 @@ export interface DataTableProps<T> extends BaseProps {
|
|
|
49
80
|
onRowClick?: (row: T) => void;
|
|
50
81
|
}
|
|
51
82
|
|
|
83
|
+
const alignStyles: Record<ColumnAlign, string> = {
|
|
84
|
+
left: 'text-left',
|
|
85
|
+
right: 'text-right',
|
|
86
|
+
center: 'text-center',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const hideBelowStyles: Record<ColumnHideBelow, string> = {
|
|
90
|
+
sm: 'hidden sm:table-cell',
|
|
91
|
+
md: 'hidden md:table-cell',
|
|
92
|
+
lg: 'hidden lg:table-cell',
|
|
93
|
+
xl: 'hidden xl:table-cell',
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function displayValue<T>(column: Column<T>, row: T): ReactNode {
|
|
97
|
+
if (column.cell) return column.cell(row);
|
|
98
|
+
return typeof column.accessor === 'function'
|
|
99
|
+
? column.accessor(row)
|
|
100
|
+
: (row[column.accessor] as ReactNode);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sortValueOf<T>(column: Column<T>, row: T): string | number | null {
|
|
104
|
+
if (column.sortValue) return column.sortValue(row);
|
|
105
|
+
const value =
|
|
106
|
+
typeof column.accessor === 'function' ? column.accessor(row) : row[column.accessor];
|
|
107
|
+
if (typeof value === 'number') return value;
|
|
108
|
+
if (typeof value === 'boolean') return value ? 1 : 0;
|
|
109
|
+
if (typeof value === 'string') return value;
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function compareValues(a: string | number | null, b: string | number | null): number {
|
|
114
|
+
if (a === null && b === null) return 0;
|
|
115
|
+
if (a === null) return 1;
|
|
116
|
+
if (b === null) return -1;
|
|
117
|
+
if (typeof a === 'number' && typeof b === 'number') return a - b;
|
|
118
|
+
return String(a).localeCompare(String(b));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function TablePagination({ className, page, totalPages, onPage }: TablePaginationProps) {
|
|
122
|
+
const windowPages = useMemo(() => {
|
|
123
|
+
const start = Math.max(1, Math.min(page - 2, totalPages - 4));
|
|
124
|
+
const out: number[] = [];
|
|
125
|
+
for (let p = start; p < start + 5 && p <= totalPages; p += 1) out.push(p);
|
|
126
|
+
return out;
|
|
127
|
+
}, [page, totalPages]);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<div className={cn('flex items-center gap-1', className)}>
|
|
131
|
+
<Button
|
|
132
|
+
size="sm"
|
|
133
|
+
variant="outline"
|
|
134
|
+
onClick={() => onPage(1)}
|
|
135
|
+
disabled={page <= 1}
|
|
136
|
+
aria-label="First page"
|
|
137
|
+
>
|
|
138
|
+
<ChevronsLeft className="h-4 w-4" />
|
|
139
|
+
</Button>
|
|
140
|
+
<Button
|
|
141
|
+
size="sm"
|
|
142
|
+
variant="outline"
|
|
143
|
+
onClick={() => onPage(page - 1)}
|
|
144
|
+
disabled={page <= 1}
|
|
145
|
+
aria-label="Previous page"
|
|
146
|
+
>
|
|
147
|
+
<ChevronLeft className="h-4 w-4" />
|
|
148
|
+
</Button>
|
|
149
|
+
{windowPages.map((p) => (
|
|
150
|
+
<Button
|
|
151
|
+
key={p}
|
|
152
|
+
size="sm"
|
|
153
|
+
variant="outline"
|
|
154
|
+
className={cn(
|
|
155
|
+
'min-w-9',
|
|
156
|
+
p === page && 'pointer-events-none bg-primary text-primary-foreground'
|
|
157
|
+
)}
|
|
158
|
+
onClick={() => onPage(p)}
|
|
159
|
+
aria-current={p === page ? 'page' : undefined}
|
|
160
|
+
>
|
|
161
|
+
{p}
|
|
162
|
+
</Button>
|
|
163
|
+
))}
|
|
164
|
+
<Button
|
|
165
|
+
size="sm"
|
|
166
|
+
variant="outline"
|
|
167
|
+
onClick={() => onPage(page + 1)}
|
|
168
|
+
disabled={page >= totalPages}
|
|
169
|
+
aria-label="Next page"
|
|
170
|
+
>
|
|
171
|
+
<ChevronRight className="h-4 w-4" />
|
|
172
|
+
</Button>
|
|
173
|
+
<Button
|
|
174
|
+
size="sm"
|
|
175
|
+
variant="outline"
|
|
176
|
+
onClick={() => onPage(totalPages)}
|
|
177
|
+
disabled={page >= totalPages}
|
|
178
|
+
aria-label="Last page"
|
|
179
|
+
>
|
|
180
|
+
<ChevronsRight className="h-4 w-4" />
|
|
181
|
+
</Button>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
52
186
|
export function DataTable<T>({
|
|
53
187
|
className,
|
|
54
188
|
data,
|
|
@@ -56,48 +190,51 @@ export function DataTable<T>({
|
|
|
56
190
|
caption,
|
|
57
191
|
size = 'md',
|
|
58
192
|
sortable = false,
|
|
193
|
+
sort,
|
|
194
|
+
onSortChange,
|
|
195
|
+
rowKey,
|
|
196
|
+
selectedId,
|
|
197
|
+
dense = false,
|
|
198
|
+
loading = false,
|
|
199
|
+
empty,
|
|
200
|
+
sticky = false,
|
|
59
201
|
paginated = false,
|
|
60
202
|
pageSize = 10,
|
|
61
203
|
emptyState,
|
|
62
204
|
onRowClick,
|
|
63
205
|
...props
|
|
64
206
|
}: DataTableProps<T>) {
|
|
65
|
-
const [
|
|
66
|
-
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
|
|
207
|
+
const [internalSort, setInternalSort] = useState<SortState | null>(null);
|
|
67
208
|
const [currentPage, setCurrentPage] = useState(1);
|
|
68
209
|
|
|
69
|
-
const
|
|
70
|
-
|
|
210
|
+
const activeSort = sort ?? internalSort;
|
|
211
|
+
const controlled = sort !== undefined;
|
|
71
212
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
213
|
+
const handleSort = (id: string) => {
|
|
214
|
+
if (!sortable) return;
|
|
215
|
+
if (controlled) {
|
|
216
|
+
onSortChange?.(id);
|
|
217
|
+
return;
|
|
77
218
|
}
|
|
219
|
+
setInternalSort((prev) =>
|
|
220
|
+
prev && prev.id === id
|
|
221
|
+
? { id, dir: prev.dir === 'asc' ? 'desc' : 'asc' }
|
|
222
|
+
: { id, dir: 'asc' }
|
|
223
|
+
);
|
|
78
224
|
};
|
|
79
225
|
|
|
80
226
|
const sortedData = useMemo(() => {
|
|
81
|
-
if (!
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (aValue === bValue) return 0;
|
|
91
|
-
if (aValue == null || bValue == null) return 0;
|
|
92
|
-
|
|
93
|
-
const comparison = (aValue as string | number) < (bValue as string | number) ? -1 : 1;
|
|
94
|
-
return sortDirection === 'asc' ? comparison : -comparison;
|
|
95
|
-
});
|
|
96
|
-
}, [data, sortColumn, sortDirection, columns, sortable]);
|
|
227
|
+
if (!activeSort || !sortable) return data;
|
|
228
|
+
const column = columns.find((col) => col.id === activeSort.id);
|
|
229
|
+
if (!column) return data;
|
|
230
|
+
const factor = activeSort.dir === 'asc' ? 1 : -1;
|
|
231
|
+
return [...data].sort(
|
|
232
|
+
(a, b) => factor * compareValues(sortValueOf(column, a), sortValueOf(column, b))
|
|
233
|
+
);
|
|
234
|
+
}, [data, activeSort, columns, sortable]);
|
|
97
235
|
|
|
98
236
|
const paginatedData = useMemo(() => {
|
|
99
237
|
if (!paginated) return sortedData;
|
|
100
|
-
|
|
101
238
|
const start = (currentPage - 1) * pageSize;
|
|
102
239
|
const end = start + pageSize;
|
|
103
240
|
return sortedData.slice(start, end);
|
|
@@ -107,27 +244,42 @@ export function DataTable<T>({
|
|
|
107
244
|
const safePage = clampPageNumber(currentPage, totalPages);
|
|
108
245
|
const pageBounds = computePageBounds(currentPage, pageSize, data.length);
|
|
109
246
|
|
|
110
|
-
const renderCell = (row: T, column: Column<T>) => {
|
|
111
|
-
if (column.cell) {
|
|
112
|
-
return column.cell(row);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const value = typeof column.accessor === 'function' ? column.accessor(row) : row[column.accessor];
|
|
116
|
-
return value as React.ReactNode;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
247
|
const sizeStyles: Record<MediumSizes, string> = {
|
|
120
248
|
sm: 'text-xs',
|
|
121
249
|
md: 'text-sm',
|
|
122
250
|
lg: 'text-base',
|
|
123
251
|
};
|
|
124
252
|
|
|
125
|
-
const
|
|
126
|
-
sm: 'px-2
|
|
127
|
-
md: 'px-4
|
|
128
|
-
lg: 'px-6
|
|
253
|
+
const pxStyles: Record<MediumSizes, string> = {
|
|
254
|
+
sm: 'px-2',
|
|
255
|
+
md: 'px-4',
|
|
256
|
+
lg: 'px-6',
|
|
129
257
|
};
|
|
130
258
|
|
|
259
|
+
const pyStyles: Record<MediumSizes, string> = dense
|
|
260
|
+
? { sm: 'py-1', md: 'py-1.5', lg: 'py-2' }
|
|
261
|
+
: { sm: 'py-2', md: 'py-3', lg: 'py-4' };
|
|
262
|
+
|
|
263
|
+
if (loading) {
|
|
264
|
+
const skeletonRows = dense ? 8 : 6;
|
|
265
|
+
const skeletonCols = Math.min(columns.length, 6);
|
|
266
|
+
return (
|
|
267
|
+
<div className={cn('space-y-3 p-2', className)} {...props}>
|
|
268
|
+
{Array.from({ length: skeletonRows }).map((_, row) => (
|
|
269
|
+
<div key={row} className="flex items-center gap-4" style={{ opacity: 1 - row * 0.08 }}>
|
|
270
|
+
{Array.from({ length: skeletonCols }).map((_, col) => (
|
|
271
|
+
<Skeleton key={col} variant="text" className="flex-1" />
|
|
272
|
+
))}
|
|
273
|
+
</div>
|
|
274
|
+
))}
|
|
275
|
+
</div>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (data.length === 0 && empty !== undefined) {
|
|
280
|
+
return <>{empty}</>;
|
|
281
|
+
}
|
|
282
|
+
|
|
131
283
|
if (data.length === 0 && emptyState) {
|
|
132
284
|
return (
|
|
133
285
|
<div className={cn('p-8 text-center', className)}>
|
|
@@ -145,96 +297,104 @@ export function DataTable<T>({
|
|
|
145
297
|
{caption && <TableCaption>{caption}</TableCaption>}
|
|
146
298
|
<TableHeader>
|
|
147
299
|
<TableRow>
|
|
148
|
-
{columns.map((column) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<span className="text-xs">
|
|
163
|
-
{sortDirection === 'asc' ? (
|
|
164
|
-
<ArrowUp className="h-3 w-3" aria-hidden="true" />
|
|
165
|
-
) : (
|
|
166
|
-
<ArrowDown className="h-3 w-3" aria-hidden="true" />
|
|
167
|
-
)}
|
|
168
|
-
</span>
|
|
300
|
+
{columns.map((column) => {
|
|
301
|
+
const isActiveSort = activeSort?.id === column.id;
|
|
302
|
+
return (
|
|
303
|
+
<TableHead
|
|
304
|
+
key={column.id}
|
|
305
|
+
className={cn(
|
|
306
|
+
sizeStyles[size],
|
|
307
|
+
pxStyles[size],
|
|
308
|
+
pyStyles[size],
|
|
309
|
+
sticky && 'sticky top-0 z-[1] bg-background/95 backdrop-blur',
|
|
310
|
+
column.align && column.align !== 'left' && alignStyles[column.align],
|
|
311
|
+
column.hideBelow && hideBelowStyles[column.hideBelow],
|
|
312
|
+
column.sortable && sortable && 'cursor-pointer select-none hover:bg-muted/50',
|
|
313
|
+
column.className
|
|
169
314
|
)}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
315
|
+
onClick={() => {
|
|
316
|
+
if (column.sortable && sortable) handleSort(column.id);
|
|
317
|
+
}}
|
|
318
|
+
aria-sort={
|
|
319
|
+
isActiveSort
|
|
320
|
+
? activeSort?.dir === 'asc'
|
|
321
|
+
? 'ascending'
|
|
322
|
+
: 'descending'
|
|
323
|
+
: column.sortable && sortable
|
|
324
|
+
? 'none'
|
|
325
|
+
: undefined
|
|
326
|
+
}
|
|
327
|
+
>
|
|
328
|
+
<div
|
|
329
|
+
className={cn(
|
|
330
|
+
'flex items-center gap-2',
|
|
331
|
+
column.align === 'right' && 'flex-row-reverse'
|
|
332
|
+
)}
|
|
333
|
+
>
|
|
334
|
+
{column.header}
|
|
335
|
+
{column.sortable && sortable && (
|
|
336
|
+
<span className="text-xs">
|
|
337
|
+
{isActiveSort ? (
|
|
338
|
+
activeSort?.dir === 'asc' ? (
|
|
339
|
+
<ArrowUp className="h-3 w-3" aria-hidden="true" />
|
|
340
|
+
) : (
|
|
341
|
+
<ArrowDown className="h-3 w-3" aria-hidden="true" />
|
|
342
|
+
)
|
|
343
|
+
) : (
|
|
344
|
+
<ChevronsUpDown className="h-3 w-3 opacity-40" aria-hidden="true" />
|
|
345
|
+
)}
|
|
346
|
+
</span>
|
|
347
|
+
)}
|
|
348
|
+
</div>
|
|
349
|
+
</TableHead>
|
|
350
|
+
);
|
|
351
|
+
})}
|
|
173
352
|
</TableRow>
|
|
174
353
|
</TableHeader>
|
|
175
354
|
<TableBody>
|
|
176
|
-
{paginatedData.map((row,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
355
|
+
{paginatedData.map((row, index) => {
|
|
356
|
+
const key = rowKey ? rowKey(row) : index;
|
|
357
|
+
const selected = rowKey !== undefined && selectedId !== undefined && rowKey(row) === selectedId;
|
|
358
|
+
return (
|
|
359
|
+
<TableRow
|
|
360
|
+
key={key}
|
|
361
|
+
className={cn(
|
|
362
|
+
onRowClick && 'cursor-pointer hover:bg-muted/50',
|
|
363
|
+
selected && 'bg-muted/60'
|
|
364
|
+
)}
|
|
365
|
+
onClick={() => onRowClick?.(row)}
|
|
366
|
+
>
|
|
367
|
+
{columns.map((column) => (
|
|
368
|
+
<TableCell
|
|
369
|
+
key={column.id}
|
|
370
|
+
className={cn(
|
|
371
|
+
pxStyles[size],
|
|
372
|
+
pyStyles[size],
|
|
373
|
+
column.align && column.align !== 'left' && alignStyles[column.align],
|
|
374
|
+
column.hideBelow && hideBelowStyles[column.hideBelow],
|
|
375
|
+
column.className
|
|
376
|
+
)}
|
|
377
|
+
>
|
|
378
|
+
{displayValue(column, row)}
|
|
379
|
+
</TableCell>
|
|
380
|
+
))}
|
|
381
|
+
</TableRow>
|
|
382
|
+
);
|
|
383
|
+
})}
|
|
192
384
|
</TableBody>
|
|
193
385
|
{paginated && (
|
|
194
386
|
<TableFooter>
|
|
195
387
|
<TableRow>
|
|
196
388
|
<TableCell colSpan={columns.length}>
|
|
197
|
-
<div className="flex items-center justify-between">
|
|
389
|
+
<div className="flex items-center justify-between gap-2">
|
|
198
390
|
<p className={cn('text-muted-foreground', sizeStyles[size])}>
|
|
199
391
|
Showing {pageBounds.startIndex} to {pageBounds.endIndex} of {data.length} results
|
|
200
392
|
</p>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
disabled={safePage === 1}
|
|
207
|
-
>
|
|
208
|
-
<ChevronsLeft className="h-4 w-4" />
|
|
209
|
-
</Button>
|
|
210
|
-
<Button
|
|
211
|
-
size="sm"
|
|
212
|
-
variant="outline"
|
|
213
|
-
onClick={() => setCurrentPage((p) => clampPageNumber(p - 1, totalPages))}
|
|
214
|
-
disabled={safePage === 1}
|
|
215
|
-
>
|
|
216
|
-
<ChevronLeft className="h-4 w-4" />
|
|
217
|
-
</Button>
|
|
218
|
-
<span className={cn('text-sm font-medium', sizeStyles[size])}>
|
|
219
|
-
Page {safePage} of {totalPages}
|
|
220
|
-
</span>
|
|
221
|
-
<Button
|
|
222
|
-
size="sm"
|
|
223
|
-
variant="outline"
|
|
224
|
-
onClick={() => setCurrentPage((p) => clampPageNumber(p + 1, totalPages))}
|
|
225
|
-
disabled={safePage === totalPages}
|
|
226
|
-
>
|
|
227
|
-
<ChevronRight className="h-4 w-4" />
|
|
228
|
-
</Button>
|
|
229
|
-
<Button
|
|
230
|
-
size="sm"
|
|
231
|
-
variant="outline"
|
|
232
|
-
onClick={() => setCurrentPage(totalPages)}
|
|
233
|
-
disabled={safePage === totalPages}
|
|
234
|
-
>
|
|
235
|
-
<ChevronsRight className="h-4 w-4" />
|
|
236
|
-
</Button>
|
|
237
|
-
</div>
|
|
393
|
+
<TablePagination
|
|
394
|
+
page={safePage}
|
|
395
|
+
totalPages={totalPages}
|
|
396
|
+
onPage={(p) => setCurrentPage(clampPageNumber(p, totalPages))}
|
|
397
|
+
/>
|
|
238
398
|
</div>
|
|
239
399
|
</TableCell>
|
|
240
400
|
</TableRow>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KeyValueList Component (Organism)
|
|
3
|
+
* @description Definition list of label → value rows (drawers, detail panels).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { forwardRef } from 'react';
|
|
7
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
8
|
+
import { cn } from '../../infrastructure/utils';
|
|
9
|
+
import type { BaseProps } from '../../domain/types';
|
|
10
|
+
|
|
11
|
+
export interface KeyValueItem {
|
|
12
|
+
label: ReactNode;
|
|
13
|
+
value: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface KeyValueListProps extends HTMLAttributes<HTMLDListElement>, BaseProps {
|
|
17
|
+
items: KeyValueItem[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const KeyValueList = forwardRef<HTMLDListElement, KeyValueListProps>(
|
|
21
|
+
({ className, items, ...props }, ref) => (
|
|
22
|
+
<dl ref={ref} className={cn('grid gap-1.5 py-1 text-sm', className)} {...props}>
|
|
23
|
+
{items.map((item, i) => (
|
|
24
|
+
<div key={i} className="flex items-baseline justify-between gap-4">
|
|
25
|
+
<dt className="text-muted-foreground">{item.label}</dt>
|
|
26
|
+
<dd className="min-w-0 text-right font-medium">{item.value}</dd>
|
|
27
|
+
</div>
|
|
28
|
+
))}
|
|
29
|
+
</dl>
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
KeyValueList.displayName = 'KeyValueList';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toaster Component (Organism)
|
|
3
|
+
* @description Toast notification system. ToastProvider wraps the app root
|
|
4
|
+
* ONCE and renders the viewport (bottom-right stack, max 4, auto-dismiss
|
|
5
|
+
* 4.2 s); pages fire toasts via useToast()({ kind, title, description }).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
import { AlertTriangle, CheckCircle2, Info, X, XCircle } from 'lucide-react';
|
|
11
|
+
import { cn } from '../../infrastructure/utils';
|
|
12
|
+
|
|
13
|
+
export type ToastKind = 'success' | 'error' | 'warning' | 'info';
|
|
14
|
+
|
|
15
|
+
export interface ToastOptions {
|
|
16
|
+
kind: ToastKind;
|
|
17
|
+
title: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ToastApi {
|
|
22
|
+
/** Fire a toast — after every mutating action is the fleet idiom. */
|
|
23
|
+
toast: (t: ToastOptions) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ToastEntry extends ToastOptions {
|
|
27
|
+
id: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ToastContext = createContext<ToastApi>({ toast: () => {} });
|
|
31
|
+
|
|
32
|
+
/** Consume from any page below the provider: const { toast } = useToast(); */
|
|
33
|
+
export function useToast(): ToastApi {
|
|
34
|
+
return useContext(ToastContext);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const kindMeta: Record<ToastKind, { icon: typeof CheckCircle2; accent: string; iconColor: string }> = {
|
|
38
|
+
success: { icon: CheckCircle2, accent: 'border-l-success', iconColor: 'text-success' },
|
|
39
|
+
error: { icon: XCircle, accent: 'border-l-destructive', iconColor: 'text-destructive' },
|
|
40
|
+
warning: { icon: AlertTriangle, accent: 'border-l-warning', iconColor: 'text-warning' },
|
|
41
|
+
info: { icon: Info, accent: 'border-l-info', iconColor: 'text-info' },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface ToastProviderProps {
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function ToastProvider({ children }: ToastProviderProps) {
|
|
49
|
+
const [toasts, setToasts] = useState<ToastEntry[]>([]);
|
|
50
|
+
const counter = useRef(0);
|
|
51
|
+
|
|
52
|
+
const dismiss = useCallback((id: number) => {
|
|
53
|
+
setToasts((list) => list.filter((t) => t.id !== id));
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const toast = useCallback(
|
|
57
|
+
(t: ToastOptions) => {
|
|
58
|
+
const id = ++counter.current;
|
|
59
|
+
setToasts((list) => [...list.slice(-3), { id, ...t }]);
|
|
60
|
+
setTimeout(() => dismiss(id), 4200);
|
|
61
|
+
},
|
|
62
|
+
[dismiss],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const value = useMemo(() => ({ toast }), [toast]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<ToastContext.Provider value={value}>
|
|
69
|
+
{children}
|
|
70
|
+
<div
|
|
71
|
+
aria-live="polite"
|
|
72
|
+
className="pointer-events-none fixed bottom-4 right-4 z-[100] flex w-[340px] max-w-[calc(100vw-2rem)] flex-col gap-2"
|
|
73
|
+
>
|
|
74
|
+
{toasts.map((t) => {
|
|
75
|
+
const meta = kindMeta[t.kind];
|
|
76
|
+
const Icon = meta.icon;
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
key={t.id}
|
|
80
|
+
className={cn(
|
|
81
|
+
'pointer-events-auto flex items-start gap-2.5 rounded-lg border border-border border-l-[3px] bg-popover p-3 text-popover-foreground shadow-lg',
|
|
82
|
+
meta.accent,
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
<Icon className={cn('mt-px h-4 w-4 shrink-0', meta.iconColor)} />
|
|
86
|
+
<div className="min-w-0 flex-1">
|
|
87
|
+
<p className="text-sm font-medium leading-snug">{t.title}</p>
|
|
88
|
+
{t.description && <p className="mt-0.5 text-xs leading-snug text-muted-foreground">{t.description}</p>}
|
|
89
|
+
</div>
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={() => dismiss(t.id)}
|
|
93
|
+
aria-label="Dismiss notification"
|
|
94
|
+
className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
|
|
95
|
+
>
|
|
96
|
+
<X className="h-3.5 w-3.5" />
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
})}
|
|
101
|
+
</div>
|
|
102
|
+
</ToastContext.Provider>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -71,8 +71,16 @@ export type { EmptyStateProps } from './EmptyState';
|
|
|
71
71
|
export { LoadingState } from './LoadingState';
|
|
72
72
|
export type { LoadingStateProps } from './LoadingState';
|
|
73
73
|
|
|
74
|
-
export { DataTable } from './DataTable';
|
|
75
|
-
export type {
|
|
74
|
+
export { DataTable, TablePagination } from './DataTable';
|
|
75
|
+
export type {
|
|
76
|
+
DataTableProps,
|
|
77
|
+
Column,
|
|
78
|
+
SortState,
|
|
79
|
+
TablePaginationProps,
|
|
80
|
+
ColumnAlign,
|
|
81
|
+
ColumnHideBelow,
|
|
82
|
+
SortDirection,
|
|
83
|
+
} from './DataTable';
|
|
76
84
|
|
|
77
85
|
export { StatCard } from './StatCard';
|
|
78
86
|
export type { StatCardProps } from './StatCard';
|
|
@@ -96,3 +104,9 @@ export {
|
|
|
96
104
|
} from './DropdownMenu';
|
|
97
105
|
|
|
98
106
|
export { ToggleGroup, ToggleGroupItem } from './ToggleGroup';
|
|
107
|
+
|
|
108
|
+
export { ToastProvider, useToast } from './Toaster';
|
|
109
|
+
export type { ToastKind, ToastOptions } from './Toaster';
|
|
110
|
+
|
|
111
|
+
export { KeyValueList } from './KeyValueList';
|
|
112
|
+
export type { KeyValueListProps, KeyValueItem } from './KeyValueList';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/web-design-system — semantic palette bridge (CSS-camp apps)
|
|
3
|
+
*
|
|
4
|
+
* Bu dosya, Tailwind derlemesi yapmayan (vanilla CSS) uygulamaların
|
|
5
|
+
* `--color-*` semantik paletini kendi renk isimlerine bağlayan KANONİK
|
|
6
|
+
* köprüdür. 8 uygulamada elle kopyalanan `packages.css` eşlemesinin
|
|
7
|
+
* paketteki tek doğru hâlidir — yeni projeler eşlemeyi buradan alır,
|
|
8
|
+
* kendi başına yazmaz.
|
|
9
|
+
*
|
|
10
|
+
* Sözleşme:
|
|
11
|
+
* 1. İçe aktarım sırası (uygulamanın ana css'i):
|
|
12
|
+
* @import "@umituz/web-design-system/styles.css";
|
|
13
|
+
* @import "@umituz/web-design-system/bridge.css";
|
|
14
|
+
* :root { --app-... } ← uygulamanın kendi token'ları (SON)
|
|
15
|
+
* 2. ZORUNLU slotlar (fallback'siz): uygulama 19 çekirdek `--app-*`
|
|
16
|
+
* tokenını tanımlamazsa ilgili renk görünür biçimde bozulur —
|
|
17
|
+
* eksik palet bilinçli olarak gürültülü başarısız olur.
|
|
18
|
+
* 3. OPSİYONEL slotlar: tanımlanmazsa DS'in açık-theme varsayılanı
|
|
19
|
+
* geçerli olur (success/warning/info üçlüsü, primary-soft, font).
|
|
20
|
+
* 4. Karanlık theme: uygulama `[data-theme="dark"] / .dark` altında
|
|
21
|
+
* `--app-*` tokenlarını yeniden tanımlar; köprü otomatik devralır.
|
|
22
|
+
* 5. `--radius-*` ve `--font-mono` bu dosyada YOKTUR: bilinçli isim
|
|
23
|
+
* çakışması doktrini — uygulama kendi css'inde (unlayered)
|
|
24
|
+
* yeniden tanımlayarak ezer.
|
|
25
|
+
* 6. Tailwind-camp uygulamalar (kaynağı `@source` ile derleyenler) bu
|
|
26
|
+
* dosyaya ihtiyaç duymaz; kendi `@theme` paletiyle ezer.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
:root {
|
|
30
|
+
/* --- ZORUNLU: yüzeyler & metin --- */
|
|
31
|
+
--color-background: var(--app-background);
|
|
32
|
+
--color-foreground: var(--app-foreground);
|
|
33
|
+
--color-card: var(--app-card);
|
|
34
|
+
--color-card-foreground: var(--app-card-foreground);
|
|
35
|
+
--color-popover: var(--app-popover);
|
|
36
|
+
--color-popover-foreground: var(--app-popover-foreground);
|
|
37
|
+
|
|
38
|
+
/* --- ZORUNLU: marka --- */
|
|
39
|
+
--color-primary: var(--app-primary);
|
|
40
|
+
--color-primary-foreground: var(--app-primary-foreground);
|
|
41
|
+
--color-secondary: var(--app-secondary);
|
|
42
|
+
--color-secondary-foreground: var(--app-secondary-foreground);
|
|
43
|
+
|
|
44
|
+
/* --- ZORUNLU: nötr --- */
|
|
45
|
+
--color-muted: var(--app-muted);
|
|
46
|
+
--color-muted-foreground: var(--app-muted-foreground);
|
|
47
|
+
--color-accent: var(--app-accent);
|
|
48
|
+
--color-accent-foreground: var(--app-accent-foreground);
|
|
49
|
+
--color-border: var(--app-border);
|
|
50
|
+
--color-input: var(--app-input);
|
|
51
|
+
|
|
52
|
+
/* --- ZORUNLU: yıkıcı & odak --- */
|
|
53
|
+
--color-destructive: var(--app-destructive);
|
|
54
|
+
--color-destructive-foreground: var(--app-destructive-foreground);
|
|
55
|
+
--color-ring: var(--app-ring);
|
|
56
|
+
|
|
57
|
+
/* --- OPSİYONEL: durum üçlüsü (DS açık-theme varsayılanıyla) --- */
|
|
58
|
+
--color-success: var(--app-success, #16a34a);
|
|
59
|
+
--color-success-foreground: var(--app-success-foreground, #ffffff);
|
|
60
|
+
--color-warning: var(--app-warning, #d97706);
|
|
61
|
+
--color-warning-foreground: var(--app-warning-foreground, #ffffff);
|
|
62
|
+
--color-info: var(--app-info, #2563eb);
|
|
63
|
+
--color-info-foreground: var(--app-info-foreground, #ffffff);
|
|
64
|
+
|
|
65
|
+
/* --- OPSİYONEL: dashboard token sözleşmesi (udb bileşenleri bekler) --- */
|
|
66
|
+
--color-primary-soft: var(--app-primary-soft, rgba(37, 99, 235, 0.1));
|
|
67
|
+
|
|
68
|
+
/* --- OPSİYONEL: tipografi --- */
|
|
69
|
+
--font-sans: var(
|
|
70
|
+
--app-font-sans,
|
|
71
|
+
ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
|
|
72
|
+
"Noto Sans", sans-serif
|
|
73
|
+
);
|
|
74
|
+
}
|