Mark Duppenthaler commited on
Commit
ca345b7
·
1 Parent(s): 81574bd

Updated with sorting rows and columns

Browse files
README.md CHANGED
@@ -1,15 +1,13 @@
1
  ---
2
- title: Omniseal Dev
3
  emoji: 🦀
4
  colorFrom: red
5
  colorTo: green
6
  sdk: docker
7
  pinned: false
8
- short_description: POC development
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
12
-
13
  ## Docker Build Instructions
14
 
15
  ### Prerequisites
@@ -24,9 +22,10 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
24
  ```bash
25
  cd backend
26
  conda env create -f environment.yml -y
 
27
  ```
28
 
29
- 2. Build frontend (outputs html, js, css into frontend/dist)
30
 
31
  ```bash
32
  cd frontend
@@ -34,7 +33,7 @@ npm install
34
  npm run build
35
  ```
36
 
37
- 3. Run backend server which serves built frontend files
38
 
39
  ```bash
40
  gunicorn --chdir backend -b 0.0.0.0:7860 app:app --reload
@@ -44,13 +43,7 @@ gunicorn --chdir backend -b 0.0.0.0:7860 app:app --reload
44
 
45
  ### Build Steps (Docker, huggingface)
46
 
47
- 1. Navigate to the project directory:
48
-
49
- ```bash
50
- cd /path/to/omniseal_dev
51
- ```
52
-
53
- 2. Build the Docker image:
54
 
55
  ```bash
56
  docker build -t omniseal-benchmark .
@@ -74,7 +67,7 @@ docker run -p 7860:7860 -v $(pwd)/backend:/app/backend omniseal-benchmark
74
 
75
  When updating the backend, you can run it in whichever build steps above to take advantage of hot-reload so you don't have to restart the server.
76
 
77
- For the frontend to take advantage of hot reload:
78
 
79
  1. Create a `.env.local` file in the frontend directory. Set `VITE_API_SERVER_URL` to where your backend server is running. When running locally it will be `VITE_API_SERVER_URL=http://localhost:7860`. This overrides the configuration in `.env` so the frontend will connect with your backend URL of choice.
80
 
 
1
  ---
2
+ title: Omniseal Leaderboard
3
  emoji: 🦀
4
  colorFrom: red
5
  colorTo: green
6
  sdk: docker
7
  pinned: false
8
+ short_description: Leaderboard for watermarking models
9
  ---
10
 
 
 
11
  ## Docker Build Instructions
12
 
13
  ### Prerequisites
 
22
  ```bash
23
  cd backend
24
  conda env create -f environment.yml -y
25
+ conda activate omniseal-benchmark-backend
26
  ```
27
 
28
+ 2. Build frontend (outputs html, js, css into frontend/dist). Note you only need this if you are updating the frontend, the repository would already have a build checked in at frontend/dist
29
 
30
  ```bash
31
  cd frontend
 
33
  npm run build
34
  ```
35
 
36
+ 3. Run backend server from project root. This would serve frontend files from port http://localhost:7860
37
 
38
  ```bash
39
  gunicorn --chdir backend -b 0.0.0.0:7860 app:app --reload
 
43
 
44
  ### Build Steps (Docker, huggingface)
45
 
46
+ 2. Build the Docker image from project root:
 
 
 
 
 
 
47
 
48
  ```bash
49
  docker build -t omniseal-benchmark .
 
67
 
68
  When updating the backend, you can run it in whichever build steps above to take advantage of hot-reload so you don't have to restart the server.
69
 
70
+ For the frontend:
71
 
72
  1. Create a `.env.local` file in the frontend directory. Set `VITE_API_SERVER_URL` to where your backend server is running. When running locally it will be `VITE_API_SERVER_URL=http://localhost:7860`. This overrides the configuration in `.env` so the frontend will connect with your backend URL of choice.
73
 
backend/mk_leaderboard.py DELETED
File without changes
frontend/dist/assets/index-BWZpYvtq.css ADDED
@@ -0,0 +1 @@
 
 
1
+ /*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-700:oklch(37.3% .034 259.733);--color-black:#000;--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--ease-in:cubic-bezier(.4,0,1,1);--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--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)}}@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%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@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}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax: "<percentage>"; inherits: true; initial-value: 0%;}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab,red,red)){:root{scrollbar-color:color-mix(in oklch,currentColor 35%,#0000)#0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E")}:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not([class*=drawer-open])>.drawer-toggle:checked){overflow:hidden}:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){scrollbar-gutter:stable;background-image:linear-gradient(var(--color-base-100),var(--color-base-100));--root-bg:var(--color-base-100)}@supports (color:color-mix(in lab,red,red)){:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){--root-bg:color-mix(in srgb,var(--color-base-100),oklch(0% 0 0) 40%)}}:where(.modal[open],.modal-open,.modal-toggle:checked+.modal):not(.modal-start,.modal-end){scrollbar-gutter:stable}:root,[data-theme]{background-color:var(--root-bg,var(--color-base-100));color:var(--color-base-content)}}@layer components;@layer utilities{.diff{webkit-user-select:none;-webkit-user-select:none;user-select:none;direction:ltr;grid-template-columns:auto 1fr;width:100%;display:grid;position:relative;overflow:hidden;container-type:inline-size}.diff:focus-visible,.diff:has(.diff-item-1:focus-visible){outline-style:var(--tw-outline-style);outline-offset:1px;outline-width:2px;outline-color:var(--color-base-content)}.diff:focus-visible .diff-resizer{min-width:90cqi;max-width:90cqi}.diff:has(.diff-item-2:focus-visible){outline-style:var(--tw-outline-style);outline-offset:1px;outline-width:2px}.diff:has(.diff-item-2:focus-visible) .diff-resizer{min-width:10cqi;max-width:10cqi}@supports (-webkit-overflow-scrolling:touch) and (overflow:-webkit-paged-x){.diff:focus .diff-resizer{min-width:10cqi;max-width:10cqi}.diff:has(.diff-item-1:focus) .diff-resizer{min-width:90cqi;max-width:90cqi}}.tab{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:hover{color:var(--color-base-content)}}.tab{--tab-p:1rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));order:var(--tab-order);height:var(--tab-height);border-color:#0000;padding-inline-start:var(--tab-p);padding-inline-end:var(--tab-p);font-size:.875rem}.tab:is(input[type=radio]){min-width:fit-content}.tab:is(input[type=radio]):after{content:attr(aria-label)}.tab:is(label){position:relative}.tab:is(label) input{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0;position:absolute;top:0;right:0;bottom:0;left:0}:is(.tab:checked,.tab:is(label:has(:checked)),.tab:is(.tab-active,[aria-selected=true]))+.tab-content{height:calc(100% - var(--tab-height) + var(--border));display:block}.tab:not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true]){color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.tab:not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true]){color:color-mix(in oklab,var(--color-base-content)50%,transparent)}}.tab:not(input):empty{cursor:default;flex-grow:1}.tab:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:focus{outline-offset:2px;outline:2px solid #0000}}.tab:focus-visible,.tab:is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab[disabled]{pointer-events:none;opacity:.4}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:1.9rem;width:.5rem;height:.5rem;transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:block;position:absolute;inset-inline-end:1.4rem;transform:translateY(-100%)rotate(45deg);box-shadow:2px 2px}.loading{pointer-events:none;aspect-ratio:1;vertical-align:middle;width:calc(var(--size-selector,.25rem)*6);background-color:currentColor;display:inline-block;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:100%;mask-size:100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.pointer-events-none{pointer-events:none}.collapse:not(td,tr,colgroup){visibility:visible}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;width:100%;transition:grid-template-rows .2s;display:grid;position:relative;overflow:hidden}.collapse>input:is([type=checkbox],[type=radio]){-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],:focus:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],:focus:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible;min-height:fit-content}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}.collapse:focus:not(.collapse-close,.collapse[open])>.collapse-title{cursor:unset}.collapse:is([open],:focus:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem;transition:padding .2s ease-out,background-color .2s ease-out}.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse.collapse-open.collapse-plus>.collapse-title:after{content:"−"}.collapse.collapse-arrow:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse.collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{content:"−"}.collapse:is(details){width:100%}.collapse:is(details) summary{display:block;position:relative}.collapse:is(details) summary::-webkit-details-marker{display:none}.collapse:is(details) summary{outline:none}.collapse-content{visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;transition:visibility .2s,padding .2s ease-out,background-color .2s ease-out}.collapse{visibility:collapse}.visible{visibility:visible}.toggle{border:var(--border)solid currentColor;color:var(--input-color);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p),var(--radius-selector-max)) + min(var(--border),var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab,red,red)){.toggle{box-shadow:0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab,red,red)){.toggle{--input-color:color-mix(in oklab,var(--color-base-content)50%,#0000)}}.toggle{--toggle-p:calc(var(--size)*.125);--size:calc(var(--size-selector,.25rem)*6);width:calc((var(--size)*2) - (var(--border) + var(--toggle-p))*2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:none}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:none}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);height:100%;box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab,red,red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000)}}.toggle:before{background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:-1px;outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border)solid currentColor;background-color:#0000}.input{cursor:text;border:var(--border)solid #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;width:clamp(3rem,20rem,100%);height:var(--size);touch-action:manipulation;border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab,red,red)){.input{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset}}.input{--size:calc(var(--size-field,.25rem)*10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.input{--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.input:where(input){display:inline-flex}.input :where(input){-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none;width:100%;height:100%;display:inline-flex}.input :where(input):focus,.input :where(input):focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input :where(input):focus,.input :where(input):focus-within{outline-offset:2px;outline:2px solid #0000}}.input :where(input[type=url]),.input :where(input[type=email]){direction:ltr}.input :where(input[type=date]){display:inline-block}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab,red,red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;z-index:1}.input:has(>input[disabled]),.input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.input:has(>input[disabled]),.input:is(:disabled,[disabled]){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]){box-shadow:none}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.input::-webkit-date-and-time-value{text-align:inherit}.input[type=number]::-webkit-inner-spin-button{margin-block:-.75rem;margin-inline-end:-.75rem}.input::-webkit-calendar-picker-indicator{position:absolute;inset-inline-end:.75em}.table{border-radius:var(--radius-box);text-align:left;width:100%;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(thead,tfoot){color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot){border-top:var(--border)solid var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(tfoot){border-top:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.steps{counter-reset:step;grid-auto-columns:1fr;grid-auto-flow:column;display:inline-grid;overflow:auto hidden}.steps .step{text-align:center;--step-bg:var(--color-base-300);--step-fg:var(--color-base-content);grid-template-rows:40px 1fr;grid-template-columns:auto;place-items:center;min-width:4rem;display:grid}.steps .step:before{width:100%;height:.5rem;color:var(--step-bg);background-color:var(--step-bg);--tw-content:"";content:var(--tw-content);border:1px solid;grid-row-start:1;grid-column-start:1;margin-inline-start:-100%;top:0}.steps .step>.step-icon,.steps .step:not(:has(.step-icon)):after{content:counter(step);counter-increment:step;z-index:1;color:var(--step-fg);background-color:var(--step-bg);border:1px solid var(--step-bg);border-radius:3.40282e38px;grid-row-start:1;grid-column-start:1;place-self:center;place-items:center;width:2rem;height:2rem;display:grid;position:relative}.steps .step:first-child:before{content:none}.steps .step[data-content]:after{content:attr(data-content)}.steps .step-neutral+.step-neutral:before,.steps .step-neutral:after,.steps .step-neutral>.step-icon{--step-bg:var(--color-neutral);--step-fg:var(--color-neutral-content)}.steps .step-primary+.step-primary:before,.steps .step-primary:after,.steps .step-primary>.step-icon{--step-bg:var(--color-primary);--step-fg:var(--color-primary-content)}.steps .step-secondary+.step-secondary:before,.steps .step-secondary:after,.steps .step-secondary>.step-icon{--step-bg:var(--color-secondary);--step-fg:var(--color-secondary-content)}.steps .step-accent+.step-accent:before,.steps .step-accent:after,.steps .step-accent>.step-icon{--step-bg:var(--color-accent);--step-fg:var(--color-accent-content)}.steps .step-info+.step-info:before,.steps .step-info:after,.steps .step-info>.step-icon{--step-bg:var(--color-info);--step-fg:var(--color-info-content)}.steps .step-success+.step-success:before,.steps .step-success:after,.steps .step-success>.step-icon{--step-bg:var(--color-success);--step-fg:var(--color-success-content)}.steps .step-warning+.step-warning:before,.steps .step-warning:after,.steps .step-warning>.step-icon{--step-bg:var(--color-warning);--step-fg:var(--color-warning-content)}.steps .step-error+.step-error:before,.steps .step-error:after,.steps .step-error>.step-icon{--step-bg:var(--color-error);--step-fg:var(--color-error-content)}.range{-webkit-appearance:none;-moz-appearance:none;appearance:none;webkit-appearance:none;--range-thumb:var(--color-base-100);--range-thumb-size:calc(var(--size-selector,.25rem)*6);--range-progress:currentColor;--range-fill:1;--range-p:.25rem;--range-bg:currentColor}@supports (color:color-mix(in lab,red,red)){.range{--range-bg:color-mix(in oklab,currentColor 10%,#0000)}}.range{cursor:pointer;vertical-align:middle;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)));width:clamp(3rem,20rem,100%);height:var(--range-thumb-size);background-color:#0000;border:none;overflow:hidden}[dir=rtl] .range{--range-dir:-1}.range:focus{outline:none}.range:focus-visible{outline-offset:2px;outline:2px solid}.range::-webkit-slider-runnable-track{background-color:var(--range-bg);border-radius:var(--radius-selector);width:100%;height:calc(var(--range-thumb-size)*.5)}@media (forced-colors:active){.range::-webkit-slider-runnable-track{border:1px solid}.range::-moz-range-track{border:1px solid}}.range::-webkit-slider-thumb{box-sizing:border-box;border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)));height:var(--range-thumb-size);width:var(--range-thumb-size);border:var(--range-p)solid;-webkit-appearance:none;-moz-appearance:none;appearance:none;webkit-appearance:none;color:var(--range-progress);box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor,0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill));background-color:currentColor;position:relative;top:50%;transform:translateY(-50%)}@supports (color:color-mix(in lab,red,red)){.range::-webkit-slider-thumb{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000),0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))}}.range::-moz-range-track{background-color:var(--range-bg);border-radius:var(--radius-selector);width:100%;height:calc(var(--range-thumb-size)*.5)}.range::-moz-range-thumb{box-sizing:border-box;border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)));height:var(--range-thumb-size);width:var(--range-thumb-size);border:var(--range-p)solid;color:var(--range-progress);box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor,0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill));background-color:currentColor;position:relative;top:50%}@supports (color:color-mix(in lab,red,red)){.range::-moz-range-thumb{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000),0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))}}.range:disabled{cursor:not-allowed;opacity:.3}.range\!{-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;webkit-appearance:none!important;--range-thumb:var(--color-base-100)!important;--range-thumb-size:calc(var(--size-selector,.25rem)*6)!important;--range-progress:currentColor!important;--range-fill:1!important;--range-p:.25rem!important;--range-bg:currentColor!important}@supports (color:color-mix(in lab,red,red)){.range\!{--range-bg:color-mix(in oklab,currentColor 10%,#0000)!important}}.range\!{cursor:pointer!important;vertical-align:middle!important;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector))!important;border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)))!important;width:clamp(3rem,20rem,100%)!important;height:var(--range-thumb-size)!important;background-color:#0000!important;border:none!important;overflow:hidden!important}[dir=rtl] .range\!{--range-dir:-1!important}.range\!:focus{outline:none!important}.range\!:focus-visible{outline-offset:2px!important;outline:2px solid!important}.range\!::-webkit-slider-runnable-track{background-color:var(--range-bg)!important;border-radius:var(--radius-selector)!important;width:100%!important;height:calc(var(--range-thumb-size)*.5)!important}@media (forced-colors:active){.range\!::-webkit-slider-runnable-track{border:1px solid!important}.range\!::-moz-range-track{border:1px solid!important}}.range\!::-webkit-slider-thumb{box-sizing:border-box!important;border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)))!important;height:var(--range-thumb-size)!important;width:var(--range-thumb-size)!important;border:var(--range-p)solid!important;-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;webkit-appearance:none!important;color:var(--range-progress)!important;box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor,0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))!important;background-color:currentColor!important;position:relative!important;top:50%!important;transform:translateY(-50%)!important}@supports (color:color-mix(in lab,red,red)){.range\!::-webkit-slider-thumb{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000),0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))!important}}.range\!::-moz-range-track{background-color:var(--range-bg)!important;border-radius:var(--radius-selector)!important;width:100%!important;height:calc(var(--range-thumb-size)*.5)!important}.range\!::-moz-range-thumb{box-sizing:border-box!important;border-radius:calc(var(--radius-selector) + min(var(--range-p),var(--radius-selector-max)))!important;height:var(--range-thumb-size)!important;width:var(--range-thumb-size)!important;border:var(--range-p)solid!important;color:var(--range-progress)!important;box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor,0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))!important;background-color:currentColor!important;position:relative!important;top:50%!important}@supports (color:color-mix(in lab,red,red)){.range\!::-moz-range-thumb{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000),0 0 0 2rem var(--range-thumb) inset,calc((var(--range-dir,1)*-100rem) - (var(--range-dir,1)*var(--range-thumb-size)/2)) 0 0 calc(100rem*var(--range-fill))!important}}.range\!:disabled{cursor:not-allowed!important;opacity:.3!important}.tabs-border .tab{--tab-border-color:#0000 #0000 var(--tab-border-color)#0000;border-radius:var(--radius-field);position:relative}.tabs-border .tab:before{--tw-content:"";content:var(--tw-content);background-color:var(--tab-border-color);border-radius:var(--radius-field);width:80%;height:3px;transition:background-color .2s;position:absolute;bottom:0;left:10%}:is(.tabs-border .tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled,[disabled]),.tabs-border .tab:is(input:checked),.tabs-border .tab:is(label:has(:checked))):before{--tab-border-color:currentColor;border-top:3px solid}.select{border:var(--border)solid #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-base-100);vertical-align:middle;width:clamp(3rem,20rem,100%);height:var(--size);touch-action:manipulation;text-overflow:ellipsis;box-shadow:0 1px var(--input-color) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.375rem;padding-inline:1rem 1.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab,red,red)){.select{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset}}.select{border-color:var(--input-color);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.select{--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.select{--size:calc(var(--size-field,.25rem)*10)}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}.select select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:inherit;border-radius:inherit;border-style:none;width:calc(100% + 2.75rem);height:calc(100% - 2px);margin-inline:-1rem -1.75rem;padding-inline:1rem 1.75rem}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}.select:focus,.select:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab,red,red)){.select:focus,.select:focus-within{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.select:focus,.select:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;z-index:1}.select:has(>select[disabled]),.select:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.select:has(>select[disabled]),.select:is(:disabled,[disabled]){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}.timeline{display:flex;position:relative}.timeline>li{grid-template-rows:var(--timeline-row-start,minmax(0,1fr))auto var(--timeline-row-end,minmax(0,1fr));grid-template-columns:var(--timeline-col-start,minmax(0,1fr))auto var(--timeline-col-end,minmax(0,1fr));flex-shrink:0;align-items:center;display:grid;position:relative}.timeline>li>hr{border:none;width:100%}.timeline>li>hr:first-child{grid-row-start:2;grid-column-start:1}.timeline>li>hr:last-child{grid-area:2/3/auto/none}@media print{.timeline>li>hr{border:.1px solid var(--color-base-300)}}.timeline :where(hr){background-color:var(--color-base-300);height:.25rem}.timeline:has(.timeline-middle hr):first-child{border-start-start-radius:0;border-start-end-radius:var(--radius-selector);border-end-end-radius:var(--radius-selector);border-end-start-radius:0}.timeline:has(.timeline-middle hr):last-child,.timeline:not(:has(.timeline-middle)) :first-child hr:last-child{border-start-start-radius:var(--radius-selector);border-start-end-radius:0;border-end-end-radius:0;border-end-start-radius:var(--radius-selector)}.timeline:not(:has(.timeline-middle)) :last-child hr:first-child{border-start-start-radius:0;border-start-end-radius:var(--radius-selector);border-end-end-radius:var(--radius-selector);border-end-start-radius:0}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.checkbox{border:var(--border)solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab,red,red)){.checkbox{border:var(--border)solid var(--input-color,color-mix(in oklab,var(--color-base-content)20%,#0000))}}.checkbox{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 0 #0000 inset,0 0 #0000;--size:calc(var(--size-selector,.25rem)*6);width:var(--size);height:var(--size);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0 3px oklch(100% 0 0/calc(var(--depth)*.1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px oklch(0% 0 0/calc(var(--depth)*.1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:none}.checkbox:disabled{cursor:not-allowed;opacity:.2}.radio{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;vertical-align:middle;border:var(--border)solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab,red,red)){.radio{border:var(--border)solid var(--input-color,color-mix(in srgb,currentColor 20%,#0000))}}.radio{box-shadow:0 1px oklch(0% 0 0/calc(var(--depth)*.1)) inset;--size:calc(var(--size-selector,.25rem)*6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor;animation:.2s ease-out radio}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px oklch(0% 0 0/calc(var(--depth)*.1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:-1px;outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.radio:disabled{cursor:not-allowed;opacity:.2}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.progress{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--radius-box);background-color:currentColor;width:100%;height:.5rem;position:relative;overflow:hidden}@supports (color:color-mix(in lab,red,red)){.progress{background-color:color-mix(in oklab,currentColor 20%,transparent)}}.progress{color:var(--color-base-content)}.progress:indeterminate{background-image:repeating-linear-gradient(90deg,currentColor -1% 10%,#0000 10% 90%);background-position-x:15%;background-size:200%;animation:5s ease-in-out infinite progress}@supports ((-moz-appearance:none)){.progress:indeterminate::-moz-progress-bar{background-color:#0000;background-image:repeating-linear-gradient(90deg,currentColor -1% 10%,#0000 10% 90%);background-position-x:15%;background-size:200%;animation:5s ease-in-out infinite progress}.progress::-moz-progress-bar{border-radius:var(--radius-box);background-color:currentColor}}@supports ((-webkit-appearance:none)){.progress::-webkit-progress-bar{border-radius:var(--radius-box);background-color:#0000}.progress::-webkit-progress-value{border-radius:var(--radius-box);background-color:currentColor}}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.top-12{top:calc(var(--spacing)*12)}.right-2{right:calc(var(--spacing)*2)}.left-0{left:calc(var(--spacing)*0)}.textarea{border:var(--border)solid #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;touch-action:manipulation;border-color:var(--input-color);width:clamp(3rem,20rem,100%);min-height:5rem;box-shadow:0 1px var(--input-color) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset;flex-shrink:1;padding-block:.5rem;padding-inline:.75rem;font-size:.875rem}@supports (color:color-mix(in lab,red,red)){.textarea{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset}}.textarea{--input-color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.textarea{--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.textarea textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab,red,red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.z-10{z-index:10}.z-20{z-index:20}.tab-content{order:var(--tabcontent-order);--tabcontent-radius-ss:0;--tabcontent-radius-se:0;--tabcontent-radius-es:0;--tabcontent-radius-ee:0;--tabcontent-order:1;width:100%;margin:var(--tabcontent-margin);border-color:#0000;border-width:var(--border);border-start-start-radius:var(--tabcontent-radius-ss);border-start-end-radius:var(--tabcontent-radius-se);border-end-end-radius:var(--tabcontent-radius-ee);border-end-start-radius:var(--tabcontent-radius-es);display:none}.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}}.container\!{width:100%!important}@media (min-width:40rem){.container\!{max-width:40rem!important}}@media (min-width:48rem){.container\!{max-width:48rem!important}}@media (min-width:64rem){.container\!{max-width:64rem!important}}@media (min-width:80rem){.container\!{max-width:80rem!important}}@media (min-width:96rem){.container\!{max-width:96rem!important}}.filter{flex-wrap:wrap;display:flex}.filter input[type=radio]{width:auto}.filter input{opacity:1;transition:margin .1s,opacity .3s,padding .3s,border-width .1s;overflow:hidden;scale:1}.filter input:not(:last-child){margin-inline-end:.25rem}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{content:"×"}.filter:not(:has(input:checked:not(.filter-reset))) .filter-reset,.filter:not(:has(input:checked:not(.filter-reset))) input[type=reset],.filter:has(input:checked:not(.filter-reset)) input:not(:checked,.filter-reset,input[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.mx-auto{margin-inline:auto}.input-sm{--size:calc(var(--size-field,.25rem)*8);font-size:.75rem}.input-sm[type=number]::-webkit-inner-spin-button{margin-block:-.5rem;margin-inline-end:-.75rem}.my-4{margin-block:calc(var(--spacing)*4)}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab,red,red)){.label{color:color-mix(in oklab,currentColor 60%,transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab,red,red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab,red,red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.mt-8{margin-top:calc(var(--spacing)*8)}.mr-2{margin-right:calc(var(--spacing)*2)}.fieldset-legend{color:var(--color-base-content);justify-content:space-between;align-items:center;gap:.5rem;margin-bottom:-.25rem;padding-block:.5rem;font-weight:600;display:flex}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab,red,red)){.status{background-color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab,red,red)){.status{color:#0000004d}@supports (color:color-mix(in lab,red,red)){.status{color:color-mix(in oklab,var(--color-black)30%,transparent)}}}.status{background-image:radial-gradient(circle at 35% 30%,oklch(1 0 0/calc(var(--depth)*.5)),#0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab,red,red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab,currentColor calc(var(--depth)*100%),#0000)}}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem)*10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.fieldset{grid-template-columns:1fr;grid-auto-rows:max-content;gap:.375rem;padding-block:.25rem;font-size:.75rem;display:grid}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.join{--join-ss:0;--join-se:0;--join-es:0;--join-ee:0;align-items:stretch;display:inline-flex}.join :where(.join-item){border-start-start-radius:var(--join-ss,0);border-start-end-radius:var(--join-se,0);border-end-end-radius:var(--join-ee,0);border-end-start-radius:var(--join-es,0)}.join :where(.join-item) *{--join-ss:var(--radius-field);--join-se:var(--radius-field);--join-es:var(--radius-field);--join-ee:var(--radius-field)}.join>.join-item:where(:first-child),.join :first-child:not(:last-child) :where(.join-item){--join-ss:var(--radius-field);--join-se:0;--join-es:var(--radius-field);--join-ee:0}.join>.join-item:where(:last-child),.join :last-child:not(:first-child) :where(.join-item){--join-ss:0;--join-se:var(--radius-field);--join-es:0;--join-ee:var(--radius-field)}.join>.join-item:where(:only-child),.join :only-child :where(.join-item){--join-ss:var(--radius-field);--join-se:var(--radius-field);--join-es:var(--radius-field);--join-ee:var(--radius-field)}.mask{vertical-align:middle;display:inline-block;-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-4{height:calc(var(--spacing)*4)}.h-64{height:calc(var(--spacing)*64)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-\[80vh\]{max-height:80vh}.min-h-screen{min-height:100vh}.loading-lg{width:calc(var(--size-selector,.25rem)*7)}.w-4{width:calc(var(--spacing)*4)}.w-11\/12{width:91.6667%}.w-48{width:calc(var(--spacing)*48)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-full{max-width:100%}.min-w-\[220px\]{min-width:220px}.min-w-max{min-width:max-content}.flex-1{flex:1}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.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}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-4{gap:calc(var(--spacing)*4)}.gap-8{gap:calc(var(--spacing)*8)}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b-3{border-bottom-style:var(--tw-border-style);border-bottom-width:3px}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-700{border-color:var(--color-gray-700)}.border-b-gray-700{border-bottom-color:var(--color-gray-700)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.loading-spinner{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E")}.radio-sm{padding:.1875rem}.radio-sm[type=radio]{--size:calc(var(--size-selector,.25rem)*5)}.p-4{padding:calc(var(--spacing)*4)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-1{padding-block:calc(var(--spacing)*1)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-8{padding-right:calc(var(--spacing)*8)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-10{padding-left:calc(var(--spacing)*10)}.text-center{text-align:center}.text-left{text-align:left}.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-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-gray-400{color:var(--color-gray-400)}.text-primary{color:var(--color-primary)}.text-red-500{color:var(--color-red-500)}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.blur{--tw-blur:blur(8px);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,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.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,visibility,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\!{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,visibility,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.hover\:bg-base-100:hover{background-color:var(--color-base-100)}.hover\:bg-base-200:hover{background-color:var(--color-base-200)}.hover\:bg-base-300:hover{background-color:var(--color-base-300)}}@media (min-width:48rem){.md\:w-1\/2{width:50%}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:gap-x-4{column-gap:calc(var(--spacing)*4)}}@media (min-width:64rem){.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes dropdown{0%{opacity:0}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{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-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-ease{syntax:"*";inherits:false}
frontend/dist/assets/index-CwrBXUIs.css DELETED
@@ -1 +0,0 @@
1
- /*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-700:oklch(37.3% .034 259.733);--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@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}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax: "<percentage>"; inherits: true; initial-value: 0%;}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab,red,red)){:root{scrollbar-color:color-mix(in oklch,currentColor 35%,#0000)#0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E")}:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not([class*=drawer-open])>.drawer-toggle:checked){overflow:hidden}:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){scrollbar-gutter:stable;background-image:linear-gradient(var(--color-base-100),var(--color-base-100));--root-bg:var(--color-base-100)}@supports (color:color-mix(in lab,red,red)){:where(:root:has(.modal-open,.modal[open],.modal:target,.modal-toggle:checked,.drawer:not(.drawer-open)>.drawer-toggle:checked)){--root-bg:color-mix(in srgb,var(--color-base-100),oklch(0% 0 0) 40%)}}:where(.modal[open],.modal-open,.modal-toggle:checked+.modal):not(.modal-start,.modal-end){scrollbar-gutter:stable}:root,[data-theme]{background-color:var(--root-bg,var(--color-base-100));color:var(--color-base-content)}}@layer components;@layer utilities{.diff{webkit-user-select:none;-webkit-user-select:none;user-select:none;direction:ltr;grid-template-columns:auto 1fr;width:100%;display:grid;position:relative;overflow:hidden;container-type:inline-size}.diff:focus-visible,.diff:has(.diff-item-1:focus-visible){outline-style:var(--tw-outline-style);outline-offset:1px;outline-width:2px;outline-color:var(--color-base-content)}.diff:focus-visible .diff-resizer{min-width:90cqi;max-width:90cqi}.diff:has(.diff-item-2:focus-visible){outline-style:var(--tw-outline-style);outline-offset:1px;outline-width:2px}.diff:has(.diff-item-2:focus-visible) .diff-resizer{min-width:10cqi;max-width:10cqi}@supports (-webkit-overflow-scrolling:touch) and (overflow:-webkit-paged-x){.diff:focus .diff-resizer{min-width:10cqi;max-width:10cqi}.diff:has(.diff-item-1:focus) .diff-resizer{min-width:90cqi;max-width:90cqi}}.tab{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:hover{color:var(--color-base-content)}}.tab{--tab-p:1rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));order:var(--tab-order);height:var(--tab-height);border-color:#0000;padding-inline-start:var(--tab-p);padding-inline-end:var(--tab-p);font-size:.875rem}.tab:is(input[type=radio]){min-width:fit-content}.tab:is(input[type=radio]):after{content:attr(aria-label)}.tab:is(label){position:relative}.tab:is(label) input{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0;position:absolute;top:0;right:0;bottom:0;left:0}:is(.tab:checked,.tab:is(label:has(:checked)),.tab:is(.tab-active,[aria-selected=true]))+.tab-content{height:calc(100% - var(--tab-height) + var(--border));display:block}.tab:not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true]){color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.tab:not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true]){color:color-mix(in oklab,var(--color-base-content)50%,transparent)}}.tab:not(input):empty{cursor:default;flex-grow:1}.tab:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:focus{outline-offset:2px;outline:2px solid #0000}}.tab:focus-visible,.tab:is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab[disabled]{pointer-events:none;opacity:.4}.loading{pointer-events:none;aspect-ratio:1;vertical-align:middle;width:calc(var(--size-selector,.25rem)*6);background-color:currentColor;display:inline-block;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:100%;mask-size:100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.pointer-events-none{pointer-events:none}.collapse:not(td,tr,colgroup){visibility:visible}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;width:100%;transition:grid-template-rows .2s;display:grid;position:relative;overflow:hidden}.collapse>input:is([type=checkbox],[type=radio]){-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],:focus:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],:focus:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible;min-height:fit-content}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}.collapse:focus:not(.collapse-close,.collapse[open])>.collapse-title{cursor:unset}.collapse:is([open],:focus:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem;transition:padding .2s ease-out,background-color .2s ease-out}.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse.collapse-open.collapse-plus>.collapse-title:after{content:"−"}.collapse.collapse-arrow:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse.collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{content:"−"}.collapse:is(details){width:100%}.collapse:is(details) summary{display:block;position:relative}.collapse:is(details) summary::-webkit-details-marker{display:none}.collapse:is(details) summary{outline:none}.collapse{visibility:collapse}.visible{visibility:visible}.toggle{border:var(--border)solid currentColor;color:var(--input-color);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p),var(--radius-selector-max)) + min(var(--border),var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab,red,red)){.toggle{box-shadow:0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab,red,red)){.toggle{--input-color:color-mix(in oklab,var(--color-base-content)50%,#0000)}}.toggle{--toggle-p:calc(var(--size)*.125);--size:calc(var(--size-selector,.25rem)*6);width:calc((var(--size)*2) - (var(--border) + var(--toggle-p))*2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:none}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:none}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);height:100%;box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab,red,red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px color-mix(in oklab,currentColor calc(var(--depth)*10%),#0000)}}.toggle:before{background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:-1px;outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border)solid currentColor;background-color:#0000}.input{cursor:text;border:var(--border)solid #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;width:clamp(3rem,20rem,100%);height:var(--size);touch-action:manipulation;border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab,red,red)){.input{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset}}.input{--size:calc(var(--size-field,.25rem)*10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.input{--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.input:where(input){display:inline-flex}.input :where(input){-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none;width:100%;height:100%;display:inline-flex}.input :where(input):focus,.input :where(input):focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input :where(input):focus,.input :where(input):focus-within{outline-offset:2px;outline:2px solid #0000}}.input :where(input[type=url]),.input :where(input[type=email]){direction:ltr}.input :where(input[type=date]){display:inline-block}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab,red,red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;z-index:1}.input:has(>input[disabled]),.input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.input:has(>input[disabled]),.input:is(:disabled,[disabled]){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]){box-shadow:none}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.input::-webkit-date-and-time-value{text-align:inherit}.input[type=number]::-webkit-inner-spin-button{margin-block:-.75rem;margin-inline-end:-.75rem}.input::-webkit-calendar-picker-indicator{position:absolute;inset-inline-end:.75em}.table{border-radius:var(--radius-box);text-align:left;width:100%;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(thead,tfoot){color:color-mix(in oklab,var(--color-base-content)60%,transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot){border-top:var(--border)solid var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(tfoot){border-top:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.table :where(thead tr,tbody tr:not(:last-child)){border-bottom:var(--border)solid color-mix(in oklch,var(--color-base-content)5%,#0000)}}.tabs-border .tab{--tab-border-color:#0000 #0000 var(--tab-border-color)#0000;border-radius:var(--radius-field);position:relative}.tabs-border .tab:before{--tw-content:"";content:var(--tw-content);background-color:var(--tab-border-color);border-radius:var(--radius-field);width:80%;height:3px;transition:background-color .2s;position:absolute;bottom:0;left:10%}:is(.tabs-border .tab:is(.tab-active,[aria-selected=true]):not(.tab-disabled,[disabled]),.tabs-border .tab:is(input:checked),.tabs-border .tab:is(label:has(:checked))):before{--tab-border-color:currentColor;border-top:3px solid}.select{border:var(--border)solid #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-base-100);vertical-align:middle;width:clamp(3rem,20rem,100%);height:var(--size);touch-action:manipulation;text-overflow:ellipsis;box-shadow:0 1px var(--input-color) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-shrink:1;align-items:center;gap:.375rem;padding-inline:1rem 1.75rem;font-size:.875rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab,red,red)){.select{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000) inset,0 -1px oklch(100% 0 0/calc(var(--depth)*.1)) inset}}.select{border-color:var(--input-color);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.select{--input-color:color-mix(in oklab,var(--color-base-content)20%,#0000)}}.select{--size:calc(var(--size-field,.25rem)*10)}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}.select select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:inherit;border-radius:inherit;border-style:none;width:calc(100% + 2.75rem);height:calc(100% - 2px);margin-inline:-1rem -1.75rem;padding-inline:1rem 1.75rem}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}.select:focus,.select:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab,red,red)){.select:focus,.select:focus-within{box-shadow:0 1px color-mix(in oklab,var(--input-color)calc(var(--depth)*10%),#0000)}}.select:focus,.select:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;z-index:1}.select:has(>select[disabled]),.select:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){.select:has(>select[disabled]),.select:is(:disabled,[disabled]){color:color-mix(in oklab,var(--color-base-content)40%,transparent)}}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab,red,red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab,var(--color-base-content)20%,transparent)}}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}.checkbox{border:var(--border)solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab,red,red)){.checkbox{border:var(--border)solid var(--input-color,color-mix(in oklab,var(--color-base-content)20%,#0000))}}.checkbox{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 0 #0000 inset,0 0 #0000;--size:calc(var(--size-selector,.25rem)*6);width:var(--size);height:var(--size);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0 3px oklch(100% 0 0/calc(var(--depth)*.1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px oklch(0% 0 0/calc(var(--depth)*.1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:none}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:none}.checkbox:disabled{cursor:not-allowed;opacity:.2}.radio{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;vertical-align:middle;border:var(--border)solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab,red,red)){.radio{border:var(--border)solid var(--input-color,color-mix(in srgb,currentColor 20%,#0000))}}.radio{box-shadow:0 1px oklch(0% 0 0/calc(var(--depth)*.1)) inset;--size:calc(var(--size-selector,.25rem)*6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto,calc(var(--noise)*100%);background-image:none,var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor;animation:.2s ease-out radio}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0/calc(var(--depth)*.1)) inset,0 8px 0 -4px oklch(100% 0 0/calc(var(--depth)*.1)) inset,0 1px oklch(0% 0 0/calc(var(--depth)*.1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:-1px;outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.radio:disabled{cursor:not-allowed;opacity:.2}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.top-12{top:calc(var(--spacing)*12)}.right-2{right:calc(var(--spacing)*2)}.left-0{left:calc(var(--spacing)*0)}.z-10{z-index:10}.z-20{z-index:20}.tab-content{order:var(--tabcontent-order);--tabcontent-radius-ss:0;--tabcontent-radius-se:0;--tabcontent-radius-es:0;--tabcontent-radius-ee:0;--tabcontent-order:1;width:100%;margin:var(--tabcontent-margin);border-color:#0000;border-width:var(--border);border-start-start-radius:var(--tabcontent-radius-ss);border-start-end-radius:var(--tabcontent-radius-se);border-end-end-radius:var(--tabcontent-radius-ee);border-end-start-radius:var(--tabcontent-radius-es);display:none}.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}}.filter{flex-wrap:wrap;display:flex}.filter input[type=radio]{width:auto}.filter input{opacity:1;transition:margin .1s,opacity .3s,padding .3s,border-width .1s;overflow:hidden;scale:1}.filter input:not(:last-child){margin-inline-end:.25rem}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{content:"×"}.filter:not(:has(input:checked:not(.filter-reset))) .filter-reset,.filter:not(:has(input:checked:not(.filter-reset))) input[type=reset],.filter:has(input:checked:not(.filter-reset)) input:not(:checked,.filter-reset,input[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.mx-auto{margin-inline:auto}.input-sm{--size:calc(var(--size-field,.25rem)*8);font-size:.75rem}.input-sm[type=number]::-webkit-inner-spin-button{margin-block:-.5rem;margin-inline-end:-.75rem}.my-4{margin-block:calc(var(--spacing)*4)}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab,red,red)){.label{color:color-mix(in oklab,currentColor 60%,transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab,red,red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab,red,red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border)solid color-mix(in oklab,currentColor 10%,#0000)}}.mt-8{margin-top:calc(var(--spacing)*8)}.mr-2{margin-right:calc(var(--spacing)*2)}.fieldset-legend{color:var(--color-base-content);justify-content:space-between;align-items:center;gap:.5rem;margin-bottom:-.25rem;padding-block:.5rem;font-weight:600;display:flex}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-2{margin-left:calc(var(--spacing)*2)}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem)*10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.fieldset{grid-template-columns:1fr;grid-auto-rows:max-content;gap:.375rem;padding-block:.25rem;font-size:.75rem;display:grid}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.join{--join-ss:0;--join-se:0;--join-es:0;--join-ee:0;align-items:stretch;display:inline-flex}.join :where(.join-item){border-start-start-radius:var(--join-ss,0);border-start-end-radius:var(--join-se,0);border-end-end-radius:var(--join-ee,0);border-end-start-radius:var(--join-es,0)}.join :where(.join-item) *{--join-ss:var(--radius-field);--join-se:var(--radius-field);--join-es:var(--radius-field);--join-ee:var(--radius-field)}.join>.join-item:where(:first-child),.join :first-child:not(:last-child) :where(.join-item){--join-ss:var(--radius-field);--join-se:0;--join-es:var(--radius-field);--join-ee:0}.join>.join-item:where(:last-child),.join :last-child:not(:first-child) :where(.join-item){--join-ss:0;--join-se:var(--radius-field);--join-es:0;--join-ee:var(--radius-field)}.join>.join-item:where(:only-child),.join :only-child :where(.join-item){--join-ss:var(--radius-field);--join-se:var(--radius-field);--join-es:var(--radius-field);--join-ee:var(--radius-field)}.flex{display:flex}.grid{display:grid}.table{display:table}.h-4{height:calc(var(--spacing)*4)}.h-64{height:calc(var(--spacing)*64)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-\[80vh\]{max-height:80vh}.min-h-screen{min-height:100vh}.loading-lg{width:calc(var(--size-selector,.25rem)*7)}.w-4{width:calc(var(--spacing)*4)}.w-11\/12{width:91.6667%}.w-48{width:calc(var(--spacing)*48)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-full{max-width:100%}.min-w-\[220px\]{min-width:220px}.min-w-max{min-width:max-content}.flex-1{flex:1}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.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}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-4{gap:calc(var(--spacing)*4)}.gap-8{gap:calc(var(--spacing)*8)}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-700{border-color:var(--color-gray-700)}.border-b-gray-700{border-bottom-color:var(--color-gray-700)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.loading-spinner{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E")}.radio-sm{padding:.1875rem}.radio-sm[type=radio]{--size:calc(var(--size-selector,.25rem)*5)}.p-4{padding:calc(var(--spacing)*4)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-1{padding-block:calc(var(--spacing)*1)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-8{padding-right:calc(var(--spacing)*8)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-10{padding-left:calc(var(--spacing)*10)}.text-center{text-align:center}.text-left{text-align:left}.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-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-gray-400{color:var(--color-gray-400)}.text-primary{color:var(--color-primary)}.text-red-500{color:var(--color-red-500)}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.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,)}@media (hover:hover){.hover\:bg-base-100:hover{background-color:var(--color-base-100)}.hover\:bg-base-200:hover{background-color:var(--color-base-200)}.hover\:bg-base-300:hover{background-color:var(--color-base-300)}}@media (min-width:48rem){.md\:w-1\/2{width:50%}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:gap-x-4{column-gap:calc(var(--spacing)*4)}}@media (min-width:64rem){.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes dropdown{0%{opacity:0}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@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-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{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-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}
 
 
frontend/dist/assets/{index-RC6rN_Gk.js → index-D2ksG3qQ.js} RENAMED
The diff for this file is too large to render. See raw diff
 
frontend/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <title>🥇 Omni Seal Bench Watermarking Leaderboard</title>
8
- <script type="module" crossorigin src="/assets/index-RC6rN_Gk.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-CwrBXUIs.css">
10
  </head>
11
  <body>
12
  <div id="root"></div>
 
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <title>🥇 Omni Seal Bench Watermarking Leaderboard</title>
8
+ <script type="module" crossorigin src="/assets/index-D2ksG3qQ.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BWZpYvtq.css">
10
  </head>
11
  <body>
12
  <div id="root"></div>
frontend/src/components/DatasetSelector.tsx CHANGED
@@ -12,8 +12,8 @@ const DatasetSelector: React.FC<DatasetSelectorProps> = ({
12
  onDatasetNameChange,
13
  }) => {
14
  return (
15
- <div className="mb-4">
16
- <fieldset className="fieldset w-full p-4 rounded border border-gray-700">
17
  <legend className="fieldset-legend font-semibold">Dataset</legend>
18
  <div className="flex flex-wrap gap-2">
19
  {datasetNames.map((datasetName) => (
 
12
  onDatasetNameChange,
13
  }) => {
14
  return (
15
+ <div className="">
16
+ <fieldset className="fieldset w-full p-4 rounded border border-gray-700 bg-base-200">
17
  <legend className="fieldset-legend font-semibold">Dataset</legend>
18
  <div className="flex flex-wrap gap-2">
19
  {datasetNames.map((datasetName) => (
frontend/src/components/IndependentMetricsTable.tsx ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+
3
+ interface Row {
4
+ metric: string
5
+ [key: string]: string | number
6
+ }
7
+
8
+ interface IndependentMetricsTableProps {
9
+ independentMetrics: string[]
10
+ tableHeader: string[]
11
+ selectedModels: Set<string>
12
+ tableRows: Row[]
13
+ }
14
+
15
+ const IndependentMetricsTable: React.FC<IndependentMetricsTableProps> = ({
16
+ independentMetrics,
17
+ tableHeader,
18
+ selectedModels,
19
+ tableRows,
20
+ }) => {
21
+ if (independentMetrics.length === 0) return null
22
+ return (
23
+ <div className="overflow-x-auto max-h-[80vh] overflow-y-auto">
24
+ <table className="table w-full min-w-max border-gray-700 border">
25
+ <thead>
26
+ <tr>
27
+ <th className="sticky left-0 top-0 bg-base-100 z-20 border-gray-700 border">Metric</th>
28
+ {tableHeader
29
+ .filter((model) => selectedModels.has(model))
30
+ .map((model) => (
31
+ <th
32
+ key={`independent-${model}`}
33
+ className="sticky top-0 bg-base-100 z-10 text-center text-xs border-gray-700 border"
34
+ >
35
+ {model}
36
+ </th>
37
+ ))}
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ {independentMetrics.sort().map((metric) => {
42
+ const row = tableRows.find((r) => r.metric === metric)
43
+ if (!row) return null
44
+ return (
45
+ <tr key={`independent-${metric}`} className="hover:bg-base-100">
46
+ <td className="sticky left-0 bg-base-100 z-10 border-gray-700 border">{metric}</td>
47
+ {tableHeader
48
+ .filter((model) => selectedModels.has(model))
49
+ .map((col) => {
50
+ const cell = row[col]
51
+ return (
52
+ <td
53
+ key={`independent-${metric}-${col}`}
54
+ className="text-center border-gray-700 border"
55
+ >
56
+ {!isNaN(Number(cell)) ? Number(Number(cell).toFixed(3)) : cell}
57
+ </td>
58
+ )
59
+ })}
60
+ </tr>
61
+ )
62
+ })}
63
+ </tbody>
64
+ </table>
65
+ </div>
66
+ )
67
+ }
68
+
69
+ export default IndependentMetricsTable
frontend/src/components/LeaderboardFilter.tsx CHANGED
@@ -20,6 +20,7 @@ const LeaderboardFilter: React.FC<FilterProps> = ({
20
  {}
21
  )
22
  const [searchTerm, setSearchTerm] = useState('')
 
23
 
24
  const toggleGroup = (group: string) => {
25
  setOpenGroups((prev) => ({ ...prev, [group]: !prev[group] }))
@@ -164,166 +165,199 @@ const LeaderboardFilter: React.FC<FilterProps> = ({
164
  setOpenSubGroups(openSubGroups)
165
  }
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  return (
168
  <div className="w-full mb-4">
169
- <fieldset className="fieldset w-full p-4 rounded border">
170
- <legend className="fieldset-legend font-semibold">Filter Metrics</legend>
171
- <div className="flex gap-2 mb-3">
172
- <div className="relative mr-2">
173
- <input
174
- type="text"
175
- placeholder="Search metrics..."
176
- className="input input-bordered border-gray-300 input-sm w-48 pr-8"
177
- value={searchTerm}
178
- onChange={(e) => {
179
- const value = e.target.value
180
- setSearchTerm(value)
181
- const openGroups: { [key: string]: boolean } = {}
182
- const openSubGroups: { [key: string]: { [key: string]: boolean } } = {}
183
- Object.entries(groups).forEach(([group, subGroups]) => {
184
- let groupHasMatch = false
185
- openSubGroups[group] = {}
186
- Object.entries(subGroups).forEach(([subGroup, metrics]) => {
187
- const hasMatch = metrics.some((metric) =>
188
- metric.toLowerCase().includes(value.toLowerCase())
189
- )
190
- openSubGroups[group][subGroup] = hasMatch || value === ''
191
- if (hasMatch) groupHasMatch = true
192
- })
193
- openGroups[group] = groupHasMatch || value === ''
194
- })
195
- setOpenGroups(openGroups)
196
- setOpenSubGroups(openSubGroups)
197
- }}
198
- />
199
- <span className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none">
200
- <svg
201
- xmlns="http://www.w3.org/2000/svg"
202
- className="h-4 w-4"
203
- fill="none"
204
- viewBox="0 0 24 24"
205
- stroke="currentColor"
206
- >
207
- <path
208
- strokeLinecap="round"
209
- strokeLinejoin="round"
210
- strokeWidth={2}
211
- d="M21 21l-4-4m0 0A7 7 0 104 4a7 7 0 0013 13z"
212
- />
213
- </svg>
214
- </span>
215
- </div>
216
- <button
217
- type="button"
218
- className="text-xs px-3 py-1 border rounded font-semibold bg-base-200 cursor-pointer"
219
- onClick={selectAllGlobal}
220
- >
221
- All
222
- </button>
223
- <button
224
- type="button"
225
- className="text-xs px-3 py-1 border rounded font-semibold bg-base-200 cursor-pointer"
226
- onClick={deselectAllGlobal}
227
- >
228
- None
229
- </button>
230
  </div>
231
- <div className="flex flex-row flex-wrap gap-4 w-full items-start">
232
- {Object.entries(groups).map(([group, subGroups]) => (
233
- <div key={group} className="flex-1 min-w-[220px] max-w-full">
234
- <div className="flex items-center gap-2 mb-1">
235
- <button
236
- type="button"
237
- onClick={() => toggleGroup(group)}
238
- className="flex-1 text-left font-medium py-1 px-2 rounded border border-gray-200 cursor-pointer"
239
- >
240
- {group} {openGroups[group] ? '▼' : '▶'}
241
- </button>
242
- <button
243
- type="button"
244
- className="text-xs px-2 py-1 border rounded cursor-pointer"
245
- onClick={() => selectAllInGroup(group)}
246
- >
247
- All
248
- </button>
249
- <button
250
- type="button"
251
- className="text-xs px-2 py-1 border rounded cursor-pointer"
252
- onClick={() => deselectAllInGroup(group)}
253
- >
254
- None
255
- </button>
256
- </div>
257
- {openGroups[group] && (
258
- <div className="ml-2">
259
- {Object.entries(subGroups).map(([subGroup, metrics]) => {
260
- const filteredMetrics = searchTerm
261
- ? metrics.filter((metric) =>
262
- metric.toLowerCase().includes(searchTerm.toLowerCase())
263
  )
264
- : metrics
265
- if (filteredMetrics.length === 0) return null
266
- return (
267
- <div key={subGroup} className="mb-2">
268
- <div className="flex items-center gap-2 mb-1">
269
- <button
270
- type="button"
271
- onClick={() => toggleSubGroup(group, subGroup)}
272
- className="flex-1 text-left py-1 px-2 rounded border border-gray-100 cursor-pointer"
273
- >
274
- {subGroup} {openSubGroups[group]?.[subGroup] ? '▼' : '▶'}
275
- </button>
276
- <button
277
- type="button"
278
- className="text-xs px-2 py-1 border rounded cursor-pointer"
279
- onClick={() => selectAllInSubGroup(group, subGroup)}
280
- >
281
- All
282
- </button>
283
- <button
284
- type="button"
285
- className="text-xs px-2 py-1 border rounded cursor-pointer"
286
- onClick={() => deselectAllInSubGroup(group, subGroup)}
287
- >
288
- None
289
- </button>
290
- </div>
291
- {openSubGroups[group]?.[subGroup] && (
292
- <div className="grid grid-cols-1 gap-1 ml-2 max-h-48 overflow-y-auto pr-2">
293
- {filteredMetrics.map((metric) => (
294
- <label key={metric} className="flex items-center gap-2 text-sm">
295
- <input
296
- type="checkbox"
297
- checked={selectedMetrics.has(metric)}
298
- onChange={(event) => {
299
- const newSet = new Set(selectedMetrics)
300
- if (event.target.checked) {
301
- newSet.add(metric)
302
- } else {
303
- newSet.delete(metric)
304
- }
305
- setSelectedMetrics(newSet)
306
- }}
307
- className="form-checkbox h-4 w-4"
308
- />
309
- <span className="truncate" title={metric}>
310
- {metric.includes('_')
311
- ? metric.split('_').slice(1).join('_')
312
- : metric}
313
- </span>
314
- </label>
315
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  </div>
317
- )}
318
- </div>
319
- )
320
- })}
321
  </div>
322
- )}
323
  </div>
324
- ))}
325
  </div>
326
- </fieldset>
327
  </div>
328
  )
329
  }
 
20
  {}
21
  )
22
  const [searchTerm, setSearchTerm] = useState('')
23
+ const [filterOpen, setFilterOpen] = useState(false)
24
 
25
  const toggleGroup = (group: string) => {
26
  setOpenGroups((prev) => ({ ...prev, [group]: !prev[group] }))
 
165
  setOpenSubGroups(openSubGroups)
166
  }
167
 
168
+ // Open all groups and subgroups if all metrics are selected (initial load)
169
+ useEffect(() => {
170
+ // Only run on initial mount
171
+ const allMetrics = Object.values(groups).flatMap((subGroups) => Object.values(subGroups).flat())
172
+ const allSelected = allMetrics.every((metric) => selectedMetrics.has(metric))
173
+ if (allSelected && Object.keys(groups).length > 0) {
174
+ const openGroups: { [key: string]: boolean } = {}
175
+ const openSubGroups: { [key: string]: { [key: string]: boolean } } = {}
176
+ Object.entries(groups).forEach(([group, subGroups]) => {
177
+ openGroups[group] = true
178
+ openSubGroups[group] = {}
179
+ Object.keys(subGroups).forEach((subGroup) => {
180
+ openSubGroups[group][subGroup] = true
181
+ })
182
+ })
183
+ setOpenGroups(openGroups)
184
+ setOpenSubGroups(openSubGroups)
185
+ }
186
+ // eslint-disable-next-line
187
+ }, [groups, selectedMetrics])
188
+
189
  return (
190
  <div className="w-full mb-4">
191
+ <div className="collapse collapse-arrow bg-base-200 border border-gray-700">
192
+ <input
193
+ type="checkbox"
194
+ className="peer"
195
+ checked={filterOpen}
196
+ onChange={() => setFilterOpen((open) => !open)}
197
+ />
198
+ <div className="collapse-title select-none ">
199
+ <span className="text-sm">Filter Individual Metrics</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  </div>
201
+ <div className="collapse-content">
202
+ <fieldset className="fieldset w-full">
203
+ <div className="flex gap-2 mb-3">
204
+ <div className="relative mr-2">
205
+ <input
206
+ type="text"
207
+ placeholder="Search metrics..."
208
+ className="input input-bordered border-gray-700 input-sm w-48 pr-8"
209
+ value={searchTerm}
210
+ onChange={(e) => {
211
+ const value = e.target.value
212
+ setSearchTerm(value)
213
+ const openGroups: { [key: string]: boolean } = {}
214
+ const openSubGroups: { [key: string]: { [key: string]: boolean } } = {}
215
+ Object.entries(groups).forEach(([group, subGroups]) => {
216
+ let groupHasMatch = false
217
+ openSubGroups[group] = {}
218
+ Object.entries(subGroups).forEach(([subGroup, metrics]) => {
219
+ const hasMatch = metrics.some((metric) =>
220
+ metric.toLowerCase().includes(value.toLowerCase())
 
 
 
 
 
 
 
 
 
 
 
 
221
  )
222
+ openSubGroups[group][subGroup] = hasMatch || value === ''
223
+ if (hasMatch) groupHasMatch = true
224
+ })
225
+ openGroups[group] = groupHasMatch || value === ''
226
+ })
227
+ setOpenGroups(openGroups)
228
+ setOpenSubGroups(openSubGroups)
229
+ }}
230
+ />
231
+ <span className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none">
232
+ <svg
233
+ xmlns="http://www.w3.org/2000/svg"
234
+ className="h-4 w-4"
235
+ fill="none"
236
+ viewBox="0 0 24 24"
237
+ stroke="currentColor"
238
+ >
239
+ <path
240
+ strokeLinecap="round"
241
+ strokeLinejoin="round"
242
+ strokeWidth={2}
243
+ d="M21 21l-4-4m0 0A7 7 0 104 4a7 7 0 0013 13z"
244
+ />
245
+ </svg>
246
+ </span>
247
+ </div>
248
+ <button
249
+ type="button"
250
+ className="text-xs px-3 py-1 border border-gray-700 rounded font-semibold bg-base-200 cursor-pointer"
251
+ onClick={selectAllGlobal}
252
+ >
253
+ All
254
+ </button>
255
+ <button
256
+ type="button"
257
+ className="text-xs px-3 py-1 border border-gray-700 rounded font-semibold bg-base-200 cursor-pointer"
258
+ onClick={deselectAllGlobal}
259
+ >
260
+ None
261
+ </button>
262
+ </div>
263
+ <div className="flex flex-row flex-wrap gap-4 w-full items-start">
264
+ {Object.entries(groups).map(([group, subGroups]) => (
265
+ <div key={group} className="flex-1 min-w-[220px] max-w-full">
266
+ <div className="flex items-center gap-2 mb-1">
267
+ <button
268
+ type="button"
269
+ onClick={() => toggleGroup(group)}
270
+ className="flex-1 text-left font-medium py-1 px-2 rounded border border-gray-700 cursor-pointer"
271
+ >
272
+ {group} {openGroups[group] ? '▼' : '▶'}
273
+ </button>
274
+ <button
275
+ type="button"
276
+ className="text-xs px-2 py-1 border border-gray-700 rounded cursor-pointer"
277
+ onClick={() => selectAllInGroup(group)}
278
+ >
279
+ All
280
+ </button>
281
+ <button
282
+ type="button"
283
+ className="text-xs px-2 py-1 border border-gray-700 rounded cursor-pointer"
284
+ onClick={() => deselectAllInGroup(group)}
285
+ >
286
+ None
287
+ </button>
288
+ </div>
289
+ {openGroups[group] && (
290
+ <div className="ml-2">
291
+ {Object.entries(subGroups).map(([subGroup, metrics]) => {
292
+ const filteredMetrics = searchTerm
293
+ ? metrics.filter((metric) =>
294
+ metric.toLowerCase().includes(searchTerm.toLowerCase())
295
+ )
296
+ : metrics
297
+ if (filteredMetrics.length === 0) return null
298
+ return (
299
+ <div key={subGroup} className="mb-2">
300
+ <div className="flex items-center gap-2 mb-1">
301
+ <button
302
+ type="button"
303
+ onClick={() => toggleSubGroup(group, subGroup)}
304
+ className="flex-1 text-left py-1 px-2 rounded border border-gray-700 cursor-pointer"
305
+ >
306
+ {subGroup} {openSubGroups[group]?.[subGroup] ? '▼' : '▶'}
307
+ </button>
308
+ <button
309
+ type="button"
310
+ className="text-xs px-2 py-1 border border-gray-700 rounded cursor-pointer"
311
+ onClick={() => selectAllInSubGroup(group, subGroup)}
312
+ >
313
+ All
314
+ </button>
315
+ <button
316
+ type="button"
317
+ className="text-xs px-2 py-1 border border-gray-700 rounded cursor-pointer"
318
+ onClick={() => deselectAllInSubGroup(group, subGroup)}
319
+ >
320
+ None
321
+ </button>
322
+ </div>
323
+ {openSubGroups[group]?.[subGroup] && (
324
+ <div className="grid grid-cols-1 gap-1 ml-2 max-h-48 overflow-y-auto pr-2">
325
+ {filteredMetrics.map((metric) => (
326
+ <label key={metric} className="flex items-center gap-2 text-sm">
327
+ <input
328
+ type="checkbox"
329
+ checked={selectedMetrics.has(metric)}
330
+ onChange={(event) => {
331
+ const newSet = new Set(selectedMetrics)
332
+ if (event.target.checked) {
333
+ newSet.add(metric)
334
+ } else {
335
+ newSet.delete(metric)
336
+ }
337
+ setSelectedMetrics(newSet)
338
+ }}
339
+ className="form-checkbox h-4 w-4"
340
+ />
341
+ <span className="truncate" title={metric}>
342
+ {metric.includes('_')
343
+ ? metric.split('_').slice(1).join('_')
344
+ : metric}
345
+ </span>
346
+ </label>
347
+ ))}
348
+ </div>
349
+ )}
350
  </div>
351
+ )
352
+ })}
353
+ </div>
354
+ )}
355
  </div>
356
+ ))}
357
  </div>
358
+ </fieldset>
359
  </div>
360
+ </div>
361
  </div>
362
  )
363
  }
frontend/src/components/LeaderboardTable.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import React, { useEffect, useState } from 'react'
2
  import LeaderboardFilter from './LeaderboardFilter'
3
  import LoadingSpinner from './LoadingSpinner'
 
4
 
5
  interface LeaderboardTableProps {
6
  benchmarkData: any
@@ -16,6 +17,12 @@ interface Groups {
16
  [group: string]: { [subgroup: string]: string[] }
17
  }
18
 
 
 
 
 
 
 
19
  const OverallMetricFilter: React.FC<{
20
  overallMetrics: string[]
21
  selectedOverallMetrics: Set<string>
@@ -31,8 +38,8 @@ const OverallMetricFilter: React.FC<{
31
  setSelectedOverallMetrics(newSelected)
32
  }
33
  return (
34
- <div className="w-full mb-4">
35
- <fieldset className="fieldset w-full p-4 rounded border border-gray-700">
36
  <legend className="fieldset-legend font-semibold">
37
  Metrics ({selectedOverallMetrics.size}/{overallMetrics.length})
38
  </legend>
@@ -60,14 +67,20 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
60
  const [tableRows, setTableRows] = useState<Row[]>([])
61
  const [tableHeader, setTableHeader] = useState<string[]>([])
62
  const [error, setError] = useState<string | null>(null)
63
- const [groups, setGroups] = useState<Groups>({})
64
- const [openGroups, setOpenGroups] = useState<{ [key: string]: boolean }>({})
65
- const [openSubGroups, setOpenSubGroups] = useState<{ [key: string]: { [key: string]: boolean } }>(
66
- {}
67
- )
68
  const [selectedMetrics, setSelectedMetrics] = useState<Set<string>>(new Set())
69
  const [overallMetrics, setOverallMetrics] = useState<string[]>([])
70
  const [selectedOverallMetrics, setSelectedOverallMetrics] = useState<Set<string>>(new Set())
 
 
 
 
 
 
71
 
72
  useEffect(() => {
73
  if (!benchmarkData) {
@@ -128,9 +141,9 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
128
  setSelectedMetrics(new Set(allMetrics))
129
  setTableHeader(headers)
130
  setTableRows(rows)
131
- setGroups(groupsData)
132
- setOpenGroups(initialOpenGroups)
133
- setOpenSubGroups(initialOpenSubGroups)
134
  setError(null)
135
  } catch (err: any) {
136
  setError('Failed to parse benchmark data, please try again: ' + err.message)
@@ -138,11 +151,11 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
138
  }, [benchmarkData])
139
 
140
  const toggleGroup = (group: string) => {
141
- setOpenGroups((prev) => ({ ...prev, [group]: !prev[group] }))
142
  }
143
 
144
  const toggleSubGroup = (group: string, subGroup: string) => {
145
- setOpenSubGroups((prev) => ({
146
  ...prev,
147
  [group]: {
148
  ...(prev[group] || {}),
@@ -151,6 +164,138 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
151
  }))
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  // Find all metrics matching a particular extracted metric name (like "log10_p_value")
155
  const findAllMetricsForName = (metricName: string): string[] => {
156
  return tableRows
@@ -218,12 +363,12 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
218
  if (!group) return metricNames
219
 
220
  // Get all metrics for the specified group
221
- const groupMetrics = Object.values(groups[group] || {}).flat()
222
 
223
  // If subgroup is specified, further filter to that subgroup
224
- if (subgroup && groups[group]?.[subgroup]) {
225
  return metricNames.filter(
226
- (metric) => groups[group][subgroup].includes(metric) && selectedMetrics.has(metric)
227
  )
228
  }
229
 
@@ -233,8 +378,101 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
233
  )
234
  }
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  return (
237
- <div className="rounded shadow">
238
  {error && <div className="text-red-500">{error}</div>}
239
  {!error && (
240
  <div className="flex flex-col gap-8">
@@ -244,11 +482,11 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
244
  selectedOverallMetrics={selectedOverallMetrics}
245
  setSelectedOverallMetrics={setSelectedOverallMetrics}
246
  />
247
- {/* <LeaderboardFilter
248
- groups={groups}
249
  selectedMetrics={selectedMetrics}
250
  setSelectedMetrics={setSelectedMetrics}
251
- /> */}
252
  </div>
253
 
254
  {selectedModels.size === 0 || selectedMetrics.size === 0 ? (
@@ -258,61 +496,12 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
258
  ) : (
259
  <>
260
  {/* Standalone metrics table */}
261
- {(() => {
262
- const standaloneMetrics = findStandaloneMetrics()
263
- if (standaloneMetrics.length === 0) return null
264
- return (
265
- <div className="overflow-x-auto max-h-[80vh] overflow-y-auto">
266
- <table className="table w-full min-w-max border-gray-700 border">
267
- <thead>
268
- <tr>
269
- <th className="sticky left-0 top-0 bg-base-100 z-20 border-gray-700 border">
270
- Metric
271
- </th>
272
- {tableHeader
273
- .filter((model) => selectedModels.has(model))
274
- .map((model) => (
275
- <th
276
- key={`standalone-${model}`}
277
- className="sticky top-0 bg-base-100 z-10 text-center text-xs border-gray-700 border"
278
- >
279
- {model}
280
- </th>
281
- ))}
282
- </tr>
283
- </thead>
284
- <tbody>
285
- {standaloneMetrics.sort().map((metric) => {
286
- const row = tableRows.find((r) => r.metric === metric)
287
- if (!row) return null
288
- return (
289
- <tr key={`standalone-${metric}`} className="hover:bg-base-100">
290
- <td className="sticky left-0 bg-base-100 z-10 border-gray-700 border">
291
- {metric}
292
- </td>
293
- {tableHeader
294
- .filter((model) => selectedModels.has(model))
295
- .map((col) => {
296
- const cell = row[col]
297
- return (
298
- <td
299
- key={`standalone-${metric}-${col}`}
300
- className="text-center border-gray-700 border"
301
- >
302
- {!isNaN(Number(cell))
303
- ? Number(Number(cell).toFixed(3))
304
- : cell}
305
- </td>
306
- )
307
- })}
308
- </tr>
309
- )
310
- })}
311
- </tbody>
312
- </table>
313
- </div>
314
- )
315
- })()}
316
 
317
  {/* Main metrics table */}
318
  <div className="overflow-x-auto max-h-[80vh] overflow-y-auto">
@@ -326,11 +515,9 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
326
  .filter((metric) => selectedOverallMetrics.has(metric))
327
  .map((metric) => (
328
  <th
329
- key={metric}
330
- colSpan={
331
- tableHeader.filter((model) => selectedModels.has(model)).length
332
- }
333
- className="sticky top-0 bg-base-100 z-10 text-center border-x border-gray-300 border border-gray-700 border"
334
  >
335
  {metric}
336
  </th>
@@ -342,27 +529,33 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
342
  .filter((metric) => selectedOverallMetrics.has(metric))
343
  .map((metric) => (
344
  <React.Fragment key={`header-models-${metric}`}>
345
- {tableHeader
346
- .filter((model) => selectedModels.has(model))
347
- .map((model) => (
 
348
  <th
349
  key={`${metric}-${model}`}
350
- className="sticky top-12 bg-base-100 z-10 text-center text-xs border-gray-700 border border-bottom-solid border-b-gray-700 border-b-2"
 
351
  >
352
  {model}
 
 
 
353
  </th>
354
- ))}
 
355
  </React.Fragment>
356
  ))}
357
  </tr>
358
  </thead>
359
  <tbody>
360
- {/* First render each group */}
361
- {Object.entries(groups).map(([group, subGroups]) => {
362
  // Skip the "Overall" group completely
363
  if (group === 'Overall') return null
364
 
365
- // Get all metrics for this group
366
  const allGroupMetrics = Object.values(subGroups).flat()
367
  // Filter to only include selected metrics
368
  const visibleGroupMetrics = filterMetricsByGroupAndSubgroup(
@@ -370,9 +563,35 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
370
  group
371
  )
372
 
373
- // Skip this group if no metrics are selected
374
  if (visibleGroupMetrics.length === 0) return null
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  return (
377
  <React.Fragment key={group}>
378
  {/* Group row with average stats for the entire group */}
@@ -380,27 +599,45 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
380
  className="bg-base-200 cursor-pointer hover:bg-base-300"
381
  onClick={() => toggleGroup(group)}
382
  >
383
- <td className="sticky left-0 bg-base-200 z-10 font-medium border-gray-700 border">
384
- {openGroups[group] ? '▼ ' : '▶ '}
385
- {group}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  </td>
387
  {/* For each metric column */}
388
  {overallMetrics
389
  .filter((metric) => selectedOverallMetrics.has(metric))
390
- .map((metric) => (
391
- // Render sub-columns for each model
392
- <React.Fragment key={`${group}-${metric}`}>
393
- {tableHeader
394
- .filter((model) => selectedModels.has(model))
395
- .map((col) => {
396
- // Find all metrics in this group that match the current metric name
397
  const allMetricsWithName = findAllMetricsForName(metric)
398
  const metricsInGroupForThisMetric =
399
  visibleGroupMetrics.filter((m) =>
400
  allMetricsWithName.includes(m)
401
  )
402
  const stats = calculateStats(metricsInGroupForThisMetric, col)
403
-
404
  return (
405
  <td
406
  key={`${group}-${metric}-${col}`}
@@ -412,21 +649,22 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
412
  </td>
413
  )
414
  })}
415
- </React.Fragment>
416
- ))}
 
417
  </tr>
418
 
419
- {/* Only render subgroups if group is open */}
420
- {openGroups[group] &&
421
- Object.entries(subGroups).map(([subGroup, metrics]) => {
422
- // Filter to only include selected metrics in this subgroup
423
  const visibleSubgroupMetrics = filterMetricsByGroupAndSubgroup(
424
  metrics,
425
  group,
426
  subGroup
427
  )
428
 
429
- // Skip this subgroup if no metrics are selected
430
  if (visibleSubgroupMetrics.length === 0) return null
431
 
432
  return (
@@ -436,147 +674,208 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
436
  className="bg-base-100 cursor-pointer hover:bg-base-200"
437
  onClick={() => toggleSubGroup(group, subGroup)}
438
  >
439
- <td className="sticky left-0 bg-base-100 z-10 pl-6 font-medium border-gray-700 border">
440
- {openSubGroups[group]?.[subGroup] ? '▼ ' : '▶ '}
441
- {subGroup}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  </td>
443
  {/* For each metric column */}
444
  {overallMetrics
445
  .filter((metric) => selectedOverallMetrics.has(metric))
446
- .map((metric) => (
447
- // Render sub-columns for each model
448
- <React.Fragment key={`${group}-${subGroup}-${metric}`}>
449
- {tableHeader
450
- .filter((model) => selectedModels.has(model))
451
- .map((col) => {
452
- // Find all metrics in this subgroup that match the current metric name
453
- const allMetricsWithName =
454
- findAllMetricsForName(metric)
455
- const metricsInSubgroupForThisMetric =
456
- visibleSubgroupMetrics.filter((m) =>
457
- allMetricsWithName.includes(m)
 
 
 
458
  )
459
- const stats = calculateStats(
460
- metricsInSubgroupForThisMetric,
461
- col
462
- )
463
-
464
- return (
465
- <td
466
- key={`${group}-${subGroup}-${metric}-${col}`}
467
- className="font-medium text-center border-gray-700 border"
468
- >
469
- {!isNaN(stats.avg)
470
- ? `${stats.avg.toFixed(3)} ± ${stats.stdDev.toFixed(3)}`
471
- : 'N/A'}
472
- </td>
473
- )
474
- })}
475
- </React.Fragment>
476
- ))}
477
  </tr>
478
 
479
  {/* Individual metric rows */}
480
- {openSubGroups[group]?.[subGroup] &&
481
- // Sort visibleSubgroupMetrics alphabetically by the clean metric name
482
- [...visibleSubgroupMetrics]
483
- .sort((a, b) => {
484
- // For metrics with format {category}_{strength}_{overall_metric_name},
485
- // First sort by category, then by overall_metric_name, then by strength
486
-
487
- // First extract the overall metric group
488
- const getOverallMetricGroup = (metric: string) => {
489
- for (const overall of overallMetrics) {
490
- if (
491
- metric.endsWith(`_${overall}`) ||
492
- metric === overall
493
- ) {
494
- return overall
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
 
496
  }
497
- return ''
498
- }
499
 
500
- const overallA = getOverallMetricGroup(a)
501
- const overallB = getOverallMetricGroup(b)
502
-
503
- // Extract the strength (last part before the overall metric)
504
- const stripOverall = (metric: string, overall: string) => {
505
- if (metric.endsWith(`_${overall}`)) {
506
- // Remove the overall metric group and any preceding underscore
507
- const stripped = metric.slice(
508
- 0,
509
- metric.length - overall.length - 1
510
- )
511
- const parts = stripped.split('_')
512
- return parts.length > 0 ? parts[parts.length - 1] : ''
 
 
 
 
 
513
  }
514
- return metric
515
- }
516
 
517
- // Extract the category (what remains after removing strength and overall_metric_name)
518
- const getCategory = (metric: string, overall: string) => {
519
- if (metric.endsWith(`_${overall}`)) {
520
- const stripped = metric.slice(
521
- 0,
522
- metric.length - overall.length - 1
523
- )
524
- const parts = stripped.split('_')
525
- // Remove the last part (strength) and join the rest (category)
526
- return parts.length > 1
527
- ? parts.slice(0, parts.length - 1).join('_')
528
- : ''
 
 
529
  }
530
- return metric
531
- }
532
 
533
- const categoryA = getCategory(a, overallA)
534
- const categoryB = getCategory(b, overallB)
535
 
536
- // First sort by category
537
- if (categoryA !== categoryB) {
538
- return categoryA.localeCompare(categoryB)
539
- }
540
 
541
- // Then sort by overall metric name
542
- if (overallA !== overallB) {
543
- return overallA.localeCompare(overallB)
544
- }
545
 
546
- // Finally sort by strength
547
- const subA = stripOverall(a, overallA)
548
- const subB = stripOverall(b, overallB)
549
-
550
- // Try to parse subA and subB as numbers, handling k/m/b suffixes
551
- const parseNumber = (str: string) => {
552
- const match = str.match(/^(\d+(?:\.\d+)?)([kKmMbB]?)$/)
553
- if (!match) return NaN
554
- let [_, num, suffix] = match
555
- let value = parseFloat(num)
556
- switch (suffix.toLowerCase()) {
557
- case 'k':
558
- value *= 1e3
559
- break
560
- case 'm':
561
- value *= 1e6
562
- break
563
- case 'b':
564
- value *= 1e9
565
- break
 
 
566
  }
567
- return value
568
- }
569
 
570
- const numA = parseNumber(subA)
571
- const numB = parseNumber(subB)
572
 
573
- if (!isNaN(numA) && !isNaN(numB)) {
574
- return numA - numB
575
- }
576
- // Fallback to string comparison if not both numbers
577
- return subA.localeCompare(subB)
578
- })
579
- .map((metric) => {
 
580
  const row = tableRows.find((r) => r.metric === metric)
581
  if (!row) return null
582
 
@@ -587,41 +886,56 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
587
 
588
  return (
589
  <tr key={metric} className="hover:bg-base-100">
590
- <td className="sticky left-0 bg-base-100 z-10 pl-10 border-gray-700 border">
591
- {metric}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
  </td>
593
- {/* For each metric column */}
594
  {overallMetrics
595
  .filter((oMetric) =>
596
  selectedOverallMetrics.has(oMetric)
597
  )
598
  .map((oMetric) => {
599
- // Only show values for the matching metric
600
  const isMatchingMetric =
601
  findAllMetricsForName(oMetric).includes(metric)
602
-
603
  if (!isMatchingMetric) {
604
- // Fill empty cells for non-matching metrics
605
  return (
606
  <React.Fragment key={`${metric}-${oMetric}`}>
607
- {tableHeader
608
- .filter((model) =>
609
- selectedModels.has(model)
610
- )
611
- .map((col) => (
612
  <td
613
  key={`${metric}-${oMetric}-${col}`}
614
  className="text-center border-gray-700 border"
615
  ></td>
616
- ))}
 
617
  </React.Fragment>
618
  )
619
  }
620
  return (
621
  <React.Fragment key={`${metric}-${oMetric}`}>
622
- {tableHeader
623
- .filter((model) => selectedModels.has(model))
624
- .map((col) => {
625
  const cell = row[col]
626
  return (
627
  <td
@@ -633,13 +947,15 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
633
  : cell}
634
  </td>
635
  )
636
- })}
 
637
  </React.Fragment>
638
  )
639
  })}
640
  </tr>
641
  )
642
- })}
 
643
  </React.Fragment>
644
  )
645
  })}
 
1
  import React, { useEffect, useState } from 'react'
2
  import LeaderboardFilter from './LeaderboardFilter'
3
  import LoadingSpinner from './LoadingSpinner'
4
+ import IndependentMetricsTable from './IndependentMetricsTable'
5
 
6
  interface LeaderboardTableProps {
7
  benchmarkData: any
 
17
  [group: string]: { [subgroup: string]: string[] }
18
  }
19
 
20
+ interface SortState {
21
+ [overallMetric: string]: {
22
+ [model: string]: { direction: 'asc' | 'desc' }
23
+ }
24
+ }
25
+
26
  const OverallMetricFilter: React.FC<{
27
  overallMetrics: string[]
28
  selectedOverallMetrics: Set<string>
 
38
  setSelectedOverallMetrics(newSelected)
39
  }
40
  return (
41
+ <div className="w-full">
42
+ <fieldset className="fieldset w-full p-4 rounded border border-gray-700 bg-base-200">
43
  <legend className="fieldset-legend font-semibold">
44
  Metrics ({selectedOverallMetrics.size}/{overallMetrics.length})
45
  </legend>
 
67
  const [tableRows, setTableRows] = useState<Row[]>([])
68
  const [tableHeader, setTableHeader] = useState<string[]>([])
69
  const [error, setError] = useState<string | null>(null)
70
+ const [groupRows, setGroupRows] = useState<Groups>({})
71
+ const [openGroupRows, setOpenGroupRows] = useState<{ [key: string]: boolean }>({})
72
+ const [openSubGroupRows, setOpenSubGroupRows] = useState<{
73
+ [key: string]: { [key: string]: boolean }
74
+ }>({})
75
  const [selectedMetrics, setSelectedMetrics] = useState<Set<string>>(new Set())
76
  const [overallMetrics, setOverallMetrics] = useState<string[]>([])
77
  const [selectedOverallMetrics, setSelectedOverallMetrics] = useState<Set<string>>(new Set())
78
+ const [sortState, setSortState] = useState<SortState>({})
79
+ const [columnSortState, setColumnSortState] = useState<SortState>({})
80
+ // Add state for row-based column sorting
81
+ const [selectedRowForSort, setSelectedRowForSort] = useState<{
82
+ [rowKey: string]: { direction: 'asc' | 'desc' }
83
+ }>({})
84
 
85
  useEffect(() => {
86
  if (!benchmarkData) {
 
141
  setSelectedMetrics(new Set(allMetrics))
142
  setTableHeader(headers)
143
  setTableRows(rows)
144
+ setGroupRows(groupsData)
145
+ setOpenGroupRows(initialOpenGroups)
146
+ setOpenSubGroupRows(initialOpenSubGroups)
147
  setError(null)
148
  } catch (err: any) {
149
  setError('Failed to parse benchmark data, please try again: ' + err.message)
 
151
  }, [benchmarkData])
152
 
153
  const toggleGroup = (group: string) => {
154
+ setOpenGroupRows((prev) => ({ ...prev, [group]: !prev[group] }))
155
  }
156
 
157
  const toggleSubGroup = (group: string, subGroup: string) => {
158
+ setOpenSubGroupRows((prev) => ({
159
  ...prev,
160
  [group]: {
161
  ...(prev[group] || {}),
 
164
  }))
165
  }
166
 
167
+ const handleSort = (overallMetric: string, model: string) => {
168
+ setSortState((prev) => {
169
+ const prevDir = prev[overallMetric]?.[model]?.direction
170
+ let newSortState: SortState = {}
171
+ if (!prevDir) {
172
+ // No sort yet, set to 'asc'
173
+ newSortState[overallMetric] = { [model]: { direction: 'asc' } }
174
+ } else if (prevDir === 'asc') {
175
+ // Was 'asc', set to 'desc'
176
+ newSortState[overallMetric] = { [model]: { direction: 'desc' } }
177
+ }
178
+ // Else revert back to unsorted state
179
+ return newSortState
180
+ })
181
+ }
182
+
183
+ // Helper to generate a stable composite key for row-based column sorting
184
+ function getRowSortKey(group: string | null, subGroup: string | null, metric: string | null) {
185
+ return `${group ?? ''}||${subGroup ?? ''}||${metric ?? ''}`
186
+ }
187
+
188
+ // Update handleColumnSort to use setSelectedRowForSort
189
+ const handleColumnSort = (
190
+ group: string | null,
191
+ subGroup: string | null,
192
+ metric: string | null
193
+ ) => {
194
+ const rowKey = getRowSortKey(group, subGroup, metric)
195
+ setSelectedRowForSort((prev) => {
196
+ const prevDir = prev[rowKey]?.direction
197
+ const newSortState: { [rowKey: string]: { direction: 'asc' | 'desc' } } = {}
198
+ if (!prevDir) {
199
+ newSortState[rowKey] = { direction: 'asc' }
200
+ } else if (prevDir === 'asc') {
201
+ newSortState[rowKey] = { direction: 'desc' }
202
+ } else if (prevDir === 'desc') {
203
+ delete newSortState[rowKey]
204
+ }
205
+ return newSortState
206
+ })
207
+ }
208
+
209
+ // Helper to get current row sort config for a row
210
+ function getRowColumnSort(group: string | null, subGroup: string | null, metric: string | null) {
211
+ return selectedRowForSort[getRowSortKey(group, subGroup, metric)] || null
212
+ }
213
+
214
+ const getSortConfig = () => {
215
+ // Find the first sorted column (overallMetric, model)
216
+ console.log({ sortState })
217
+ for (const overallMetric of overallMetrics) {
218
+ if (!selectedOverallMetrics.has(overallMetric)) continue
219
+ const models = tableHeader.filter((model) => selectedModels.has(model))
220
+ for (const model of models) {
221
+ if (sortState[overallMetric]?.[model]) {
222
+ return { overallMetric, model, direction: sortState[overallMetric][model].direction }
223
+ }
224
+ }
225
+ }
226
+ return null
227
+ }
228
+
229
+ // Move getRowSortConfig above sortModelColumns so it is defined before use
230
+ const getRowSortConfig = () => {
231
+ for (const overallMetric of overallMetrics) {
232
+ if (!selectedOverallMetrics.has(overallMetric)) continue
233
+ const models = tableHeader.filter((model) => selectedModels.has(model))
234
+ for (const model of models) {
235
+ if (sortState[overallMetric]?.[model]) {
236
+ return { overallMetric, model, direction: sortState[overallMetric][model].direction }
237
+ }
238
+ }
239
+ }
240
+ return null
241
+ }
242
+
243
+ const getColumnSortConfig = () => {
244
+ for (const overallMetric of overallMetrics) {
245
+ if (!selectedOverallMetrics.has(overallMetric)) continue
246
+ if (columnSortState[overallMetric]?.['__col__']) {
247
+ return { overallMetric, direction: columnSortState[overallMetric]['__col__'].direction }
248
+ }
249
+ }
250
+ return null
251
+ }
252
+
253
+ const sortModelColumns = (models: string[], overallMetric: string): string[] => {
254
+ // Column sort takes precedence; if no column sort, return models in default order
255
+ const columnSortConfig = getColumnSortConfig()
256
+ console.log({ columnSortConfig, overallMetric })
257
+ if (columnSortConfig && columnSortConfig.overallMetric === overallMetric) {
258
+ // Sort by average value for each model in this overallMetric
259
+ return [...models].sort((a, b) => {
260
+ const valsA = tableRows
261
+ .filter((row) => findAllMetricsForName(overallMetric).includes(row.metric as string))
262
+ .map((row) => Number(row[a]))
263
+ .filter((v) => !isNaN(v))
264
+ const valsB = tableRows
265
+ .filter((row) => findAllMetricsForName(overallMetric).includes(row.metric as string))
266
+ .map((row) => Number(row[b]))
267
+ .filter((v) => !isNaN(v))
268
+ const avgA = valsA.length ? valsA.reduce((s, v) => s + v, 0) / valsA.length : NaN
269
+ const avgB = valsB.length ? valsB.reduce((s, v) => s + v, 0) / valsB.length : NaN
270
+ if (isNaN(avgA) && isNaN(avgB)) return 0
271
+ if (isNaN(avgA)) return 1
272
+ if (isNaN(avgB)) return -1
273
+ return columnSortConfig.direction === 'asc' ? avgA - avgB : avgB - avgA
274
+ })
275
+ }
276
+ // No column sort: return models in default order
277
+ return models
278
+ }
279
+
280
+ const sortRowsBySubcolumn = (
281
+ rows: string[],
282
+ overallMetric: string,
283
+ model: string,
284
+ direction: 'asc' | 'desc'
285
+ ) => {
286
+ return [...rows].sort((a, b) => {
287
+ const rowA = tableRows.find((r) => r.metric === a)
288
+ const rowB = tableRows.find((r) => r.metric === b)
289
+ if (!rowA || !rowB) return 0
290
+ const valA = Number(rowA[model])
291
+ const valB = Number(rowB[model])
292
+ if (isNaN(valA) && isNaN(valB)) return 0
293
+ if (isNaN(valA)) return 1
294
+ if (isNaN(valB)) return -1
295
+ return direction === 'asc' ? valA - valB : valB - valA
296
+ })
297
+ }
298
+
299
  // Find all metrics matching a particular extracted metric name (like "log10_p_value")
300
  const findAllMetricsForName = (metricName: string): string[] => {
301
  return tableRows
 
363
  if (!group) return metricNames
364
 
365
  // Get all metrics for the specified group
366
+ const groupMetrics = Object.values(groupRows[group] || {}).flat() as string[]
367
 
368
  // If subgroup is specified, further filter to that subgroup
369
+ if (subgroup && groupRows[group]?.[subgroup]) {
370
  return metricNames.filter(
371
+ (metric) => groupRows[group][subgroup].includes(metric) && selectedMetrics.has(metric)
372
  )
373
  }
374
 
 
378
  )
379
  }
380
 
381
+ // Before rendering group rows:
382
+ const groupSortConfig = getSortConfig()
383
+ let groupEntries = Object.entries(groupRows).filter(([group]) => group !== 'Overall')
384
+ if (groupSortConfig) {
385
+ groupEntries = groupEntries.sort(([groupA, subGroupsA], [groupB, subGroupsB]) => {
386
+ // For each group, get all metrics in the group for the selected overallMetric
387
+ const allMetricsWithName = findAllMetricsForName(groupSortConfig.overallMetric)
388
+ const getGroupAvg = (subGroups: { [key: string]: string[] }) => {
389
+ const allGroupMetrics = Object.values(subGroups).flat()
390
+ const metricsInGroupForThisMetric = allGroupMetrics.filter((m) =>
391
+ allMetricsWithName.includes(m)
392
+ )
393
+ const stats = calculateStats(metricsInGroupForThisMetric, groupSortConfig.model)
394
+ return stats.avg
395
+ }
396
+ const avgA = getGroupAvg(subGroupsA)
397
+ const avgB = getGroupAvg(subGroupsB)
398
+ if (isNaN(avgA) && isNaN(avgB)) return 0
399
+ if (isNaN(avgA)) return 1
400
+ if (isNaN(avgB)) return -1
401
+ return groupSortConfig.direction === 'asc' ? avgA - avgB : avgB - avgA
402
+ })
403
+ }
404
+
405
+ // Compute model order for each overall metric before rendering
406
+ const modelOrderByOverallMetric: { [metric: string]: string[] } = {}
407
+ overallMetrics
408
+ .filter((metric) => selectedOverallMetrics.has(metric))
409
+ .forEach((metric) => {
410
+ // Check if there is an active row-based column sort for this metric
411
+ let sortedModels: string[] | null = null
412
+ // Find the active rowKey for this metric in rowColumnSort
413
+ const activeRowKey = Object.keys(selectedRowForSort).find((rowKey) => {
414
+ // rowKey format: group||subGroup||metric
415
+ const [group, subGroup, rowMetric] = rowKey.split('||')
416
+ // If rowMetric is empty, it's a group or subgroup row
417
+ if (rowMetric === '' && metric === metric) return true
418
+ // If rowMetric matches this metric, it's an individual metric row
419
+ if (rowMetric && findAllMetricsForName(metric).includes(rowMetric)) return true
420
+ return false
421
+ })
422
+ if (activeRowKey && selectedRowForSort[activeRowKey]) {
423
+ const direction = selectedRowForSort[activeRowKey].direction
424
+ const [group, subGroup, rowMetric] = activeRowKey.split('||')
425
+ const models = tableHeader.filter((model) => selectedModels.has(model))
426
+ if (!rowMetric) {
427
+ // Group or subgroup row: sort by average for this group/subgroup and metric
428
+ // Find all metrics in this group/subgroup for this overall metric
429
+ let relevantMetrics: string[] = []
430
+ if (group && !subGroup) {
431
+ // Group row
432
+ const groupMetrics = Object.values(groupRows[group] || {}).flat() as string[]
433
+ relevantMetrics = groupMetrics.filter((m: string) =>
434
+ findAllMetricsForName(metric).includes(m)
435
+ )
436
+ } else if (group && subGroup) {
437
+ // Subgroup row
438
+ relevantMetrics = (groupRows[group]?.[subGroup] || []).filter((m: string) =>
439
+ findAllMetricsForName(metric).includes(m)
440
+ )
441
+ }
442
+ sortedModels = [...models].sort((a, b) => {
443
+ const statsA = calculateStats(relevantMetrics, a)
444
+ const statsB = calculateStats(relevantMetrics, b)
445
+ if (isNaN(statsA.avg) && isNaN(statsB.avg)) return 0
446
+ if (isNaN(statsA.avg)) return 1
447
+ if (isNaN(statsB.avg)) return -1
448
+ return direction === 'asc' ? statsA.avg - statsB.avg : statsB.avg - statsA.avg
449
+ })
450
+ } else {
451
+ // Individual metric row: sort by value for that metric
452
+ sortedModels = [...models].sort((a, b) => {
453
+ const rowA = tableRows.find((r) => r.metric === rowMetric)
454
+ const rowB = rowA // same row
455
+ const valA = rowA ? Number(rowA[a]) : NaN
456
+ const valB = rowB ? Number(rowB[b]) : NaN
457
+ if (isNaN(valA) && isNaN(valB)) return 0
458
+ if (isNaN(valA)) return 1
459
+ if (isNaN(valB)) return -1
460
+ return direction === 'asc' ? valA - valB : valB - valA
461
+ })
462
+ }
463
+ }
464
+ modelOrderByOverallMetric[metric] =
465
+ sortedModels ||
466
+ sortModelColumns(
467
+ tableHeader.filter((model) => selectedModels.has(model)),
468
+ metric
469
+ )
470
+ })
471
+
472
+ console.log({ modelOrderByOverallMetric })
473
+
474
  return (
475
+ <div className="rounded">
476
  {error && <div className="text-red-500">{error}</div>}
477
  {!error && (
478
  <div className="flex flex-col gap-8">
 
482
  selectedOverallMetrics={selectedOverallMetrics}
483
  setSelectedOverallMetrics={setSelectedOverallMetrics}
484
  />
485
+ <LeaderboardFilter
486
+ groups={groupRows}
487
  selectedMetrics={selectedMetrics}
488
  setSelectedMetrics={setSelectedMetrics}
489
+ />
490
  </div>
491
 
492
  {selectedModels.size === 0 || selectedMetrics.size === 0 ? (
 
496
  ) : (
497
  <>
498
  {/* Standalone metrics table */}
499
+ <IndependentMetricsTable
500
+ independentMetrics={findStandaloneMetrics()}
501
+ tableHeader={tableHeader}
502
+ selectedModels={selectedModels}
503
+ tableRows={tableRows}
504
+ />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
  {/* Main metrics table */}
507
  <div className="overflow-x-auto max-h-[80vh] overflow-y-auto">
 
515
  .filter((metric) => selectedOverallMetrics.has(metric))
516
  .map((metric) => (
517
  <th
518
+ key={`header-metric-${metric}`}
519
+ className="bg-base-100 z-10 text-center text-xs border-gray-700 border"
520
+ colSpan={modelOrderByOverallMetric[metric].length}
 
 
521
  >
522
  {metric}
523
  </th>
 
529
  .filter((metric) => selectedOverallMetrics.has(metric))
530
  .map((metric) => (
531
  <React.Fragment key={`header-models-${metric}`}>
532
+ {modelOrderByOverallMetric[metric].map((model) => {
533
+ const isSorted = sortState[metric]?.[model]?.direction !== undefined
534
+ const direction = sortState[metric]?.[model]?.direction || 'desc'
535
+ return (
536
  <th
537
  key={`${metric}-${model}`}
538
+ className="sticky top-12 bg-base-100 z-10 text-center text-xs border-gray-700 border border-bottom-solid border-b-gray-700 border-b-3 cursor-pointer select-none"
539
+ onClick={() => handleSort(metric, model)}
540
  >
541
  {model}
542
+ <span className="ml-1">
543
+ {isSorted ? (direction === 'asc' ? '↑' : '↓') : '⇅'}
544
+ </span>
545
  </th>
546
+ )
547
+ })}
548
  </React.Fragment>
549
  ))}
550
  </tr>
551
  </thead>
552
  <tbody>
553
+ {/* First render each group row */}
554
+ {groupEntries.map(([group, subGroups]) => {
555
  // Skip the "Overall" group completely
556
  if (group === 'Overall') return null
557
 
558
+ // Get all metrics for this group row
559
  const allGroupMetrics = Object.values(subGroups).flat()
560
  // Filter to only include selected metrics
561
  const visibleGroupMetrics = filterMetricsByGroupAndSubgroup(
 
563
  group
564
  )
565
 
566
+ // Skip this group row if no metrics are selected
567
  if (visibleGroupMetrics.length === 0) return null
568
 
569
+ // Sort subgroups by average if sort config is active
570
+ let subGroupEntries = Object.entries(subGroups)
571
+ if (groupSortConfig) {
572
+ const allMetricsWithName = findAllMetricsForName(
573
+ groupSortConfig.overallMetric
574
+ )
575
+ const getSubGroupAvg = (metrics: string[]) => {
576
+ const metricsInSubGroupForThisMetric = metrics.filter((m) =>
577
+ allMetricsWithName.includes(m)
578
+ )
579
+ const stats = calculateStats(
580
+ metricsInSubGroupForThisMetric,
581
+ groupSortConfig.model
582
+ )
583
+ return stats.avg
584
+ }
585
+ subGroupEntries = subGroupEntries.sort(([, metricsA], [, metricsB]) => {
586
+ const avgA = getSubGroupAvg(metricsA)
587
+ const avgB = getSubGroupAvg(metricsB)
588
+ if (isNaN(avgA) && isNaN(avgB)) return 0
589
+ if (isNaN(avgA)) return 1
590
+ if (isNaN(avgB)) return -1
591
+ return groupSortConfig.direction === 'asc' ? avgA - avgB : avgB - avgA
592
+ })
593
+ }
594
+
595
  return (
596
  <React.Fragment key={group}>
597
  {/* Group row with average stats for the entire group */}
 
599
  className="bg-base-200 cursor-pointer hover:bg-base-300"
600
  onClick={() => toggleGroup(group)}
601
  >
602
+ <td className="sticky left-0 bg-base-200 z-10 font-medium border-gray-700 border cursor-pointer select-none flex items-center gap-1">
603
+ <span>{openGroupRows[group] ? '▼ ' : '▶ '}</span>
604
+ <span className="flex-1">{group}</span>
605
+ {/* Sort icon: only this triggers sort, and shows default if unsorted */}
606
+ <span
607
+ className="ml-1 cursor-pointer"
608
+ onClick={(e) => {
609
+ e.stopPropagation()
610
+ handleColumnSort(group, null, null)
611
+ }}
612
+ title={
613
+ getRowColumnSort(group, null, null)
614
+ ? getRowColumnSort(group, null, null)?.direction === 'asc'
615
+ ? 'Sort descending'
616
+ : 'Clear sort'
617
+ : 'Sort by this row'
618
+ }
619
+ >
620
+ {getRowColumnSort(group, null, null)
621
+ ? getRowColumnSort(group, null, null)?.direction === 'asc'
622
+ ? '↑'
623
+ : '↓'
624
+ : '⇅'}
625
+ </span>
626
  </td>
627
  {/* For each metric column */}
628
  {overallMetrics
629
  .filter((metric) => selectedOverallMetrics.has(metric))
630
+ .map((metric) => {
631
+ const rowKey = getRowSortKey(group, null, null)
632
+ return (
633
+ <React.Fragment key={`${group}-${metric}`}>
634
+ {modelOrderByOverallMetric[metric].map((col: string) => {
 
 
635
  const allMetricsWithName = findAllMetricsForName(metric)
636
  const metricsInGroupForThisMetric =
637
  visibleGroupMetrics.filter((m) =>
638
  allMetricsWithName.includes(m)
639
  )
640
  const stats = calculateStats(metricsInGroupForThisMetric, col)
 
641
  return (
642
  <td
643
  key={`${group}-${metric}-${col}`}
 
649
  </td>
650
  )
651
  })}
652
+ </React.Fragment>
653
+ )
654
+ })}
655
  </tr>
656
 
657
+ {/* Only render subgroups if group row is open */}
658
+ {openGroupRows[group] &&
659
+ subGroupEntries.map(([subGroup, metrics]) => {
660
+ // Filter to only include selected metrics in this subgroup row
661
  const visibleSubgroupMetrics = filterMetricsByGroupAndSubgroup(
662
  metrics,
663
  group,
664
  subGroup
665
  )
666
 
667
+ // Skip this subgroup row if no metrics are selected
668
  if (visibleSubgroupMetrics.length === 0) return null
669
 
670
  return (
 
674
  className="bg-base-100 cursor-pointer hover:bg-base-200"
675
  onClick={() => toggleSubGroup(group, subGroup)}
676
  >
677
+ <td className="sticky left-0 bg-base-100 z-10 pl-6 font-medium border-gray-700 border cursor-pointer select-none flex items-center gap-1">
678
+ <span>
679
+ {openSubGroupRows[group]?.[subGroup] ? '▼ ' : '▶ '}
680
+ </span>
681
+ <span className="flex-1">{subGroup}</span>
682
+ <span
683
+ className="ml-1 cursor-pointer"
684
+ onClick={(e) => {
685
+ e.stopPropagation()
686
+ handleColumnSort(group, subGroup, null)
687
+ }}
688
+ title={
689
+ getRowColumnSort(group, subGroup, null)
690
+ ? getRowColumnSort(group, subGroup, null)?.direction ===
691
+ 'asc'
692
+ ? 'Sort descending'
693
+ : 'Clear sort'
694
+ : 'Sort by this row'
695
+ }
696
+ >
697
+ {getRowColumnSort(group, subGroup, null)
698
+ ? getRowColumnSort(group, subGroup, null)?.direction ===
699
+ 'asc'
700
+ ? '↑'
701
+ : '↓'
702
+ : '⇅'}
703
+ </span>
704
  </td>
705
  {/* For each metric column */}
706
  {overallMetrics
707
  .filter((metric) => selectedOverallMetrics.has(metric))
708
+ .map((metric) => {
709
+ const rowKey = getRowSortKey(group, subGroup, null)
710
+ return (
711
+ <React.Fragment key={`${group}-${subGroup}-${metric}`}>
712
+ {modelOrderByOverallMetric[metric].map(
713
+ (col: string) => {
714
+ const allMetricsWithName =
715
+ findAllMetricsForName(metric)
716
+ const metricsInSubgroupForThisMetric =
717
+ visibleSubgroupMetrics.filter((m) =>
718
+ allMetricsWithName.includes(m)
719
+ )
720
+ const stats = calculateStats(
721
+ metricsInSubgroupForThisMetric,
722
+ col
723
  )
724
+ return (
725
+ <td
726
+ key={`${group}-${subGroup}-${metric}-${col}`}
727
+ className="font-medium text-center border-gray-700 border"
728
+ >
729
+ {!isNaN(stats.avg)
730
+ ? `${stats.avg.toFixed(3)} ± ${stats.stdDev.toFixed(3)}`
731
+ : 'N/A'}
732
+ </td>
733
+ )
734
+ }
735
+ )}
736
+ </React.Fragment>
737
+ )
738
+ })}
 
 
 
739
  </tr>
740
 
741
  {/* Individual metric rows */}
742
+ {openSubGroupRows[group]?.[subGroup] &&
743
+ (() => {
744
+ // Sorting logic for individual metric rows
745
+ const sortConfig = getSortConfig()
746
+ let sortedMetrics = [...visibleSubgroupMetrics]
747
+ console.log(
748
+ 'Sorting metrics for subgroup:',
749
+ group,
750
+ subGroup,
751
+ 'with config:',
752
+ sortConfig
753
+ )
754
+ if (sortConfig) {
755
+ // Only sort metrics that match the selected overallMetric and model
756
+ const allMetricsWithName = findAllMetricsForName(
757
+ sortConfig.overallMetric
758
+ )
759
+ const metricsInSubgroupForThisMetric = sortedMetrics.filter(
760
+ (m) => allMetricsWithName.includes(m)
761
+ )
762
+ const metricsNotInSubgroupForThisMetric =
763
+ sortedMetrics.filter(
764
+ (m) => !allMetricsWithName.includes(m)
765
+ )
766
+ // Only apply subcolumn sort to matching metrics, leave others in original order
767
+ sortedMetrics = [
768
+ ...sortRowsBySubcolumn(
769
+ metricsInSubgroupForThisMetric,
770
+ sortConfig.overallMetric,
771
+ sortConfig.model,
772
+ sortConfig.direction
773
+ ),
774
+ ...metricsNotInSubgroupForThisMetric,
775
+ ]
776
+ } else {
777
+ // Fallback sort logic (category, overall, strength)
778
+ sortedMetrics = sortedMetrics.sort((a, b) => {
779
+ // For metrics with format {category}_{strength}_{overall_metric_name},
780
+ // First sort by category, then by overall_metric_name, then by strength
781
+
782
+ // First extract the overall metric group
783
+ const getOverallMetricGroup = (metric: string) => {
784
+ for (const overall of overallMetrics) {
785
+ if (
786
+ metric.endsWith(`_${overall}`) ||
787
+ metric === overall
788
+ ) {
789
+ return overall
790
+ }
791
  }
792
+ return ''
793
  }
 
 
794
 
795
+ const overallA = getOverallMetricGroup(a)
796
+ const overallB = getOverallMetricGroup(b)
797
+
798
+ // Extract the strength (last part before the overall metric)
799
+ const stripOverall = (
800
+ metric: string,
801
+ overall: string
802
+ ) => {
803
+ if (metric.endsWith(`_${overall}`)) {
804
+ // Remove the overall metric group and any preceding underscore
805
+ const stripped = metric.slice(
806
+ 0,
807
+ metric.length - overall.length - 1
808
+ )
809
+ const parts = stripped.split('_')
810
+ return parts.length > 0 ? parts[parts.length - 1] : ''
811
+ }
812
+ return metric
813
  }
 
 
814
 
815
+ // Extract the category (what remains after removing strength and overall_metric_name)
816
+ const getCategory = (metric: string, overall: string) => {
817
+ if (metric.endsWith(`_${overall}`)) {
818
+ const stripped = metric.slice(
819
+ 0,
820
+ metric.length - overall.length - 1
821
+ )
822
+ const parts = stripped.split('_')
823
+ // Remove the last part (strength) and join the rest (category)
824
+ return parts.length > 1
825
+ ? parts.slice(0, parts.length - 1).join('_')
826
+ : ''
827
+ }
828
+ return metric
829
  }
 
 
830
 
831
+ const categoryA = getCategory(a, overallA)
832
+ const categoryB = getCategory(b, overallB)
833
 
834
+ // First sort by category
835
+ if (categoryA !== categoryB) {
836
+ return categoryA.localeCompare(categoryB)
837
+ }
838
 
839
+ // Then sort by overall metric name
840
+ if (overallA !== overallB) {
841
+ return overallA.localeCompare(overallB)
842
+ }
843
 
844
+ // Finally sort by strength
845
+ const subA = stripOverall(a, overallA)
846
+ const subB = stripOverall(b, overallB)
847
+
848
+ // Try to parse subA and subB as numbers, handling k/m/b suffixes
849
+ const parseNumber = (str: string) => {
850
+ const match = str.match(/^\d+(?:\.\d+)?([kKmMbB]?)$/)
851
+ if (!match) return NaN
852
+ let [_, suffix] = match
853
+ let value = parseFloat(str)
854
+ switch (suffix?.toLowerCase()) {
855
+ case 'k':
856
+ value *= 1e3
857
+ break
858
+ case 'm':
859
+ value *= 1e6
860
+ break
861
+ case 'b':
862
+ value *= 1e9
863
+ break
864
+ }
865
+ return value
866
  }
 
 
867
 
868
+ const numA = parseNumber(subA)
869
+ const numB = parseNumber(subB)
870
 
871
+ if (!isNaN(numA) && !isNaN(numB)) {
872
+ return numA - numB
873
+ }
874
+ // Fallback to string comparison if not both numbers
875
+ return subA.localeCompare(subB)
876
+ })
877
+ }
878
+ return sortedMetrics.map((metric) => {
879
  const row = tableRows.find((r) => r.metric === metric)
880
  if (!row) return null
881
 
 
886
 
887
  return (
888
  <tr key={metric} className="hover:bg-base-100">
889
+ <td className="sticky left-0 bg-base-100 z-10 pl-10 border-gray-700 border cursor-pointer select-none flex items-center gap-1">
890
+ <span className="flex-1">{metric}</span>
891
+ <span
892
+ className="ml-1 cursor-pointer"
893
+ onClick={(e) => {
894
+ e.stopPropagation()
895
+ handleColumnSort(group, subGroup, metric)
896
+ }}
897
+ title={
898
+ getRowColumnSort(group, subGroup, metric)
899
+ ? getRowColumnSort(group, subGroup, metric)
900
+ ?.direction === 'asc'
901
+ ? 'Sort descending'
902
+ : 'Clear sort'
903
+ : 'Sort by this row'
904
+ }
905
+ >
906
+ {getRowColumnSort(group, subGroup, metric)
907
+ ? getRowColumnSort(group, subGroup, metric)
908
+ ?.direction === 'asc'
909
+ ? '▲'
910
+ : '▼'
911
+ : '⇅'}
912
+ </span>
913
  </td>
 
914
  {overallMetrics
915
  .filter((oMetric) =>
916
  selectedOverallMetrics.has(oMetric)
917
  )
918
  .map((oMetric) => {
 
919
  const isMatchingMetric =
920
  findAllMetricsForName(oMetric).includes(metric)
 
921
  if (!isMatchingMetric) {
 
922
  return (
923
  <React.Fragment key={`${metric}-${oMetric}`}>
924
+ {modelOrderByOverallMetric[oMetric].map(
925
+ (col) => (
 
 
 
926
  <td
927
  key={`${metric}-${oMetric}-${col}`}
928
  className="text-center border-gray-700 border"
929
  ></td>
930
+ )
931
+ )}
932
  </React.Fragment>
933
  )
934
  }
935
  return (
936
  <React.Fragment key={`${metric}-${oMetric}`}>
937
+ {modelOrderByOverallMetric[oMetric].map(
938
+ (col) => {
 
939
  const cell = row[col]
940
  return (
941
  <td
 
947
  : cell}
948
  </td>
949
  )
950
+ }
951
+ )}
952
  </React.Fragment>
953
  )
954
  })}
955
  </tr>
956
  )
957
+ })
958
+ })()}
959
  </React.Fragment>
960
  )
961
  })}
frontend/src/components/ModelFilter.tsx CHANGED
@@ -18,8 +18,8 @@ const ModelFilter: React.FC<ModelFilterProps> = ({ models, selectedModels, setSe
18
  }
19
 
20
  return (
21
- <div className="w-full mb-4">
22
- <fieldset className="fieldset w-full p-4 rounded border border-gray-700">
23
  <legend className="fieldset-legend font-semibold">
24
  Models ({selectedModels.size}/{models.length})
25
  </legend>
 
18
  }
19
 
20
  return (
21
+ <div className="w-full">
22
+ <fieldset className="fieldset w-full p-4 rounded border border-gray-700 bg-base-200">
23
  <legend className="fieldset-legend font-semibold">
24
  Models ({selectedModels.size}/{models.length})
25
  </legend>