From d8033a1e2721f4a3344d79ca4ca12d3f18192d8e Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 18:21:21 +0200 Subject: [PATCH 01/12] Update track generation --- Gemfile | 7 + Gemfile.lock | 5 + app/assets/builds/tailwind.css | 6421 +++++++++++++++++++- app/models/concerns/distanceable.rb | 41 +- app/models/track.rb | 106 + app/services/tracks/generator.rb | 64 +- app/services/tracks/segmentation.rb | 14 +- app/services/tracks/track_builder.rb | 32 + app/views/stats/index.html.erb | 6 +- config/environments/development.rb | 11 + config/environments/test.rb | 6 + lib/optimized_tracks_v1.rb | 145 + lib/optimized_tracks_v2.rb | 291 + lib/results.md | 122 + lib/timestamps.rb | 1 - lib/tracks_optimization_benchmark.rb | 625 ++ tracks_performance_optimization_options.md | 235 + 17 files changed, 8103 insertions(+), 29 deletions(-) create mode 100644 lib/optimized_tracks_v1.rb create mode 100644 lib/optimized_tracks_v2.rb create mode 100644 lib/results.md create mode 100644 lib/tracks_optimization_benchmark.rb create mode 100644 tracks_performance_optimization_options.md diff --git a/Gemfile b/Gemfile index 614a2e95..ce746208 100644 --- a/Gemfile +++ b/Gemfile @@ -52,6 +52,7 @@ gem 'jwt' group :development, :test do gem 'brakeman', require: false + gem 'bullet' gem 'bundler-audit', require: false gem 'debug', platforms: %i[mri mingw x64_mingw] gem 'dotenv-rails' @@ -78,3 +79,9 @@ group :development do gem 'foreman' gem 'rubocop-rails', require: false end + +# group :production do +# gem 'uglifier' +# end + +# gem 'sassc-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 4b955b5a..c5a1fd55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,6 +113,9 @@ GEM brakeman (7.0.2) racc builder (3.3.0) + bullet (8.0.8) + activesupport (>= 3.0.0) + uniform_notifier (~> 1.11) bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) @@ -486,6 +489,7 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) + uniform_notifier (1.17.0) uri (1.0.3) useragent (0.16.11) warden (1.2.9) @@ -519,6 +523,7 @@ DEPENDENCIES aws-sdk-s3 (~> 1.177.0) bootsnap brakeman + bullet bundler-audit capybara chartkick diff --git a/app/assets/builds/tailwind.css b/app/assets/builds/tailwind.css index 5efebdd7..e3cabadd 100644 --- a/app/assets/builds/tailwind.css +++ b/app/assets/builds/tailwind.css @@ -1,6 +1,6415 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:Inter var,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}:root,[data-theme]{background-color:var(--fallback-b1,oklch(var(--b1)/1));color:var(--fallback-bc,oklch(var(--bc)/1))}@supports not (color:oklch(0 0 0)){:root{color-scheme:light;--fallback-p:#491eff;--fallback-pc:#d4dbff;--fallback-s:#ff41c7;--fallback-sc:#fff9fc;--fallback-a:#00cfbd;--fallback-ac:#00100d;--fallback-n:#2b3440;--fallback-nc:#d7dde4;--fallback-b1:#fff;--fallback-b2:#e5e6e6;--fallback-b3:#e5e6e6;--fallback-bc:#1f2937;--fallback-in:#00b3f0;--fallback-inc:#000;--fallback-su:#00ca92;--fallback-suc:#000;--fallback-wa:#ffc22d;--fallback-wac:#000;--fallback-er:#ff6f70;--fallback-erc:#000}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--fallback-p:#7582ff;--fallback-pc:#050617;--fallback-s:#ff71cf;--fallback-sc:#190211;--fallback-a:#00c7b5;--fallback-ac:#000e0c;--fallback-n:#2a323c;--fallback-nc:#a6adbb;--fallback-b1:#1d232a;--fallback-b2:#191e24;--fallback-b3:#15191e;--fallback-bc:#a6adbb;--fallback-in:#00b3f0;--fallback-inc:#000;--fallback-su:#00ca92;--fallback-suc:#000;--fallback-wa:#ffc22d;--fallback-wac:#000;--fallback-er:#ff6f70;--fallback-erc:#000}}}html{-webkit-tap-highlight-color:transparent}:root{color-scheme:light;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.89824 0.06192 275.75;--ac:0.15352 0.0368 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.4912 0.3096 275.75;--s:0.6971 0.329 342.55;--sc:0.9871 0.0106 342.55;--a:0.7676 0.184 183.61;--n:0.321785 0.02476 255.701624;--nc:0.894994 0.011585 252.096176;--b1:1 0 0;--b2:0.961151 0 0;--b3:0.924169 0.00108 197.137559;--bc:0.278078 0.029596 256.847952}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.13138 0.0392 275.75;--sc:0.1496 0.052 342.55;--ac:0.14902 0.0334 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.6569 0.196 275.75;--s:0.748 0.26 342.55;--a:0.7451 0.167 183.61;--n:0.313815 0.021108 254.139175;--nc:0.746477 0.0216 264.435964;--b1:0.253267 0.015896 252.417568;--b2:0.232607 0.013807 253.100675;--b3:0.211484 0.01165 254.087939;--bc:0.746477 0.0216 264.435964}}[data-theme=light]{color-scheme:light;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.89824 0.06192 275.75;--ac:0.15352 0.0368 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.4912 0.3096 275.75;--s:0.6971 0.329 342.55;--sc:0.9871 0.0106 342.55;--a:0.7676 0.184 183.61;--n:0.321785 0.02476 255.701624;--nc:0.894994 0.011585 252.096176;--b1:1 0 0;--b2:0.961151 0 0;--b3:0.924169 0.00108 197.137559;--bc:0.278078 0.029596 256.847952}[data-theme=dark]{color-scheme:dark;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.13138 0.0392 275.75;--sc:0.1496 0.052 342.55;--ac:0.14902 0.0334 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.6569 0.196 275.75;--s:0.748 0.26 342.55;--a:0.7451 0.167 183.61;--n:0.313815 0.021108 254.139175;--nc:0.746477 0.0216 264.435964;--b1:0.253267 0.015896 252.417568;--b2:0.232607 0.013807 253.100675;--b3:0.211484 0.01165 254.087939;--bc:0.746477 0.0216 264.435964}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate{background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}@media (forced-colors:active) {[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:transparent}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.alert{align-content:flex-start;align-items:center;border-radius:var(--rounded-box,1rem);border-width:1px;display:grid;gap:1rem;grid-auto-flow:row;justify-items:center;text-align:center;width:100%;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));padding:1rem;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--alert-bg:var(--fallback-b2,oklch(var(--b2)/1));--alert-bg-mix:var(--fallback-b1,oklch(var(--b1)/1));background-color:var(--alert-bg)}@media (min-width:640px){.alert{grid-auto-flow:column;grid-template-columns:auto minmax(auto,1fr);justify-items:start;text-align:start}}.avatar.placeholder>div{align-items:center;display:flex;justify-content:center}.badge{align-items:center;border-radius:var(--rounded-badge,1.9rem);border-width:1px;display:inline-flex;font-size:.875rem;height:1.25rem;justify-content:center;line-height:1.25rem;padding-left:.563rem;padding-right:.563rem;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);width:-moz-fit-content;width:fit-content;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}@media (hover:hover){.label a:hover{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.menu li>:not(ul,.menu-title,details,.btn).active,.menu li>:not(ul,.menu-title,details,.btn):active,.menu li>details>summary:active{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.radio-primary:hover{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.tab:hover{--tw-text-opacity:1}.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]):hover,.tabs-boxed :is(input:checked):hover{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.table tr.hover:hover,.table tr.hover:nth-child(2n):hover{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}.table-zebra tr.hover:hover,.table-zebra tr.hover:nth-child(2n):hover{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}}.btn{align-items:center;animation:button-pop var(--animation-btn,.25s) ease-out;border-color:transparent;border-color:oklch(var(--btn-color,var(--b2))/var(--tw-border-opacity));border-radius:var(--rounded-btn,.5rem);border-width:var(--border-btn,1px);cursor:pointer;display:inline-flex;flex-shrink:0;flex-wrap:wrap;font-size:.875rem;font-weight:600;gap:.5rem;height:3rem;justify-content:center;line-height:1em;min-height:3rem;padding-left:1rem;padding-right:1rem;text-align:center;text-decoration-line:none;transition-duration:.2s;transition-property:color,background-color,border-color,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(0,0,.2,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);background-color:oklch(var(--btn-color,var(--b2))/var(--tw-bg-opacity));box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:var(--fallback-bc,oklch(var(--bc)/1));--tw-bg-opacity:1;--tw-border-opacity:1}.btn-disabled,.btn:disabled,.btn[disabled]{pointer-events:none}:where(.btn:is(input[type=checkbox])),:where(.btn:is(input[type=radio])){-webkit-appearance:none;-moz-appearance:none;appearance:none;width:auto}.btn:is(input[type=checkbox]):after,.btn:is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.card{border-radius:var(--rounded-box,1rem);display:flex;flex-direction:column;position:relative}.card:focus{outline:2px solid transparent;outline-offset:2px}.card-body{display:flex;flex:1 1 auto;flex-direction:column;gap:.5rem;padding:var(--padding-card,2rem)}.card-body :where(p){flex-grow:1}.card-actions{align-items:flex-start;display:flex;flex-wrap:wrap;gap:.5rem}.card figure{align-items:center;display:flex;justify-content:center}.card.image-full{display:grid}.card.image-full:before{border-radius:var(--rounded-box,1rem);content:"";position:relative;z-index:10;--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));opacity:.75}.card.image-full:before,.card.image-full>*{grid-column-start:1;grid-row-start:1}.card.image-full>figure img{height:100%;-o-object-fit:cover;object-fit:cover}.card.image-full>.card-body{position:relative;z-index:20;--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.checkbox{flex-shrink:0;--chkbg:var(--fallback-bc,oklch(var(--bc)/1));--chkfg:var(--fallback-b1,oklch(var(--b1)/1));-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:var(--rounded-btn,.5rem);border-width:1px;cursor:pointer;height:1.5rem;width:1.5rem;--tw-border-opacity:0.2}.divider{align-items:center;align-self:stretch;display:flex;flex-direction:row;height:1rem;margin-bottom:1rem;margin-top:1rem;white-space:nowrap}.divider:after,.divider:before{flex-grow:1;height:.125rem;width:100%;--tw-content:"";background-color:var(--fallback-bc,oklch(var(--bc)/.1));content:var(--tw-content)}.\!drawer{display:grid!important;grid-auto-columns:max-content auto!important;position:relative!important;width:100%!important}.drawer{display:grid;grid-auto-columns:max-content auto;position:relative;width:100%}.dropdown{display:inline-block;position:relative}.dropdown>:not(summary):focus{outline:2px solid transparent;outline-offset:2px}.dropdown .dropdown-content{position:absolute}.dropdown:is(:not(details)) .dropdown-content{opacity:0;transform-origin:top;visibility:hidden;--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.dropdown-end .dropdown-content{inset-inline-end:0}.dropdown-left .dropdown-content{bottom:auto;inset-inline-end:100%;top:0;transform-origin:right}.dropdown-right .dropdown-content{bottom:auto;inset-inline-start:100%;top:0;transform-origin:left}.dropdown-bottom .dropdown-content{bottom:auto;top:100%;transform-origin:top}.dropdown-top .dropdown-content{bottom:100%;top:auto;transform-origin:bottom}.dropdown-end.dropdown-left .dropdown-content,.dropdown-end.dropdown-right .dropdown-content{bottom:0;top:auto}.dropdown.dropdown-open .dropdown-content,.dropdown:focus-within .dropdown-content,.dropdown:not(.dropdown-hover):focus .dropdown-content{opacity:1;visibility:visible}@media (hover:hover){.dropdown.dropdown-hover:hover .dropdown-content{opacity:1;visibility:visible}.btm-nav>.disabled:hover,.btm-nav>[disabled]:hover{pointer-events:none;--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btn:hover{--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn:hover{background-color:color-mix(in oklab,oklch(var(--btn-color,var(--b2))/var(--tw-bg-opacity,1)) 90%,#000);border-color:color-mix(in oklab,oklch(var(--btn-color,var(--b2))/var(--tw-border-opacity,1)) 90%,#000)}}@supports not (color:oklch(0 0 0)){.btn:hover{background-color:var(--btn-color,var(--fallback-b2));border-color:var(--btn-color,var(--fallback-b2))}}.btn.glass:hover{--glass-opacity:25%;--glass-border-opacity:15%}.btn-ghost:hover{border-color:transparent}@supports (color:oklch(0 0 0)){.btn-ghost:hover{background-color:var(--fallback-bc,oklch(var(--bc)/.2))}}.btn-outline:hover{--tw-border-opacity:1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}.btn-outline.btn-primary:hover{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-primary:hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}}.btn-outline.btn-secondary:hover{--tw-text-opacity:1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-secondary:hover{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000)}}.btn-outline.btn-accent:hover{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-accent:hover{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000)}}.btn-outline.btn-success:hover{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-success:hover{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000)}}.btn-outline.btn-info:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-info:hover{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}.btn-outline.btn-warning:hover{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-warning:hover{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000)}}.btn-outline.btn-error:hover{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-error:hover{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000)}}.btn-disabled:hover,.btn:disabled:hover,.btn[disabled]:hover{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}@supports (color:color-mix(in oklab,black,black)){.btn:is(input[type=checkbox]:checked):hover,.btn:is(input[type=radio]:checked):hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}}.dropdown.dropdown-hover:hover .dropdown-content{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(.active,.btn):hover,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{cursor:pointer;outline:2px solid transparent;outline-offset:2px}@supports (color:oklch(0 0 0)){:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(.active,.btn):hover,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{background-color:var(--fallback-bc,oklch(var(--bc)/.1))}}.tab[disabled],.tab[disabled]:hover{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));cursor:not-allowed;--tw-text-opacity:0.2}}.dropdown:is(details) summary::-webkit-details-marker{display:none}.file-input{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:var(--rounded-btn,.5rem);border-width:1px;flex-shrink:1;font-size:1rem;height:3rem;line-height:2;line-height:1.5rem;overflow:hidden;padding-inline-end:1rem;--tw-border-opacity:0;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.file-input::file-selector-button{align-items:center;border-style:solid;cursor:pointer;display:inline-flex;flex-shrink:0;flex-wrap:wrap;font-size:.875rem;height:100%;justify-content:center;line-height:1.25rem;line-height:1em;margin-inline-end:1rem;padding-left:1rem;padding-right:1rem;text-align:center;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-border-opacity:1;border-color:var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));font-weight:600;text-transform:uppercase;--tw-text-opacity:1;animation:button-pop var(--animation-btn,.25s) ease-out;border-width:var(--border-btn,1px);color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));text-decoration-line:none}.footer{-moz-column-gap:1rem;column-gap:1rem;font-size:.875rem;grid-auto-flow:row;line-height:1.25rem;row-gap:2.5rem;width:100%}.footer,.footer>*{display:grid;place-items:start}.footer>*{gap:.5rem}@media (min-width:48rem){.footer{grid-auto-flow:column}.footer-center{grid-auto-flow:row dense}}.form-control{flex-direction:column}.form-control,.label{display:flex}.label{align-items:center;justify-content:space-between;padding:.5rem .25rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.hero{background-position:50%;background-size:cover;display:grid;place-items:center;width:100%}.hero>*{grid-column-start:1;grid-row-start:1}.hero-content{align-items:center;display:flex;gap:1rem;justify-content:center;max-width:80rem;padding:1rem;z-index:0}.indicator{display:inline-flex;position:relative;width:-moz-max-content;width:max-content}.indicator :where(.indicator-item){position:absolute;white-space:nowrap;z-index:1}.input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:transparent;border-radius:var(--rounded-btn,.5rem);border-width:1px;flex-shrink:1;font-size:1rem;height:3rem;line-height:2;line-height:1.5rem;padding-left:1rem;padding-right:1rem;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.input-md[type=number]::-webkit-inner-spin-button,.input[type=number]::-webkit-inner-spin-button{margin-bottom:-1rem;margin-top:-1rem;margin-inline-end:-1rem}.input-xs[type=number]::-webkit-inner-spin-button{margin-bottom:-.25rem;margin-top:-.25rem;margin-inline-end:0}.input-sm[type=number]::-webkit-inner-spin-button{margin-bottom:0;margin-top:0;margin-inline-end:0}.join{align-items:stretch;border-radius:var(--rounded-btn,.5rem);display:inline-flex}.join :where(.join-item){border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:0;border-start-start-radius:0}.join .join-item:not(:first-child):not(:last-child),.join :not(:first-child):not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:0;border-start-start-radius:0}.join .join-item:first-child:not(:last-child),.join :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-start-end-radius:0}.join .dropdown .join-item:first-child:not(:last-child),.join :first-child:not(:last-child) .dropdown .join-item{border-end-end-radius:inherit;border-start-end-radius:inherit}.join :where(.join-item:first-child:not(:last-child)),.join :where(:first-child:not(:last-child) .join-item){border-end-start-radius:inherit;border-start-start-radius:inherit}.join .join-item:last-child:not(:first-child),.join :last-child:not(:first-child) .join-item{border-end-start-radius:0;border-start-start-radius:0}.join :where(.join-item:last-child:not(:first-child)),.join :where(:last-child:not(:first-child) .join-item){border-end-end-radius:inherit;border-start-end-radius:inherit}@supports not selector(:has(*)){:where(.join *){border-radius:inherit}}@supports selector(:has(*)){:where(.join :has(.join-item)){border-radius:inherit}}.link{cursor:pointer;text-decoration-line:underline}.menu{display:flex;flex-direction:column;flex-wrap:wrap;font-size:.875rem;line-height:1.25rem;padding:.5rem}.menu :where(li ul){margin-inline-start:1rem;padding-inline-start:.5rem;position:relative;white-space:nowrap}.menu :where(li:not(.menu-title)>:not(ul,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){align-content:flex-start;align-items:center;display:grid;gap:.5rem;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;-webkit-user-select:none;-moz-user-select:none;user-select:none}.menu li.disabled{color:var(--fallback-bc,oklch(var(--bc)/.3));cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;user-select:none}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}:where(.menu li){align-items:stretch;display:flex;flex-direction:column;flex-shrink:0;flex-wrap:wrap;position:relative}:where(.menu li) .badge{justify-self:end}.modal{background-color:transparent;color:inherit;display:grid;height:100%;inset:0;justify-items:center;margin:0;max-height:none;max-width:none;opacity:0;overflow-y:hidden;overscroll-behavior:contain;padding:0;pointer-events:none;position:fixed;transition-duration:.2s;transition-property:transform,opacity,visibility;transition-timing-function:cubic-bezier(0,0,.2,1);width:100%;z-index:999}:where(.modal){align-items:center}.modal-box{grid-column-start:1;grid-row-start:1;max-height:calc(100vh - 5em);max-width:32rem;width:91.666667%;--tw-scale-x:.9;--tw-scale-y:.9;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));box-shadow:0 25px 50px -12px rgba(0,0,0,.25);overflow-y:auto;overscroll-behavior:contain;padding:1.5rem;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.modal-open,.modal-toggle:checked+.modal,.modal:target,.modal[open]{opacity:1;pointer-events:auto;visibility:visible}.modal-action{display:flex;justify-content:flex-end;margin-top:1.5rem}.modal-toggle{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:0;opacity:0;position:fixed;width:0}:root:has(:is(.modal-open,.modal:target,.modal-toggle:checked+.modal,.modal[open])){overflow:hidden}.navbar{align-items:center;display:flex;min-height:4rem;padding:var(--navbar-padding,.5rem);width:100%}:where(.navbar>:not(script,style)){align-items:center;display:inline-flex}.navbar-start{justify-content:flex-start;width:50%}.navbar-center{flex-shrink:0}.navbar-end{justify-content:flex-end;width:50%}.progress{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-radius:var(--rounded-box,1rem);height:.5rem;overflow:hidden;position:relative;width:100%}.radio{flex-shrink:0;--chkbg:var(--bc);-webkit-appearance:none;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:9999px;border-width:1px;width:1.5rem;--tw-border-opacity:0.2}.radio,.range{-moz-appearance:none;appearance:none;cursor:pointer;height:1.5rem}.range{-webkit-appearance:none;width:100%;--range-shdw:var(--fallback-bc,oklch(var(--bc)/1));background-color:transparent;border-radius:var(--rounded-box,1rem);overflow:hidden}.range:focus{outline:none}.select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:transparent;border-radius:var(--rounded-btn,.5rem);border-width:1px;cursor:pointer;display:inline-flex;font-size:.875rem;height:3rem;line-height:1.25rem;line-height:2;min-height:3rem;padding-left:1rem;padding-right:2.5rem;-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));background-image:linear-gradient(45deg,transparent 50%,currentColor 0),linear-gradient(135deg,currentColor 50%,transparent 0);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}.select[multiple]{height:auto}.stack{display:inline-grid;place-items:center;align-items:flex-end}.stack>*{grid-column-start:1;grid-row-start:1;opacity:.6;transform:translateY(10%) scale(.9);width:100%;z-index:1}.stack>:nth-child(2){opacity:.8;transform:translateY(5%) scale(.95);z-index:2}.stack>:first-child{opacity:1;transform:translateY(0) scale(1);z-index:3}.stats{border-radius:var(--rounded-box,1rem);display:inline-grid;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}:where(.stats){grid-auto-flow:column;overflow-x:auto}.stat{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));-moz-column-gap:1rem;column-gap:1rem;display:inline-grid;grid-template-columns:repeat(1,1fr);width:100%;--tw-border-opacity:0.1;padding:1rem 1.5rem}.stat-title{color:var(--fallback-bc,oklch(var(--bc)/.6))}.stat-title,.stat-value{grid-column-start:1;white-space:nowrap}.stat-value{font-size:2.25rem;font-weight:800;line-height:2.5rem}.stat-desc{color:var(--fallback-bc,oklch(var(--bc)/.6));font-size:.75rem;grid-column-start:1;line-height:1rem;white-space:nowrap}.steps .step{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));grid-template-columns:auto;grid-template-rows:repeat(2,minmax(0,1fr));grid-template-rows:40px 1fr;min-width:4rem;place-items:center;text-align:center}.tabs{align-items:flex-end;display:grid}.tabs-lifted:has(.tab-content[class*=" rounded-"]) .tab:first-child:not(.tab-active),.tabs-lifted:has(.tab-content[class^=rounded-]) .tab:first-child:not(.tab-active){border-bottom-color:transparent}.tab{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;display:inline-flex;flex-wrap:wrap;font-size:.875rem;grid-row-start:1;height:2rem;justify-content:center;line-height:1.25rem;line-height:2;position:relative;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;--tab-padding:1rem;--tw-text-opacity:0.5;--tab-color:var(--fallback-bc,oklch(var(--bc)/1));--tab-bg:var(--fallback-b1,oklch(var(--b1)/1));--tab-border-color:var(--fallback-b3,oklch(var(--b3)/1));color:var(--tab-color);padding-inline-end:var(--tab-padding,1rem);padding-inline-start:var(--tab-padding,1rem)}.tab:is(input[type=radio]){border-bottom-left-radius:0;border-bottom-right-radius:0;width:auto}.tab:is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:not(input):empty{cursor:default;grid-column-start:span 9999}.tab-active+.tab-content:nth-child(2),:checked+.tab-content:nth-child(2){border-start-start-radius:0}.tab-active+.tab-content,input.tab:checked+.tab-content{display:block}.table{border-radius:var(--rounded-box,1rem);font-size:.875rem;line-height:1.25rem;position:relative;text-align:left;width:100%}.table :where(.table-pin-rows thead tr){position:sticky;top:0;z-index:1;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.table :where(.table-pin-rows tfoot tr){bottom:0;position:sticky;z-index:1;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.table :where(.table-pin-cols tr th){left:0;position:sticky;right:0;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.timeline{display:flex;position:relative}:where(.timeline>li){align-items:center;display:grid;flex-shrink:0;grid-template-columns:var(--timeline-col-start,minmax(0,1fr)) auto var( - --timeline-col-end,minmax(0,1fr) - );grid-template-rows:var(--timeline-row-start,minmax(0,1fr)) auto var( - --timeline-row-end,minmax(0,1fr) - );position:relative}.timeline>li>hr{border-width:0;width:100%}:where(.timeline>li>hr):first-child{grid-column-start:1;grid-row-start:2}:where(.timeline>li>hr):last-child{grid-column-end:none;grid-column-start:3;grid-row-end:auto;grid-row-start:2}.timeline-start{align-self:flex-end;grid-column-end:4;grid-column-start:1;grid-row-end:2;grid-row-start:1;justify-self:center;margin:.25rem}.timeline-middle{grid-column-start:2;grid-row-start:2}.timeline-end{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.toggle{flex-shrink:0;--tglbg:var(--fallback-b1,oklch(var(--b1)/1));--handleoffset:1.5rem;--handleoffsetcalculator:calc(var(--handleoffset)*-1);--togglehandleborder:0 0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:currentColor;border-color:currentColor;border-radius:var(--rounded-badge,1.9rem);border-width:1px;box-shadow:var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset,var(--togglehandleborder);color:var(--fallback-bc,oklch(var(--bc)/.5));cursor:pointer;height:1.5rem;transition:background,box-shadow var(--animation-input,.2s) ease-out;width:3rem}.alert-info{border-color:var(--fallback-in,oklch(var(--in)/.2));--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));--alert-bg:var(--fallback-in,oklch(var(--in)/1));--alert-bg-mix:var(--fallback-b1,oklch(var(--b1)/1))}.badge-primary{background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.badge-primary,.badge-secondary{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.badge-secondary{background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)));border-color:var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity)));color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}.badge-success{background-color:var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity)));color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}.badge-success,.badge-warning{border-color:transparent;--tw-bg-opacity:1;--tw-text-opacity:1}.badge-warning{background-color:var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity)));color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}.badge-outline{border-color:currentColor;--tw-border-opacity:0.5;background-color:transparent;color:currentColor}.badge-outline.badge-neutral{--tw-text-opacity:1;color:var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity)))}.badge-outline.badge-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}.badge-outline.badge-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}.badge-outline.badge-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}.badge-outline.badge-info{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.badge-outline.badge-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}.badge-outline.badge-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}.badge-outline.badge-error{--tw-text-opacity:1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}.btm-nav>:where(.active){border-top-width:2px;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.btm-nav>.disabled,.btm-nav>[disabled]{pointer-events:none;--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btm-nav>* .label{font-size:1rem;line-height:1.5rem}.btn:active:focus,.btn:active:hover{animation:button-pop 0s ease-out;transform:scale(var(--btn-focus-scale,.97))}@supports not (color:oklch(0 0 0)){.btn{background-color:var(--btn-color,var(--fallback-b2));border-color:var(--btn-color,var(--fallback-b2))}.btn-primary{--btn-color:var(--fallback-p)}.btn-neutral{--btn-color:var(--fallback-n)}.btn-info{--btn-color:var(--fallback-in)}.btn-success{--btn-color:var(--fallback-su)}.btn-warning{--btn-color:var(--fallback-wa)}.btn-error{--btn-color:var(--fallback-er)}}@supports (color:color-mix(in oklab,black,black)){.btn-active{background-color:color-mix(in oklab,oklch(var(--btn-color,var(--b3))/var(--tw-bg-opacity,1)) 90%,#000);border-color:color-mix(in oklab,oklch(var(--btn-color,var(--b3))/var(--tw-border-opacity,1)) 90%,#000)}.btn-outline.btn-primary.btn-active{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}.btn-outline.btn-secondary.btn-active{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000)}.btn-outline.btn-accent.btn-active{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000)}.btn-outline.btn-success.btn-active{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000)}.btn-outline.btn-info.btn-active{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}.btn-outline.btn-warning.btn-active{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000)}.btn-outline.btn-error.btn-active{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000)}}.btn:focus-visible{outline-offset:2px;outline-style:solid;outline-width:2px}.btn-primary{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}@supports (color:oklch(0 0 0)){.btn-primary{--btn-color:var(--p)}.btn-neutral{--btn-color:var(--n)}.btn-info{--btn-color:var(--in)}.btn-success{--btn-color:var(--su)}.btn-warning{--btn-color:var(--wa)}.btn-error{--btn-color:var(--er)}}.btn-neutral{--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));outline-color:var(--fallback-n,oklch(var(--n)/1))}.btn-info{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));outline-color:var(--fallback-in,oklch(var(--in)/1))}.btn-success{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));outline-color:var(--fallback-su,oklch(var(--su)/1))}.btn-warning{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));outline-color:var(--fallback-wa,oklch(var(--wa)/1))}.btn-error{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));outline-color:var(--fallback-er,oklch(var(--er)/1))}.btn.glass{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.btn.glass.btn-active{--glass-opacity:25%;--glass-border-opacity:15%}.btn-ghost{background-color:transparent;border-color:transparent;border-width:1px;color:currentColor;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.btn-ghost.btn-active{background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-color:transparent}.btn-link.btn-active{background-color:transparent;border-color:transparent;text-decoration-line:underline}.btn-outline{background-color:transparent;border-color:currentColor;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.btn-outline.btn-active{--tw-border-opacity:1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}.btn-outline.btn-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}.btn-outline.btn-primary.btn-active{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.btn-outline.btn-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}.btn-outline.btn-secondary.btn-active{--tw-text-opacity:1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}.btn-outline.btn-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}.btn-outline.btn-accent.btn-active{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}.btn-outline.btn-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}.btn-outline.btn-success.btn-active{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}.btn-outline.btn-info{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.btn-outline.btn-info.btn-active{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}.btn-outline.btn-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}.btn-outline.btn-warning.btn-active{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}.btn-outline.btn-error{--tw-text-opacity:1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}.btn-outline.btn-error.btn-active{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}.btn.btn-disabled,.btn:disabled,.btn[disabled]{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btn:is(input[type=checkbox]:checked),.btn:is(input[type=radio]:checked){--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.btn:is(input[type=checkbox]:checked):focus-visible,.btn:is(input[type=radio]:checked):focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}@keyframes button-pop{0%{transform:scale(var(--btn-focus-scale,.98))}40%{transform:scale(1.02)}to{transform:scale(1)}}.card :where(figure:first-child){border-end-end-radius:unset;border-end-start-radius:unset;border-start-end-radius:inherit;border-start-start-radius:inherit;overflow:hidden}.card :where(figure:last-child){border-end-end-radius:inherit;border-end-start-radius:inherit;border-start-end-radius:unset;border-start-start-radius:unset;overflow:hidden}.card:focus-visible{outline:2px solid currentColor;outline-offset:2px}.card.bordered{border-width:1px;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}.card.compact .card-body{font-size:.875rem;line-height:1.25rem;padding:1rem}.card-title{align-items:center;display:flex;font-size:1.25rem;font-weight:600;gap:.5rem;line-height:1.75rem}.card.image-full :where(figure){border-radius:inherit;overflow:hidden}.checkbox:focus{box-shadow:none}.checkbox:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/1));outline-offset:2px;outline-style:solid;outline-width:2px}.checkbox:checked,.checkbox[aria-checked=true],.checkbox[checked=true]{animation:checkmark var(--animation-input,.2s) ease-out;background-color:var(--chkbg);background-image:linear-gradient(-45deg,transparent 65%,var(--chkbg) 65.99%),linear-gradient(45deg,transparent 75%,var(--chkbg) 75.99%),linear-gradient(-45deg,var(--chkbg) 40%,transparent 40.99%),linear-gradient(45deg,var(--chkbg) 30%,var(--chkfg) 30.99%,var(--chkfg) 40%,transparent 40.99%),linear-gradient(-45deg,var(--chkfg) 50%,var(--chkbg) 50.99%);background-repeat:no-repeat}.checkbox:indeterminate{--tw-bg-opacity:1;animation:checkmark var(--animation-input,.2s) ease-out;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-image:linear-gradient(90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(-90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(0deg,var(--chkbg) 43%,var(--chkfg) 43%,var(--chkfg) 57%,var(--chkbg) 57%);background-repeat:no-repeat}.checkbox:disabled{border-color:transparent;cursor:not-allowed;--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.2}@keyframes checkmark{0%{background-position-y:5px}50%{background-position-y:-2px}to{background-position-y:0}}.divider:not(:empty){gap:1rem}.drawer-toggle:focus-visible~.drawer-content label.drawer-button{outline-offset:2px;outline-style:solid;outline-width:2px}.dropdown.dropdown-open .dropdown-content,.dropdown:focus .dropdown-content,.dropdown:focus-within .dropdown-content{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.file-input-bordered{--tw-border-opacity:0.2}.file-input:focus{outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.file-input-disabled,.file-input[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));--tw-text-opacity:0.2}.file-input-disabled::-moz-placeholder,.file-input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.file-input-disabled::placeholder,.file-input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.file-input-disabled::file-selector-button,.file-input[disabled]::file-selector-button{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.footer-title{font-weight:700;margin-bottom:.5rem;opacity:.6;text-transform:uppercase}.label-text{font-size:.875rem;line-height:1.25rem}.label-text,.label-text-alt{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.label-text-alt{font-size:.75rem;line-height:1rem}.input input{--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));background-color:transparent}.input input:focus{outline:2px solid transparent;outline-offset:2px}.input[list]::-webkit-calendar-picker-indicator{line-height:1em}.input-bordered{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}.input:focus,.input:focus-within{border-color:var(--fallback-bc,oklch(var(--bc)/.2));box-shadow:none;outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.input-ghost{--tw-bg-opacity:0.05}.input-ghost:focus,.input-ghost:focus-within{--tw-bg-opacity:1;--tw-text-opacity:1;box-shadow:none;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.input-primary{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.input-primary:focus,.input-primary:focus-within{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}.input-error{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))}.input-error:focus,.input-error:focus-within{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)));outline-color:var(--fallback-er,oklch(var(--er)/1))}.input-disabled,.input:disabled,.input[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/.4))}.input-disabled::-moz-placeholder,.input:disabled::-moz-placeholder,.input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.input-disabled::placeholder,.input:disabled::placeholder,.input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.input::-webkit-date-and-time-value{text-align:inherit}.join>:where(:not(:first-child)){margin-bottom:0;margin-top:0;margin-inline-start:-1px}.join-item:focus{isolation:isolate}.link-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){@media (hover:hover){.link-primary:hover{color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,#000)}}}.link:focus{outline:2px solid transparent;outline-offset:2px}.link:focus-visible{outline:2px solid currentColor;outline-offset:2px}.loading{aspect-ratio:1/1;background-color:currentColor;display:inline-block;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;pointer-events:none;width:1.5rem}.loading,.loading-spinner{-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' stroke='%23000'%3E%3Cstyle%3E@keyframes spinner_zKoa{to{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%25{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%25{stroke-dasharray:42 150;stroke-dashoffset:-16}95%25,to{stroke-dasharray:42 150;stroke-dashoffset:-59}}%3C/style%3E%3Cg style='transform-origin:center;animation:spinner_zKoa 2s linear infinite'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' class='spinner_V8m1' style='stroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite'/%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' stroke='%23000'%3E%3Cstyle%3E@keyframes spinner_zKoa{to{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%25{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%25{stroke-dasharray:42 150;stroke-dashoffset:-16}95%25,to{stroke-dasharray:42 150;stroke-dashoffset:-59}}%3C/style%3E%3Cg style='transform-origin:center;animation:spinner_zKoa 2s linear infinite'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' class='spinner_V8m1' style='stroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite'/%3E%3C/g%3E%3C/svg%3E")}.loading-dots{-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cstyle%3E@keyframes spinner_8HQG{0%25,57.14%25{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%25{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}%3C/style%3E%3Ccircle cx='4' cy='12' r='3' class='spinner_qM83'/%3E%3Ccircle cx='12' cy='12' r='3' class='spinner_qM83' style='animation-delay:.1s'/%3E%3Ccircle cx='20' cy='12' r='3' class='spinner_qM83' style='animation-delay:.2s'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cstyle%3E@keyframes spinner_8HQG{0%25,57.14%25{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%25{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}%3C/style%3E%3Ccircle cx='4' cy='12' r='3' class='spinner_qM83'/%3E%3Ccircle cx='12' cy='12' r='3' class='spinner_qM83' style='animation-delay:.1s'/%3E%3Ccircle cx='20' cy='12' r='3' class='spinner_qM83' style='animation-delay:.2s'/%3E%3C/svg%3E")}.loading-sm{width:1.25rem}.loading-md{width:1.5rem}.loading-lg{width:2.5rem}:where(.menu li:empty){--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));height:1px;margin:.5rem 1rem;opacity:.1}.menu :where(li ul):before{bottom:.75rem;inset-inline-start:0;position:absolute;top:.75rem;width:1px;--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));content:"";opacity:.1}.menu :where(li:not(.menu-title)>:not(ul,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn,.5rem);padding:.5rem 1rem;text-align:start;text-wrap:balance;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):is(summary):not(.active,.btn):focus-visible,:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(summary,.active,.btn).focus,:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(summary,.active,.btn):focus,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):is(summary):not(.active,.btn):focus-visible,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn).focus,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn):focus{background-color:var(--fallback-bc,oklch(var(--bc)/.1));cursor:pointer;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));outline:2px solid transparent;outline-offset:2px}.menu li>:not(ul,.menu-title,details,.btn).active,.menu li>:not(ul,.menu-title,details,.btn):active,.menu li>details>summary:active{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.menu :where(li>details>summary)::-webkit-details-marker{display:none}.menu :where(li>.menu-dropdown-toggle):after,.menu :where(li>details>summary):after{box-shadow:2px 2px;content:"";display:block;height:.5rem;justify-self:end;margin-top:-.5rem;pointer-events:none;transform:rotate(45deg);transform-origin:75% 75%;transition-duration:.3s;transition-property:transform,margin-top;transition-timing-function:cubic-bezier(.4,0,.2,1);width:.5rem}.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after,.menu :where(li>details[open]>summary):after{margin-top:0;transform:rotate(225deg)}.mockup-phone .display{border-radius:40px;margin-top:-25px;overflow:hidden}.mockup-browser .mockup-browser-toolbar .input{display:block;height:1.75rem;margin-left:auto;margin-right:auto;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:24rem;--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));direction:ltr;padding-left:2rem}.mockup-browser .mockup-browser-toolbar .input:before{aspect-ratio:1/1;height:.75rem;left:.5rem;--tw-translate-y:-50%;border-color:currentColor;border-radius:9999px;border-width:2px}.mockup-browser .mockup-browser-toolbar .input:after,.mockup-browser .mockup-browser-toolbar .input:before{content:"";opacity:.6;position:absolute;top:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mockup-browser .mockup-browser-toolbar .input:after{height:.5rem;left:1.25rem;--tw-translate-y:25%;--tw-rotate:-45deg;border-color:currentColor;border-radius:9999px;border-width:1px}.modal::backdrop,.modal:not(dialog:not(.modal-open)){animation:modal-pop .2s ease-out;background-color:#0006}.modal-backdrop{align-self:stretch;color:transparent;display:grid;grid-column-start:1;grid-row-start:1;justify-self:stretch;z-index:-1}.modal-open .modal-box,.modal-toggle:checked+.modal .modal-box,.modal:target .modal-box,.modal[open] .modal-box{--tw-translate-y:0px;--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-action>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}@keyframes modal-pop{0%{opacity:0}}.progress::-moz-progress-bar{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}.progress-primary::-moz-progress-bar{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}.progress:indeterminate{--progress-color:var(--fallback-bc,oklch(var(--bc)/1));animation:progress-loading 5s ease-in-out infinite;background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-position-x:15%;background-size:200%}.progress-primary:indeterminate{--progress-color:var(--fallback-p,oklch(var(--p)/1))}.progress::-webkit-progress-bar{background-color:transparent;border-radius:var(--rounded-box,1rem)}.progress::-webkit-progress-value{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}.progress-primary::-webkit-progress-value{--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}.progress:indeterminate::-moz-progress-bar{animation:progress-loading 5s ease-in-out infinite;background-color:transparent;background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-position-x:15%;background-size:200%}@keyframes progress-loading{50%{background-position-x:-115%}}.radio:focus{box-shadow:none}.radio:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/1));outline-offset:2px;outline-style:solid;outline-width:2px}.radio:checked,.radio[aria-checked=true]{--tw-bg-opacity:1;animation:radiomark var(--animation-input,.2s) ease-out;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-image:none;box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}.radio-primary{--chkbg:var(--p);--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.radio-primary:focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}.radio-primary:checked,.radio-primary[aria-checked=true]{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.radio:disabled{cursor:not-allowed;opacity:.2}@keyframes radiomark{0%{box-shadow:0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset}50%{box-shadow:0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset}to{box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}}.range:focus-visible::-webkit-slider-thumb{--focus-shadow:0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 2rem var(--range-shdw) inset}.range:focus-visible::-moz-range-thumb{--focus-shadow:0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 2rem var(--range-shdw) inset}.range::-webkit-slider-runnable-track{background-color:var(--fallback-bc,oklch(var(--bc)/.1));border-radius:var(--rounded-box,1rem);height:.5rem;width:100%}.range::-moz-range-track{background-color:var(--fallback-bc,oklch(var(--bc)/.1));border-radius:var(--rounded-box,1rem);height:.5rem;width:100%}.range::-webkit-slider-thumb{border-radius:var(--rounded-box,1rem);border-style:none;height:1.5rem;position:relative;width:1.5rem;--tw-bg-opacity:1;appearance:none;-webkit-appearance:none;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));color:var(--range-shdw);top:50%;transform:translateY(-50%);--filler-size:100rem;--filler-offset:0.6rem;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow,0 0),calc(var(--filler-size)*-1 - var(--filler-offset)) 0 0 var(--filler-size)}.range::-moz-range-thumb{border-radius:var(--rounded-box,1rem);border-style:none;height:1.5rem;position:relative;width:1.5rem;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));color:var(--range-shdw);top:50%;--filler-size:100rem;--filler-offset:0.5rem;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow,0 0),calc(var(--filler-size)*-1 - var(--filler-offset)) 0 0 var(--filler-size)}@keyframes rating-pop{0%{transform:translateY(-.125em)}40%{transform:translateY(-.125em)}to{transform:translateY(0)}}.select-bordered,.select:focus{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}.select:focus{box-shadow:none;outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.select-disabled,.select:disabled,.select[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.select-disabled::-moz-placeholder,.select:disabled::-moz-placeholder,.select[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.select-disabled::placeholder,.select:disabled::placeholder,.select[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.select-multiple,.select[multiple],.select[size].select:not([size="1"]){background-image:none;padding-right:1rem}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}:where(.stats)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(0px*(1 - var(--tw-divide-y-reverse))) calc(1px*var(--tw-divide-x-reverse)) calc(0px*var(--tw-divide-y-reverse)) calc(1px*(1 - var(--tw-divide-x-reverse)))}:is([dir=rtl] .stats>:not([hidden])~:not([hidden])){--tw-divide-x-reverse:1}.steps .step:before{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));content:"";height:.5rem;margin-inline-start:-100%;top:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));width:100%}.steps .step:after,.steps .step:before{grid-column-start:1;grid-row-start:1;--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));--tw-text-opacity:1}.steps .step:after{border-radius:9999px;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));content:counter(step);counter-increment:step;display:grid;height:2rem;place-items:center;place-self:center;position:relative;width:2rem;z-index:1}.steps .step:first-child:before{content:none}.steps .step[data-content]:after{content:attr(data-content)}.tabs-lifted>.tab:focus-visible{border-end-end-radius:0;border-end-start-radius:0}.tab.tab-active:not(.tab-disabled):not([disabled]),.tab:is(input:checked){border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity:1;--tw-text-opacity:1}.tab:focus{outline:2px solid transparent;outline-offset:2px}.tab:focus-visible{outline:2px solid currentColor;outline-offset:-5px}.tab-disabled,.tab[disabled]{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));cursor:not-allowed;--tw-text-opacity:0.2}.tabs-bordered>.tab{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity:0.2;border-bottom-width:calc(var(--tab-border, 1px) + 1px);border-style:solid}.tabs-lifted>.tab{border:var(--tab-border,1px) solid transparent;border-bottom-color:var(--tab-border-color);border-start-end-radius:var(--tab-radius,.5rem);border-start-start-radius:var(--tab-radius,.5rem);border-width:0 0 var(--tab-border,1px) 0;padding-inline-end:var(--tab-padding,1rem);padding-inline-start:var(--tab-padding,1rem);padding-top:var(--tab-border,1px)}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]),.tabs-lifted>.tab:is(input:checked){background-color:var(--tab-bg);border-inline-end-color:var(--tab-border-color);border-inline-start-color:var(--tab-border-color);border-top-color:var(--tab-border-color);border-width:var(--tab-border,1px) var(--tab-border,1px) 0 var(--tab-border,1px);padding-inline-end:calc(var(--tab-padding, 1rem) - var(--tab-border, 1px));padding-bottom:var(--tab-border,1px);padding-inline-start:calc(var(--tab-padding, 1rem) - var(--tab-border, 1px));padding-top:0}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):before,.tabs-lifted>.tab:is(input:checked):before{background-position:0 0,100% 0;background-repeat:no-repeat;background-size:var(--tab-radius,.5rem);bottom:0;content:"";display:block;height:var(--tab-radius,.5rem);position:absolute;width:calc(100% + var(--tab-radius, .5rem)*2);z-index:1;--tab-grad:calc(69% - var(--tab-border, 1px));--radius-start:radial-gradient(circle at top left,transparent var(--tab-grad),var(--tab-border-color) calc(var(--tab-grad) + 0.25px),var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)),var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px));--radius-end:radial-gradient(circle at top right,transparent var(--tab-grad),var(--tab-border-color) calc(var(--tab-grad) + 0.25px),var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)),var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px));background-image:var(--radius-start),var(--radius-end)}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before,.tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-end);background-position:100% 0}[dir=rtl] .tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before,[dir=rtl] .tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-start);background-position:0 0}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before,.tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-start);background-position:0 0}[dir=rtl] .tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before,[dir=rtl] .tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-end);background-position:100% 0}.tabs-lifted>.tab-active:not(.tab-disabled):not([disabled])+.tabs-lifted .tab-active:not(.tab-disabled):not([disabled]):before,.tabs-lifted>.tab:is(input:checked)+.tabs-lifted .tab:is(input:checked):before{background-image:var(--radius-end);background-position:100% 0}.tabs-boxed{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));padding:.25rem}.tabs-boxed,.tabs-boxed .tab{border-radius:var(--rounded-btn,.5rem)}.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]),.tabs-boxed :is(input:checked){--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}:is([dir=rtl] .table){text-align:right}.table :where(th,td){padding:.75rem 1rem;vertical-align:middle}.table tr.active,.table tr.active:nth-child(2n),.table-zebra tbody tr:nth-child(2n){--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}.table-zebra tr.active,.table-zebra tr.active:nth-child(2n),.table-zebra-zebra tbody tr:nth-child(2n){--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}.table :where(thead,tbody) :where(tr:first-child:last-child),.table :where(thead,tbody) :where(tr:not(:last-child)){border-bottom-width:1px;--tw-border-opacity:1;border-bottom-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}.table :where(thead,tfoot){color:var(--fallback-bc,oklch(var(--bc)/.6));font-size:.75rem;font-weight:700;line-height:1rem;white-space:nowrap}.timeline hr{height:.25rem}:where(.timeline hr){--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}:where(.timeline:has(.timeline-middle) hr):first-child{border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}:where(.timeline:has(.timeline-middle) hr):last-child{border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}:where(.timeline:not(:has(.timeline-middle)) :first-child hr:last-child){border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}:where(.timeline:not(:has(.timeline-middle)) :last-child hr:first-child){border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}.timeline-box{border-radius:var(--rounded-box,1rem);border-width:1px;--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));padding:.5rem 1rem;--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@keyframes toast-pop{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}[dir=rtl] .toggle{--handleoffsetcalculator:calc(var(--handleoffset)*1)}.toggle:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.toggle:hover{background-color:currentColor}.toggle:checked,.toggle[aria-checked=true],.toggle[checked=true]{background-image:none;--handleoffsetcalculator:var(--handleoffset);--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}[dir=rtl] .toggle:checked,[dir=rtl] .toggle[aria-checked=true],[dir=rtl] .toggle[checked=true]{--handleoffsetcalculator:calc(var(--handleoffset)*-1)}.toggle:indeterminate{--tw-text-opacity:1;box-shadow:calc(var(--handleoffset)/2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset)/-2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}[dir=rtl] .toggle:indeterminate{box-shadow:calc(var(--handleoffset)/2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset)/-2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset}.toggle:disabled{cursor:not-allowed;--tw-border-opacity:1;background-color:transparent;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));opacity:.3;--togglehandleborder:0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset,var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset}.glass,.glass.btn-active{-webkit-backdrop-filter:blur(var(--glass-blur,40px));backdrop-filter:blur(var(--glass-blur,40px));background-color:transparent;background-image:linear-gradient(135deg,rgb(255 255 255/var(--glass-opacity,30%)) 0,transparent 100%),linear-gradient(var(--glass-reflex-degree,100deg),rgb(255 255 255/var(--glass-reflex-opacity,10%)) 25%,transparent 25%);border:none;box-shadow:0 0 0 1px rgb(255 255 255/var(--glass-border-opacity,10%)) inset,0 0 0 2px rgb(0 0 0/5%);text-shadow:0 1px rgb(0 0 0/var(--glass-text-shadow-opacity,5%))}@media (hover:hover){.glass.btn-active{-webkit-backdrop-filter:blur(var(--glass-blur,40px));backdrop-filter:blur(var(--glass-blur,40px));background-color:transparent;background-image:linear-gradient(135deg,rgb(255 255 255/var(--glass-opacity,30%)) 0,transparent 100%),linear-gradient(var(--glass-reflex-degree,100deg),rgb(255 255 255/var(--glass-reflex-opacity,10%)) 25%,transparent 25%);border:none;box-shadow:0 0 0 1px rgb(255 255 255/var(--glass-border-opacity,10%)) inset,0 0 0 2px rgb(0 0 0/5%);text-shadow:0 1px rgb(0 0 0/var(--glass-text-shadow-opacity,5%))}}.badge-xs{font-size:.75rem;height:.75rem;line-height:.75rem;padding-left:.313rem;padding-right:.313rem}.badge-sm{font-size:.75rem;height:1rem;line-height:1rem;padding-left:.438rem;padding-right:.438rem}.btm-nav-xs>:where(.active){border-top-width:1px}.btm-nav-sm>:where(.active){border-top-width:2px}.btm-nav-md>:where(.active){border-top-width:2px}.btm-nav-lg>:where(.active){border-top-width:4px}.btn-xs{font-size:.75rem;height:1.5rem;min-height:1.5rem;padding-left:.5rem;padding-right:.5rem}.btn-sm{font-size:.875rem;height:2rem;min-height:2rem;padding-left:.75rem;padding-right:.75rem}.btn-square:where(.btn-xs){height:1.5rem;padding:0;width:1.5rem}.btn-square:where(.btn-sm){height:2rem;padding:0;width:2rem}.btn-circle:where(.btn-xs){border-radius:9999px;height:1.5rem;padding:0;width:1.5rem}.btn-circle:where(.btn-sm){border-radius:9999px;height:2rem;padding:0;width:2rem}[type=checkbox].checkbox-sm{height:1.25rem;width:1.25rem}.indicator :where(.indicator-item){bottom:auto;inset-inline-end:0;inset-inline-start:auto;top:0;--tw-translate-y:-50%;--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item)){--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-start){inset-inline-end:auto;inset-inline-start:0;--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-start)){--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-center){inset-inline-end:50%;inset-inline-start:50%;--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-center)){--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-end){inset-inline-end:0;inset-inline-start:auto;--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-end)){--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-bottom){bottom:0;top:auto;--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-middle){bottom:50%;top:50%;--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-top){bottom:auto;top:0;--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.input-xs{font-size:.75rem;height:1.5rem;line-height:1rem;line-height:1.625;padding-left:.5rem;padding-right:.5rem}.input-sm{font-size:.875rem;height:2rem;line-height:2rem;padding-left:.75rem;padding-right:.75rem}.join.join-vertical{flex-direction:column}.join.join-vertical .join-item:first-child:not(:last-child),.join.join-vertical :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:inherit;border-start-start-radius:inherit}.join.join-vertical .join-item:last-child:not(:first-child),.join.join-vertical :last-child:not(:first-child) .join-item{border-end-end-radius:inherit;border-end-start-radius:inherit;border-start-end-radius:0;border-start-start-radius:0}.join.join-horizontal{flex-direction:row}.join.join-horizontal .join-item:first-child:not(:last-child),.join.join-horizontal :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:inherit;border-start-end-radius:0;border-start-start-radius:inherit}.join.join-horizontal .join-item:last-child:not(:first-child),.join.join-horizontal :last-child:not(:first-child) .join-item{border-end-end-radius:inherit;border-end-start-radius:0;border-start-end-radius:inherit;border-start-start-radius:0}.menu-horizontal{display:inline-flex;flex-direction:row}.menu-horizontal>li:not(.menu-title)>details>ul{position:absolute}.select-sm{font-size:.875rem;height:2rem;line-height:2rem;min-height:2rem;padding-left:.75rem;padding-right:2rem}[dir=rtl] .select-sm{padding-left:2rem;padding-right:.75rem}.stats-vertical{grid-auto-flow:row}.steps-horizontal .step{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));place-items:center;text-align:center}.steps-vertical .step{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(1,minmax(0,1fr))}.tabs-md :where(.tab){font-size:.875rem;height:2rem;line-height:1.25rem;line-height:2;--tab-padding:1rem}.tabs-lg :where(.tab){font-size:1.125rem;height:3rem;line-height:1.75rem;line-height:2;--tab-padding:1.25rem}.tabs-sm :where(.tab){font-size:.875rem;height:1.5rem;line-height:.75rem;--tab-padding:0.75rem}.tabs-xs :where(.tab){font-size:.75rem;height:1.25rem;line-height:.75rem;--tab-padding:0.5rem}.timeline-vertical{flex-direction:column}.timeline-compact .timeline-start,.timeline-horizontal.timeline-compact .timeline-start{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.timeline-compact li:has(.timeline-start) .timeline-end,.timeline-horizontal.timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:none;grid-row-start:auto}.timeline-vertical.timeline-compact>li{--timeline-col-start:0}.timeline-vertical.timeline-compact .timeline-start{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.timeline-vertical.timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:auto;grid-row-start:none}:where(.timeline-vertical>li){--timeline-row-start:minmax(0,1fr);--timeline-row-end:minmax(0,1fr);justify-items:center}.timeline-vertical>li>hr{height:100%}:where(.timeline-vertical>li>hr):first-child{grid-column-start:2;grid-row-start:1}:where(.timeline-vertical>li>hr):last-child{grid-column-end:auto;grid-column-start:2;grid-row-end:none;grid-row-start:3}.timeline-vertical .timeline-start{align-self:center;grid-column-end:2;grid-column-start:1;grid-row-end:4;grid-row-start:1;justify-self:end}.timeline-vertical .timeline-end{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.timeline-vertical:where(.timeline-snap-icon)>li{--timeline-col-start:minmax(0,1fr);--timeline-row-start:0.5rem}.timeline-horizontal .timeline-start{align-self:flex-end;grid-column-end:4;grid-column-start:1;grid-row-end:2;grid-row-start:1;justify-self:center}.timeline-horizontal .timeline-end{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center}.timeline-horizontal:where(.timeline-snap-icon)>li,:where(.timeline-snap-icon)>li{--timeline-col-start:0.5rem;--timeline-row-start:minmax(0,1fr)}.tooltip{--tooltip-offset:calc(100% + 1px + var(--tooltip-tail, 0px))}.tooltip:before{content:var(--tw-content);pointer-events:none;position:absolute;z-index:1;--tw-content:attr(data-tip)}.tooltip-top:before,.tooltip:before{bottom:var(--tooltip-offset);left:50%;right:auto;top:auto;transform:translateX(-50%)}.tooltip-bottom:before{bottom:auto;left:50%;right:auto;top:var(--tooltip-offset);transform:translateX(-50%)}.card-compact .card-body{font-size:.875rem;line-height:1.25rem;padding:1rem}.card-compact .card-title{margin-bottom:.25rem}.card-normal .card-body{font-size:1rem;line-height:1.5rem;padding:var(--padding-card,2rem)}.card-normal .card-title{margin-bottom:.75rem}.join.join-vertical>:where(:not(:first-child)){margin-left:0;margin-right:0;margin-top:-1px}.join.join-horizontal>:where(:not(:first-child)){margin-bottom:0;margin-top:0;margin-inline-start:-1px}.menu-horizontal>li:not(.menu-title)>details>ul{margin-inline-start:0;margin-top:1rem;padding-bottom:.5rem;padding-inline-end:.5rem;padding-top:.5rem}.menu-horizontal>li>details>ul:before{content:none}:where(.menu-horizontal>li:not(.menu-title)>details>ul){border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.menu-sm :where(li:not(.menu-title)>:not(ul,details,.menu-title)),.menu-sm :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn,.5rem);font-size:.875rem;line-height:1.25rem;padding:.25rem .75rem}.menu-sm .menu-title{padding:.5rem .75rem}.modal-top :where(.modal-box){max-width:none;width:100%;--tw-translate-y:-2.5rem;--tw-scale-x:1;--tw-scale-y:1;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:0;border-top-right-radius:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-middle :where(.modal-box){max-width:32rem;width:91.666667%;--tw-translate-y:0px;--tw-scale-x:.9;--tw-scale-y:.9;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-bottom :where(.modal-box){max-width:none;width:100%;--tw-translate-y:2.5rem;--tw-scale-x:1;--tw-scale-y:1;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.stats-vertical>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(1px*(1 - var(--tw-divide-y-reverse))) calc(0px*var(--tw-divide-x-reverse)) calc(1px*var(--tw-divide-y-reverse)) calc(0px*(1 - var(--tw-divide-x-reverse)))}.stats-vertical{overflow-y:auto}.steps-horizontal .step{grid-template-columns:auto;grid-template-rows:40px 1fr;min-width:4rem}.steps-horizontal .step:before{height:.5rem;width:100%;--tw-translate-x:0px;--tw-translate-y:0px;content:"";margin-inline-start:-100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .steps-horizontal .step):before{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.steps-vertical .step{gap:.5rem;grid-template-columns:40px 1fr;grid-template-rows:auto;justify-items:start;min-height:4rem}.steps-vertical .step:before{height:100%;width:.5rem;--tw-translate-x:-50%;--tw-translate-y:-50%;margin-inline-start:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .steps-vertical .step):before{--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.timeline-vertical>li>hr{width:.25rem}:where(.timeline-vertical:has(.timeline-middle)>li>hr):first-child{border-bottom-left-radius:var(--rounded-badge,1.9rem);border-bottom-right-radius:var(--rounded-badge,1.9rem);border-top-left-radius:0;border-top-right-radius:0}:where(.timeline-vertical:has(.timeline-middle)>li>hr):last-child{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-badge,1.9rem);border-top-right-radius:var(--rounded-badge,1.9rem)}:where(.timeline-vertical:not(:has(.timeline-middle)) :first-child>hr:last-child){border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-badge,1.9rem);border-top-right-radius:var(--rounded-badge,1.9rem)}:where(.timeline-vertical:not(:has(.timeline-middle)) :last-child>hr:first-child){border-bottom-left-radius:var(--rounded-badge,1.9rem);border-bottom-right-radius:var(--rounded-badge,1.9rem);border-top-left-radius:0;border-top-right-radius:0}:where(.timeline-horizontal:has(.timeline-middle)>li>hr):first-child{border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}:where(.timeline-horizontal:has(.timeline-middle)>li>hr):last-child{border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}.tooltip{display:inline-block;position:relative;text-align:center;--tooltip-tail:0.1875rem;--tooltip-color:var(--fallback-n,oklch(var(--n)/1));--tooltip-text-color:var(--fallback-nc,oklch(var(--nc)/1));--tooltip-tail-offset:calc(100% + 0.0625rem - var(--tooltip-tail))}.tooltip:after,.tooltip:before{opacity:0;transition-delay:.1s;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.tooltip:after{border-style:solid;border-width:var(--tooltip-tail,0);content:"";display:block;height:0;position:absolute;width:0}.tooltip:before{background-color:var(--tooltip-color);border-radius:.25rem;color:var(--tooltip-text-color);font-size:.875rem;line-height:1.25rem;max-width:20rem;padding:.25rem .5rem;width:-moz-max-content;width:max-content}.tooltip.tooltip-open:after,.tooltip.tooltip-open:before,.tooltip:hover:after,.tooltip:hover:before{opacity:1;transition-delay:75ms}.tooltip:has(:focus-visible):after,.tooltip:has(:focus-visible):before{opacity:1;transition-delay:75ms}.tooltip:not([data-tip]):hover:after,.tooltip:not([data-tip]):hover:before{opacity:0;visibility:hidden}.tooltip-top:after,.tooltip:after{border-color:var(--tooltip-color) transparent transparent transparent;bottom:var(--tooltip-tail-offset);left:50%;right:auto;top:auto;transform:translateX(-50%)}.tooltip-bottom:after{border-color:transparent transparent var(--tooltip-color) transparent;bottom:auto;left:50%;right:auto;top:var(--tooltip-tail-offset);transform:translateX(-50%)}.fade-out{opacity:0;transition:opacity .15s ease-in-out}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.left-2{left:.5rem}.right-0{right:0}.right-5{right:1.25rem}.top-0{top:0}.top-2{top:.5rem}.top-5{top:1.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.z-\[1\]{z-index:1}.z-\[5000\]{z-index:5000}.z-\[6000\]{z-index:6000}.m-0{margin:0}.m-2{margin:.5rem}.m-5{margin:1.25rem}.m-auto{margin:auto}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-bottom:2.5rem;margin-top:2.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-4{margin-bottom:1rem;margin-top:1rem}.my-5{margin-bottom:1.25rem;margin-top:1.25rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[250px\]{height:250px}.h-\[25rem\]{height:25rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.max-h-96{max-height:24rem}.min-h-80{min-height:20rem}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-10\/12{width:83.333333%}.w-4{width:1rem}.w-4\/12{width:33.333333%}.w-5{width:1.25rem}.w-52{width:13rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-80{width:20rem}.w-96{width:24rem}.w-auto{width:auto}.w-full{width:100%}.min-w-52{min-width:13rem}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.translate-x-full{--tw-translate-x:100%}.transform,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-base-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-divide-opacity,1)))}.justify-self-end{justify-self:end}.justify-self-center{justify-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-box{border-radius:var(--rounded-box,1rem)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-dashed{border-style:dashed}.border-base-300{--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity,1)))}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.border-error{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity,1)))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity,1))}.border-sky-500{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity,1))}.border-opacity-30{--tw-border-opacity:0.3}.bg-base-100{--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity,1)))}.bg-base-200{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity,1)))}.bg-base-300{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity,1)))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-blue-900{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-neutral{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity,1)))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-secondary-content{--tw-bg-opacity:1;background-color:var(--fallback-sc,oklch(var(--sc)/var(--tw-bg-opacity,1)))}.stroke-current{stroke:currentColor}.stroke-info{stroke:var(--fallback-in,oklch(var(--in)/1))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-20{padding-bottom:5rem;padding-top:5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-10{padding-right:2.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.normal-case{text-transform:none}.italic{font-style:italic}.text-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity,1)))}.text-accent-content{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity,1)))}.text-base-content{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity,1)))}.text-base-content\/60{color:var(--fallback-bc,oklch(var(--bc)/.6))}.text-base-content\/70{color:var(--fallback-bc,oklch(var(--bc)/.7))}.text-base-content\/80{color:var(--fallback-bc,oklch(var(--bc)/.8))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-neutral-content{--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity,1)))}.text-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity,1)))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity,1)))}.text-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity,1)))}.text-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity,1)))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.decoration-dotted{text-decoration-style:dotted}.placeholder-base-content\/70::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/.7))}.placeholder-base-content\/70::placeholder{color:var(--fallback-bc,oklch(var(--bc)/.7))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.grayscale{--tw-grayscale:grayscale(100%)}.filter,.grayscale{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-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-shadow{transition-duration:.15s;transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@tailwind daisyui;.leaflet-right-panel{background:#fff;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.3);margin-right:10px;margin-top:80px;transform:none;transition:right .3s ease-in-out;z-index:400}.leaflet-right-panel.controls-shifted{right:310px}.leaflet-control-button{background-color:#fff!important;color:#374151!important}.leaflet-control-button:hover{background-color:#f3f4f6!important}.leaflet-drawer{background:hsla(0,0%,100%,.5);box-shadow:-2px 0 5px rgba(0,0,0,.1);height:100%;position:absolute;right:0;top:0;transform:translateX(100%);transition:transform .3s ease-in-out;width:338px;z-index:450}.leaflet-drawer.open{transform:translateX(0)}.leaflet-control-button,.leaflet-control-layers,.toggle-panel-button{transition:right .3s ease-in-out;z-index:500}.controls-shifted{right:338px!important}.leaflet-control-custom{align-items:center;background-color:#fff;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.3);cursor:pointer;display:flex;height:30px;justify-content:center;width:30px}.leaflet-control-custom:hover{background-color:#f3f4f6}#selection-tool-button.active{background-color:#60a5fa;color:#fff}#cancel-selection-button{margin-bottom:1rem;width:100%}@media (hover:hover){.hover\:btn-ghost:hover:hover{border-color:transparent}@supports (color:oklch(0 0 0)){.hover\:btn-ghost:hover:hover{background-color:var(--fallback-bc,oklch(var(--bc)/.2))}}.hover\:btn-info:hover.btn-outline:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.hover\:btn-info:hover.btn-outline:hover{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}}@supports not (color:oklch(0 0 0)){.hover\:btn-info:hover{--btn-color:var(--fallback-in)}}@supports (color:color-mix(in oklab,black,black)){.hover\:btn-info:hover.btn-outline.btn-active{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}@supports (color:oklch(0 0 0)){.hover\:btn-info:hover{--btn-color:var(--in)}}.hover\:btn-info:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));outline-color:var(--fallback-in,oklch(var(--in)/1))}.hover\:btn-ghost:hover{background-color:transparent;border-color:transparent;border-width:1px;color:currentColor;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.hover\:btn-ghost:hover.btn-active{background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-color:transparent}.hover\:btn-info:hover.btn-outline{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.hover\:btn-info:hover.btn-outline.btn-active{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}.hover\:input-primary:hover{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.hover\:input-primary:hover:focus,.hover\:input-primary:hover:focus-within{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}.focus\:input-ghost:focus{--tw-bg-opacity:0.05}.focus\:input-ghost:focus:focus,.focus\:input-ghost:focus:focus-within{--tw-bg-opacity:1;--tw-text-opacity:1;box-shadow:none;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}@media not all and (min-width:768px){.max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal{--timeline-row-start:0}.max-md\:timeline-compact .timeline-horizontal .timeline-start,.max-md\:timeline-compact .timeline-start{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:none;grid-row-start:auto}.max-md\:timeline-compact.timeline-vertical>li{--timeline-col-start:0}.max-md\:timeline-compact.timeline-vertical .timeline-start{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.max-md\:timeline-compact.timeline-vertical li:has(.timeline-start) .timeline-end{grid-column-start:auto;grid-row-start:none}}@media (min-width:1024px){.lg\:stats-horizontal{grid-auto-flow:column}.lg\:stats-horizontal>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(0px*(1 - var(--tw-divide-y-reverse))) calc(1px*var(--tw-divide-x-reverse)) calc(0px*var(--tw-divide-y-reverse)) calc(1px*(1 - var(--tw-divide-x-reverse)))}.lg\:stats-horizontal{overflow-x:auto}:is([dir=rtl] .lg\:stats-horizontal){--tw-divide-x-reverse:1}}.last\:border-0:last-child{border-width:0}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-accent:hover{--tw-bg-opacity:1;background-color:var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity,1)))}.hover\:bg-base-200:hover{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity,1)))}.hover\:bg-base-300:hover{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity,1)))}.hover\:text-accent-content:hover{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity,1)))}.hover\:underline:hover{text-decoration-line:underline}.hover\:no-underline:hover{text-decoration-line:none}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-blue-500\/50:hover{--tw-shadow-color:rgba(59,130,246,.5);--tw-shadow:var(--tw-shadow-colored)}.focus\:border-primary:focus{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity,1)))}.focus\:bg-base-100:focus{--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity,1)))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:inline{display:inline}.sm\:w-1\/12{width:8.333333%}.sm\:w-2\/12{width:16.666667%}.sm\:w-6\/12{width:50%}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}}@media (min-width:768px){.md\:h-64{height:16rem}.md\:min-h-64{min-height:16rem}.md\:w-1\/12{width:8.333333%}.md\:w-2\/12{width:16.666667%}.md\:w-2\/3{width:66.666667%}.md\:w-3\/12{width:25%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-end{align-items:flex-end}.md\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.md\:text-end{text-align:end}}@media (min-width:1024px){.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-1\/12{width:8.333333%}.lg\:w-1\/2{width:50%}.lg\:w-2\/12{width:16.666667%}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:flex-row-reverse{flex-direction:row-reverse}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.lg\:text-left{text-align:left}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}} \ No newline at end of file +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* +! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Inter var, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} + +:root, +[data-theme] { + background-color: var(--fallback-b1,oklch(var(--b1)/1)); + color: var(--fallback-bc,oklch(var(--bc)/1)); +} + +@supports not (color: oklch(0 0 0)) { + :root { + color-scheme: light; + --fallback-p: #491eff; + --fallback-pc: #d4dbff; + --fallback-s: #ff41c7; + --fallback-sc: #fff9fc; + --fallback-a: #00cfbd; + --fallback-ac: #00100d; + --fallback-n: #2b3440; + --fallback-nc: #d7dde4; + --fallback-b1: #ffffff; + --fallback-b2: #e5e6e6; + --fallback-b3: #e5e6e6; + --fallback-bc: #1f2937; + --fallback-in: #00b3f0; + --fallback-inc: #000000; + --fallback-su: #00ca92; + --fallback-suc: #000000; + --fallback-wa: #ffc22d; + --fallback-wac: #000000; + --fallback-er: #ff6f70; + --fallback-erc: #000000; + } + + @media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + --fallback-p: #7582ff; + --fallback-pc: #050617; + --fallback-s: #ff71cf; + --fallback-sc: #190211; + --fallback-a: #00c7b5; + --fallback-ac: #000e0c; + --fallback-n: #2a323c; + --fallback-nc: #a6adbb; + --fallback-b1: #1d232a; + --fallback-b2: #191e24; + --fallback-b3: #15191e; + --fallback-bc: #a6adbb; + --fallback-in: #00b3f0; + --fallback-inc: #000000; + --fallback-su: #00ca92; + --fallback-suc: #000000; + --fallback-wa: #ffc22d; + --fallback-wac: #000000; + --fallback-er: #ff6f70; + --fallback-erc: #000000; + } + } +} + +html { + -webkit-tap-highlight-color: transparent; +} + +:root { + color-scheme: light; + --in: 0.7206 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 0.8471 0.199 83.87; + --er: 0.7176 0.221 22.18; + --pc: 0.89824 0.06192 275.75; + --ac: 0.15352 0.0368 183.61; + --inc: 0 0 0; + --suc: 0 0 0; + --wac: 0 0 0; + --erc: 0 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 0.4912 0.3096 275.75; + --s: 0.6971 0.329 342.55; + --sc: 0.9871 0.0106 342.55; + --a: 0.7676 0.184 183.61; + --n: 0.321785 0.02476 255.701624; + --nc: 0.894994 0.011585 252.096176; + --b1: 1 0 0; + --b2: 0.961151 0 0; + --b3: 0.924169 0.00108 197.137559; + --bc: 0.278078 0.029596 256.847952; +} + +@media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + --in: 0.7206 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 0.8471 0.199 83.87; + --er: 0.7176 0.221 22.18; + --pc: 0.13138 0.0392 275.75; + --sc: 0.1496 0.052 342.55; + --ac: 0.14902 0.0334 183.61; + --inc: 0 0 0; + --suc: 0 0 0; + --wac: 0 0 0; + --erc: 0 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 0.6569 0.196 275.75; + --s: 0.748 0.26 342.55; + --a: 0.7451 0.167 183.61; + --n: 0.313815 0.021108 254.139175; + --nc: 0.746477 0.0216 264.435964; + --b1: 0.253267 0.015896 252.417568; + --b2: 0.232607 0.013807 253.100675; + --b3: 0.211484 0.01165 254.087939; + --bc: 0.746477 0.0216 264.435964; + } +} + +[data-theme=light] { + color-scheme: light; + --in: 0.7206 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 0.8471 0.199 83.87; + --er: 0.7176 0.221 22.18; + --pc: 0.89824 0.06192 275.75; + --ac: 0.15352 0.0368 183.61; + --inc: 0 0 0; + --suc: 0 0 0; + --wac: 0 0 0; + --erc: 0 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 0.4912 0.3096 275.75; + --s: 0.6971 0.329 342.55; + --sc: 0.9871 0.0106 342.55; + --a: 0.7676 0.184 183.61; + --n: 0.321785 0.02476 255.701624; + --nc: 0.894994 0.011585 252.096176; + --b1: 1 0 0; + --b2: 0.961151 0 0; + --b3: 0.924169 0.00108 197.137559; + --bc: 0.278078 0.029596 256.847952; +} + +[data-theme=dark] { + color-scheme: dark; + --in: 0.7206 0.191 231.6; + --su: 64.8% 0.150 160; + --wa: 0.8471 0.199 83.87; + --er: 0.7176 0.221 22.18; + --pc: 0.13138 0.0392 275.75; + --sc: 0.1496 0.052 342.55; + --ac: 0.14902 0.0334 183.61; + --inc: 0 0 0; + --suc: 0 0 0; + --wac: 0 0 0; + --erc: 0 0 0; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: .2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; + --p: 0.6569 0.196 275.75; + --s: 0.748 0.26 342.55; + --a: 0.7451 0.167 183.61; + --n: 0.313815 0.021108 254.139175; + --nc: 0.746477 0.0216 264.435964; + --b1: 0.253267 0.015896 252.417568; + --b2: 0.232607 0.013807 253.100675; + --b3: 0.211484 0.01165 254.087939; + --bc: 0.746477 0.0216 264.435964; +} + +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +.\!container { + width: 100% !important; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .\!container { + max-width: 640px !important; + } + + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .\!container { + max-width: 768px !important; + } + + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .\!container { + max-width: 1024px !important; + } + + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .\!container { + max-width: 1280px !important; + } + + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .\!container { + max-width: 1536px !important; + } + + .container { + max-width: 1536px; + } +} + +.alert { + display: grid; + width: 100%; + grid-auto-flow: row; + align-content: flex-start; + align-items: center; + justify-items: center; + gap: 1rem; + text-align: center; + border-radius: var(--rounded-box, 1rem); + border-width: 1px; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + padding: 1rem; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --alert-bg: var(--fallback-b2,oklch(var(--b2)/1)); + --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1)); + background-color: var(--alert-bg); +} + +@media (min-width: 640px) { + .alert { + grid-auto-flow: column; + grid-template-columns: auto minmax(auto,1fr); + justify-items: start; + text-align: start; + } +} + +.avatar.placeholder > div { + display: flex; + align-items: center; + justify-content: center; +} + +.badge { + display: inline-flex; + align-items: center; + justify-content: center; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; + height: 1.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + width: -moz-fit-content; + width: fit-content; + padding-left: 0.563rem; + padding-right: 0.563rem; + border-radius: var(--rounded-badge, 1.9rem); + border-width: 1px; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +@media (hover:hover) { + .label a:hover { + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + } + + .menu li > *:not(ul, .menu-title, details, .btn):active, +.menu li > *:not(ul, .menu-title, details, .btn).active, +.menu li > details > summary:active { + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); + } + + .radio-primary:hover { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + } + + .tab:hover { + --tw-text-opacity: 1; + } + + .tabs-boxed .tab-active:not(.tab-disabled):not([disabled]):hover, + .tabs-boxed :is(input:checked):hover { + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); + } + + .table tr.hover:hover, + .table tr.hover:nth-child(even):hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + } + + .table-zebra tr.hover:hover, + .table-zebra tr.hover:nth-child(even):hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); + } +} + +.btn { + display: inline-flex; + height: 3rem; + min-height: 3rem; + flex-shrink: 0; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; + border-radius: var(--rounded-btn, 0.5rem); + border-color: transparent; + border-color: oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity)); + padding-left: 1rem; + padding-right: 1rem; + text-align: center; + font-size: 0.875rem; + line-height: 1em; + gap: 0.5rem; + font-weight: 600; + text-decoration-line: none; + transition-duration: 200ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + border-width: var(--border-btn, 1px); + animation: button-pop var(--animation-btn, 0.25s) ease-out; + transition-property: color, background-color, border-color, opacity, box-shadow, transform; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline-color: var(--fallback-bc,oklch(var(--bc)/1)); + background-color: oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity)); + --tw-bg-opacity: 1; + --tw-border-opacity: 1; +} + +.btn-disabled, + .btn[disabled], + .btn:disabled { + pointer-events: none; +} + +:where(.btn:is(input[type="checkbox"])), +:where(.btn:is(input[type="radio"])) { + width: auto; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.btn:is(input[type="checkbox"]):after, +.btn:is(input[type="radio"]):after { + --tw-content: attr(aria-label); + content: var(--tw-content); +} + +.card { + position: relative; + display: flex; + flex-direction: column; + border-radius: var(--rounded-box, 1rem); +} + +.card:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.card-body { + display: flex; + flex: 1 1 auto; + flex-direction: column; + padding: var(--padding-card, 2rem); + gap: 0.5rem; +} + +.card-body :where(p) { + flex-grow: 1; +} + +.card-actions { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 0.5rem; +} + +.card figure { + display: flex; + align-items: center; + justify-content: center; +} + +.card.image-full { + display: grid; +} + +.card.image-full:before { + position: relative; + content: ""; + z-index: 10; + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + opacity: 0.75; +} + +.card.image-full:before, + .card.image-full > * { + grid-column-start: 1; + grid-row-start: 1; +} + +.card.image-full > figure img { + height: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +.card.image-full > .card-body { + position: relative; + z-index: 20; + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); +} + +.checkbox { + flex-shrink: 0; + --chkbg: var(--fallback-bc,oklch(var(--bc)/1)); + --chkfg: var(--fallback-b1,oklch(var(--b1)/1)); + height: 1.5rem; + width: 1.5rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: var(--rounded-btn, 0.5rem); + border-width: 1px; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 0.2; +} + +.divider { + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; + margin-top: 1rem; + margin-bottom: 1rem; + height: 1rem; + white-space: nowrap; +} + +.divider:before, + .divider:after { + height: 0.125rem; + width: 100%; + flex-grow: 1; + --tw-content: ''; + content: var(--tw-content); + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); +} + +.\!drawer { + position: relative !important; + display: grid !important; + grid-auto-columns: max-content auto !important; + width: 100% !important; +} + +.drawer { + position: relative; + display: grid; + grid-auto-columns: max-content auto; + width: 100%; +} + +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown > *:not(summary):focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.dropdown .dropdown-content { + position: absolute; +} + +.dropdown:is(:not(details)) .dropdown-content { + visibility: hidden; + opacity: 0; + transform-origin: top; + --tw-scale-x: .95; + --tw-scale-y: .95; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; +} + +.dropdown-end .dropdown-content { + inset-inline-end: 0px; +} + +.dropdown-left .dropdown-content { + bottom: auto; + inset-inline-end: 100%; + top: 0px; + transform-origin: right; +} + +.dropdown-right .dropdown-content { + bottom: auto; + inset-inline-start: 100%; + top: 0px; + transform-origin: left; +} + +.dropdown-bottom .dropdown-content { + bottom: auto; + top: 100%; + transform-origin: top; +} + +.dropdown-top .dropdown-content { + bottom: 100%; + top: auto; + transform-origin: bottom; +} + +.dropdown-end.dropdown-right .dropdown-content { + bottom: 0px; + top: auto; +} + +.dropdown-end.dropdown-left .dropdown-content { + bottom: 0px; + top: auto; +} + +.dropdown.dropdown-open .dropdown-content, +.dropdown:not(.dropdown-hover):focus .dropdown-content, +.dropdown:focus-within .dropdown-content { + visibility: visible; + opacity: 1; +} + +@media (hover: hover) { + .dropdown.dropdown-hover:hover .dropdown-content { + visibility: visible; + opacity: 1; + } + + .btm-nav > *.disabled:hover, + .btm-nav > *[disabled]:hover { + pointer-events: none; + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; + } + + .btn:hover { + --tw-border-opacity: 1; + border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn:hover { + background-color: color-mix( + in oklab, + oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, + black + ); + border-color: color-mix( + in oklab, + oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, + black + ); + } + } + + @supports not (color: oklch(0 0 0)) { + .btn:hover { + background-color: var(--btn-color, var(--fallback-b2)); + border-color: var(--btn-color, var(--fallback-b2)); + } + } + + .btn.glass:hover { + --glass-opacity: 25%; + --glass-border-opacity: 15%; + } + + .btn-ghost:hover { + border-color: transparent; + } + + @supports (color: oklch(0 0 0)) { + .btn-ghost:hover { + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + } + } + + .btn-outline:hover { + --tw-border-opacity: 1; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity))); + } + + .btn-outline.btn-primary:hover { + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-primary:hover { + background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + } + } + + .btn-outline.btn-secondary:hover { + --tw-text-opacity: 1; + color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-secondary:hover { + background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); + } + } + + .btn-outline.btn-accent:hover { + --tw-text-opacity: 1; + color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-accent:hover { + background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); + } + } + + .btn-outline.btn-success:hover { + --tw-text-opacity: 1; + color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-success:hover { + background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); + } + } + + .btn-outline.btn-info:hover { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-info:hover { + background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + } + } + + .btn-outline.btn-warning:hover { + --tw-text-opacity: 1; + color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-warning:hover { + background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); + } + } + + .btn-outline.btn-error:hover { + --tw-text-opacity: 1; + color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn-outline.btn-error:hover { + background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); + } + } + + .btn-disabled:hover, + .btn[disabled]:hover, + .btn:disabled:hover { + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.2; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; + } + + @supports (color: color-mix(in oklab, black, black)) { + .btn:is(input[type="checkbox"]:checked):hover, .btn:is(input[type="radio"]:checked):hover { + background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + } + } + + .dropdown.dropdown-hover:hover .dropdown-content { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { + cursor: pointer; + outline: 2px solid transparent; + outline-offset: 2px; + } + + @supports (color: oklch(0 0 0)) { + :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); + } + } + + .tab[disabled], + .tab[disabled]:hover { + cursor: not-allowed; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; + } +} + +.dropdown:is(details) summary::-webkit-details-marker { + display: none; +} + +.file-input { + height: 3rem; + flex-shrink: 1; + padding-inline-end: 1rem; + font-size: 1rem; + line-height: 2; + line-height: 1.5rem; + overflow: hidden; + border-radius: var(--rounded-btn, 0.5rem); + border-width: 1px; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 0; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.file-input::file-selector-button { + margin-inline-end: 1rem; + display: inline-flex; + height: 100%; + flex-shrink: 0; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; + padding-left: 1rem; + padding-right: 1rem; + text-align: center; + font-size: 0.875rem; + line-height: 1.25rem; + line-height: 1em; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; + border-style: solid; + --tw-border-opacity: 1; + border-color: var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + font-weight: 600; + text-transform: uppercase; + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); + text-decoration-line: none; + border-width: var(--border-btn, 1px); + animation: button-pop var(--animation-btn, 0.25s) ease-out; +} + +.footer { + display: grid; + width: 100%; + grid-auto-flow: row; + place-items: start; + -moz-column-gap: 1rem; + column-gap: 1rem; + row-gap: 2.5rem; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.footer > * { + display: grid; + place-items: start; + gap: 0.5rem; +} + +@media (min-width: 48rem) { + .footer { + grid-auto-flow: column; + } + + .footer-center { + grid-auto-flow: row dense; + } +} + +.form-control { + display: flex; + flex-direction: column; +} + +.label { + display: flex; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + align-items: center; + justify-content: space-between; + padding-left: 0.25rem; + padding-right: 0.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.hero { + display: grid; + width: 100%; + place-items: center; + background-size: cover; + background-position: center; +} + +.hero > * { + grid-column-start: 1; + grid-row-start: 1; +} + +.hero-content { + z-index: 0; + display: flex; + align-items: center; + justify-content: center; + max-width: 80rem; + gap: 1rem; + padding: 1rem; +} + +.indicator { + position: relative; + display: inline-flex; + width: -moz-max-content; + width: max-content; +} + +.indicator :where(.indicator-item) { + z-index: 1; + position: absolute; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + white-space: nowrap; +} + +.input { + flex-shrink: 1; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 3rem; + padding-left: 1rem; + padding-right: 1rem; + font-size: 1rem; + line-height: 2; + line-height: 1.5rem; + border-radius: var(--rounded-btn, 0.5rem); + border-width: 1px; + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.input[type="number"]::-webkit-inner-spin-button, +.input-md[type="number"]::-webkit-inner-spin-button { + margin-top: -1rem; + margin-bottom: -1rem; + margin-inline-end: -1rem; +} + +.input-xs[type="number"]::-webkit-inner-spin-button { + margin-top: -0.25rem; + margin-bottom: -0.25rem; + margin-inline-end: -0px; +} + +.input-sm[type="number"]::-webkit-inner-spin-button { + margin-top: 0px; + margin-bottom: 0px; + margin-inline-end: -0px; +} + +.join { + display: inline-flex; + align-items: stretch; + border-radius: var(--rounded-btn, 0.5rem); +} + +.join :where(.join-item) { + border-start-end-radius: 0; + border-end-end-radius: 0; + border-end-start-radius: 0; + border-start-start-radius: 0; +} + +.join .join-item:not(:first-child):not(:last-child), + .join *:not(:first-child):not(:last-child) .join-item { + border-start-end-radius: 0; + border-end-end-radius: 0; + border-end-start-radius: 0; + border-start-start-radius: 0; +} + +.join .join-item:first-child:not(:last-child), + .join *:first-child:not(:last-child) .join-item { + border-start-end-radius: 0; + border-end-end-radius: 0; +} + +.join .dropdown .join-item:first-child:not(:last-child), + .join *:first-child:not(:last-child) .dropdown .join-item { + border-start-end-radius: inherit; + border-end-end-radius: inherit; +} + +.join :where(.join-item:first-child:not(:last-child)), + .join :where(*:first-child:not(:last-child) .join-item) { + border-end-start-radius: inherit; + border-start-start-radius: inherit; +} + +.join .join-item:last-child:not(:first-child), + .join *:last-child:not(:first-child) .join-item { + border-end-start-radius: 0; + border-start-start-radius: 0; +} + +.join :where(.join-item:last-child:not(:first-child)), + .join :where(*:last-child:not(:first-child) .join-item) { + border-start-end-radius: inherit; + border-end-end-radius: inherit; +} + +@supports not selector(:has(*)) { + :where(.join *) { + border-radius: inherit; + } +} + +@supports selector(:has(*)) { + :where(.join *:has(.join-item)) { + border-radius: inherit; + } +} + +.link { + cursor: pointer; + text-decoration-line: underline; +} + +.menu { + display: flex; + flex-direction: column; + flex-wrap: wrap; + font-size: 0.875rem; + line-height: 1.25rem; + padding: 0.5rem; +} + +.menu :where(li ul) { + position: relative; + white-space: nowrap; + margin-inline-start: 1rem; + padding-inline-start: 0.5rem; +} + +.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), .menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + display: grid; + grid-auto-flow: column; + align-content: flex-start; + align-items: center; + gap: 0.5rem; + grid-auto-columns: minmax(auto, max-content) auto max-content; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.menu li.disabled { + cursor: not-allowed; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + color: var(--fallback-bc,oklch(var(--bc)/0.3)); +} + +.menu :where(li > .menu-dropdown:not(.menu-dropdown-show)) { + display: none; +} + +:where(.menu li) { + position: relative; + display: flex; + flex-shrink: 0; + flex-direction: column; + flex-wrap: wrap; + align-items: stretch; +} + +:where(.menu li) .badge { + justify-self: end; +} + +.modal { + pointer-events: none; + position: fixed; + inset: 0px; + margin: 0px; + display: grid; + height: 100%; + max-height: none; + width: 100%; + max-width: none; + justify-items: center; + padding: 0px; + opacity: 0; + overscroll-behavior: contain; + z-index: 999; + background-color: transparent; + color: inherit; + transition-duration: 200ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-property: transform, opacity, visibility; + overflow-y: hidden; +} + +:where(.modal) { + align-items: center; +} + +.modal-box { + max-height: calc(100vh - 5em); + grid-column-start: 1; + grid-row-start: 1; + width: 91.666667%; + max-width: 32rem; + --tw-scale-x: .9; + --tw-scale-y: .9; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-bottom-right-radius: var(--rounded-box, 1rem); + border-bottom-left-radius: var(--rounded-box, 1rem); + border-top-left-radius: var(--rounded-box, 1rem); + border-top-right-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + padding: 1.5rem; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; + box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px; + overflow-y: auto; + overscroll-behavior: contain; +} + +.modal-open, +.modal:target, +.modal-toggle:checked + .modal, +.modal[open] { + pointer-events: auto; + visibility: visible; + opacity: 1; +} + +.modal-action { + display: flex; + margin-top: 1.5rem; + justify-content: flex-end; +} + +.modal-toggle { + position: fixed; + height: 0px; + width: 0px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + opacity: 0; +} + +:root:has(:is(.modal-open, .modal:target, .modal-toggle:checked + .modal, .modal[open])) { + overflow: hidden; +} + +.navbar { + display: flex; + align-items: center; + padding: var(--navbar-padding, 0.5rem); + min-height: 4rem; + width: 100%; +} + +:where(.navbar > *:not(script, style)) { + display: inline-flex; + align-items: center; +} + +.navbar-start { + width: 50%; + justify-content: flex-start; +} + +.navbar-center { + flex-shrink: 0; +} + +.navbar-end { + width: 50%; + justify-content: flex-end; +} + +.progress { + position: relative; + width: 100%; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + overflow: hidden; + height: 0.5rem; + border-radius: var(--rounded-box, 1rem); + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.radio { + flex-shrink: 0; + --chkbg: var(--bc); + height: 1.5rem; + width: 1.5rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 9999px; + border-width: 1px; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 0.2; +} + +.range { + height: 1.5rem; + width: 100%; + cursor: pointer; + -moz-appearance: none; + appearance: none; + -webkit-appearance: none; + --range-shdw: var(--fallback-bc,oklch(var(--bc)/1)); + overflow: hidden; + border-radius: var(--rounded-box, 1rem); + background-color: transparent; +} + +.range:focus { + outline: none; +} + +.select { + display: inline-flex; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 3rem; + min-height: 3rem; + padding-left: 1rem; + padding-right: 2.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + line-height: 2; + border-radius: var(--rounded-btn, 0.5rem); + border-width: 1px; + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), + linear-gradient(135deg, currentColor 50%, transparent 50%); + background-position: calc(100% - 20px) calc(1px + 50%), + calc(100% - 16.1px) calc(1px + 50%); + background-size: 4px 4px, + 4px 4px; + background-repeat: no-repeat; +} + +.select[multiple] { + height: auto; +} + +.stack { + display: inline-grid; + place-items: center; + align-items: flex-end; +} + +.stack > * { + grid-column-start: 1; + grid-row-start: 1; + transform: translateY(10%) scale(0.9); + z-index: 1; + width: 100%; + opacity: 0.6; +} + +.stack > *:nth-child(2) { + transform: translateY(5%) scale(0.95); + z-index: 2; + opacity: 0.8; +} + +.stack > *:nth-child(1) { + transform: translateY(0) scale(1); + z-index: 3; + opacity: 1; +} + +.stats { + display: inline-grid; + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +:where(.stats) { + grid-auto-flow: column; + overflow-x: auto; +} + +.stat { + display: inline-grid; + width: 100%; + grid-template-columns: repeat(1, 1fr); + -moz-column-gap: 1rem; + column-gap: 1rem; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 0.1; + padding-left: 1.5rem; + padding-right: 1.5rem; + padding-top: 1rem; + padding-bottom: 1rem; +} + +.stat-title { + grid-column-start: 1; + white-space: nowrap; + color: var(--fallback-bc,oklch(var(--bc)/0.6)); +} + +.stat-value { + grid-column-start: 1; + white-space: nowrap; + font-size: 2.25rem; + line-height: 2.5rem; + font-weight: 800; +} + +.stat-desc { + grid-column-start: 1; + white-space: nowrap; + font-size: 0.75rem; + line-height: 1rem; + color: var(--fallback-bc,oklch(var(--bc)/0.6)); +} + +.steps .step { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + grid-template-columns: auto; + grid-template-rows: repeat(2, minmax(0, 1fr)); + grid-template-rows: 40px 1fr; + place-items: center; + text-align: center; + min-width: 4rem; +} + +.tabs { + display: grid; + align-items: flex-end; +} + +.tabs-lifted:has(.tab-content[class^="rounded-"]) .tab:first-child:not(.tab-active), + .tabs-lifted:has(.tab-content[class*=" rounded-"]) .tab:first-child:not(.tab-active) { + border-bottom-color: transparent; +} + +.tab { + position: relative; + grid-row-start: 1; + display: inline-flex; + height: 2rem; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; + text-align: center; + font-size: 0.875rem; + line-height: 1.25rem; + line-height: 2; + --tab-padding: 1rem; + --tw-text-opacity: 0.5; + --tab-color: var(--fallback-bc,oklch(var(--bc)/1)); + --tab-bg: var(--fallback-b1,oklch(var(--b1)/1)); + --tab-border-color: var(--fallback-b3,oklch(var(--b3)/1)); + color: var(--tab-color); + padding-inline-start: var(--tab-padding, 1rem); + padding-inline-end: var(--tab-padding, 1rem); +} + +.tab:is(input[type="radio"]) { + width: auto; + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +.tab:is(input[type="radio"]):after { + --tw-content: attr(aria-label); + content: var(--tw-content); +} + +.tab:not(input):empty { + cursor: default; + grid-column-start: span 9999; +} + +:checked + .tab-content:nth-child(2), + .tab-active + .tab-content:nth-child(2) { + border-start-start-radius: 0px; +} + +input.tab:checked + .tab-content, +.tab-active + .tab-content { + display: block; +} + +.table { + position: relative; + width: 100%; + border-radius: var(--rounded-box, 1rem); + text-align: left; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.table :where(.table-pin-rows thead tr) { + position: sticky; + top: 0px; + z-index: 1; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.table :where(.table-pin-rows tfoot tr) { + position: sticky; + bottom: 0px; + z-index: 1; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.table :where(.table-pin-cols tr th) { + position: sticky; + left: 0px; + right: 0px; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.timeline { + position: relative; + display: flex; +} + +:where(.timeline > li) { + position: relative; + display: grid; + flex-shrink: 0; + align-items: center; + 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) + ); +} + +.timeline > li > hr { + width: 100%; + border-width: 0px; +} + +:where(.timeline > li > hr):first-child { + grid-column-start: 1; + grid-row-start: 2; +} + +:where(.timeline > li > hr):last-child { + grid-column-start: 3; + grid-column-end: none; + grid-row-start: 2; + grid-row-end: auto; +} + +.timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 2; + margin: 0.25rem; + align-self: flex-end; + justify-self: center; +} + +.timeline-middle { + grid-column-start: 2; + grid-row-start: 2; +} + +.timeline-end { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; +} + +.toggle { + flex-shrink: 0; + --tglbg: var(--fallback-b1,oklch(var(--b1)/1)); + --handleoffset: 1.5rem; + --handleoffsetcalculator: calc(var(--handleoffset) * -1); + --togglehandleborder: 0 0; + height: 1.5rem; + width: 3rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: var(--rounded-badge, 1.9rem); + border-width: 1px; + border-color: currentColor; + background-color: currentColor; + color: var(--fallback-bc,oklch(var(--bc)/0.5)); + transition: background, + box-shadow var(--animation-input, 0.2s) ease-out; + box-shadow: var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset, + var(--togglehandleborder); +} + +.alert-info { + border-color: var(--fallback-in,oklch(var(--in)/0.2)); + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + --alert-bg: var(--fallback-in,oklch(var(--in)/1)); + --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1)); +} + +.badge-primary { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.badge-secondary { + --tw-border-opacity: 1; + border-color: var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); +} + +.badge-success { + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); +} + +.badge-warning { + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); +} + +.badge-outline { + border-color: currentColor; + --tw-border-opacity: 0.5; + background-color: transparent; + color: currentColor; +} + +.badge-outline.badge-neutral { + --tw-text-opacity: 1; + color: var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity))); +} + +.badge-outline.badge-primary { + --tw-text-opacity: 1; + color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); +} + +.badge-outline.badge-secondary { + --tw-text-opacity: 1; + color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity))); +} + +.badge-outline.badge-accent { + --tw-text-opacity: 1; + color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); +} + +.badge-outline.badge-info { + --tw-text-opacity: 1; + color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); +} + +.badge-outline.badge-success { + --tw-text-opacity: 1; + color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); +} + +.badge-outline.badge-warning { + --tw-text-opacity: 1; + color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity))); +} + +.badge-outline.badge-error { + --tw-text-opacity: 1; + color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); +} + +.btm-nav > *:where(.active) { + border-top-width: 2px; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + +.btm-nav > *.disabled, + .btm-nav > *[disabled] { + pointer-events: none; + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + +.btm-nav > * .label { + font-size: 1rem; + line-height: 1.5rem; +} + +.btn:active:hover, + .btn:active:focus { + animation: button-pop 0s ease-out; + transform: scale(var(--btn-focus-scale, 0.97)); +} + +@supports not (color: oklch(0 0 0)) { + .btn { + background-color: var(--btn-color, var(--fallback-b2)); + border-color: var(--btn-color, var(--fallback-b2)); + } + + .btn-primary { + --btn-color: var(--fallback-p); + } + + .btn-neutral { + --btn-color: var(--fallback-n); + } + + .btn-info { + --btn-color: var(--fallback-in); + } + + .btn-success { + --btn-color: var(--fallback-su); + } + + .btn-warning { + --btn-color: var(--fallback-wa); + } + + .btn-error { + --btn-color: var(--fallback-er); + } +} + +@supports (color: color-mix(in oklab, black, black)) { + .btn-active { + background-color: color-mix( + in oklab, + oklch(var(--btn-color, var(--b3)) / var(--tw-bg-opacity, 1)) 90%, + black + ); + border-color: color-mix( + in oklab, + oklch(var(--btn-color, var(--b3)) / var(--tw-border-opacity, 1)) 90%, + black + ); + } + + .btn-outline.btn-primary.btn-active { + background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); + } + + .btn-outline.btn-secondary.btn-active { + background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); + } + + .btn-outline.btn-accent.btn-active { + background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); + } + + .btn-outline.btn-success.btn-active { + background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); + } + + .btn-outline.btn-info.btn-active { + background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + } + + .btn-outline.btn-warning.btn-active { + background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); + } + + .btn-outline.btn-error.btn-active { + background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); + } +} + +.btn:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; +} + +.btn-primary { + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +@supports (color: oklch(0 0 0)) { + .btn-primary { + --btn-color: var(--p); + } + + .btn-neutral { + --btn-color: var(--n); + } + + .btn-info { + --btn-color: var(--in); + } + + .btn-success { + --btn-color: var(--su); + } + + .btn-warning { + --btn-color: var(--wa); + } + + .btn-error { + --btn-color: var(--er); + } +} + +.btn-neutral { + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); + outline-color: var(--fallback-n,oklch(var(--n)/1)); +} + +.btn-info { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + outline-color: var(--fallback-in,oklch(var(--in)/1)); +} + +.btn-success { + --tw-text-opacity: 1; + color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); + outline-color: var(--fallback-su,oklch(var(--su)/1)); +} + +.btn-warning { + --tw-text-opacity: 1; + color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); + outline-color: var(--fallback-wa,oklch(var(--wa)/1)); +} + +.btn-error { + --tw-text-opacity: 1; + color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); + outline-color: var(--fallback-er,oklch(var(--er)/1)); +} + +.btn.glass { + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline-color: currentColor; +} + +.btn.glass.btn-active { + --glass-opacity: 25%; + --glass-border-opacity: 15%; +} + +.btn-ghost { + border-width: 1px; + border-color: transparent; + background-color: transparent; + color: currentColor; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline-color: currentColor; +} + +.btn-ghost.btn-active { + border-color: transparent; + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.btn-link.btn-active { + border-color: transparent; + background-color: transparent; + text-decoration-line: underline; +} + +.btn-outline { + border-color: currentColor; + background-color: transparent; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.btn-outline.btn-active { + --tw-border-opacity: 1; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity))); +} + +.btn-outline.btn-primary { + --tw-text-opacity: 1; + color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); +} + +.btn-outline.btn-primary.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.btn-outline.btn-secondary { + --tw-text-opacity: 1; + color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity))); +} + +.btn-outline.btn-secondary.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); +} + +.btn-outline.btn-accent { + --tw-text-opacity: 1; + color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); +} + +.btn-outline.btn-accent.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); +} + +.btn-outline.btn-success { + --tw-text-opacity: 1; + color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); +} + +.btn-outline.btn-success.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); +} + +.btn-outline.btn-info { + --tw-text-opacity: 1; + color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); +} + +.btn-outline.btn-info.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); +} + +.btn-outline.btn-warning { + --tw-text-opacity: 1; + color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity))); +} + +.btn-outline.btn-warning.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); +} + +.btn-outline.btn-error { + --tw-text-opacity: 1; + color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); +} + +.btn-outline.btn-error.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); +} + +.btn.btn-disabled, + .btn[disabled], + .btn:disabled { + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.2; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + +.btn:is(input[type="checkbox"]:checked), +.btn:is(input[type="radio"]:checked) { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.btn:is(input[type="checkbox"]:checked):focus-visible, .btn:is(input[type="radio"]:checked):focus-visible { + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +@keyframes button-pop { + 0% { + transform: scale(var(--btn-focus-scale, 0.98)); + } + + 40% { + transform: scale(1.02); + } + + 100% { + transform: scale(1); + } +} + +.card :where(figure:first-child) { + overflow: hidden; + border-start-start-radius: inherit; + border-start-end-radius: inherit; + border-end-start-radius: unset; + border-end-end-radius: unset; +} + +.card :where(figure:last-child) { + overflow: hidden; + border-start-start-radius: unset; + border-start-end-radius: unset; + border-end-start-radius: inherit; + border-end-end-radius: inherit; +} + +.card:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} + +.card.bordered { + border-width: 1px; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); +} + +.card.compact .card-body { + padding: 1rem; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.card-title { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 1.25rem; + line-height: 1.75rem; + font-weight: 600; +} + +.card.image-full :where(figure) { + overflow: hidden; + border-radius: inherit; +} + +.checkbox:focus { + box-shadow: none; +} + +.checkbox:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/1)); +} + +.checkbox:checked, + .checkbox[checked="true"], + .checkbox[aria-checked="true"] { + background-repeat: no-repeat; + animation: checkmark var(--animation-input, 0.2s) ease-out; + background-color: var(--chkbg); + background-image: linear-gradient(-45deg, transparent 65%, var(--chkbg) 65.99%), + linear-gradient(45deg, transparent 75%, var(--chkbg) 75.99%), + linear-gradient(-45deg, var(--chkbg) 40%, transparent 40.99%), + linear-gradient( + 45deg, + var(--chkbg) 30%, + var(--chkfg) 30.99%, + var(--chkfg) 40%, + transparent 40.99% + ), + linear-gradient(-45deg, var(--chkfg) 50%, var(--chkbg) 50.99%); +} + +.checkbox:indeterminate { + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + background-repeat: no-repeat; + animation: checkmark var(--animation-input, 0.2s) ease-out; + background-image: linear-gradient(90deg, transparent 80%, var(--chkbg) 80%), + linear-gradient(-90deg, transparent 80%, var(--chkbg) 80%), + linear-gradient(0deg, var(--chkbg) 43%, var(--chkfg) 43%, var(--chkfg) 57%, var(--chkbg) 57%); +} + +.checkbox:disabled { + cursor: not-allowed; + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + opacity: 0.2; +} + +@keyframes checkmark { + 0% { + background-position-y: 5px; + } + + 50% { + background-position-y: -2px; + } + + 100% { + background-position-y: 0; + } +} + +.divider:not(:empty) { + gap: 1rem; +} + +.drawer-toggle:focus-visible ~ .drawer-content label.drawer-button { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; +} + +.dropdown.dropdown-open .dropdown-content, +.dropdown:focus .dropdown-content, +.dropdown:focus-within .dropdown-content { + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.file-input-bordered { + --tw-border-opacity: 0.2; +} + +.file-input:focus { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.file-input-disabled, + .file-input[disabled] { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + --tw-text-opacity: 0.2; +} + +.file-input-disabled::-moz-placeholder, .file-input[disabled]::-moz-placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.file-input-disabled::placeholder, + .file-input[disabled]::placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.file-input-disabled::file-selector-button, .file-input[disabled]::file-selector-button { + --tw-border-opacity: 0; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-bg-opacity: 0.2; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + +.footer-title { + margin-bottom: 0.5rem; + font-weight: 700; + text-transform: uppercase; + opacity: 0.6; +} + +.label-text { + font-size: 0.875rem; + line-height: 1.25rem; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +.label-text-alt { + font-size: 0.75rem; + line-height: 1rem; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +.input input { + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + background-color: transparent; +} + +.input input:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.input[list]::-webkit-calendar-picker-indicator { + line-height: 1em; +} + +.input-bordered { + border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.input:focus, + .input:focus-within { + box-shadow: none; + border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.input-ghost { + --tw-bg-opacity: 0.05; +} + +.input-ghost:focus, + .input-ghost:focus-within { + --tw-bg-opacity: 1; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + box-shadow: none; +} + +.input-primary { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.input-primary:focus, + .input-primary:focus-within { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.input-error { + --tw-border-opacity: 1; + border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity))); +} + +.input-error:focus, + .input-error:focus-within { + --tw-border-opacity: 1; + border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity))); + outline-color: var(--fallback-er,oklch(var(--er)/1)); +} + +.input-disabled, + .input:disabled, + .input[disabled] { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + color: var(--fallback-bc,oklch(var(--bc)/0.4)); +} + +.input-disabled::-moz-placeholder, .input:disabled::-moz-placeholder, .input[disabled]::-moz-placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.input-disabled::placeholder, + .input:disabled::placeholder, + .input[disabled]::placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.input::-webkit-date-and-time-value { + text-align: inherit; +} + +.join > :where(*:not(:first-child)) { + margin-top: 0px; + margin-bottom: 0px; + margin-inline-start: -1px; +} + +.join-item:focus { + isolation: isolate; +} + +.link-primary { + --tw-text-opacity: 1; + color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); +} + +@supports (color:color-mix(in oklab,black,black)) { + @media (hover:hover) { + .link-primary:hover { + color: color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,black); + } + } +} + +.link:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.link:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} + +.loading { + pointer-events: none; + display: inline-block; + aspect-ratio: 1 / 1; + width: 1.5rem; + background-color: currentColor; + -webkit-mask-size: 100%; + mask-size: 100%; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); +} + +.loading-spinner { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); +} + +.loading-dots { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_qM83%7Banimation:spinner_8HQG 1.05s infinite%7D.spinner_oXPr%7Banimation-delay:.1s%7D.spinner_ZTLf%7Banimation-delay:.2s%7D@keyframes spinner_8HQG%7B0%25,57.14%25%7Banimation-timing-function:cubic-bezier(0.33,.66,.66,1);transform:translate(0)%7D28.57%25%7Banimation-timing-function:cubic-bezier(0.33,0,.66,.33);transform:translateY(-6px)%7D100%25%7Btransform:translate(0)%7D%7D%3C/style%3E%3Ccircle class='spinner_qM83' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_oXPr' cx='12' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_ZTLf' cx='20' cy='12' r='3'/%3E%3C/svg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_qM83%7Banimation:spinner_8HQG 1.05s infinite%7D.spinner_oXPr%7Banimation-delay:.1s%7D.spinner_ZTLf%7Banimation-delay:.2s%7D@keyframes spinner_8HQG%7B0%25,57.14%25%7Banimation-timing-function:cubic-bezier(0.33,.66,.66,1);transform:translate(0)%7D28.57%25%7Banimation-timing-function:cubic-bezier(0.33,0,.66,.33);transform:translateY(-6px)%7D100%25%7Btransform:translate(0)%7D%7D%3C/style%3E%3Ccircle class='spinner_qM83' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_oXPr' cx='12' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_ZTLf' cx='20' cy='12' r='3'/%3E%3C/svg%3E"); +} + +.loading-sm { + width: 1.25rem; +} + +.loading-md { + width: 1.5rem; +} + +.loading-lg { + width: 2.5rem; +} + +:where(.menu li:empty) { + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + opacity: 0.1; + margin: 0.5rem 1rem; + height: 1px; +} + +.menu :where(li ul):before { + position: absolute; + bottom: 0.75rem; + inset-inline-start: 0px; + top: 0.75rem; + width: 1px; + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + opacity: 0.1; + content: ""; +} + +.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), +.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + border-radius: var(--rounded-btn, 0.5rem); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + text-align: start; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 200ms; + text-wrap: balance; +} + +:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):is(summary):not(.active, .btn):focus-visible, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):is(summary):not(.active, .btn):focus-visible { + cursor: pointer; + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.menu li > *:not(ul, .menu-title, details, .btn):active, +.menu li > *:not(ul, .menu-title, details, .btn).active, +.menu li > details > summary:active { + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); +} + +.menu :where(li > details > summary)::-webkit-details-marker { + display: none; +} + +.menu :where(li > details > summary):after, +.menu :where(li > .menu-dropdown-toggle):after { + justify-self: end; + display: block; + margin-top: -0.5rem; + height: 0.5rem; + width: 0.5rem; + transform: rotate(45deg); + transition-property: transform, margin-top; + transition-duration: 0.3s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + content: ""; + transform-origin: 75% 75%; + box-shadow: 2px 2px; + pointer-events: none; +} + +.menu :where(li > details[open] > summary):after, +.menu :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { + transform: rotate(225deg); + margin-top: 0; +} + +.mockup-phone .display { + overflow: hidden; + border-radius: 40px; + margin-top: -25px; +} + +.mockup-browser .mockup-browser-toolbar .input { + position: relative; + margin-left: auto; + margin-right: auto; + display: block; + height: 1.75rem; + width: 24rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + padding-left: 2rem; + direction: ltr; +} + +.mockup-browser .mockup-browser-toolbar .input:before { + content: ""; + position: absolute; + left: 0.5rem; + top: 50%; + aspect-ratio: 1 / 1; + height: 0.75rem; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-radius: 9999px; + border-width: 2px; + border-color: currentColor; + opacity: 0.6; +} + +.mockup-browser .mockup-browser-toolbar .input:after { + content: ""; + position: absolute; + left: 1.25rem; + top: 50%; + height: 0.5rem; + --tw-translate-y: 25%; + --tw-rotate: -45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-radius: 9999px; + border-width: 1px; + border-color: currentColor; + opacity: 0.6; +} + +.modal:not(dialog:not(.modal-open)), + .modal::backdrop { + background-color: #0006; + animation: modal-pop 0.2s ease-out; +} + +.modal-backdrop { + z-index: -1; + grid-column-start: 1; + grid-row-start: 1; + display: grid; + align-self: stretch; + justify-self: stretch; + color: transparent; +} + +.modal-open .modal-box, +.modal-toggle:checked + .modal .modal-box, +.modal:target .modal-box, +.modal[open] .modal-box { + --tw-translate-y: 0px; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.modal-action > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +@keyframes modal-pop { + 0% { + opacity: 0; + } +} + +.progress::-moz-progress-bar { + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); +} + +.progress-primary::-moz-progress-bar { + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); +} + +.progress:indeterminate { + --progress-color: var(--fallback-bc,oklch(var(--bc)/1)); + background-image: repeating-linear-gradient( + 90deg, + var(--progress-color) -1%, + var(--progress-color) 10%, + transparent 10%, + transparent 90% + ); + background-size: 200%; + background-position-x: 15%; + animation: progress-loading 5s ease-in-out infinite; +} + +.progress-primary:indeterminate { + --progress-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.progress::-webkit-progress-bar { + border-radius: var(--rounded-box, 1rem); + background-color: transparent; +} + +.progress::-webkit-progress-value { + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); +} + +.progress-primary::-webkit-progress-value { + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); +} + +.progress:indeterminate::-moz-progress-bar { + background-color: transparent; + background-image: repeating-linear-gradient( + 90deg, + var(--progress-color) -1%, + var(--progress-color) 10%, + transparent 10%, + transparent 90% + ); + background-size: 200%; + background-position-x: 15%; + animation: progress-loading 5s ease-in-out infinite; +} + +@keyframes progress-loading { + 50% { + background-position-x: -115%; + } +} + +.radio:focus { + box-shadow: none; +} + +.radio:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/1)); +} + +.radio:checked, + .radio[aria-checked="true"] { + --tw-bg-opacity: 1; + background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); + background-image: none; + animation: radiomark var(--animation-input, 0.2s) ease-out; + box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; +} + +.radio-primary { + --chkbg: var(--p); + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.radio-primary:focus-visible { + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.radio-primary:checked, + .radio-primary[aria-checked="true"] { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.radio:disabled { + cursor: not-allowed; + opacity: 0.2; +} + +@keyframes radiomark { + 0% { + box-shadow: 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } + + 50% { + box-shadow: 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } + + 100% { + box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, + 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; + } +} + +.range:focus-visible::-webkit-slider-thumb { + --focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 2rem var(--range-shdw) inset; +} + +.range:focus-visible::-moz-range-thumb { + --focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 2rem var(--range-shdw) inset; +} + +.range::-webkit-slider-runnable-track { + height: 0.5rem; + width: 100%; + border-radius: var(--rounded-box, 1rem); + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); +} + +.range::-moz-range-track { + height: 0.5rem; + width: 100%; + border-radius: var(--rounded-box, 1rem); + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); +} + +.range::-webkit-slider-thumb { + position: relative; + height: 1.5rem; + width: 1.5rem; + border-radius: var(--rounded-box, 1rem); + border-style: none; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + appearance: none; + -webkit-appearance: none; + top: 50%; + color: var(--range-shdw); + transform: translateY(-50%); + --filler-size: 100rem; + --filler-offset: 0.6rem; + box-shadow: 0 0 0 3px var(--range-shdw) inset, + var(--focus-shadow, 0 0), + calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size); +} + +.range::-moz-range-thumb { + position: relative; + height: 1.5rem; + width: 1.5rem; + border-radius: var(--rounded-box, 1rem); + border-style: none; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + top: 50%; + color: var(--range-shdw); + --filler-size: 100rem; + --filler-offset: 0.5rem; + box-shadow: 0 0 0 3px var(--range-shdw) inset, + var(--focus-shadow, 0 0), + calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size); +} + +@keyframes rating-pop { + 0% { + transform: translateY(-0.125em); + } + + 40% { + transform: translateY(-0.125em); + } + + 100% { + transform: translateY(0); + } +} + +.select-bordered { + border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.select:focus { + box-shadow: none; + border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.select-disabled, + .select:disabled, + .select[disabled] { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + +.select-disabled::-moz-placeholder, .select:disabled::-moz-placeholder, .select[disabled]::-moz-placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.select-disabled::placeholder, + .select:disabled::placeholder, + .select[disabled]::placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.select-multiple, + .select[multiple], + .select[size].select:not([size="1"]) { + background-image: none; + padding-right: 1rem; +} + +[dir="rtl"] .select { + background-position: calc(0% + 12px) calc(1px + 50%), + calc(0% + 16px) calc(1px + 50%); +} + +@keyframes skeleton { + from { + background-position: 150%; + } + + to { + background-position: -50%; + } +} + +:where(.stats) > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(1px * var(--tw-divide-x-reverse)); + border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); + --tw-divide-y-reverse: 0; + border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); +} + +:is([dir="rtl"] .stats > :not([hidden]) ~ :not([hidden])) { + --tw-divide-x-reverse: 1; +} + +.steps .step:before { + top: 0px; + grid-column-start: 1; + grid-row-start: 1; + height: 0.5rem; + width: 100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + content: ""; + margin-inline-start: -100%; +} + +.steps .step:after { + content: counter(step); + counter-increment: step; + z-index: 1; + position: relative; + grid-column-start: 1; + grid-row-start: 1; + display: grid; + height: 2rem; + width: 2rem; + place-items: center; + place-self: center; + border-radius: 9999px; + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +.steps .step:first-child:before { + content: none; +} + +.steps .step[data-content]:after { + content: attr(data-content); +} + +.tabs-lifted > .tab:focus-visible { + border-end-end-radius: 0; + border-end-start-radius: 0; +} + +.tab.tab-active:not(.tab-disabled):not([disabled]), + .tab:is(input:checked) { + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 1; + --tw-text-opacity: 1; +} + +.tab:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.tab:focus-visible { + outline: 2px solid currentColor; + outline-offset: -5px; +} + +.tab-disabled, + .tab[disabled] { + cursor: not-allowed; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + --tw-text-opacity: 0.2; +} + +.tabs-bordered > .tab { + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + --tw-border-opacity: 0.2; + border-style: solid; + border-bottom-width: calc(var(--tab-border, 1px) + 1px); +} + +.tabs-lifted > .tab { + border: var(--tab-border, 1px) solid transparent; + border-width: 0 0 var(--tab-border, 1px) 0; + border-start-start-radius: var(--tab-radius, 0.5rem); + border-start-end-radius: var(--tab-radius, 0.5rem); + border-bottom-color: var(--tab-border-color); + padding-inline-start: var(--tab-padding, 1rem); + padding-inline-end: var(--tab-padding, 1rem); + padding-top: var(--tab-border, 1px); +} + +.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]), + .tabs-lifted > .tab:is(input:checked) { + background-color: var(--tab-bg); + border-width: var(--tab-border, 1px) var(--tab-border, 1px) 0 var(--tab-border, 1px); + border-inline-start-color: var(--tab-border-color); + border-inline-end-color: var(--tab-border-color); + border-top-color: var(--tab-border-color); + padding-inline-start: calc(var(--tab-padding, 1rem) - var(--tab-border, 1px)); + padding-inline-end: calc(var(--tab-padding, 1rem) - var(--tab-border, 1px)); + padding-bottom: var(--tab-border, 1px); + padding-top: 0; +} + +.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):before, .tabs-lifted > .tab:is(input:checked):before { + z-index: 1; + content: ""; + display: block; + position: absolute; + width: calc(100% + var(--tab-radius, 0.5rem) * 2); + height: var(--tab-radius, 0.5rem); + bottom: 0; + background-size: var(--tab-radius, 0.5rem); + background-position: top left, + top right; + background-repeat: no-repeat; + --tab-grad: calc(69% - var(--tab-border, 1px)); + --radius-start: radial-gradient( + circle at top left, + transparent var(--tab-grad), + var(--tab-border-color) calc(var(--tab-grad) + 0.25px), + var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), + var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px) + ); + --radius-end: radial-gradient( + circle at top right, + transparent var(--tab-grad), + var(--tab-border-color) calc(var(--tab-grad) + 0.25px), + var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), + var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px) + ); + background-image: var(--radius-start), var(--radius-end); +} + +.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before, .tabs-lifted > .tab:is(input:checked):first-child:before { + background-image: var(--radius-end); + background-position: top right; +} + +[dir="rtl"] .tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before, [dir="rtl"] .tabs-lifted > .tab:is(input:checked):first-child:before { + background-image: var(--radius-start); + background-position: top left; +} + +.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before, .tabs-lifted > .tab:is(input:checked):last-child:before { + background-image: var(--radius-start); + background-position: top left; +} + +[dir="rtl"] .tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before, [dir="rtl"] .tabs-lifted > .tab:is(input:checked):last-child:before { + background-image: var(--radius-end); + background-position: top right; +} + +.tabs-lifted + > .tab-active:not(.tab-disabled):not([disabled]) + + .tabs-lifted + .tab-active:not(.tab-disabled):not([disabled]):before, .tabs-lifted > .tab:is(input:checked) + .tabs-lifted .tab:is(input:checked):before { + background-image: var(--radius-end); + background-position: top right; +} + +.tabs-boxed { + border-radius: var(--rounded-btn, 0.5rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + padding: 0.25rem; +} + +.tabs-boxed .tab { + border-radius: var(--rounded-btn, 0.5rem); +} + +.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]), + .tabs-boxed :is(input:checked) { + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +:is([dir="rtl"] .table) { + text-align: right; +} + +.table :where(th, td) { + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + vertical-align: middle; +} + +.table tr.active, + .table tr.active:nth-child(even), + .table-zebra tbody tr:nth-child(even) { + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); +} + +.table-zebra tr.active, + .table-zebra tr.active:nth-child(even), + .table-zebra-zebra tbody tr:nth-child(even) { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); +} + +.table :where(thead, tbody) :where(tr:not(:last-child)), + .table :where(thead, tbody) :where(tr:first-child:last-child) { + border-bottom-width: 1px; + --tw-border-opacity: 1; + border-bottom-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); +} + +.table :where(thead, tfoot) { + white-space: nowrap; + font-size: 0.75rem; + line-height: 1rem; + font-weight: 700; + color: var(--fallback-bc,oklch(var(--bc)/0.6)); +} + +.timeline hr { + height: 0.25rem; +} + +:where(.timeline hr) { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); +} + +:where(.timeline:has(.timeline-middle) hr):first-child { + border-start-end-radius: var(--rounded-badge, 1.9rem); + border-end-end-radius: var(--rounded-badge, 1.9rem); + border-start-start-radius: 0px; + border-end-start-radius: 0px; +} + +:where(.timeline:has(.timeline-middle) hr):last-child { + border-start-start-radius: var(--rounded-badge, 1.9rem); + border-end-start-radius: var(--rounded-badge, 1.9rem); + border-start-end-radius: 0px; + border-end-end-radius: 0px; +} + +:where(.timeline:not(:has(.timeline-middle)) :first-child hr:last-child) { + border-start-start-radius: var(--rounded-badge, 1.9rem); + border-end-start-radius: var(--rounded-badge, 1.9rem); + border-start-end-radius: 0px; + border-end-end-radius: 0px; +} + +:where(.timeline:not(:has(.timeline-middle)) :last-child hr:first-child) { + border-start-end-radius: var(--rounded-badge, 1.9rem); + border-end-end-radius: var(--rounded-badge, 1.9rem); + border-start-start-radius: 0px; + border-end-start-radius: 0px; +} + +.timeline-box { + border-radius: var(--rounded-box, 1rem); + border-width: 1px; + --tw-border-opacity: 1; + border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +@keyframes toast-pop { + 0% { + transform: scale(0.9); + opacity: 0; + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +[dir="rtl"] .toggle { + --handleoffsetcalculator: calc(var(--handleoffset) * 1); +} + +.toggle:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.toggle:hover { + background-color: currentColor; +} + +.toggle:checked, + .toggle[checked="true"], + .toggle[aria-checked="true"] { + background-image: none; + --handleoffsetcalculator: var(--handleoffset); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +[dir="rtl"] .toggle:checked, [dir="rtl"] .toggle[checked="true"], [dir="rtl"] .toggle[aria-checked="true"] { + --handleoffsetcalculator: calc(var(--handleoffset) * -1); +} + +.toggle:indeterminate { + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +[dir="rtl"] .toggle:indeterminate { + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +.toggle:disabled { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + background-color: transparent; + opacity: 0.3; + --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset, + var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset; +} + +.glass, + .glass.btn-active { + border: none; + -webkit-backdrop-filter: blur(var(--glass-blur, 40px)); + backdrop-filter: blur(var(--glass-blur, 40px)); + background-color: transparent; + background-image: linear-gradient( + 135deg, + rgb(255 255 255 / var(--glass-opacity, 30%)) 0%, + rgb(0 0 0 / 0%) 100% + ), + linear-gradient( + var(--glass-reflex-degree, 100deg), + rgb(255 255 255 / var(--glass-reflex-opacity, 10%)) 25%, + rgb(0 0 0 / 0%) 25% + ); + box-shadow: 0 0 0 1px rgb(255 255 255 / var(--glass-border-opacity, 10%)) inset, + 0 0 0 2px rgb(0 0 0 / 5%); + text-shadow: 0 1px rgb(0 0 0 / var(--glass-text-shadow-opacity, 5%)); +} + +@media (hover: hover) { + .glass.btn-active { + border: none; + -webkit-backdrop-filter: blur(var(--glass-blur, 40px)); + backdrop-filter: blur(var(--glass-blur, 40px)); + background-color: transparent; + background-image: linear-gradient( + 135deg, + rgb(255 255 255 / var(--glass-opacity, 30%)) 0%, + rgb(0 0 0 / 0%) 100% + ), + linear-gradient( + var(--glass-reflex-degree, 100deg), + rgb(255 255 255 / var(--glass-reflex-opacity, 10%)) 25%, + rgb(0 0 0 / 0%) 25% + ); + box-shadow: 0 0 0 1px rgb(255 255 255 / var(--glass-border-opacity, 10%)) inset, + 0 0 0 2px rgb(0 0 0 / 5%); + text-shadow: 0 1px rgb(0 0 0 / var(--glass-text-shadow-opacity, 5%)); + } +} + +.badge-xs { + height: 0.75rem; + font-size: 0.75rem; + line-height: .75rem; + padding-left: 0.313rem; + padding-right: 0.313rem; +} + +.badge-sm { + height: 1rem; + font-size: 0.75rem; + line-height: 1rem; + padding-left: 0.438rem; + padding-right: 0.438rem; +} + +.btm-nav-xs > *:where(.active) { + border-top-width: 1px; +} + +.btm-nav-sm > *:where(.active) { + border-top-width: 2px; +} + +.btm-nav-md > *:where(.active) { + border-top-width: 2px; +} + +.btm-nav-lg > *:where(.active) { + border-top-width: 4px; +} + +.btn-xs { + height: 1.5rem; + min-height: 1.5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + font-size: 0.75rem; +} + +.btn-sm { + height: 2rem; + min-height: 2rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + font-size: 0.875rem; +} + +.btn-square:where(.btn-xs) { + height: 1.5rem; + width: 1.5rem; + padding: 0px; +} + +.btn-square:where(.btn-sm) { + height: 2rem; + width: 2rem; + padding: 0px; +} + +.btn-circle:where(.btn-xs) { + height: 1.5rem; + width: 1.5rem; + border-radius: 9999px; + padding: 0px; +} + +.btn-circle:where(.btn-sm) { + height: 2rem; + width: 2rem; + border-radius: 9999px; + padding: 0px; +} + +[type="checkbox"].checkbox-sm { + height: 1.25rem; + width: 1.25rem; +} + +.indicator :where(.indicator-item) { + bottom: auto; + inset-inline-end: 0px; + inset-inline-start: auto; + top: 0px; + --tw-translate-y: -50%; + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +:is([dir="rtl"] .indicator :where(.indicator-item)) { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-start) { + inset-inline-end: auto; + inset-inline-start: 0px; + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +:is([dir="rtl"] .indicator :where(.indicator-item.indicator-start)) { + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-center) { + inset-inline-end: 50%; + inset-inline-start: 50%; + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +:is([dir="rtl"] .indicator :where(.indicator-item.indicator-center)) { + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-end) { + inset-inline-end: 0px; + inset-inline-start: auto; + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +:is([dir="rtl"] .indicator :where(.indicator-item.indicator-end)) { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-bottom) { + bottom: 0px; + top: auto; + --tw-translate-y: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-middle) { + bottom: 50%; + top: 50%; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.indicator :where(.indicator-item.indicator-top) { + bottom: auto; + top: 0px; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.input-xs { + height: 1.5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + font-size: 0.75rem; + line-height: 1rem; + line-height: 1.625; +} + +.input-sm { + height: 2rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + font-size: 0.875rem; + line-height: 2rem; +} + +.join.join-vertical { + flex-direction: column; +} + +.join.join-vertical .join-item:first-child:not(:last-child), + .join.join-vertical *:first-child:not(:last-child) .join-item { + border-end-start-radius: 0; + border-end-end-radius: 0; + border-start-start-radius: inherit; + border-start-end-radius: inherit; +} + +.join.join-vertical .join-item:last-child:not(:first-child), + .join.join-vertical *:last-child:not(:first-child) .join-item { + border-start-start-radius: 0; + border-start-end-radius: 0; + border-end-start-radius: inherit; + border-end-end-radius: inherit; +} + +.join.join-horizontal { + flex-direction: row; +} + +.join.join-horizontal .join-item:first-child:not(:last-child), + .join.join-horizontal *:first-child:not(:last-child) .join-item { + border-end-end-radius: 0; + border-start-end-radius: 0; + border-end-start-radius: inherit; + border-start-start-radius: inherit; +} + +.join.join-horizontal .join-item:last-child:not(:first-child), + .join.join-horizontal *:last-child:not(:first-child) .join-item { + border-end-start-radius: 0; + border-start-start-radius: 0; + border-end-end-radius: inherit; + border-start-end-radius: inherit; +} + +.menu-horizontal { + display: inline-flex; + flex-direction: row; +} + +.menu-horizontal > li:not(.menu-title) > details > ul { + position: absolute; +} + +.select-sm { + height: 2rem; + min-height: 2rem; + padding-left: 0.75rem; + padding-right: 2rem; + font-size: 0.875rem; + line-height: 2rem; +} + +[dir="rtl"] .select-sm { + padding-left: 2rem; + padding-right: 0.75rem; +} + +.stats-vertical { + grid-auto-flow: row; +} + +.steps-horizontal .step { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + grid-template-rows: repeat(2, minmax(0, 1fr)); + place-items: center; + text-align: center; +} + +.steps-vertical .step { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: repeat(1, minmax(0, 1fr)); +} + +.tabs-md :where(.tab) { + height: 2rem; + font-size: 0.875rem; + line-height: 1.25rem; + line-height: 2; + --tab-padding: 1rem; +} + +.tabs-lg :where(.tab) { + height: 3rem; + font-size: 1.125rem; + line-height: 1.75rem; + line-height: 2; + --tab-padding: 1.25rem; +} + +.tabs-sm :where(.tab) { + height: 1.5rem; + font-size: 0.875rem; + line-height: .75rem; + --tab-padding: 0.75rem; +} + +.tabs-xs :where(.tab) { + height: 1.25rem; + font-size: 0.75rem; + line-height: .75rem; + --tab-padding: 0.5rem; +} + +.timeline-vertical { + flex-direction: column; +} + +.timeline-compact .timeline-start, .timeline-horizontal.timeline-compact .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; +} + +.timeline-compact li:has(.timeline-start) .timeline-end, .timeline-horizontal.timeline-compact li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; +} + +.timeline-vertical.timeline-compact > li { + --timeline-col-start: 0; +} + +.timeline-vertical.timeline-compact .timeline-start { + grid-column-start: 3; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 4; + align-self: center; + justify-self: start; +} + +.timeline-vertical.timeline-compact li:has(.timeline-start) .timeline-end { + grid-column-start: auto; + grid-row-start: none; +} + +:where(.timeline-vertical > li) { + --timeline-row-start: minmax(0, 1fr); + --timeline-row-end: minmax(0, 1fr); + justify-items: center; +} + +.timeline-vertical > li > hr { + height: 100%; +} + +:where(.timeline-vertical > li > hr):first-child { + grid-column-start: 2; + grid-row-start: 1; +} + +:where(.timeline-vertical > li > hr):last-child { + grid-column-start: 2; + grid-column-end: auto; + grid-row-start: 3; + grid-row-end: none; +} + +.timeline-vertical .timeline-start { + grid-column-start: 1; + grid-column-end: 2; + grid-row-start: 1; + grid-row-end: 4; + align-self: center; + justify-self: end; +} + +.timeline-vertical .timeline-end { + grid-column-start: 3; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 4; + align-self: center; + justify-self: start; +} + +.timeline-vertical:where(.timeline-snap-icon) > li { + --timeline-col-start: minmax(0, 1fr); + --timeline-row-start: 0.5rem; +} + +.timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 2; + align-self: flex-end; + justify-self: center; +} + +.timeline-horizontal .timeline-end { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + align-self: flex-start; + justify-self: center; +} + +:where(.timeline-snap-icon) > li, +.timeline-horizontal:where(.timeline-snap-icon) > li { + --timeline-col-start: 0.5rem; + --timeline-row-start: minmax(0, 1fr); +} + +.tooltip { + position: relative; + display: inline-block; + --tooltip-offset: calc(100% + 1px + var(--tooltip-tail, 0px)); +} + +.tooltip:before { + position: absolute; + pointer-events: none; + z-index: 1; + content: var(--tw-content); + --tw-content: attr(data-tip); +} + +.tooltip:before, .tooltip-top:before { + transform: translateX(-50%); + top: auto; + left: 50%; + right: auto; + bottom: var(--tooltip-offset); +} + +.tooltip-bottom:before { + transform: translateX(-50%); + top: var(--tooltip-offset); + left: 50%; + right: auto; + bottom: auto; +} + +.card-compact .card-body { + padding: 1rem; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.card-compact .card-title { + margin-bottom: 0.25rem; +} + +.card-normal .card-body { + padding: var(--padding-card, 2rem); + font-size: 1rem; + line-height: 1.5rem; +} + +.card-normal .card-title { + margin-bottom: 0.75rem; +} + +.join.join-vertical > :where(*:not(:first-child)) { + margin-left: 0px; + margin-right: 0px; + margin-top: -1px; +} + +.join.join-horizontal > :where(*:not(:first-child)) { + margin-top: 0px; + margin-bottom: 0px; + margin-inline-start: -1px; +} + +.menu-horizontal > li:not(.menu-title) > details > ul { + margin-inline-start: 0px; + margin-top: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-inline-end: 0.5rem; +} + +.menu-horizontal > li > details > ul:before { + content: none; +} + +:where(.menu-horizontal > li:not(.menu-title) > details > ul) { + border-radius: var(--rounded-box, 1rem); + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.menu-sm :where(li:not(.menu-title) > *:not(ul, details, .menu-title)), .menu-sm :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + border-radius: var(--rounded-btn, 0.5rem); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.menu-sm .menu-title { + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.modal-top :where(.modal-box) { + width: 100%; + max-width: none; + --tw-translate-y: -2.5rem; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-bottom-right-radius: var(--rounded-box, 1rem); + border-bottom-left-radius: var(--rounded-box, 1rem); + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +.modal-middle :where(.modal-box) { + width: 91.666667%; + max-width: 32rem; + --tw-translate-y: 0px; + --tw-scale-x: .9; + --tw-scale-y: .9; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-top-left-radius: var(--rounded-box, 1rem); + border-top-right-radius: var(--rounded-box, 1rem); + border-bottom-right-radius: var(--rounded-box, 1rem); + border-bottom-left-radius: var(--rounded-box, 1rem); +} + +.modal-bottom :where(.modal-box) { + width: 100%; + max-width: none; + --tw-translate-y: 2.5rem; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + border-top-left-radius: var(--rounded-box, 1rem); + border-top-right-radius: var(--rounded-box, 1rem); + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +.stats-vertical > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(0px * var(--tw-divide-x-reverse)); + border-left-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.stats-vertical { + overflow-y: auto; +} + +.steps-horizontal .step { + grid-template-rows: 40px 1fr; + grid-template-columns: auto; + min-width: 4rem; +} + +.steps-horizontal .step:before { + height: 0.5rem; + width: 100%; + --tw-translate-x: 0px; + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + content: ""; + margin-inline-start: -100%; +} + +:is([dir="rtl"] .steps-horizontal .step):before { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.steps-vertical .step { + gap: 0.5rem; + grid-template-columns: 40px 1fr; + grid-template-rows: auto; + min-height: 4rem; + justify-items: start; +} + +.steps-vertical .step:before { + height: 100%; + width: 0.5rem; + --tw-translate-x: -50%; + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + margin-inline-start: 50%; +} + +:is([dir="rtl"] .steps-vertical .step):before { + --tw-translate-x: 50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.timeline-vertical > li > hr { + width: 0.25rem; +} + +:where(.timeline-vertical:has(.timeline-middle) > li > hr):first-child { + border-bottom-right-radius: var(--rounded-badge, 1.9rem); + border-bottom-left-radius: var(--rounded-badge, 1.9rem); + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +:where(.timeline-vertical:has(.timeline-middle) > li > hr):last-child { + border-top-left-radius: var(--rounded-badge, 1.9rem); + border-top-right-radius: var(--rounded-badge, 1.9rem); + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +:where(.timeline-vertical:not(:has(.timeline-middle)) :first-child > hr:last-child) { + border-top-left-radius: var(--rounded-badge, 1.9rem); + border-top-right-radius: var(--rounded-badge, 1.9rem); + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +:where(.timeline-vertical:not(:has(.timeline-middle)) :last-child > hr:first-child) { + border-bottom-right-radius: var(--rounded-badge, 1.9rem); + border-bottom-left-radius: var(--rounded-badge, 1.9rem); + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +:where(.timeline-horizontal:has(.timeline-middle) > li > hr):first-child { + border-start-end-radius: var(--rounded-badge, 1.9rem); + border-end-end-radius: var(--rounded-badge, 1.9rem); + border-start-start-radius: 0px; + border-end-start-radius: 0px; +} + +:where(.timeline-horizontal:has(.timeline-middle) > li > hr):last-child { + border-start-start-radius: var(--rounded-badge, 1.9rem); + border-end-start-radius: var(--rounded-badge, 1.9rem); + border-start-end-radius: 0px; + border-end-end-radius: 0px; +} + +.tooltip { + position: relative; + display: inline-block; + text-align: center; + --tooltip-tail: 0.1875rem; + --tooltip-color: var(--fallback-n,oklch(var(--n)/1)); + --tooltip-text-color: var(--fallback-nc,oklch(var(--nc)/1)); + --tooltip-tail-offset: calc(100% + 0.0625rem - var(--tooltip-tail)); +} + +.tooltip:before, +.tooltip:after { + opacity: 0; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-delay: 100ms; + transition-duration: 200ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.tooltip:after { + position: absolute; + content: ""; + border-style: solid; + border-width: var(--tooltip-tail, 0); + width: 0; + height: 0; + display: block; +} + +.tooltip:before { + max-width: 20rem; + border-radius: 0.25rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + background-color: var(--tooltip-color); + color: var(--tooltip-text-color); + width: -moz-max-content; + width: max-content; +} + +.tooltip.tooltip-open:before { + opacity: 1; + transition-delay: 75ms; +} + +.tooltip.tooltip-open:after { + opacity: 1; + transition-delay: 75ms; +} + +.tooltip:hover:before { + opacity: 1; + transition-delay: 75ms; +} + +.tooltip:hover:after { + opacity: 1; + transition-delay: 75ms; +} + +.tooltip:has(:focus-visible):after, +.tooltip:has(:focus-visible):before { + opacity: 1; + transition-delay: 75ms; +} + +.tooltip:not([data-tip]):hover:before, +.tooltip:not([data-tip]):hover:after { + visibility: hidden; + opacity: 0; +} + +.tooltip:after, .tooltip-top:after { + transform: translateX(-50%); + border-color: var(--tooltip-color) transparent transparent transparent; + top: auto; + left: 50%; + right: auto; + bottom: var(--tooltip-tail-offset); +} + +.tooltip-bottom:after { + transform: translateX(-50%); + border-color: transparent transparent var(--tooltip-color) transparent; + top: var(--tooltip-tail-offset); + left: 50%; + right: auto; + bottom: auto; +} + +.fade-out { + opacity: 0; + transition: opacity 150ms ease-in-out; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.left-2 { + left: 0.5rem; +} + +.right-0 { + right: 0px; +} + +.right-5 { + right: 1.25rem; +} + +.top-0 { + top: 0px; +} + +.top-2 { + top: 0.5rem; +} + +.top-5 { + top: 1.25rem; +} + +.z-0 { + z-index: 0; +} + +.z-10 { + z-index: 10; +} + +.z-50 { + z-index: 50; +} + +.z-\[1\] { + z-index: 1; +} + +.z-\[5000\] { + z-index: 5000; +} + +.z-\[6000\] { + z-index: 6000; +} + +.m-0 { + margin: 0px; +} + +.m-2 { + margin: 0.5rem; +} + +.m-5 { + margin: 1.25rem; +} + +.m-auto { + margin: auto; +} + +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.mx-4 { + margin-left: 1rem; + margin-right: 1rem; +} + +.mx-5 { + margin-left: 1.25rem; + margin-right: 1.25rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-10 { + margin-top: 2.5rem; + margin-bottom: 2.5rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-3 { + margin-top: 0.75rem; + margin-bottom: 0.75rem; +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.my-5 { + margin-top: 1.25rem; + margin-bottom: 1.25rem; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-5 { + margin-bottom: 1.25rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.ml-auto { + margin-left: auto; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-10 { + margin-top: 2.5rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-5 { + margin-top: 1.25rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-8 { + margin-top: 2rem; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.contents { + display: contents; +} + +.hidden { + display: none; +} + +.h-4 { + height: 1rem; +} + +.h-48 { + height: 12rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-96 { + height: 24rem; +} + +.h-\[250px\] { + height: 250px; +} + +.h-\[25rem\] { + height: 25rem; +} + +.h-fit { + height: -moz-fit-content; + height: fit-content; +} + +.h-full { + height: 100%; +} + +.max-h-96 { + max-height: 24rem; +} + +.min-h-80 { + min-height: 20rem; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-1\/2 { + width: 50%; +} + +.w-10\/12 { + width: 83.333333%; +} + +.w-4 { + width: 1rem; +} + +.w-4\/12 { + width: 33.333333%; +} + +.w-5 { + width: 1.25rem; +} + +.w-52 { + width: 13rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-64 { + width: 16rem; +} + +.w-8 { + width: 2rem; +} + +.w-80 { + width: 20rem; +} + +.w-96 { + width: 24rem; +} + +.w-auto { + width: auto; +} + +.w-full { + width: 100%; +} + +.min-w-52 { + min-width: 13rem; +} + +.min-w-full { + min-width: 100%; +} + +.max-w-2xl { + max-width: 42rem; +} + +.max-w-3xl { + max-width: 48rem; +} + +.max-w-5xl { + max-width: 64rem; +} + +.max-w-md { + max-width: 28rem; +} + +.max-w-sm { + max-width: 24rem; +} + +.max-w-xs { + max-width: 20rem; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.shrink-0 { + flex-shrink: 0; +} + +.translate-x-full { + --tw-translate-x: 100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.cursor-not-allowed { + cursor: not-allowed; +} + +.cursor-pointer { + cursor: pointer; +} + +.resize { + resize: both; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-col-reverse { + flex-direction: column-reverse; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-5 { + gap: 1.25rem; +} + +.gap-6 { + gap: 1.5rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(2rem * var(--tw-space-y-reverse)); +} + +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-base-300 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-divide-opacity, 1))); +} + +.justify-self-end { + justify-self: end; +} + +.justify-self-center { + justify-self: center; +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-x-auto { + overflow-x: auto; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-ellipsis { + text-overflow: ellipsis; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-box { + border-radius: var(--rounded-box, 1rem); +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-t-none { + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +.border { + border-width: 1px; +} + +.border-2 { + border-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-dashed { + border-style: dashed; +} + +.border-base-300 { + --tw-border-opacity: 1; + border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity, 1))); +} + +.border-blue-300 { + --tw-border-opacity: 1; + border-color: rgb(147 197 253 / var(--tw-border-opacity, 1)); +} + +.border-error { + --tw-border-opacity: 1; + border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity, 1))); +} + +.border-gray-500 { + --tw-border-opacity: 1; + border-color: rgb(107 114 128 / var(--tw-border-opacity, 1)); +} + +.border-red-300 { + --tw-border-opacity: 1; + border-color: rgb(252 165 165 / var(--tw-border-opacity, 1)); +} + +.border-sky-500 { + --tw-border-opacity: 1; + border-color: rgb(14 165 233 / var(--tw-border-opacity, 1)); +} + +.border-opacity-30 { + --tw-border-opacity: 0.3; +} + +.bg-base-100 { + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); +} + +.bg-base-200 { + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); +} + +.bg-base-300 { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity, 1))); +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1)); +} + +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1)); +} + +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); +} + +.bg-blue-900 { + --tw-bg-opacity: 1; + background-color: rgb(30 58 138 / var(--tw-bg-opacity, 1)); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1)); +} + +.bg-green-50 { + --tw-bg-opacity: 1; + background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity, 1)); +} + +.bg-neutral { + --tw-bg-opacity: 1; + background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity, 1))); +} + +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1)); +} + +.bg-red-50 { + --tw-bg-opacity: 1; + background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); +} + +.bg-secondary-content { + --tw-bg-opacity: 1; + background-color: var(--fallback-sc,oklch(var(--sc)/var(--tw-bg-opacity, 1))); +} + +.stroke-current { + stroke: currentColor; +} + +.stroke-info { + stroke: var(--fallback-in,oklch(var(--in)/1)); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-0 { + padding: 0px; +} + +.p-2 { + padding: 0.5rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-4 { + padding: 1rem; +} + +.p-5 { + padding: 1.25rem; +} + +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-20 { + padding-top: 5rem; + padding-bottom: 5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pl-6 { + padding-left: 1.5rem; +} + +.pr-10 { + padding-right: 2.5rem; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.font-mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.text-5xl { + font-size: 3rem; + line-height: 1; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-black { + font-weight: 900; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.normal-case { + text-transform: none; +} + +.italic { + font-style: italic; +} + +.text-accent { + --tw-text-opacity: 1; + color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity, 1))); +} + +.text-accent-content { + --tw-text-opacity: 1; + color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity, 1))); +} + +.text-base-content { + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity, 1))); +} + +.text-base-content\/60 { + color: var(--fallback-bc,oklch(var(--bc)/0.6)); +} + +.text-base-content\/70 { + color: var(--fallback-bc,oklch(var(--bc)/0.7)); +} + +.text-base-content\/80 { + color: var(--fallback-bc,oklch(var(--bc)/0.8)); +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity, 1)); +} + +.text-blue-700 { + --tw-text-opacity: 1; + color: rgb(29 78 216 / var(--tw-text-opacity, 1)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity, 1)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity, 1)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity, 1)); +} + +.text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity, 1)); +} + +.text-neutral-content { + --tw-text-opacity: 1; + color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity, 1))); +} + +.text-primary { + --tw-text-opacity: 1; + color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity, 1))); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity, 1)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity, 1)); +} + +.text-secondary { + --tw-text-opacity: 1; + color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity, 1))); +} + +.text-success { + --tw-text-opacity: 1; + color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity, 1))); +} + +.text-warning { + --tw-text-opacity: 1; + color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity, 1))); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); +} + +.underline { + text-decoration-line: underline; +} + +.decoration-dotted { + text-decoration-style: dotted; +} + +.placeholder-base-content\/70::-moz-placeholder { + color: var(--fallback-bc,oklch(var(--bc)/0.7)); +} + +.placeholder-base-content\/70::placeholder { + color: var(--fallback-bc,oklch(var(--bc)/0.7)); +} + +.opacity-0 { + opacity: 0; +} + +.opacity-50 { + opacity: 0.5; +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-2xl { + --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-xl { + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.grayscale { + --tw-grayscale: grayscale(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.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, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-shadow { + transition-property: box-shadow; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-200 { + transition-duration: 200ms; +} + +.duration-300 { + transition-duration: 300ms; +} + +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +@tailwind daisyui; + +/* + +@layer components { + .btn-primary { + @apply py-2 px-4 bg-blue-200; + } +} + +*/ + +/* Leaflet Panel Styles */ + +.leaflet-right-panel { + margin-top: 80px; + /* Give space for controls above */ + margin-right: 10px; + transform: none; + transition: right 0.3s ease-in-out; + z-index: 400; + background: white; + border-radius: 4px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); +} + +.leaflet-right-panel.controls-shifted { + right: 310px; +} + +.leaflet-control-button { + background-color: white !important; + color: #374151 !important; +} + +.leaflet-control-button:hover { + background-color: #f3f4f6 !important; +} + +/* Drawer Panel Styles */ + +.leaflet-drawer { + position: absolute; + top: 0; + right: 0; + width: 338px; + height: 100%; + background: rgba(255, 255, 255, 0.5); + transform: translateX(100%); + transition: transform 0.3s ease-in-out; + z-index: 450; + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); +} + +.leaflet-drawer.open { + transform: translateX(0); +} + +/* Controls transition */ + +.leaflet-control-layers, +.leaflet-control-button, +.toggle-panel-button { + transition: right 0.3s ease-in-out; + z-index: 500; +} + +.controls-shifted { + right: 338px !important; +} + +/* Selection Tool Styles */ + +.leaflet-control-custom { + background-color: white; + border-radius: 4px; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); +} + +.leaflet-control-custom:hover { + background-color: #f3f4f6; +} + +#selection-tool-button.active { + background-color: #60a5fa; + color: white; +} + +/* Cancel Selection Button */ + +#cancel-selection-button { + margin-bottom: 1rem; + width: 100%; +} + +@media (hover: hover) { + .hover\:btn-ghost:hover:hover { + border-color: transparent; + } + + @supports (color: oklch(0 0 0)) { + .hover\:btn-ghost:hover:hover { + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + } + } + + .hover\:btn-ghost:hover:hover { + border-color: transparent; + } + + @supports (color: oklch(0 0 0)) { + .hover\:btn-ghost:hover:hover { + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + } + } + + .hover\:btn-info:hover.btn-outline:hover { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .hover\:btn-info:hover.btn-outline:hover { + background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + } + } + + .hover\:btn-info:hover.btn-outline:hover { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + } + + @supports (color: color-mix(in oklab, black, black)) { + .hover\:btn-info:hover.btn-outline:hover { + background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + } + } +} + +@supports not (color: oklch(0 0 0)) { + .hover\:btn-info:hover { + --btn-color: var(--fallback-in); + } +} + +@supports (color: color-mix(in oklab, black, black)) { + .hover\:btn-info:hover.btn-outline.btn-active { + background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); + } +} + +@supports (color: oklch(0 0 0)) { + .hover\:btn-info:hover { + --btn-color: var(--in); + } +} + +.hover\:btn-info:hover { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + outline-color: var(--fallback-in,oklch(var(--in)/1)); +} + +.hover\:btn-ghost:hover { + border-width: 1px; + border-color: transparent; + background-color: transparent; + color: currentColor; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + outline-color: currentColor; +} + +.hover\:btn-ghost:hover.btn-active { + border-color: transparent; + background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.hover\:btn-info:hover.btn-outline { + --tw-text-opacity: 1; + color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); +} + +.hover\:btn-info:hover.btn-outline.btn-active { + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); +} + +.hover\:input-primary:hover { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.hover\:input-primary:hover:focus,.hover\:input-primary:hover:focus-within { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.focus\:input-ghost:focus { + --tw-bg-opacity: 0.05; +} + +.focus\:input-ghost:focus:focus,.focus\:input-ghost:focus:focus-within { + --tw-bg-opacity: 1; + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + box-shadow: none; +} + +@media not all and (min-width: 768px) { + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; + } + + .max-md\:timeline-compact.timeline-vertical > li { + --timeline-col-start: 0; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; + } + + .max-md\:timeline-compact.timeline-vertical > li { + --timeline-col-start: 0; + } + + .max-md\:timeline-compact.timeline-vertical .timeline-start { + grid-column-start: 3; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 4; + align-self: center; + justify-self: start; + } + + .max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal { + --timeline-row-start: 0; + } + + .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { + grid-column-start: 1; + grid-column-end: 4; + grid-row-start: 3; + grid-row-end: 4; + margin: 0.25rem; + align-self: flex-start; + justify-self: center; + } + + .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { + grid-column-start: none; + grid-row-start: auto; + } + + .max-md\:timeline-compact.timeline-vertical > li { + --timeline-col-start: 0; + } + + .max-md\:timeline-compact.timeline-vertical .timeline-start { + grid-column-start: 3; + grid-column-end: 4; + grid-row-start: 1; + grid-row-end: 4; + align-self: center; + justify-self: start; + } + + .max-md\:timeline-compact.timeline-vertical li:has(.timeline-start) .timeline-end { + grid-column-start: auto; + grid-row-start: none; + } +} + +@media (min-width: 1024px) { + .lg\:stats-horizontal { + grid-auto-flow: column; + } + + .lg\:stats-horizontal > :not([hidden]) ~ :not([hidden]) { + --tw-divide-x-reverse: 0; + border-right-width: calc(1px * var(--tw-divide-x-reverse)); + border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); + --tw-divide-y-reverse: 0; + border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); + } + + .lg\:stats-horizontal { + overflow-x: auto; + } + + :is([dir="rtl"] .lg\:stats-horizontal) { + --tw-divide-x-reverse: 1; + } +} + +.last\:border-0:last-child { + border-width: 0px; +} + +.hover\:scale-105:hover { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:cursor-pointer:hover { + cursor: pointer; +} + +.hover\:bg-accent:hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity, 1))); +} + +.hover\:bg-base-200:hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); +} + +.hover\:bg-base-300:hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity, 1))); +} + +.hover\:text-accent-content:hover { + --tw-text-opacity: 1; + color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity, 1))); +} + +.hover\:underline:hover { + text-decoration-line: underline; +} + +.hover\:no-underline:hover { + text-decoration-line: none; +} + +.hover\:shadow-2xl:hover { + --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.hover\:shadow-lg:hover { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.hover\:shadow-blue-500\/50:hover { + --tw-shadow-color: rgb(59 130 246 / 0.5); + --tw-shadow: var(--tw-shadow-colored); +} + +.focus\:border-primary:focus { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity, 1))); +} + +.focus\:bg-base-100:focus { + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); +} + +.group:hover .group-hover\:opacity-100 { + opacity: 1; +} + +@media (min-width: 640px) { + .sm\:inline { + display: inline; + } + + .sm\:w-1\/12 { + width: 8.333333%; + } + + .sm\:w-2\/12 { + width: 16.666667%; + } + + .sm\:w-6\/12 { + width: 50%; + } + + .sm\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sm\:flex-row { + flex-direction: row; + } + + .sm\:items-end { + align-items: flex-end; + } + + .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + + .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } +} + +@media (min-width: 768px) { + .md\:h-64 { + height: 16rem; + } + + .md\:min-h-64 { + min-height: 16rem; + } + + .md\:w-1\/12 { + width: 8.333333%; + } + + .md\:w-2\/12 { + width: 16.666667%; + } + + .md\:w-2\/3 { + width: 66.666667%; + } + + .md\:w-3\/12 { + width: 25%; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:flex-row { + flex-direction: row; + } + + .md\:items-end { + align-items: flex-end; + } + + .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + + .md\:text-end { + text-align: end; + } +} + +@media (min-width: 1024px) { + .lg\:flex { + display: flex; + } + + .lg\:hidden { + display: none; + } + + .lg\:w-1\/12 { + width: 8.333333%; + } + + .lg\:w-1\/2 { + width: 50%; + } + + .lg\:w-2\/12 { + width: 16.666667%; + } + + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:flex-row { + flex-direction: row; + } + + .lg\:flex-row-reverse { + flex-direction: row-reverse; + } + + .lg\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + + .lg\:text-left { + text-align: left; + } +} + +@media (prefers-color-scheme: dark) { + .dark\:bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1)); + } +} diff --git a/app/models/concerns/distanceable.rb b/app/models/concerns/distanceable.rb index 7ddc190d..f9c28477 100644 --- a/app/models/concerns/distanceable.rb +++ b/app/models/concerns/distanceable.rb @@ -50,16 +50,43 @@ module Distanceable return 0 if points.length < 2 - total_meters = points.each_cons(2).sum do |point1, point2| - connection.select_value( - 'SELECT ST_Distance(ST_GeomFromEWKT($1)::geography, ST_GeomFromEWKT($2)::geography)', - nil, - [point1.lonlat, point2.lonlat] - ) - end + # OPTIMIZED: Single SQL query instead of N individual queries + total_meters = calculate_batch_distances(points).sum total_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] end + + # Optimized batch distance calculation using single SQL query + def calculate_batch_distances(points) + return [] if points.length < 2 + + point_pairs = points.each_cons(2).to_a + return [] if point_pairs.empty? + + # Create a VALUES clause with all point pairs + values_clause = point_pairs.map.with_index do |(p1, p2), index| + "(#{index}, ST_GeomFromEWKT('#{p1.lonlat}')::geography, ST_GeomFromEWKT('#{p2.lonlat}')::geography)" + end.join(', ') + + # Single query to calculate all distances + results = connection.execute(<<-SQL.squish) + WITH point_pairs AS ( + SELECT + pair_id, + point1, + point2 + FROM (VALUES #{values_clause}) AS t(pair_id, point1, point2) + ) + SELECT + pair_id, + ST_Distance(point1, point2) as distance_meters + FROM point_pairs + ORDER BY pair_id + SQL + + # Return array of distances in meters + results.map { |row| row['distance_meters'].to_f } + end end def distance_to(other_point, unit = :km) diff --git a/app/models/track.rb b/app/models/track.rb index 9e9724a7..b79de93e 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -25,6 +25,112 @@ class Track < ApplicationRecord .first end + # Optimized SQL segmentation using PostgreSQL window functions + def self.segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: false) + time_threshold_seconds = time_threshold_minutes * 60 + + where_clause = if untracked_only + "WHERE user_id = $1 AND timestamp BETWEEN $2 AND $3 AND track_id IS NULL" + else + "WHERE user_id = $1 AND timestamp BETWEEN $2 AND $3" + end + + sql = <<~SQL + WITH points_with_gaps AS ( + SELECT + id, + timestamp, + lonlat, + LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, + LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, + ST_Distance( + lonlat::geography, + LAG(lonlat) OVER (ORDER BY timestamp)::geography + ) as distance_meters, + (timestamp - LAG(timestamp) OVER (ORDER BY timestamp)) as time_diff_seconds + FROM points + #{where_clause} + ORDER BY timestamp + ), + segment_breaks AS ( + SELECT *, + CASE + WHEN prev_lonlat IS NULL THEN 1 + WHEN time_diff_seconds > $4 THEN 1 + WHEN distance_meters > $5 THEN 1 + ELSE 0 + END as is_break + FROM points_with_gaps + ), + segments AS ( + SELECT *, + SUM(is_break) OVER (ORDER BY timestamp ROWS UNBOUNDED PRECEDING) as segment_id + FROM segment_breaks + ) + SELECT + segment_id, + array_agg(id ORDER BY timestamp) as point_ids, + count(*) as point_count, + min(timestamp) as start_timestamp, + max(timestamp) as end_timestamp, + sum(COALESCE(distance_meters, 0)) as total_distance_meters + FROM segments + GROUP BY segment_id + HAVING count(*) >= 2 + ORDER BY segment_id + SQL + + results = Point.connection.exec_query( + sql, + 'segment_points_in_sql', + [user_id, start_timestamp, end_timestamp, time_threshold_seconds, distance_threshold_meters] + ) + + # Convert results to segment data + segments_data = [] + results.each do |row| + segments_data << { + segment_id: row['segment_id'].to_i, + point_ids: parse_postgres_array(row['point_ids']), + point_count: row['point_count'].to_i, + start_timestamp: row['start_timestamp'].to_i, + end_timestamp: row['end_timestamp'].to_i, + total_distance_meters: row['total_distance_meters'].to_f + } + end + + segments_data + end + + # Get actual Point objects for each segment with pre-calculated distances + def self.get_segments_with_points(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: false) + segments_data = segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: untracked_only) + + # Get all point IDs we need + all_point_ids = segments_data.flat_map { |seg| seg[:point_ids] } + + # Single query to get all points + points_by_id = Point.where(id: all_point_ids).index_by(&:id) + + # Build segments with actual Point objects + segments_data.map do |seg_data| + { + points: seg_data[:point_ids].map { |id| points_by_id[id] }.compact, + pre_calculated_distance: seg_data[:total_distance_meters], + start_timestamp: seg_data[:start_timestamp], + end_timestamp: seg_data[:end_timestamp] + } + end + end + + # Parse PostgreSQL array format like "{1,2,3}" into Ruby array + def self.parse_postgres_array(pg_array_string) + return [] if pg_array_string.nil? || pg_array_string.empty? + + # Remove curly braces and split by comma + pg_array_string.gsub(/[{}]/, '').split(',').map(&:to_i) + end + private def broadcast_track_created diff --git a/app/services/tracks/generator.rb b/app/services/tracks/generator.rb index 9ffcdbb7..f39b9f8b 100644 --- a/app/services/tracks/generator.rb +++ b/app/services/tracks/generator.rb @@ -40,21 +40,32 @@ class Tracks::Generator def call clean_existing_tracks if should_clean_tracks? - points = load_points - Rails.logger.debug "Generator: loaded #{points.size} points for user #{user.id} in #{mode} mode" - return 0 if points.empty? + # Get timestamp range for SQL query + start_timestamp, end_timestamp = get_timestamp_range + + Rails.logger.debug "Generator: querying points for user #{user.id} in #{mode} mode" + + # Use optimized SQL segmentation with pre-calculated distances + untracked_only = (mode == :incremental) + segments = Track.get_segments_with_points( + user.id, + start_timestamp, + end_timestamp, + time_threshold_minutes, + distance_threshold_meters, + untracked_only: untracked_only + ) - segments = split_points_into_segments(points) - Rails.logger.debug "Generator: created #{segments.size} segments" + Rails.logger.debug "Generator: created #{segments.size} segments via SQL" tracks_created = 0 - segments.each do |segment| - track = create_track_from_segment(segment) + segments.each do |segment_data| + track = create_track_from_segment_optimized(segment_data) tracks_created += 1 if track end - Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in #{mode} mode" + Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in optimized #{mode} mode" tracks_created end @@ -99,6 +110,18 @@ class Tracks::Generator user.tracked_points.where(timestamp: day_range).order(:timestamp) end + def create_track_from_segment_optimized(segment_data) + points = segment_data[:points] + pre_calculated_distance = segment_data[:pre_calculated_distance] + + Rails.logger.debug "Generator: processing segment with #{points.size} points" + return unless points.size >= 2 + + track = create_track_from_points_optimized(points, pre_calculated_distance) + Rails.logger.debug "Generator: created track #{track&.id}" + track + end + def create_track_from_segment(segment) Rails.logger.debug "Generator: processing segment with #{segment.size} points" return unless segment.size >= 2 @@ -171,6 +194,31 @@ class Tracks::Generator scope.destroy_all end + # Get timestamp range for SQL query based on mode + def get_timestamp_range + case mode + when :bulk + if start_at && end_at + [start_at.to_i, end_at.to_i] + else + # Get full range for user + first_point = user.tracked_points.order(:timestamp).first + last_point = user.tracked_points.order(:timestamp).last + [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] + end + when :daily + day = start_at&.to_date || Date.current + [day.beginning_of_day.to_i, day.end_of_day.to_i] + when :incremental + # For incremental, we need all untracked points up to end_at + first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first + end_timestamp = end_at ? end_at.to_i : Time.current.to_i + [first_point&.timestamp || 0, end_timestamp] + else + raise ArgumentError, "Unknown mode: #{mode}" + end + end + # Threshold methods from safe_settings def distance_threshold_meters @distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i diff --git a/app/services/tracks/segmentation.rb b/app/services/tracks/segmentation.rb index 57ca3b03..9f0842cc 100644 --- a/app/services/tracks/segmentation.rb +++ b/app/services/tracks/segmentation.rb @@ -86,11 +86,15 @@ module Tracks::Segmentation end def calculate_km_distance_between_points(point1, point2) - lat1, lon1 = point_coordinates(point1) - lat2, lon2 = point_coordinates(point2) - - # Use Geocoder to match behavior with frontend (same library used elsewhere in app) - Geocoder::Calculations.distance_between([lat1, lon1], [lat2, lon2], units: :km) + # OPTIMIZED: Use PostGIS for more accurate distance calculation (same as track distance) + # This maintains consistency with track distance calculations + distance_meters = Point.connection.select_value( + 'SELECT ST_Distance(ST_GeomFromEWKT($1)::geography, ST_GeomFromEWKT($2)::geography)', + nil, + [point1.lonlat, point2.lonlat] + ) + + distance_meters.to_f / 1000.0 # Convert meters to kilometers end def should_finalize_segment?(segment_points, grace_period_minutes = 5) diff --git a/app/services/tracks/track_builder.rb b/app/services/tracks/track_builder.rb index 99830bc1..95bbcae3 100644 --- a/app/services/tracks/track_builder.rb +++ b/app/services/tracks/track_builder.rb @@ -82,6 +82,38 @@ module Tracks::TrackBuilder end end + # Optimized version that uses pre-calculated distance from SQL + def create_track_from_points_optimized(points, pre_calculated_distance) + return nil if points.size < 2 + + track = Track.new( + user_id: user.id, + start_at: Time.zone.at(points.first.timestamp), + end_at: Time.zone.at(points.last.timestamp), + original_path: build_path(points) + ) + + # Use pre-calculated distance from SQL instead of recalculating + track.distance = pre_calculated_distance.round + track.duration = calculate_duration(points) + track.avg_speed = calculate_average_speed(track.distance, track.duration) + + # Calculate elevation statistics (no DB queries needed) + elevation_stats = calculate_elevation_stats(points) + track.elevation_gain = elevation_stats[:gain] + track.elevation_loss = elevation_stats[:loss] + track.elevation_max = elevation_stats[:max] + track.elevation_min = elevation_stats[:min] + + if track.save + Point.where(id: points.map(&:id)).update_all(track_id: track.id) + track + else + Rails.logger.error "Failed to create track for user #{user.id}: #{track.errors.full_messages.join(', ')}" + nil + end + end + def build_path(points) Tracks::BuildPath.new(points).call end diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index a5ab367b..af42647b 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -21,7 +21,9 @@ <% end %> - <%= link_to 'Update stats', update_all_stats_path, data: { turbo_method: :put }, class: 'btn btn-primary mt-5' %> + <% if current_user.active? %> + <%= link_to 'Update stats', update_all_stats_path, data: { turbo_method: :put }, class: 'btn btn-primary mt-5' %> + <% end %>
<% @stats.each do |year, stats| %> @@ -33,7 +35,7 @@ <%= link_to '[Map]', map_url(year_timespan(year)), class: 'underline hover:no-underline' %>
- Last updated: <%= human_date(stats.first.updated_at) %> + Last update: <%= human_date(stats.first.updated_at) %> <%= link_to '🔄', update_year_month_stats_path(year, :all), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %>
diff --git a/config/environments/development.rb b/config/environments/development.rb index c940de0e..a5b3fce4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -3,6 +3,17 @@ require 'active_support/core_ext/integer/time' Rails.application.configure do + unless ENV['SELF_HOSTED'] == 'true' + config.after_initialize do + Bullet.enable = true + Bullet.alert = true + Bullet.bullet_logger = true + Bullet.console = true + Bullet.rails_logger = true + Bullet.add_footer = true + end + end + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded any time diff --git a/config/environments/test.rb b/config/environments/test.rb index e138d076..b4884952 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -8,6 +8,12 @@ require 'active_support/core_ext/integer/time' # and recreated between test runs. Don't rely on the data there! Rails.application.configure do + config.after_initialize do + Bullet.enable = true + Bullet.bullet_logger = true + Bullet.raise = true # raise an error if n+1 query occurs + end + # Settings specified here will take precedence over those in config/application.rb. # While tests run files are not watched, reloading is not necessary. diff --git a/lib/optimized_tracks_v1.rb b/lib/optimized_tracks_v1.rb new file mode 100644 index 00000000..08969aa4 --- /dev/null +++ b/lib/optimized_tracks_v1.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +# Optimization V1: LAG-based distance calculation with Ruby segmentation +# This keeps the existing Ruby segmentation logic but uses PostgreSQL LAG +# for batch distance calculations instead of individual queries + +module OptimizedTracksV1 + extend ActiveSupport::Concern + + module ClassMethods + # V1: Use LAG to get all consecutive distances in a single query + def calculate_all_consecutive_distances(points) + return [] if points.length < 2 + + point_ids = points.map(&:id).join(',') + + results = connection.execute(<<-SQL.squish) + WITH points_with_previous AS ( + SELECT + id, + timestamp, + lonlat, + LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, + LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, + LAG(id) OVER (ORDER BY timestamp) as prev_id + FROM points + WHERE id IN (#{point_ids}) + ) + SELECT + id, + prev_id, + timestamp, + prev_timestamp, + ST_Distance(lonlat::geography, prev_lonlat::geography) as distance_meters, + (timestamp - prev_timestamp) as time_diff_seconds + FROM points_with_previous + WHERE prev_lonlat IS NOT NULL + ORDER BY timestamp + SQL + + # Return hash mapping point_id => {distance_to_previous, time_diff} + distance_map = {} + results.each do |row| + distance_map[row['id'].to_i] = { + distance_meters: row['distance_meters'].to_f, + time_diff_seconds: row['time_diff_seconds'].to_i, + prev_id: row['prev_id'].to_i + } + end + + distance_map + end + + # V1: Optimized total distance using LAG (already exists in distanceable.rb) + def total_distance_lag(points, unit = :m) + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" + end + + return 0 if points.length < 2 + + point_ids = points.map(&:id).join(',') + + distance_in_meters = connection.select_value(<<-SQL.squish) + WITH points_with_previous AS ( + SELECT + lonlat, + LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat + FROM points + WHERE id IN (#{point_ids}) + ) + SELECT COALESCE( + SUM(ST_Distance(lonlat::geography, prev_lonlat::geography)), + 0 + ) + FROM points_with_previous + WHERE prev_lonlat IS NOT NULL + SQL + + distance_in_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] + end + end +end + +# Optimized segmentation module using pre-calculated distances +module OptimizedSegmentationV1 + extend ActiveSupport::Concern + + private + + def split_points_into_segments_v1(points) + return [] if points.empty? + + # V1: Pre-calculate all distances and time diffs in one query + if points.size > 1 + distance_data = Point.calculate_all_consecutive_distances(points) + else + distance_data = {} + end + + segments = [] + current_segment = [] + + points.each do |point| + if current_segment.empty? + # First point always starts a segment + current_segment = [point] + elsif should_start_new_segment_v1?(point, current_segment.last, distance_data) + # Finalize current segment if it has enough points + segments << current_segment if current_segment.size >= 2 + current_segment = [point] + else + current_segment << point + end + end + + # Don't forget the last segment + segments << current_segment if current_segment.size >= 2 + + segments + end + + def should_start_new_segment_v1?(current_point, previous_point, distance_data) + return false if previous_point.nil? + + # Get pre-calculated data for this point + point_data = distance_data[current_point.id] + return false unless point_data + + # Check time threshold + time_threshold_seconds = time_threshold_minutes.to_i * 60 + return true if point_data[:time_diff_seconds] > time_threshold_seconds + + # Check distance threshold + distance_meters = point_data[:distance_meters] + return true if distance_meters > distance_threshold_meters + + false + end +end + +# Add methods to Point class +class Point + extend OptimizedTracksV1::ClassMethods +end \ No newline at end of file diff --git a/lib/optimized_tracks_v2.rb b/lib/optimized_tracks_v2.rb new file mode 100644 index 00000000..03981fe6 --- /dev/null +++ b/lib/optimized_tracks_v2.rb @@ -0,0 +1,291 @@ +# frozen_string_literal: true + +# Optimization V2: Full SQL segmentation using PostgreSQL window functions +# This does both distance calculation AND segmentation entirely in SQL + +module OptimizedTracksV2 + extend ActiveSupport::Concern + + module ClassMethods + # V2: Complete segmentation in SQL using LAG and window functions + def segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) + time_threshold_seconds = time_threshold_minutes * 60 + + sql = <<~SQL + WITH points_with_gaps AS ( + SELECT + id, + timestamp, + lonlat, + LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, + LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, + ST_Distance( + lonlat::geography, + LAG(lonlat) OVER (ORDER BY timestamp)::geography + ) as distance_meters, + (timestamp - LAG(timestamp) OVER (ORDER BY timestamp)) as time_diff_seconds + FROM points + WHERE user_id = $1 + AND timestamp BETWEEN $2 AND $3 + ORDER BY timestamp + ), + segment_breaks AS ( + SELECT *, + CASE + WHEN prev_lonlat IS NULL THEN 1 + WHEN time_diff_seconds > $4 THEN 1 + WHEN distance_meters > $5 THEN 1 + ELSE 0 + END as is_break + FROM points_with_gaps + ), + segments AS ( + SELECT *, + SUM(is_break) OVER (ORDER BY timestamp ROWS UNBOUNDED PRECEDING) as segment_id + FROM segment_breaks + ) + SELECT + segment_id, + array_agg(id ORDER BY timestamp) as point_ids, + count(*) as point_count, + min(timestamp) as start_timestamp, + max(timestamp) as end_timestamp, + sum(COALESCE(distance_meters, 0)) as total_distance_meters + FROM segments + GROUP BY segment_id + HAVING count(*) >= 2 + ORDER BY segment_id + SQL + + results = connection.exec_query( + sql, + 'segment_points_in_sql', + [user_id, start_timestamp, end_timestamp, time_threshold_seconds, distance_threshold_meters] + ) + + # Convert results to segment data + segments_data = [] + results.each do |row| + segments_data << { + segment_id: row['segment_id'].to_i, + point_ids: parse_postgres_array(row['point_ids']), + point_count: row['point_count'].to_i, + start_timestamp: row['start_timestamp'].to_i, + end_timestamp: row['end_timestamp'].to_i, + total_distance_meters: row['total_distance_meters'].to_f + } + end + + segments_data + end + + # V2: Get actual Point objects for each segment + def get_segments_with_points(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) + segments_data = segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) + + # Get all point IDs we need + all_point_ids = segments_data.flat_map { |seg| seg[:point_ids] } + + # Single query to get all points + points_by_id = Point.where(id: all_point_ids).index_by(&:id) + + # Build segments with actual Point objects + segments_data.map do |seg_data| + { + points: seg_data[:point_ids].map { |id| points_by_id[id] }.compact, + pre_calculated_distance: seg_data[:total_distance_meters], + start_timestamp: seg_data[:start_timestamp], + end_timestamp: seg_data[:end_timestamp] + } + end + end + + private + + # Parse PostgreSQL array format like "{1,2,3}" into Ruby array + def parse_postgres_array(pg_array_string) + return [] if pg_array_string.nil? || pg_array_string.empty? + + # Remove curly braces and split by comma + pg_array_string.gsub(/[{}]/, '').split(',').map(&:to_i) + end + end +end + +# Optimized generator using V2 SQL segmentation +class OptimizedTracksGeneratorV2 + attr_reader :user, :start_at, :end_at, :mode + + def initialize(user, start_at: nil, end_at: nil, mode: :bulk) + @user = user + @start_at = start_at + @end_at = end_at + @mode = mode.to_sym + end + + def call + clean_existing_tracks if should_clean_tracks? + + # Get timestamp range for SQL query + start_timestamp, end_timestamp = get_timestamp_range + + Rails.logger.debug "OptimizedGeneratorV2: querying points for user #{user.id} in #{mode} mode" + + # V2: Get segments directly from SQL with pre-calculated distances + segments = Point.get_segments_with_points( + user.id, + start_timestamp, + end_timestamp, + time_threshold_minutes, + distance_threshold_meters + ) + + Rails.logger.debug "OptimizedGeneratorV2: created #{segments.size} segments via SQL" + + tracks_created = 0 + + segments.each do |segment_data| + track = create_track_from_segment_v2(segment_data) + tracks_created += 1 if track + end + + Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in optimized V2 #{mode} mode" + tracks_created + end + + private + + def create_track_from_segment_v2(segment_data) + points = segment_data[:points] + pre_calculated_distance = segment_data[:pre_calculated_distance] + + Rails.logger.debug "OptimizedGeneratorV2: processing segment with #{points.size} points" + return unless points.size >= 2 + + track = Track.new( + user_id: user.id, + start_at: Time.zone.at(points.first.timestamp), + end_at: Time.zone.at(points.last.timestamp), + original_path: build_path(points) + ) + + # V2: Use pre-calculated distance from SQL + track.distance = pre_calculated_distance.round + track.duration = calculate_duration(points) + track.avg_speed = calculate_average_speed(track.distance, track.duration) + + # Calculate elevation statistics (no DB queries needed) + elevation_stats = calculate_elevation_stats(points) + track.elevation_gain = elevation_stats[:gain] + track.elevation_loss = elevation_stats[:loss] + track.elevation_max = elevation_stats[:max] + track.elevation_min = elevation_stats[:min] + + if track.save + Point.where(id: points.map(&:id)).update_all(track_id: track.id) + track + else + Rails.logger.error "Failed to create track for user #{user.id}: #{track.errors.full_messages.join(', ')}" + nil + end + end + + def get_timestamp_range + case mode + when :bulk + if start_at && end_at + [start_at.to_i, end_at.to_i] + else + # Get full range for user + first_point = user.tracked_points.order(:timestamp).first + last_point = user.tracked_points.order(:timestamp).last + [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] + end + when :daily + day = start_at&.to_date || Date.current + [day.beginning_of_day.to_i, day.end_of_day.to_i] + when :incremental + # For incremental, we need all untracked points up to end_at + first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first + end_timestamp = end_at ? end_at.to_i : Time.current.to_i + [first_point&.timestamp || 0, end_timestamp] + end + end + + def should_clean_tracks? + case mode + when :bulk, :daily then true + else false + end + end + + def clean_existing_tracks + case mode + when :bulk + scope = user.tracks + if start_at && end_at + scope = scope.where(start_at: start_at..end_at) + end + scope.destroy_all + when :daily + day = start_at&.to_date || Date.current + range = day.beginning_of_day..day.end_of_day + user.tracks.where(start_at: range).destroy_all + end + end + + # Helper methods (same as original) + def build_path(points) + Tracks::BuildPath.new(points).call + end + + def calculate_duration(points) + points.last.timestamp - points.first.timestamp + end + + def calculate_average_speed(distance_in_meters, duration_seconds) + return 0.0 if duration_seconds <= 0 || distance_in_meters <= 0 + + speed_mps = distance_in_meters.to_f / duration_seconds + (speed_mps * 3.6).round(2) # m/s to km/h + end + + def calculate_elevation_stats(points) + altitudes = points.map(&:altitude).compact + return { gain: 0, loss: 0, max: 0, min: 0 } if altitudes.empty? + + elevation_gain = 0 + elevation_loss = 0 + previous_altitude = altitudes.first + + altitudes[1..].each do |altitude| + diff = altitude - previous_altitude + if diff > 0 + elevation_gain += diff + else + elevation_loss += diff.abs + end + previous_altitude = altitude + end + + { + gain: elevation_gain.round, + loss: elevation_loss.round, + max: altitudes.max, + min: altitudes.min + } + end + + def distance_threshold_meters + @distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i + end + + def time_threshold_minutes + @time_threshold_minutes ||= user.safe_settings.minutes_between_routes.to_i + end +end + +# Add methods to Point class +class Point + extend OptimizedTracksV2::ClassMethods +end \ No newline at end of file diff --git a/lib/results.md b/lib/results.md new file mode 100644 index 00000000..b9d3bcc4 --- /dev/null +++ b/lib/results.md @@ -0,0 +1,122 @@ +## Original + +Generator: created track 227296 +Generated 1437 tracks for user 1 in bulk mode +✅ Generation completed successfully + +============================================================ +📊 BENCHMARK RESULTS +============================================================ +Status: ✅ SUCCESS +Execution Time: 1m 28.5s +Tracks Created: 1437 +Timeframe Coverage: 8.0% of user's total data + +💾 Memory Usage: + Start: 210.9MB + End: 433.2MB + Memory Increase: +222.3MB + +🗄️ Database Performance: + Total Queries: 115920 + Total Query Time: 50453.1ms + Average Query Time: 0.44ms + Slow Queries (>100ms): 63 + 1. 983.24ms - SELECT COUNT(*) FROM "points" WHERE "points"."user_id" = $1 AND "points"."timestamp" BETWEEN $2 A... + 2. 2826.02ms - SELECT "points".* FROM "points" WHERE "points"."user_id" = $1 AND "points"."timestamp" BETWEEN $2... + 3. 217.02ms - UPDATE "points" SET "track_id" = $1 WHERE "points"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, ... + +✔️ Post-Generation Validation: + Points in Timeframe: 111609 + Points with Tracks: 110167 + Points without Tracks: 1442 + Track Records: 1437 + ✅ Data integrity: PASS + +🔍 Performance Analysis: + Speed Rating: 🚀 Excellent (1m 28.5s) + Memory Rating: 🧡 High (433.2MB peak) + Recommendation: Consider database optimization or smaller batch sizes + +🔮 Extrapolation for Full Dataset: + Full Dataset Size: 1,403,662 points + Scaling Factor: 12.6x + Estimated Full Time: 18m 32.8s + Estimated Full Memory: 5447.6MB + +============================================================ +📋 BENCHMARK SUMMARY +============================================================ +⏱️ Total Time: 1m 28.5s +📍 Points Processed: 111,609 +🛤️ Tracks Created: 1437 +🚀 Processing Speed: 1261.4 points/second +📅 Timeframe: 2024-01-01 to 2024-12-31 +👤 User: demo@dawarich.app (ID: 1) +✅ Status: COMPLETED + + +## Iteration 1 + +Generator: created track 244784 +Generated 1435 tracks for user 1 in optimized bulk mode +✅ Generation completed successfully + +============================================================ +📊 BENCHMARK RESULTS +============================================================ +Status: ✅ SUCCESS +Execution Time: 56.4s +Tracks Created: 1435 +Points Processed: 111,609 +Processing Speed: 1978.3 points/second +Average Points/Track: 77.8 +Timeframe Coverage: 8.0% of user's total data + +💾 Memory Usage: + Start: 297.2MB + End: 407.5MB + Memory Increase: +110.3MB + +🗄️ Database Performance: + Total Queries: 7178 + Total Query Time: 44521.33ms + Average Query Time: 6.2ms + Slow Queries (>100ms): 88 + 1. 2338.43ms - WITH points_with_gaps AS ( + SELECT + id, + timestamp, + lonlat, + LAG(lonlat) OVER (ORDE... + 2. 4156.84ms - SELECT "points".* FROM "points" WHERE "points"."id" IN (2163775, 2163776, 2163777, 2163778, 21637... + 3. 298.62ms - UPDATE "points" SET "track_id" = $1 WHERE "points"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, ... + +✔️ Post-Generation Validation: + Points in Timeframe: 111609 + Points with Tracks: 110123 + Points without Tracks: 1486 + Track Records: 1435 + ✅ Data integrity: PASS + +🔍 Performance Analysis: + Speed Rating: 🚀 Excellent (56.4s) + Memory Rating: 🧡 High (407.5MB peak) + Recommendation: Consider database optimization or smaller batch sizes + +🔮 Extrapolation for Full Dataset: + Full Dataset Size: 1,403,662 points + Scaling Factor: 12.6x + Estimated Full Time: 11m 49.5s + Estimated Full Memory: 5125.0MB + +============================================================ +📋 BENCHMARK SUMMARY +============================================================ +⏱️ Total Time: 56.4s +📍 Points Processed: 111,609 +🛤️ Tracks Created: 1435 +🚀 Processing Speed: 1978.3 points/second +📅 Timeframe: 2024-01-01 to 2024-12-31 +👤 User: demo@dawarich.app (ID: 1) +✅ Status: COMPLETED diff --git a/lib/timestamps.rb b/lib/timestamps.rb index ea7358cc..2154a3ef 100644 --- a/lib/timestamps.rb +++ b/lib/timestamps.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true module Timestamps - def self.parse_timestamp(timestamp) begin # if the timestamp is in ISO 8601 format, try to parse it diff --git a/lib/tracks_optimization_benchmark.rb b/lib/tracks_optimization_benchmark.rb new file mode 100644 index 00000000..bac160d5 --- /dev/null +++ b/lib/tracks_optimization_benchmark.rb @@ -0,0 +1,625 @@ +# frozen_string_literal: true + +require_relative 'optimized_tracks_v1' +require_relative 'optimized_tracks_v2' + +# Benchmark script to compare three different track generation approaches: +# - Original: Individual distance queries (current implementation) +# - V1: LAG-based distance pre-calculation with Ruby segmentation +# - V2: Full SQL segmentation with PostgreSQL window functions +# +# Usage: +# rails runner lib/tracks_optimization_benchmark.rb USER_ID START_DATE END_DATE + +class TracksOptimizationBenchmark + attr_reader :user, :start_date, :end_date, :start_timestamp, :end_timestamp + + def initialize(user_id, start_date, end_date) + @user = User.find(user_id) + @start_date = Date.parse(start_date) + @end_date = Date.parse(end_date) + @start_timestamp = @start_date.beginning_of_day.to_i + @end_timestamp = @end_date.end_of_day.to_i + + puts "🔬 Track Generation Optimization Benchmark" + puts "👤 User: #{user.email} (ID: #{user.id})" + puts "📅 Timeframe: #{start_date} to #{end_date}" + + check_data_availability + end + + def run_all_benchmarks + results = {} + + puts "\n" + "=" * 80 + puts "🏃 RUNNING ALL BENCHMARKS" + puts "=" * 80 + + # Test Original approach + puts "\n1️⃣ Testing ORIGINAL approach..." + results[:original] = benchmark_original + + # Test V1 approach + puts "\n2️⃣ Testing V1 (LAG + Ruby) approach..." + results[:v1] = benchmark_v1 + + # Test V2 approach + puts "\n3️⃣ Testing V2 (Full SQL) approach..." + results[:v2] = benchmark_v2 + + # Compare results + puts "\n" + "=" * 80 + puts "📊 PERFORMANCE COMPARISON" + puts "=" * 80 + compare_results(results) + + # Save results to files + save_results_to_files(results) + + results + end + + private + + def check_data_availability + point_count = user.tracked_points.where(timestamp: start_timestamp..end_timestamp).count + existing_tracks = user.tracks.where(start_at: Time.zone.at(start_timestamp)..Time.zone.at(end_timestamp)).count + + puts "📊 Dataset: #{point_count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse} points" + puts "🛤️ Existing tracks: #{existing_tracks}" + + if point_count == 0 + puts "❌ No points found in timeframe" + exit 1 + end + + if point_count > 50000 + puts "⚠️ Large dataset detected. This benchmark may take a while..." + end + end + + def benchmark_original + puts " Using standard Tracks::Generator..." + + # Clean existing tracks + cleanup_tracks + + # Monitor performance + memory_start = get_memory_mb + query_monitor = QueryMonitor.new + query_monitor.start + + start_time = Time.current + + begin + generator = Tracks::Generator.new( + user, + start_at: Time.zone.at(start_timestamp), + end_at: Time.zone.at(end_timestamp), + mode: :bulk + ) + tracks_created = generator.call + success = true + rescue => e + success = false + error = e.message + tracks_created = 0 + end + + end_time = Time.current + memory_end = get_memory_mb + query_monitor.stop + + execution_time = end_time - start_time + + result = { + approach: "Original", + success: success, + error: error, + execution_time: execution_time, + tracks_created: tracks_created, + memory_increase: memory_end - memory_start, + query_count: query_monitor.query_count, + query_time_ms: query_monitor.total_time_ms + } + + print_result(result) + result + end + + def benchmark_v1 + puts " Using V1: LAG + Ruby segmentation..." + + # Clean existing tracks + cleanup_tracks + + # For V1, we need to modify the existing generator to use our optimized methods + # This is a simplified test - in practice we'd modify the actual generator + + memory_start = get_memory_mb + query_monitor = QueryMonitor.new + query_monitor.start + + start_time = Time.current + + begin + # Load points + points = user.tracked_points + .where(timestamp: start_timestamp..end_timestamp) + .order(:timestamp) + + # V1: Use optimized segmentation with pre-calculated distances + if points.size > 1 + distance_data = Point.calculate_all_consecutive_distances(points) + else + distance_data = {} + end + + # Segment using V1 approach (simplified for benchmark) + segments = split_points_with_precalculated_distances(points, distance_data) + + tracks_created = 0 + segments.each do |segment| + if segment.size >= 2 + track = create_track_v1(segment) + tracks_created += 1 if track + end + end + + success = true + rescue => e + success = false + error = e.message + tracks_created = 0 + end + + end_time = Time.current + memory_end = get_memory_mb + query_monitor.stop + + execution_time = end_time - start_time + + result = { + approach: "V1 (LAG + Ruby)", + success: success, + error: error, + execution_time: execution_time, + tracks_created: tracks_created, + memory_increase: memory_end - memory_start, + query_count: query_monitor.query_count, + query_time_ms: query_monitor.total_time_ms + } + + print_result(result) + result + end + + def benchmark_v2 + puts " Using V2: Full SQL segmentation..." + + cleanup_tracks + + memory_start = get_memory_mb + query_monitor = QueryMonitor.new + query_monitor.start + + start_time = Time.current + + begin + generator = OptimizedTracksGeneratorV2.new( + user, + start_at: Time.zone.at(start_timestamp), + end_at: Time.zone.at(end_timestamp), + mode: :bulk + ) + tracks_created = generator.call + success = true + rescue => e + success = false + error = e.message + tracks_created = 0 + end + + end_time = Time.current + memory_end = get_memory_mb + query_monitor.stop + + execution_time = end_time - start_time + + result = { + approach: "V2 (Full SQL)", + success: success, + error: error, + execution_time: execution_time, + tracks_created: tracks_created, + memory_increase: memory_end - memory_start, + query_count: query_monitor.query_count, + query_time_ms: query_monitor.total_time_ms + } + + print_result(result) + result + end + + def split_points_with_precalculated_distances(points, distance_data) + return [] if points.empty? + + segments = [] + current_segment = [] + + points.each do |point| + if current_segment.empty? + current_segment = [point] + elsif should_break_segment_v1?(point, current_segment.last, distance_data) + segments << current_segment if current_segment.size >= 2 + current_segment = [point] + else + current_segment << point + end + end + + segments << current_segment if current_segment.size >= 2 + segments + end + + def should_break_segment_v1?(current_point, previous_point, distance_data) + return false if previous_point.nil? + + point_data = distance_data[current_point.id] + return false unless point_data + + time_threshold_seconds = user.safe_settings.minutes_between_routes.to_i * 60 + distance_threshold_meters = user.safe_settings.meters_between_routes.to_i + + return true if point_data[:time_diff_seconds] > time_threshold_seconds + return true if point_data[:distance_meters] > distance_threshold_meters + + false + end + + def create_track_v1(points) + return nil if points.size < 2 + + track = Track.new( + user_id: user.id, + start_at: Time.zone.at(points.first.timestamp), + end_at: Time.zone.at(points.last.timestamp), + original_path: build_path(points) + ) + + # Use LAG-based distance calculation + track.distance = Point.total_distance_lag(points, :m).round + track.duration = points.last.timestamp - points.first.timestamp + track.avg_speed = calculate_average_speed(track.distance, track.duration) + + # Elevation stats (same as original) + elevation_stats = calculate_elevation_stats(points) + track.elevation_gain = elevation_stats[:gain] + track.elevation_loss = elevation_stats[:loss] + track.elevation_max = elevation_stats[:max] + track.elevation_min = elevation_stats[:min] + + if track.save + Point.where(id: points.map(&:id)).update_all(track_id: track.id) + track + else + nil + end + end + + def cleanup_tracks + user.tracks.where(start_at: Time.zone.at(start_timestamp)..Time.zone.at(end_timestamp)).destroy_all + end + + def print_result(result) + status = result[:success] ? "✅ SUCCESS" : "❌ FAILED" + puts " #{status}" + puts " ⏱️ Time: #{format_duration(result[:execution_time])}" + puts " 🛤️ Tracks: #{result[:tracks_created]}" + puts " 💾 Memory: +#{result[:memory_increase].round(1)}MB" + puts " 🗄️ Queries: #{result[:query_count]} (#{result[:query_time_ms].round(1)}ms)" + puts " ❌ Error: #{result[:error]}" if result[:error] + end + + def compare_results(results) + return unless results[:original] && results[:v1] && results[:v2] + + puts sprintf("%-20s %-10s %-12s %-10s %-15s %-10s", + "Approach", "Time", "Tracks", "Memory", "Queries", "Query Time") + puts "-" * 80 + + [:original, :v1, :v2].each do |approach| + result = results[approach] + next unless result[:success] + + puts sprintf("%-20s %-10s %-12s %-10s %-15s %-10s", + result[:approach], + format_duration(result[:execution_time]), + result[:tracks_created], + "+#{result[:memory_increase].round(1)}MB", + result[:query_count], + "#{result[:query_time_ms].round(1)}ms") + end + + # Calculate improvements + if results[:original][:success] + original_time = results[:original][:execution_time] + original_queries = results[:original][:query_count] + + puts "\n🚀 Performance Improvements vs Original:" + + if results[:v1][:success] + v1_speedup = (original_time / results[:v1][:execution_time]).round(2) + v1_query_reduction = ((original_queries - results[:v1][:query_count]) / original_queries.to_f * 100).round(1) + puts " V1: #{v1_speedup}x faster, #{v1_query_reduction}% fewer queries" + end + + if results[:v2][:success] + v2_speedup = (original_time / results[:v2][:execution_time]).round(2) + v2_query_reduction = ((original_queries - results[:v2][:query_count]) / original_queries.to_f * 100).round(1) + puts " V2: #{v2_speedup}x faster, #{v2_query_reduction}% fewer queries" + end + end + end + + def save_results_to_files(results) + timestamp = Time.current.strftime('%Y%m%d_%H%M%S') + point_count = user.tracked_points.where(timestamp: start_timestamp..end_timestamp).count + + # Create detailed results structure + benchmark_data = { + meta: { + timestamp: Time.current.iso8601, + user_id: user.id, + user_email: user.email, + start_date: start_date.strftime('%Y-%m-%d'), + end_date: end_date.strftime('%Y-%m-%d'), + point_count: point_count, + ruby_version: RUBY_VERSION, + rails_version: Rails.version, + database_adapter: ActiveRecord::Base.connection.adapter_name + }, + results: results, + performance_analysis: analyze_performance_data(results) + } + + # Save JSON results for programmatic analysis + json_filename = "tracks_optimization_#{timestamp}.json" + json_path = Rails.root.join('lib', json_filename) + File.write(json_path, JSON.pretty_generate(benchmark_data)) + + # Save human-readable markdown report + md_filename = "tracks_optimization_#{timestamp}.md" + md_path = Rails.root.join('lib', md_filename) + File.write(md_path, generate_markdown_report(benchmark_data)) + + puts "\n💾 Results saved:" + puts " 📄 JSON: #{json_path}" + puts " 📝 Report: #{md_path}" + end + + def analyze_performance_data(results) + return {} unless results[:original] && results[:original][:success] + + original = results[:original] + analysis = { + baseline: { + execution_time: original[:execution_time], + query_count: original[:query_count], + memory_usage: original[:memory_increase] + } + } + + [:v1, :v2].each do |version| + next unless results[version] && results[version][:success] + + result = results[version] + analysis[version] = { + speedup_factor: (original[:execution_time] / result[:execution_time]).round(2), + query_reduction_percent: ((original[:query_count] - result[:query_count]) / original[:query_count].to_f * 100).round(1), + memory_change_percent: ((result[:memory_increase] - original[:memory_increase]) / original[:memory_increase].to_f * 100).round(1), + execution_time_saved: (original[:execution_time] - result[:execution_time]).round(2) + } + end + + analysis + end + + def generate_markdown_report(benchmark_data) + meta = benchmark_data[:meta] + results = benchmark_data[:results] + analysis = benchmark_data[:performance_analysis] + + report = <<~MD + # Tracks Generation Optimization Benchmark Report + + **Generated:** #{meta[:timestamp]} + **User:** #{meta[:user_email]} (ID: #{meta[:user_id]}) + **Timeframe:** #{meta[:start_date]} to #{meta[:end_date]} + **Dataset:** #{meta[:point_count].to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse} points + **Environment:** Ruby #{meta[:ruby_version]}, Rails #{meta[:rails_version]}, #{meta[:database_adapter]} + + ## Summary + + This benchmark compares three approaches to track generation: + - **Original:** Individual PostGIS queries for each distance calculation + - **V1 (LAG + Ruby):** PostgreSQL LAG for batch distance calculation, Ruby segmentation + - **V2 (Full SQL):** Complete segmentation using PostgreSQL window functions + + ## Results + + | Approach | Status | Time | Tracks | Memory | Queries | Query Time | + |----------|--------|------|--------|--------|---------|------------| + MD + + [:original, :v1, :v2].each do |approach| + next unless results[approach] + + result = results[approach] + status = result[:success] ? "✅" : "❌" + + report += "| #{result[:approach]} | #{status} | #{format_duration(result[:execution_time])} | #{result[:tracks_created]} | +#{result[:memory_increase].round(1)}MB | #{result[:query_count]} | #{result[:query_time_ms].round(1)}ms |\n" + end + + if analysis[:v1] || analysis[:v2] + report += "\n## Performance Improvements\n\n" + + if analysis[:v1] + v1 = analysis[:v1] + report += "### V1 (LAG + Ruby) vs Original\n" + report += "- **#{v1[:speedup_factor]}x faster** execution\n" + report += "- **#{v1[:query_reduction_percent]}% fewer** database queries\n" + report += "- **#{format_duration(v1[:execution_time_saved])} time saved**\n" + report += "- Memory change: #{v1[:memory_change_percent] > 0 ? '+' : ''}#{v1[:memory_change_percent]}%\n\n" + end + + if analysis[:v2] + v2 = analysis[:v2] + report += "### V2 (Full SQL) vs Original\n" + report += "- **#{v2[:speedup_factor]}x faster** execution\n" + report += "- **#{v2[:query_reduction_percent]}% fewer** database queries\n" + report += "- **#{format_duration(v2[:execution_time_saved])} time saved**\n" + report += "- Memory change: #{v2[:memory_change_percent] > 0 ? '+' : ''}#{v2[:memory_change_percent]}%\n\n" + end + end + + # Add detailed results + report += "## Detailed Results\n\n" + + [:original, :v1, :v2].each do |approach| + next unless results[approach] + + result = results[approach] + report += "### #{result[:approach]}\n\n" + + if result[:success] + report += "- ✅ **Status:** Success\n" + report += "- ⏱️ **Execution Time:** #{format_duration(result[:execution_time])}\n" + report += "- 🛤️ **Tracks Created:** #{result[:tracks_created]}\n" + report += "- 💾 **Memory Increase:** +#{result[:memory_increase].round(1)}MB\n" + report += "- 🗄️ **Database Queries:** #{result[:query_count]}\n" + report += "- ⚡ **Query Time:** #{result[:query_time_ms].round(1)}ms\n" + + if result[:query_count] > 0 + avg_query_time = (result[:query_time_ms] / result[:query_count]).round(2) + report += "- 📊 **Average Query Time:** #{avg_query_time}ms\n" + end + else + report += "- ❌ **Status:** Failed\n" + report += "- 🚨 **Error:** #{result[:error]}\n" + end + + report += "\n" + end + + report += "## Recommendations\n\n" + + if analysis[:v2] && analysis[:v2][:speedup_factor] > analysis.dig(:v1, :speedup_factor).to_f + report += "🚀 **V2 (Full SQL)** shows the best performance with #{analysis[:v2][:speedup_factor]}x speedup.\n\n" + report += "Benefits:\n" + report += "- Minimal database queries (#{results.dig(:v2, :query_count)} vs #{results.dig(:original, :query_count)})\n" + report += "- Fastest execution time\n" + report += "- Leverages PostgreSQL's optimized window functions\n\n" + elsif analysis[:v1] + report += "🏃 **V1 (LAG + Ruby)** provides good performance improvements with #{analysis[:v1][:speedup_factor]}x speedup.\n\n" + end + + if results[:original] && results[:original][:query_count] > 50000 + report += "⚠️ **Current implementation** makes excessive database queries (#{results[:original][:query_count]}) for this dataset size.\n\n" + end + + report += "---\n*Generated by TracksOptimizationBenchmark*" + + report + end + + # Helper methods + def get_memory_mb + `ps -o rss= -p #{Process.pid}`.to_i / 1024.0 + end + + def format_duration(seconds) + if seconds < 60 + "#{seconds.round(1)}s" + else + minutes = (seconds / 60).floor + remaining_seconds = (seconds % 60).round(1) + "#{minutes}m #{remaining_seconds}s" + end + end + + def build_path(points) + Tracks::BuildPath.new(points).call + end + + def calculate_average_speed(distance_in_meters, duration_seconds) + return 0.0 if duration_seconds <= 0 || distance_in_meters <= 0 + speed_mps = distance_in_meters.to_f / duration_seconds + (speed_mps * 3.6).round(2) + end + + def calculate_elevation_stats(points) + altitudes = points.map(&:altitude).compact + return { gain: 0, loss: 0, max: 0, min: 0 } if altitudes.empty? + + elevation_gain = 0 + elevation_loss = 0 + previous_altitude = altitudes.first + + altitudes[1..].each do |altitude| + diff = altitude - previous_altitude + if diff > 0 + elevation_gain += diff + else + elevation_loss += diff.abs + end + previous_altitude = altitude + end + + { gain: elevation_gain.round, loss: elevation_loss.round, max: altitudes.max, min: altitudes.min } + end +end + +# Simple query monitor for this benchmark +class QueryMonitor + attr_reader :query_count, :total_time_ms + + def initialize + @query_count = 0 + @total_time_ms = 0 + end + + def start + @subscription = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + next if event.payload[:name]&.include?('SCHEMA') + + @query_count += 1 + @total_time_ms += event.duration + end + end + + def stop + ActiveSupport::Notifications.unsubscribe(@subscription) if @subscription + end +end + +# Command line interface +if __FILE__ == $0 + if ARGV.length < 3 + puts "Usage: rails runner #{__FILE__} USER_ID START_DATE END_DATE" + puts "" + puts "Example:" + puts " rails runner #{__FILE__} 1 2024-01-01 2024-01-31" + exit 1 + end + + user_id = ARGV[0].to_i + start_date = ARGV[1] + end_date = ARGV[2] + + benchmark = TracksOptimizationBenchmark.new(user_id, start_date, end_date) + results = benchmark.run_all_benchmarks + + puts "\n🎉 Benchmark completed! Check results above." +end \ No newline at end of file diff --git a/tracks_performance_optimization_options.md b/tracks_performance_optimization_options.md new file mode 100644 index 00000000..8f7c6601 --- /dev/null +++ b/tracks_performance_optimization_options.md @@ -0,0 +1,235 @@ +# Tracks Feature Performance Optimization Options + +## Current State Analysis + +### Performance Characteristics +- **Time Complexity:** O(n log n) where n = number of GPS points +- **Memory Usage:** Loads entire dataset into memory (~200-400 bytes per point) +- **Processing Mode:** Single-threaded, sequential segmentation +- **Database Load:** Multiple PostGIS distance calculations per point pair + +### Performance Estimates (Bulk Mode) +| Points | Processing Time | Memory Usage | Database Load | +|--------|----------------|--------------|---------------| +| 10K | 30-60 seconds | ~50 MB | Low | +| 100K | 5-15 minutes | ~200 MB | Medium | +| 1M+ | 30-90 minutes | 400+ MB | High | + +### Current Bottlenecks +1. **Memory constraints** - Loading all points at once +2. **PostGIS distance calculations** - Sequential, not optimized +3. **Single-threaded processing** - No parallelization +4. **No progress indication** - Users can't track long-running operations + +--- + +## Optimization Options + +### Option 1: Enhanced Time-Based Batching +**Complexity:** Low | **Impact:** High | **Risk:** Low + +#### Implementation +- Extend existing `:daily` mode with configurable batch sizes +- Add 1-point overlap between batches to maintain segmentation accuracy +- Implement batch-aware progress reporting + +#### Benefits +- **Memory reduction:** 90%+ reduction (from 400MB to ~40MB for 1M points) +- **Better UX:** Progress indication and cancellation support +- **Incremental processing:** Can resume interrupted operations +- **Lower DB pressure:** Smaller query result sets + +#### Changes Required +```ruby +# Enhanced generator with configurable batching +Tracks::Generator.new( + user, + mode: :batched, + batch_size: 24.hours, + enable_overlap: true +).call +``` + +#### Edge Cases to Handle +- Tracks spanning batch boundaries (solved with overlap) +- Midnight-crossing tracks in daily mode +- Deduplication of overlapping segments + +--- + +### Option 2: Spatial Indexing Optimization +**Complexity:** Medium | **Impact:** Medium | **Risk:** Low + +#### Implementation +- Replace individual PostGIS calls with batch distance calculations +- Implement spatial clustering for nearby points before segmentation +- Use PostGIS window functions for distance calculations + +#### Benefits +- **Faster distance calculations:** Batch operations vs individual queries +- **Reduced DB round-trips:** Single query for multiple distance calculations +- **Better index utilization:** Leverage existing spatial indexes + +#### Changes Required +```sql +-- Batch distance calculation approach +WITH point_distances AS ( + SELECT + id, + timestamp, + ST_Distance( + lonlat::geography, + LAG(lonlat::geography) OVER (ORDER BY timestamp) + ) as distance_to_previous + FROM points + WHERE user_id = ? + ORDER BY timestamp +) +SELECT * FROM point_distances WHERE distance_to_previous > ? +``` + +--- + +### Option 3: Parallel Processing with Worker Pools +**Complexity:** High | **Impact:** High | **Risk:** Medium + +#### Implementation +- Split large datasets into non-overlapping time ranges +- Process multiple batches in parallel using Sidekiq workers +- Implement coordination mechanism for dependent segments + +#### Benefits +- **Faster processing:** Utilize multiple CPU cores +- **Scalable:** Performance scales with worker capacity +- **Background processing:** Non-blocking for users + +#### Challenges +- **Complex coordination:** Managing dependencies between batches +- **Resource competition:** Multiple workers accessing same user's data +- **Error handling:** Partial failure scenarios + +#### Architecture +```ruby +# Parallel processing coordinator +class Tracks::ParallelGenerator + def call + time_ranges = split_into_parallel_ranges + + time_ranges.map do |range| + Tracks::BatchProcessorJob.perform_later(user_id, range) + end + end +end +``` + +--- + +### Option 4: Incremental Algorithm Enhancement +**Complexity:** Medium | **Impact:** Medium | **Risk:** Medium + +#### Implementation +- Enhance existing `:incremental` mode with smarter buffering +- Implement sliding window approach for active track detection +- Add automatic track finalization based on time gaps + +#### Benefits +- **Real-time processing:** Process points as they arrive +- **Lower memory footprint:** Only active segments in memory +- **Better for live tracking:** Immediate track updates + +#### Current Limitations +- Existing incremental mode processes untracked points only +- No automatic track finalization +- Limited to single active track per user + +--- + +### Option 5: Database-Level Optimization +**Complexity:** Low-Medium | **Impact:** Medium | **Risk:** Low + +#### Implementation +- Add composite indexes for common query patterns +- Implement materialized views for expensive calculations +- Use database-level segmentation logic + +#### Benefits +- **Faster queries:** Better index utilization +- **Reduced Ruby processing:** Move logic to database +- **Consistent performance:** Database optimizations benefit all modes + +#### Proposed Indexes +```sql +-- Optimized for bulk processing +CREATE INDEX CONCURRENTLY idx_points_user_timestamp_track +ON points(user_id, timestamp) WHERE track_id IS NULL; + +-- Optimized for incremental processing +CREATE INDEX CONCURRENTLY idx_points_untracked_timestamp +ON points(timestamp) WHERE track_id IS NULL; +``` + +--- + +## Recommended Implementation Strategy + +### Phase 1: Quick Wins (Week 1-2) +1. **Implement Enhanced Time-Based Batching** (Option 1) + - Extend existing daily mode with overlap + - Add progress reporting + - Configurable batch sizes + +### Phase 2: Database Optimization (Week 3) +2. **Add Database-Level Optimizations** (Option 5) + - Create optimized indexes + - Implement batch distance calculations + +### Phase 3: Advanced Features (Week 4-6) +3. **Spatial Indexing Optimization** (Option 2) + - Replace individual distance calculations + - Implement spatial clustering + +### Phase 4: Future Enhancements +4. **Parallel Processing** (Option 3) - Consider for v2 +5. **Incremental Enhancement** (Option 4) - For real-time features + +--- + +## Risk Assessment + +### Low Risk +- **Time-based batching:** Builds on existing daily mode +- **Database indexes:** Standard optimization technique +- **Progress reporting:** UI enhancement only + +### Medium Risk +- **Spatial optimization:** Requires careful testing of distance calculations +- **Incremental enhancement:** Changes to existing algorithm logic + +### High Risk +- **Parallel processing:** Complex coordination, potential race conditions +- **Major algorithm changes:** Could introduce segmentation bugs + +--- + +## Success Metrics + +### Performance Targets +- **Memory usage:** < 100MB for datasets up to 1M points +- **Processing time:** < 10 minutes for 1M points +- **User experience:** Progress indication and cancellation + +### Monitoring Points +- Database query performance +- Memory consumption during processing +- User-reported processing times +- Track generation accuracy (no regression) + +--- + +## Next Steps + +1. **Choose initial approach** based on urgency and resources +2. **Create feature branch** for selected optimization +3. **Implement comprehensive testing** including edge cases +4. **Monitor performance** in staging environment +5. **Gradual rollout** with feature flags \ No newline at end of file From 5a4a5e9625c826ea0a72927ed11849d9fe48aeea Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 19:30:56 +0200 Subject: [PATCH 02/12] Add composite index for track generation --- .app_version | 2 +- CHANGELOG.md | 7 + ...55_add_track_generation_composite_index.rb | 9 + db/schema.rb | 3 +- lib/optimized_tracks_v1.rb | 145 ---- lib/optimized_tracks_v2.rb | 291 -------- lib/results.md | 122 ---- lib/tracks_optimization_benchmark.rb | 625 ------------------ tracks_performance_optimization_options.md | 235 ------- 9 files changed, 18 insertions(+), 1421 deletions(-) create mode 100644 db/migrate/20250723164055_add_track_generation_composite_index.rb delete mode 100644 lib/optimized_tracks_v1.rb delete mode 100644 lib/optimized_tracks_v2.rb delete mode 100644 lib/results.md delete mode 100644 lib/tracks_optimization_benchmark.rb delete mode 100644 tracks_performance_optimization_options.md diff --git a/.app_version b/.app_version index 0f721773..e8262eb5 100644 --- a/.app_version +++ b/.app_version @@ -1 +1 @@ -0.30.2 +0.30.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c5a882..a51f35d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +# [0.30.3] - 2025-07-23 + +## Changed + +- Track generation is now significantly faster and less resource intensive. + + # [0.30.2] - 2025-07-22 ## Fixed diff --git a/db/migrate/20250723164055_add_track_generation_composite_index.rb b/db/migrate/20250723164055_add_track_generation_composite_index.rb new file mode 100644 index 00000000..1685011a --- /dev/null +++ b/db/migrate/20250723164055_add_track_generation_composite_index.rb @@ -0,0 +1,9 @@ +class AddTrackGenerationCompositeIndex < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def change + add_index :points, [:user_id, :timestamp, :track_id], + algorithm: :concurrently, + name: 'idx_points_track_generation', if_not_exists: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 402729b9..903fe090 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_07_21_204404) do +ActiveRecord::Schema[8.0].define(version: 2025_07_03_193657) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" enable_extension "postgis" @@ -146,7 +146,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_21_204404) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.geography "lonlat", limit: {srid: 4326, type: "st_point", geographic: true} - t.index "(((geodata -> 'properties'::text) ->> 'osm_id'::text))", name: "index_places_on_geodata_osm_id" t.index ["lonlat"], name: "index_places_on_lonlat", using: :gist end diff --git a/lib/optimized_tracks_v1.rb b/lib/optimized_tracks_v1.rb deleted file mode 100644 index 08969aa4..00000000 --- a/lib/optimized_tracks_v1.rb +++ /dev/null @@ -1,145 +0,0 @@ -# frozen_string_literal: true - -# Optimization V1: LAG-based distance calculation with Ruby segmentation -# This keeps the existing Ruby segmentation logic but uses PostgreSQL LAG -# for batch distance calculations instead of individual queries - -module OptimizedTracksV1 - extend ActiveSupport::Concern - - module ClassMethods - # V1: Use LAG to get all consecutive distances in a single query - def calculate_all_consecutive_distances(points) - return [] if points.length < 2 - - point_ids = points.map(&:id).join(',') - - results = connection.execute(<<-SQL.squish) - WITH points_with_previous AS ( - SELECT - id, - timestamp, - lonlat, - LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, - LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, - LAG(id) OVER (ORDER BY timestamp) as prev_id - FROM points - WHERE id IN (#{point_ids}) - ) - SELECT - id, - prev_id, - timestamp, - prev_timestamp, - ST_Distance(lonlat::geography, prev_lonlat::geography) as distance_meters, - (timestamp - prev_timestamp) as time_diff_seconds - FROM points_with_previous - WHERE prev_lonlat IS NOT NULL - ORDER BY timestamp - SQL - - # Return hash mapping point_id => {distance_to_previous, time_diff} - distance_map = {} - results.each do |row| - distance_map[row['id'].to_i] = { - distance_meters: row['distance_meters'].to_f, - time_diff_seconds: row['time_diff_seconds'].to_i, - prev_id: row['prev_id'].to_i - } - end - - distance_map - end - - # V1: Optimized total distance using LAG (already exists in distanceable.rb) - def total_distance_lag(points, unit = :m) - unless ::DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" - end - - return 0 if points.length < 2 - - point_ids = points.map(&:id).join(',') - - distance_in_meters = connection.select_value(<<-SQL.squish) - WITH points_with_previous AS ( - SELECT - lonlat, - LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat - FROM points - WHERE id IN (#{point_ids}) - ) - SELECT COALESCE( - SUM(ST_Distance(lonlat::geography, prev_lonlat::geography)), - 0 - ) - FROM points_with_previous - WHERE prev_lonlat IS NOT NULL - SQL - - distance_in_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] - end - end -end - -# Optimized segmentation module using pre-calculated distances -module OptimizedSegmentationV1 - extend ActiveSupport::Concern - - private - - def split_points_into_segments_v1(points) - return [] if points.empty? - - # V1: Pre-calculate all distances and time diffs in one query - if points.size > 1 - distance_data = Point.calculate_all_consecutive_distances(points) - else - distance_data = {} - end - - segments = [] - current_segment = [] - - points.each do |point| - if current_segment.empty? - # First point always starts a segment - current_segment = [point] - elsif should_start_new_segment_v1?(point, current_segment.last, distance_data) - # Finalize current segment if it has enough points - segments << current_segment if current_segment.size >= 2 - current_segment = [point] - else - current_segment << point - end - end - - # Don't forget the last segment - segments << current_segment if current_segment.size >= 2 - - segments - end - - def should_start_new_segment_v1?(current_point, previous_point, distance_data) - return false if previous_point.nil? - - # Get pre-calculated data for this point - point_data = distance_data[current_point.id] - return false unless point_data - - # Check time threshold - time_threshold_seconds = time_threshold_minutes.to_i * 60 - return true if point_data[:time_diff_seconds] > time_threshold_seconds - - # Check distance threshold - distance_meters = point_data[:distance_meters] - return true if distance_meters > distance_threshold_meters - - false - end -end - -# Add methods to Point class -class Point - extend OptimizedTracksV1::ClassMethods -end \ No newline at end of file diff --git a/lib/optimized_tracks_v2.rb b/lib/optimized_tracks_v2.rb deleted file mode 100644 index 03981fe6..00000000 --- a/lib/optimized_tracks_v2.rb +++ /dev/null @@ -1,291 +0,0 @@ -# frozen_string_literal: true - -# Optimization V2: Full SQL segmentation using PostgreSQL window functions -# This does both distance calculation AND segmentation entirely in SQL - -module OptimizedTracksV2 - extend ActiveSupport::Concern - - module ClassMethods - # V2: Complete segmentation in SQL using LAG and window functions - def segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) - time_threshold_seconds = time_threshold_minutes * 60 - - sql = <<~SQL - WITH points_with_gaps AS ( - SELECT - id, - timestamp, - lonlat, - LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, - LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, - ST_Distance( - lonlat::geography, - LAG(lonlat) OVER (ORDER BY timestamp)::geography - ) as distance_meters, - (timestamp - LAG(timestamp) OVER (ORDER BY timestamp)) as time_diff_seconds - FROM points - WHERE user_id = $1 - AND timestamp BETWEEN $2 AND $3 - ORDER BY timestamp - ), - segment_breaks AS ( - SELECT *, - CASE - WHEN prev_lonlat IS NULL THEN 1 - WHEN time_diff_seconds > $4 THEN 1 - WHEN distance_meters > $5 THEN 1 - ELSE 0 - END as is_break - FROM points_with_gaps - ), - segments AS ( - SELECT *, - SUM(is_break) OVER (ORDER BY timestamp ROWS UNBOUNDED PRECEDING) as segment_id - FROM segment_breaks - ) - SELECT - segment_id, - array_agg(id ORDER BY timestamp) as point_ids, - count(*) as point_count, - min(timestamp) as start_timestamp, - max(timestamp) as end_timestamp, - sum(COALESCE(distance_meters, 0)) as total_distance_meters - FROM segments - GROUP BY segment_id - HAVING count(*) >= 2 - ORDER BY segment_id - SQL - - results = connection.exec_query( - sql, - 'segment_points_in_sql', - [user_id, start_timestamp, end_timestamp, time_threshold_seconds, distance_threshold_meters] - ) - - # Convert results to segment data - segments_data = [] - results.each do |row| - segments_data << { - segment_id: row['segment_id'].to_i, - point_ids: parse_postgres_array(row['point_ids']), - point_count: row['point_count'].to_i, - start_timestamp: row['start_timestamp'].to_i, - end_timestamp: row['end_timestamp'].to_i, - total_distance_meters: row['total_distance_meters'].to_f - } - end - - segments_data - end - - # V2: Get actual Point objects for each segment - def get_segments_with_points(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) - segments_data = segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters) - - # Get all point IDs we need - all_point_ids = segments_data.flat_map { |seg| seg[:point_ids] } - - # Single query to get all points - points_by_id = Point.where(id: all_point_ids).index_by(&:id) - - # Build segments with actual Point objects - segments_data.map do |seg_data| - { - points: seg_data[:point_ids].map { |id| points_by_id[id] }.compact, - pre_calculated_distance: seg_data[:total_distance_meters], - start_timestamp: seg_data[:start_timestamp], - end_timestamp: seg_data[:end_timestamp] - } - end - end - - private - - # Parse PostgreSQL array format like "{1,2,3}" into Ruby array - def parse_postgres_array(pg_array_string) - return [] if pg_array_string.nil? || pg_array_string.empty? - - # Remove curly braces and split by comma - pg_array_string.gsub(/[{}]/, '').split(',').map(&:to_i) - end - end -end - -# Optimized generator using V2 SQL segmentation -class OptimizedTracksGeneratorV2 - attr_reader :user, :start_at, :end_at, :mode - - def initialize(user, start_at: nil, end_at: nil, mode: :bulk) - @user = user - @start_at = start_at - @end_at = end_at - @mode = mode.to_sym - end - - def call - clean_existing_tracks if should_clean_tracks? - - # Get timestamp range for SQL query - start_timestamp, end_timestamp = get_timestamp_range - - Rails.logger.debug "OptimizedGeneratorV2: querying points for user #{user.id} in #{mode} mode" - - # V2: Get segments directly from SQL with pre-calculated distances - segments = Point.get_segments_with_points( - user.id, - start_timestamp, - end_timestamp, - time_threshold_minutes, - distance_threshold_meters - ) - - Rails.logger.debug "OptimizedGeneratorV2: created #{segments.size} segments via SQL" - - tracks_created = 0 - - segments.each do |segment_data| - track = create_track_from_segment_v2(segment_data) - tracks_created += 1 if track - end - - Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in optimized V2 #{mode} mode" - tracks_created - end - - private - - def create_track_from_segment_v2(segment_data) - points = segment_data[:points] - pre_calculated_distance = segment_data[:pre_calculated_distance] - - Rails.logger.debug "OptimizedGeneratorV2: processing segment with #{points.size} points" - return unless points.size >= 2 - - track = Track.new( - user_id: user.id, - start_at: Time.zone.at(points.first.timestamp), - end_at: Time.zone.at(points.last.timestamp), - original_path: build_path(points) - ) - - # V2: Use pre-calculated distance from SQL - track.distance = pre_calculated_distance.round - track.duration = calculate_duration(points) - track.avg_speed = calculate_average_speed(track.distance, track.duration) - - # Calculate elevation statistics (no DB queries needed) - elevation_stats = calculate_elevation_stats(points) - track.elevation_gain = elevation_stats[:gain] - track.elevation_loss = elevation_stats[:loss] - track.elevation_max = elevation_stats[:max] - track.elevation_min = elevation_stats[:min] - - if track.save - Point.where(id: points.map(&:id)).update_all(track_id: track.id) - track - else - Rails.logger.error "Failed to create track for user #{user.id}: #{track.errors.full_messages.join(', ')}" - nil - end - end - - def get_timestamp_range - case mode - when :bulk - if start_at && end_at - [start_at.to_i, end_at.to_i] - else - # Get full range for user - first_point = user.tracked_points.order(:timestamp).first - last_point = user.tracked_points.order(:timestamp).last - [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] - end - when :daily - day = start_at&.to_date || Date.current - [day.beginning_of_day.to_i, day.end_of_day.to_i] - when :incremental - # For incremental, we need all untracked points up to end_at - first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first - end_timestamp = end_at ? end_at.to_i : Time.current.to_i - [first_point&.timestamp || 0, end_timestamp] - end - end - - def should_clean_tracks? - case mode - when :bulk, :daily then true - else false - end - end - - def clean_existing_tracks - case mode - when :bulk - scope = user.tracks - if start_at && end_at - scope = scope.where(start_at: start_at..end_at) - end - scope.destroy_all - when :daily - day = start_at&.to_date || Date.current - range = day.beginning_of_day..day.end_of_day - user.tracks.where(start_at: range).destroy_all - end - end - - # Helper methods (same as original) - def build_path(points) - Tracks::BuildPath.new(points).call - end - - def calculate_duration(points) - points.last.timestamp - points.first.timestamp - end - - def calculate_average_speed(distance_in_meters, duration_seconds) - return 0.0 if duration_seconds <= 0 || distance_in_meters <= 0 - - speed_mps = distance_in_meters.to_f / duration_seconds - (speed_mps * 3.6).round(2) # m/s to km/h - end - - def calculate_elevation_stats(points) - altitudes = points.map(&:altitude).compact - return { gain: 0, loss: 0, max: 0, min: 0 } if altitudes.empty? - - elevation_gain = 0 - elevation_loss = 0 - previous_altitude = altitudes.first - - altitudes[1..].each do |altitude| - diff = altitude - previous_altitude - if diff > 0 - elevation_gain += diff - else - elevation_loss += diff.abs - end - previous_altitude = altitude - end - - { - gain: elevation_gain.round, - loss: elevation_loss.round, - max: altitudes.max, - min: altitudes.min - } - end - - def distance_threshold_meters - @distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i - end - - def time_threshold_minutes - @time_threshold_minutes ||= user.safe_settings.minutes_between_routes.to_i - end -end - -# Add methods to Point class -class Point - extend OptimizedTracksV2::ClassMethods -end \ No newline at end of file diff --git a/lib/results.md b/lib/results.md deleted file mode 100644 index b9d3bcc4..00000000 --- a/lib/results.md +++ /dev/null @@ -1,122 +0,0 @@ -## Original - -Generator: created track 227296 -Generated 1437 tracks for user 1 in bulk mode -✅ Generation completed successfully - -============================================================ -📊 BENCHMARK RESULTS -============================================================ -Status: ✅ SUCCESS -Execution Time: 1m 28.5s -Tracks Created: 1437 -Timeframe Coverage: 8.0% of user's total data - -💾 Memory Usage: - Start: 210.9MB - End: 433.2MB - Memory Increase: +222.3MB - -🗄️ Database Performance: - Total Queries: 115920 - Total Query Time: 50453.1ms - Average Query Time: 0.44ms - Slow Queries (>100ms): 63 - 1. 983.24ms - SELECT COUNT(*) FROM "points" WHERE "points"."user_id" = $1 AND "points"."timestamp" BETWEEN $2 A... - 2. 2826.02ms - SELECT "points".* FROM "points" WHERE "points"."user_id" = $1 AND "points"."timestamp" BETWEEN $2... - 3. 217.02ms - UPDATE "points" SET "track_id" = $1 WHERE "points"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, ... - -✔️ Post-Generation Validation: - Points in Timeframe: 111609 - Points with Tracks: 110167 - Points without Tracks: 1442 - Track Records: 1437 - ✅ Data integrity: PASS - -🔍 Performance Analysis: - Speed Rating: 🚀 Excellent (1m 28.5s) - Memory Rating: 🧡 High (433.2MB peak) - Recommendation: Consider database optimization or smaller batch sizes - -🔮 Extrapolation for Full Dataset: - Full Dataset Size: 1,403,662 points - Scaling Factor: 12.6x - Estimated Full Time: 18m 32.8s - Estimated Full Memory: 5447.6MB - -============================================================ -📋 BENCHMARK SUMMARY -============================================================ -⏱️ Total Time: 1m 28.5s -📍 Points Processed: 111,609 -🛤️ Tracks Created: 1437 -🚀 Processing Speed: 1261.4 points/second -📅 Timeframe: 2024-01-01 to 2024-12-31 -👤 User: demo@dawarich.app (ID: 1) -✅ Status: COMPLETED - - -## Iteration 1 - -Generator: created track 244784 -Generated 1435 tracks for user 1 in optimized bulk mode -✅ Generation completed successfully - -============================================================ -📊 BENCHMARK RESULTS -============================================================ -Status: ✅ SUCCESS -Execution Time: 56.4s -Tracks Created: 1435 -Points Processed: 111,609 -Processing Speed: 1978.3 points/second -Average Points/Track: 77.8 -Timeframe Coverage: 8.0% of user's total data - -💾 Memory Usage: - Start: 297.2MB - End: 407.5MB - Memory Increase: +110.3MB - -🗄️ Database Performance: - Total Queries: 7178 - Total Query Time: 44521.33ms - Average Query Time: 6.2ms - Slow Queries (>100ms): 88 - 1. 2338.43ms - WITH points_with_gaps AS ( - SELECT - id, - timestamp, - lonlat, - LAG(lonlat) OVER (ORDE... - 2. 4156.84ms - SELECT "points".* FROM "points" WHERE "points"."id" IN (2163775, 2163776, 2163777, 2163778, 21637... - 3. 298.62ms - UPDATE "points" SET "track_id" = $1 WHERE "points"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, ... - -✔️ Post-Generation Validation: - Points in Timeframe: 111609 - Points with Tracks: 110123 - Points without Tracks: 1486 - Track Records: 1435 - ✅ Data integrity: PASS - -🔍 Performance Analysis: - Speed Rating: 🚀 Excellent (56.4s) - Memory Rating: 🧡 High (407.5MB peak) - Recommendation: Consider database optimization or smaller batch sizes - -🔮 Extrapolation for Full Dataset: - Full Dataset Size: 1,403,662 points - Scaling Factor: 12.6x - Estimated Full Time: 11m 49.5s - Estimated Full Memory: 5125.0MB - -============================================================ -📋 BENCHMARK SUMMARY -============================================================ -⏱️ Total Time: 56.4s -📍 Points Processed: 111,609 -🛤️ Tracks Created: 1435 -🚀 Processing Speed: 1978.3 points/second -📅 Timeframe: 2024-01-01 to 2024-12-31 -👤 User: demo@dawarich.app (ID: 1) -✅ Status: COMPLETED diff --git a/lib/tracks_optimization_benchmark.rb b/lib/tracks_optimization_benchmark.rb deleted file mode 100644 index bac160d5..00000000 --- a/lib/tracks_optimization_benchmark.rb +++ /dev/null @@ -1,625 +0,0 @@ -# frozen_string_literal: true - -require_relative 'optimized_tracks_v1' -require_relative 'optimized_tracks_v2' - -# Benchmark script to compare three different track generation approaches: -# - Original: Individual distance queries (current implementation) -# - V1: LAG-based distance pre-calculation with Ruby segmentation -# - V2: Full SQL segmentation with PostgreSQL window functions -# -# Usage: -# rails runner lib/tracks_optimization_benchmark.rb USER_ID START_DATE END_DATE - -class TracksOptimizationBenchmark - attr_reader :user, :start_date, :end_date, :start_timestamp, :end_timestamp - - def initialize(user_id, start_date, end_date) - @user = User.find(user_id) - @start_date = Date.parse(start_date) - @end_date = Date.parse(end_date) - @start_timestamp = @start_date.beginning_of_day.to_i - @end_timestamp = @end_date.end_of_day.to_i - - puts "🔬 Track Generation Optimization Benchmark" - puts "👤 User: #{user.email} (ID: #{user.id})" - puts "📅 Timeframe: #{start_date} to #{end_date}" - - check_data_availability - end - - def run_all_benchmarks - results = {} - - puts "\n" + "=" * 80 - puts "🏃 RUNNING ALL BENCHMARKS" - puts "=" * 80 - - # Test Original approach - puts "\n1️⃣ Testing ORIGINAL approach..." - results[:original] = benchmark_original - - # Test V1 approach - puts "\n2️⃣ Testing V1 (LAG + Ruby) approach..." - results[:v1] = benchmark_v1 - - # Test V2 approach - puts "\n3️⃣ Testing V2 (Full SQL) approach..." - results[:v2] = benchmark_v2 - - # Compare results - puts "\n" + "=" * 80 - puts "📊 PERFORMANCE COMPARISON" - puts "=" * 80 - compare_results(results) - - # Save results to files - save_results_to_files(results) - - results - end - - private - - def check_data_availability - point_count = user.tracked_points.where(timestamp: start_timestamp..end_timestamp).count - existing_tracks = user.tracks.where(start_at: Time.zone.at(start_timestamp)..Time.zone.at(end_timestamp)).count - - puts "📊 Dataset: #{point_count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse} points" - puts "🛤️ Existing tracks: #{existing_tracks}" - - if point_count == 0 - puts "❌ No points found in timeframe" - exit 1 - end - - if point_count > 50000 - puts "⚠️ Large dataset detected. This benchmark may take a while..." - end - end - - def benchmark_original - puts " Using standard Tracks::Generator..." - - # Clean existing tracks - cleanup_tracks - - # Monitor performance - memory_start = get_memory_mb - query_monitor = QueryMonitor.new - query_monitor.start - - start_time = Time.current - - begin - generator = Tracks::Generator.new( - user, - start_at: Time.zone.at(start_timestamp), - end_at: Time.zone.at(end_timestamp), - mode: :bulk - ) - tracks_created = generator.call - success = true - rescue => e - success = false - error = e.message - tracks_created = 0 - end - - end_time = Time.current - memory_end = get_memory_mb - query_monitor.stop - - execution_time = end_time - start_time - - result = { - approach: "Original", - success: success, - error: error, - execution_time: execution_time, - tracks_created: tracks_created, - memory_increase: memory_end - memory_start, - query_count: query_monitor.query_count, - query_time_ms: query_monitor.total_time_ms - } - - print_result(result) - result - end - - def benchmark_v1 - puts " Using V1: LAG + Ruby segmentation..." - - # Clean existing tracks - cleanup_tracks - - # For V1, we need to modify the existing generator to use our optimized methods - # This is a simplified test - in practice we'd modify the actual generator - - memory_start = get_memory_mb - query_monitor = QueryMonitor.new - query_monitor.start - - start_time = Time.current - - begin - # Load points - points = user.tracked_points - .where(timestamp: start_timestamp..end_timestamp) - .order(:timestamp) - - # V1: Use optimized segmentation with pre-calculated distances - if points.size > 1 - distance_data = Point.calculate_all_consecutive_distances(points) - else - distance_data = {} - end - - # Segment using V1 approach (simplified for benchmark) - segments = split_points_with_precalculated_distances(points, distance_data) - - tracks_created = 0 - segments.each do |segment| - if segment.size >= 2 - track = create_track_v1(segment) - tracks_created += 1 if track - end - end - - success = true - rescue => e - success = false - error = e.message - tracks_created = 0 - end - - end_time = Time.current - memory_end = get_memory_mb - query_monitor.stop - - execution_time = end_time - start_time - - result = { - approach: "V1 (LAG + Ruby)", - success: success, - error: error, - execution_time: execution_time, - tracks_created: tracks_created, - memory_increase: memory_end - memory_start, - query_count: query_monitor.query_count, - query_time_ms: query_monitor.total_time_ms - } - - print_result(result) - result - end - - def benchmark_v2 - puts " Using V2: Full SQL segmentation..." - - cleanup_tracks - - memory_start = get_memory_mb - query_monitor = QueryMonitor.new - query_monitor.start - - start_time = Time.current - - begin - generator = OptimizedTracksGeneratorV2.new( - user, - start_at: Time.zone.at(start_timestamp), - end_at: Time.zone.at(end_timestamp), - mode: :bulk - ) - tracks_created = generator.call - success = true - rescue => e - success = false - error = e.message - tracks_created = 0 - end - - end_time = Time.current - memory_end = get_memory_mb - query_monitor.stop - - execution_time = end_time - start_time - - result = { - approach: "V2 (Full SQL)", - success: success, - error: error, - execution_time: execution_time, - tracks_created: tracks_created, - memory_increase: memory_end - memory_start, - query_count: query_monitor.query_count, - query_time_ms: query_monitor.total_time_ms - } - - print_result(result) - result - end - - def split_points_with_precalculated_distances(points, distance_data) - return [] if points.empty? - - segments = [] - current_segment = [] - - points.each do |point| - if current_segment.empty? - current_segment = [point] - elsif should_break_segment_v1?(point, current_segment.last, distance_data) - segments << current_segment if current_segment.size >= 2 - current_segment = [point] - else - current_segment << point - end - end - - segments << current_segment if current_segment.size >= 2 - segments - end - - def should_break_segment_v1?(current_point, previous_point, distance_data) - return false if previous_point.nil? - - point_data = distance_data[current_point.id] - return false unless point_data - - time_threshold_seconds = user.safe_settings.minutes_between_routes.to_i * 60 - distance_threshold_meters = user.safe_settings.meters_between_routes.to_i - - return true if point_data[:time_diff_seconds] > time_threshold_seconds - return true if point_data[:distance_meters] > distance_threshold_meters - - false - end - - def create_track_v1(points) - return nil if points.size < 2 - - track = Track.new( - user_id: user.id, - start_at: Time.zone.at(points.first.timestamp), - end_at: Time.zone.at(points.last.timestamp), - original_path: build_path(points) - ) - - # Use LAG-based distance calculation - track.distance = Point.total_distance_lag(points, :m).round - track.duration = points.last.timestamp - points.first.timestamp - track.avg_speed = calculate_average_speed(track.distance, track.duration) - - # Elevation stats (same as original) - elevation_stats = calculate_elevation_stats(points) - track.elevation_gain = elevation_stats[:gain] - track.elevation_loss = elevation_stats[:loss] - track.elevation_max = elevation_stats[:max] - track.elevation_min = elevation_stats[:min] - - if track.save - Point.where(id: points.map(&:id)).update_all(track_id: track.id) - track - else - nil - end - end - - def cleanup_tracks - user.tracks.where(start_at: Time.zone.at(start_timestamp)..Time.zone.at(end_timestamp)).destroy_all - end - - def print_result(result) - status = result[:success] ? "✅ SUCCESS" : "❌ FAILED" - puts " #{status}" - puts " ⏱️ Time: #{format_duration(result[:execution_time])}" - puts " 🛤️ Tracks: #{result[:tracks_created]}" - puts " 💾 Memory: +#{result[:memory_increase].round(1)}MB" - puts " 🗄️ Queries: #{result[:query_count]} (#{result[:query_time_ms].round(1)}ms)" - puts " ❌ Error: #{result[:error]}" if result[:error] - end - - def compare_results(results) - return unless results[:original] && results[:v1] && results[:v2] - - puts sprintf("%-20s %-10s %-12s %-10s %-15s %-10s", - "Approach", "Time", "Tracks", "Memory", "Queries", "Query Time") - puts "-" * 80 - - [:original, :v1, :v2].each do |approach| - result = results[approach] - next unless result[:success] - - puts sprintf("%-20s %-10s %-12s %-10s %-15s %-10s", - result[:approach], - format_duration(result[:execution_time]), - result[:tracks_created], - "+#{result[:memory_increase].round(1)}MB", - result[:query_count], - "#{result[:query_time_ms].round(1)}ms") - end - - # Calculate improvements - if results[:original][:success] - original_time = results[:original][:execution_time] - original_queries = results[:original][:query_count] - - puts "\n🚀 Performance Improvements vs Original:" - - if results[:v1][:success] - v1_speedup = (original_time / results[:v1][:execution_time]).round(2) - v1_query_reduction = ((original_queries - results[:v1][:query_count]) / original_queries.to_f * 100).round(1) - puts " V1: #{v1_speedup}x faster, #{v1_query_reduction}% fewer queries" - end - - if results[:v2][:success] - v2_speedup = (original_time / results[:v2][:execution_time]).round(2) - v2_query_reduction = ((original_queries - results[:v2][:query_count]) / original_queries.to_f * 100).round(1) - puts " V2: #{v2_speedup}x faster, #{v2_query_reduction}% fewer queries" - end - end - end - - def save_results_to_files(results) - timestamp = Time.current.strftime('%Y%m%d_%H%M%S') - point_count = user.tracked_points.where(timestamp: start_timestamp..end_timestamp).count - - # Create detailed results structure - benchmark_data = { - meta: { - timestamp: Time.current.iso8601, - user_id: user.id, - user_email: user.email, - start_date: start_date.strftime('%Y-%m-%d'), - end_date: end_date.strftime('%Y-%m-%d'), - point_count: point_count, - ruby_version: RUBY_VERSION, - rails_version: Rails.version, - database_adapter: ActiveRecord::Base.connection.adapter_name - }, - results: results, - performance_analysis: analyze_performance_data(results) - } - - # Save JSON results for programmatic analysis - json_filename = "tracks_optimization_#{timestamp}.json" - json_path = Rails.root.join('lib', json_filename) - File.write(json_path, JSON.pretty_generate(benchmark_data)) - - # Save human-readable markdown report - md_filename = "tracks_optimization_#{timestamp}.md" - md_path = Rails.root.join('lib', md_filename) - File.write(md_path, generate_markdown_report(benchmark_data)) - - puts "\n💾 Results saved:" - puts " 📄 JSON: #{json_path}" - puts " 📝 Report: #{md_path}" - end - - def analyze_performance_data(results) - return {} unless results[:original] && results[:original][:success] - - original = results[:original] - analysis = { - baseline: { - execution_time: original[:execution_time], - query_count: original[:query_count], - memory_usage: original[:memory_increase] - } - } - - [:v1, :v2].each do |version| - next unless results[version] && results[version][:success] - - result = results[version] - analysis[version] = { - speedup_factor: (original[:execution_time] / result[:execution_time]).round(2), - query_reduction_percent: ((original[:query_count] - result[:query_count]) / original[:query_count].to_f * 100).round(1), - memory_change_percent: ((result[:memory_increase] - original[:memory_increase]) / original[:memory_increase].to_f * 100).round(1), - execution_time_saved: (original[:execution_time] - result[:execution_time]).round(2) - } - end - - analysis - end - - def generate_markdown_report(benchmark_data) - meta = benchmark_data[:meta] - results = benchmark_data[:results] - analysis = benchmark_data[:performance_analysis] - - report = <<~MD - # Tracks Generation Optimization Benchmark Report - - **Generated:** #{meta[:timestamp]} - **User:** #{meta[:user_email]} (ID: #{meta[:user_id]}) - **Timeframe:** #{meta[:start_date]} to #{meta[:end_date]} - **Dataset:** #{meta[:point_count].to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse} points - **Environment:** Ruby #{meta[:ruby_version]}, Rails #{meta[:rails_version]}, #{meta[:database_adapter]} - - ## Summary - - This benchmark compares three approaches to track generation: - - **Original:** Individual PostGIS queries for each distance calculation - - **V1 (LAG + Ruby):** PostgreSQL LAG for batch distance calculation, Ruby segmentation - - **V2 (Full SQL):** Complete segmentation using PostgreSQL window functions - - ## Results - - | Approach | Status | Time | Tracks | Memory | Queries | Query Time | - |----------|--------|------|--------|--------|---------|------------| - MD - - [:original, :v1, :v2].each do |approach| - next unless results[approach] - - result = results[approach] - status = result[:success] ? "✅" : "❌" - - report += "| #{result[:approach]} | #{status} | #{format_duration(result[:execution_time])} | #{result[:tracks_created]} | +#{result[:memory_increase].round(1)}MB | #{result[:query_count]} | #{result[:query_time_ms].round(1)}ms |\n" - end - - if analysis[:v1] || analysis[:v2] - report += "\n## Performance Improvements\n\n" - - if analysis[:v1] - v1 = analysis[:v1] - report += "### V1 (LAG + Ruby) vs Original\n" - report += "- **#{v1[:speedup_factor]}x faster** execution\n" - report += "- **#{v1[:query_reduction_percent]}% fewer** database queries\n" - report += "- **#{format_duration(v1[:execution_time_saved])} time saved**\n" - report += "- Memory change: #{v1[:memory_change_percent] > 0 ? '+' : ''}#{v1[:memory_change_percent]}%\n\n" - end - - if analysis[:v2] - v2 = analysis[:v2] - report += "### V2 (Full SQL) vs Original\n" - report += "- **#{v2[:speedup_factor]}x faster** execution\n" - report += "- **#{v2[:query_reduction_percent]}% fewer** database queries\n" - report += "- **#{format_duration(v2[:execution_time_saved])} time saved**\n" - report += "- Memory change: #{v2[:memory_change_percent] > 0 ? '+' : ''}#{v2[:memory_change_percent]}%\n\n" - end - end - - # Add detailed results - report += "## Detailed Results\n\n" - - [:original, :v1, :v2].each do |approach| - next unless results[approach] - - result = results[approach] - report += "### #{result[:approach]}\n\n" - - if result[:success] - report += "- ✅ **Status:** Success\n" - report += "- ⏱️ **Execution Time:** #{format_duration(result[:execution_time])}\n" - report += "- 🛤️ **Tracks Created:** #{result[:tracks_created]}\n" - report += "- 💾 **Memory Increase:** +#{result[:memory_increase].round(1)}MB\n" - report += "- 🗄️ **Database Queries:** #{result[:query_count]}\n" - report += "- ⚡ **Query Time:** #{result[:query_time_ms].round(1)}ms\n" - - if result[:query_count] > 0 - avg_query_time = (result[:query_time_ms] / result[:query_count]).round(2) - report += "- 📊 **Average Query Time:** #{avg_query_time}ms\n" - end - else - report += "- ❌ **Status:** Failed\n" - report += "- 🚨 **Error:** #{result[:error]}\n" - end - - report += "\n" - end - - report += "## Recommendations\n\n" - - if analysis[:v2] && analysis[:v2][:speedup_factor] > analysis.dig(:v1, :speedup_factor).to_f - report += "🚀 **V2 (Full SQL)** shows the best performance with #{analysis[:v2][:speedup_factor]}x speedup.\n\n" - report += "Benefits:\n" - report += "- Minimal database queries (#{results.dig(:v2, :query_count)} vs #{results.dig(:original, :query_count)})\n" - report += "- Fastest execution time\n" - report += "- Leverages PostgreSQL's optimized window functions\n\n" - elsif analysis[:v1] - report += "🏃 **V1 (LAG + Ruby)** provides good performance improvements with #{analysis[:v1][:speedup_factor]}x speedup.\n\n" - end - - if results[:original] && results[:original][:query_count] > 50000 - report += "⚠️ **Current implementation** makes excessive database queries (#{results[:original][:query_count]}) for this dataset size.\n\n" - end - - report += "---\n*Generated by TracksOptimizationBenchmark*" - - report - end - - # Helper methods - def get_memory_mb - `ps -o rss= -p #{Process.pid}`.to_i / 1024.0 - end - - def format_duration(seconds) - if seconds < 60 - "#{seconds.round(1)}s" - else - minutes = (seconds / 60).floor - remaining_seconds = (seconds % 60).round(1) - "#{minutes}m #{remaining_seconds}s" - end - end - - def build_path(points) - Tracks::BuildPath.new(points).call - end - - def calculate_average_speed(distance_in_meters, duration_seconds) - return 0.0 if duration_seconds <= 0 || distance_in_meters <= 0 - speed_mps = distance_in_meters.to_f / duration_seconds - (speed_mps * 3.6).round(2) - end - - def calculate_elevation_stats(points) - altitudes = points.map(&:altitude).compact - return { gain: 0, loss: 0, max: 0, min: 0 } if altitudes.empty? - - elevation_gain = 0 - elevation_loss = 0 - previous_altitude = altitudes.first - - altitudes[1..].each do |altitude| - diff = altitude - previous_altitude - if diff > 0 - elevation_gain += diff - else - elevation_loss += diff.abs - end - previous_altitude = altitude - end - - { gain: elevation_gain.round, loss: elevation_loss.round, max: altitudes.max, min: altitudes.min } - end -end - -# Simple query monitor for this benchmark -class QueryMonitor - attr_reader :query_count, :total_time_ms - - def initialize - @query_count = 0 - @total_time_ms = 0 - end - - def start - @subscription = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - next if event.payload[:name]&.include?('SCHEMA') - - @query_count += 1 - @total_time_ms += event.duration - end - end - - def stop - ActiveSupport::Notifications.unsubscribe(@subscription) if @subscription - end -end - -# Command line interface -if __FILE__ == $0 - if ARGV.length < 3 - puts "Usage: rails runner #{__FILE__} USER_ID START_DATE END_DATE" - puts "" - puts "Example:" - puts " rails runner #{__FILE__} 1 2024-01-01 2024-01-31" - exit 1 - end - - user_id = ARGV[0].to_i - start_date = ARGV[1] - end_date = ARGV[2] - - benchmark = TracksOptimizationBenchmark.new(user_id, start_date, end_date) - results = benchmark.run_all_benchmarks - - puts "\n🎉 Benchmark completed! Check results above." -end \ No newline at end of file diff --git a/tracks_performance_optimization_options.md b/tracks_performance_optimization_options.md deleted file mode 100644 index 8f7c6601..00000000 --- a/tracks_performance_optimization_options.md +++ /dev/null @@ -1,235 +0,0 @@ -# Tracks Feature Performance Optimization Options - -## Current State Analysis - -### Performance Characteristics -- **Time Complexity:** O(n log n) where n = number of GPS points -- **Memory Usage:** Loads entire dataset into memory (~200-400 bytes per point) -- **Processing Mode:** Single-threaded, sequential segmentation -- **Database Load:** Multiple PostGIS distance calculations per point pair - -### Performance Estimates (Bulk Mode) -| Points | Processing Time | Memory Usage | Database Load | -|--------|----------------|--------------|---------------| -| 10K | 30-60 seconds | ~50 MB | Low | -| 100K | 5-15 minutes | ~200 MB | Medium | -| 1M+ | 30-90 minutes | 400+ MB | High | - -### Current Bottlenecks -1. **Memory constraints** - Loading all points at once -2. **PostGIS distance calculations** - Sequential, not optimized -3. **Single-threaded processing** - No parallelization -4. **No progress indication** - Users can't track long-running operations - ---- - -## Optimization Options - -### Option 1: Enhanced Time-Based Batching -**Complexity:** Low | **Impact:** High | **Risk:** Low - -#### Implementation -- Extend existing `:daily` mode with configurable batch sizes -- Add 1-point overlap between batches to maintain segmentation accuracy -- Implement batch-aware progress reporting - -#### Benefits -- **Memory reduction:** 90%+ reduction (from 400MB to ~40MB for 1M points) -- **Better UX:** Progress indication and cancellation support -- **Incremental processing:** Can resume interrupted operations -- **Lower DB pressure:** Smaller query result sets - -#### Changes Required -```ruby -# Enhanced generator with configurable batching -Tracks::Generator.new( - user, - mode: :batched, - batch_size: 24.hours, - enable_overlap: true -).call -``` - -#### Edge Cases to Handle -- Tracks spanning batch boundaries (solved with overlap) -- Midnight-crossing tracks in daily mode -- Deduplication of overlapping segments - ---- - -### Option 2: Spatial Indexing Optimization -**Complexity:** Medium | **Impact:** Medium | **Risk:** Low - -#### Implementation -- Replace individual PostGIS calls with batch distance calculations -- Implement spatial clustering for nearby points before segmentation -- Use PostGIS window functions for distance calculations - -#### Benefits -- **Faster distance calculations:** Batch operations vs individual queries -- **Reduced DB round-trips:** Single query for multiple distance calculations -- **Better index utilization:** Leverage existing spatial indexes - -#### Changes Required -```sql --- Batch distance calculation approach -WITH point_distances AS ( - SELECT - id, - timestamp, - ST_Distance( - lonlat::geography, - LAG(lonlat::geography) OVER (ORDER BY timestamp) - ) as distance_to_previous - FROM points - WHERE user_id = ? - ORDER BY timestamp -) -SELECT * FROM point_distances WHERE distance_to_previous > ? -``` - ---- - -### Option 3: Parallel Processing with Worker Pools -**Complexity:** High | **Impact:** High | **Risk:** Medium - -#### Implementation -- Split large datasets into non-overlapping time ranges -- Process multiple batches in parallel using Sidekiq workers -- Implement coordination mechanism for dependent segments - -#### Benefits -- **Faster processing:** Utilize multiple CPU cores -- **Scalable:** Performance scales with worker capacity -- **Background processing:** Non-blocking for users - -#### Challenges -- **Complex coordination:** Managing dependencies between batches -- **Resource competition:** Multiple workers accessing same user's data -- **Error handling:** Partial failure scenarios - -#### Architecture -```ruby -# Parallel processing coordinator -class Tracks::ParallelGenerator - def call - time_ranges = split_into_parallel_ranges - - time_ranges.map do |range| - Tracks::BatchProcessorJob.perform_later(user_id, range) - end - end -end -``` - ---- - -### Option 4: Incremental Algorithm Enhancement -**Complexity:** Medium | **Impact:** Medium | **Risk:** Medium - -#### Implementation -- Enhance existing `:incremental` mode with smarter buffering -- Implement sliding window approach for active track detection -- Add automatic track finalization based on time gaps - -#### Benefits -- **Real-time processing:** Process points as they arrive -- **Lower memory footprint:** Only active segments in memory -- **Better for live tracking:** Immediate track updates - -#### Current Limitations -- Existing incremental mode processes untracked points only -- No automatic track finalization -- Limited to single active track per user - ---- - -### Option 5: Database-Level Optimization -**Complexity:** Low-Medium | **Impact:** Medium | **Risk:** Low - -#### Implementation -- Add composite indexes for common query patterns -- Implement materialized views for expensive calculations -- Use database-level segmentation logic - -#### Benefits -- **Faster queries:** Better index utilization -- **Reduced Ruby processing:** Move logic to database -- **Consistent performance:** Database optimizations benefit all modes - -#### Proposed Indexes -```sql --- Optimized for bulk processing -CREATE INDEX CONCURRENTLY idx_points_user_timestamp_track -ON points(user_id, timestamp) WHERE track_id IS NULL; - --- Optimized for incremental processing -CREATE INDEX CONCURRENTLY idx_points_untracked_timestamp -ON points(timestamp) WHERE track_id IS NULL; -``` - ---- - -## Recommended Implementation Strategy - -### Phase 1: Quick Wins (Week 1-2) -1. **Implement Enhanced Time-Based Batching** (Option 1) - - Extend existing daily mode with overlap - - Add progress reporting - - Configurable batch sizes - -### Phase 2: Database Optimization (Week 3) -2. **Add Database-Level Optimizations** (Option 5) - - Create optimized indexes - - Implement batch distance calculations - -### Phase 3: Advanced Features (Week 4-6) -3. **Spatial Indexing Optimization** (Option 2) - - Replace individual distance calculations - - Implement spatial clustering - -### Phase 4: Future Enhancements -4. **Parallel Processing** (Option 3) - Consider for v2 -5. **Incremental Enhancement** (Option 4) - For real-time features - ---- - -## Risk Assessment - -### Low Risk -- **Time-based batching:** Builds on existing daily mode -- **Database indexes:** Standard optimization technique -- **Progress reporting:** UI enhancement only - -### Medium Risk -- **Spatial optimization:** Requires careful testing of distance calculations -- **Incremental enhancement:** Changes to existing algorithm logic - -### High Risk -- **Parallel processing:** Complex coordination, potential race conditions -- **Major algorithm changes:** Could introduce segmentation bugs - ---- - -## Success Metrics - -### Performance Targets -- **Memory usage:** < 100MB for datasets up to 1M points -- **Processing time:** < 10 minutes for 1M points -- **User experience:** Progress indication and cancellation - -### Monitoring Points -- Database query performance -- Memory consumption during processing -- User-reported processing times -- Track generation accuracy (no regression) - ---- - -## Next Steps - -1. **Choose initial approach** based on urgency and resources -2. **Create feature branch** for selected optimization -3. **Implement comprehensive testing** including edge cases -4. **Monitor performance** in staging environment -5. **Gradual rollout** with feature flags \ No newline at end of file From 4239f5b31a1781da1761b26f23b34b32ec2dcb28 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 19:32:51 +0200 Subject: [PATCH 03/12] Remove bullet gem --- Gemfile | 7 ------- Gemfile.lock | 5 ----- config/environments/development.rb | 11 ----------- config/environments/test.rb | 6 ------ 4 files changed, 29 deletions(-) diff --git a/Gemfile b/Gemfile index ce746208..614a2e95 100644 --- a/Gemfile +++ b/Gemfile @@ -52,7 +52,6 @@ gem 'jwt' group :development, :test do gem 'brakeman', require: false - gem 'bullet' gem 'bundler-audit', require: false gem 'debug', platforms: %i[mri mingw x64_mingw] gem 'dotenv-rails' @@ -79,9 +78,3 @@ group :development do gem 'foreman' gem 'rubocop-rails', require: false end - -# group :production do -# gem 'uglifier' -# end - -# gem 'sassc-rails' diff --git a/Gemfile.lock b/Gemfile.lock index c5a1fd55..4b955b5a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,9 +113,6 @@ GEM brakeman (7.0.2) racc builder (3.3.0) - bullet (8.0.8) - activesupport (>= 3.0.0) - uniform_notifier (~> 1.11) bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) @@ -489,7 +486,6 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uniform_notifier (1.17.0) uri (1.0.3) useragent (0.16.11) warden (1.2.9) @@ -523,7 +519,6 @@ DEPENDENCIES aws-sdk-s3 (~> 1.177.0) bootsnap brakeman - bullet bundler-audit capybara chartkick diff --git a/config/environments/development.rb b/config/environments/development.rb index a5b3fce4..c940de0e 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -3,17 +3,6 @@ require 'active_support/core_ext/integer/time' Rails.application.configure do - unless ENV['SELF_HOSTED'] == 'true' - config.after_initialize do - Bullet.enable = true - Bullet.alert = true - Bullet.bullet_logger = true - Bullet.console = true - Bullet.rails_logger = true - Bullet.add_footer = true - end - end - # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded any time diff --git a/config/environments/test.rb b/config/environments/test.rb index b4884952..e138d076 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -8,12 +8,6 @@ require 'active_support/core_ext/integer/time' # and recreated between test runs. Don't rely on the data there! Rails.application.configure do - config.after_initialize do - Bullet.enable = true - Bullet.bullet_logger = true - Bullet.raise = true # raise an error if n+1 query occurs - end - # Settings specified here will take precedence over those in config/application.rb. # While tests run files are not watched, reloading is not necessary. From 278a4d28b5bdab9576c138631dea4507b43e6811 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 19:33:38 +0200 Subject: [PATCH 04/12] Remove tailwind.css --- app/assets/builds/tailwind.css | 6415 -------------------------------- 1 file changed, 6415 deletions(-) delete mode 100644 app/assets/builds/tailwind.css diff --git a/app/assets/builds/tailwind.css b/app/assets/builds/tailwind.css deleted file mode 100644 index e3cabadd..00000000 --- a/app/assets/builds/tailwind.css +++ /dev/null @@ -1,6415 +0,0 @@ -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -/* -! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: Inter var, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden]:where(:not([hidden="until-found"])) { - display: none; -} - -:root, -[data-theme] { - background-color: var(--fallback-b1,oklch(var(--b1)/1)); - color: var(--fallback-bc,oklch(var(--bc)/1)); -} - -@supports not (color: oklch(0 0 0)) { - :root { - color-scheme: light; - --fallback-p: #491eff; - --fallback-pc: #d4dbff; - --fallback-s: #ff41c7; - --fallback-sc: #fff9fc; - --fallback-a: #00cfbd; - --fallback-ac: #00100d; - --fallback-n: #2b3440; - --fallback-nc: #d7dde4; - --fallback-b1: #ffffff; - --fallback-b2: #e5e6e6; - --fallback-b3: #e5e6e6; - --fallback-bc: #1f2937; - --fallback-in: #00b3f0; - --fallback-inc: #000000; - --fallback-su: #00ca92; - --fallback-suc: #000000; - --fallback-wa: #ffc22d; - --fallback-wac: #000000; - --fallback-er: #ff6f70; - --fallback-erc: #000000; - } - - @media (prefers-color-scheme: dark) { - :root { - color-scheme: dark; - --fallback-p: #7582ff; - --fallback-pc: #050617; - --fallback-s: #ff71cf; - --fallback-sc: #190211; - --fallback-a: #00c7b5; - --fallback-ac: #000e0c; - --fallback-n: #2a323c; - --fallback-nc: #a6adbb; - --fallback-b1: #1d232a; - --fallback-b2: #191e24; - --fallback-b3: #15191e; - --fallback-bc: #a6adbb; - --fallback-in: #00b3f0; - --fallback-inc: #000000; - --fallback-su: #00ca92; - --fallback-suc: #000000; - --fallback-wa: #ffc22d; - --fallback-wac: #000000; - --fallback-er: #ff6f70; - --fallback-erc: #000000; - } - } -} - -html { - -webkit-tap-highlight-color: transparent; -} - -:root { - color-scheme: light; - --in: 0.7206 0.191 231.6; - --su: 64.8% 0.150 160; - --wa: 0.8471 0.199 83.87; - --er: 0.7176 0.221 22.18; - --pc: 0.89824 0.06192 275.75; - --ac: 0.15352 0.0368 183.61; - --inc: 0 0 0; - --suc: 0 0 0; - --wac: 0 0 0; - --erc: 0 0 0; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: .2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; - --p: 0.4912 0.3096 275.75; - --s: 0.6971 0.329 342.55; - --sc: 0.9871 0.0106 342.55; - --a: 0.7676 0.184 183.61; - --n: 0.321785 0.02476 255.701624; - --nc: 0.894994 0.011585 252.096176; - --b1: 1 0 0; - --b2: 0.961151 0 0; - --b3: 0.924169 0.00108 197.137559; - --bc: 0.278078 0.029596 256.847952; -} - -@media (prefers-color-scheme: dark) { - :root { - color-scheme: dark; - --in: 0.7206 0.191 231.6; - --su: 64.8% 0.150 160; - --wa: 0.8471 0.199 83.87; - --er: 0.7176 0.221 22.18; - --pc: 0.13138 0.0392 275.75; - --sc: 0.1496 0.052 342.55; - --ac: 0.14902 0.0334 183.61; - --inc: 0 0 0; - --suc: 0 0 0; - --wac: 0 0 0; - --erc: 0 0 0; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: .2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; - --p: 0.6569 0.196 275.75; - --s: 0.748 0.26 342.55; - --a: 0.7451 0.167 183.61; - --n: 0.313815 0.021108 254.139175; - --nc: 0.746477 0.0216 264.435964; - --b1: 0.253267 0.015896 252.417568; - --b2: 0.232607 0.013807 253.100675; - --b3: 0.211484 0.01165 254.087939; - --bc: 0.746477 0.0216 264.435964; - } -} - -[data-theme=light] { - color-scheme: light; - --in: 0.7206 0.191 231.6; - --su: 64.8% 0.150 160; - --wa: 0.8471 0.199 83.87; - --er: 0.7176 0.221 22.18; - --pc: 0.89824 0.06192 275.75; - --ac: 0.15352 0.0368 183.61; - --inc: 0 0 0; - --suc: 0 0 0; - --wac: 0 0 0; - --erc: 0 0 0; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: .2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; - --p: 0.4912 0.3096 275.75; - --s: 0.6971 0.329 342.55; - --sc: 0.9871 0.0106 342.55; - --a: 0.7676 0.184 183.61; - --n: 0.321785 0.02476 255.701624; - --nc: 0.894994 0.011585 252.096176; - --b1: 1 0 0; - --b2: 0.961151 0 0; - --b3: 0.924169 0.00108 197.137559; - --bc: 0.278078 0.029596 256.847952; -} - -[data-theme=dark] { - color-scheme: dark; - --in: 0.7206 0.191 231.6; - --su: 64.8% 0.150 160; - --wa: 0.8471 0.199 83.87; - --er: 0.7176 0.221 22.18; - --pc: 0.13138 0.0392 275.75; - --sc: 0.1496 0.052 342.55; - --ac: 0.14902 0.0334 183.61; - --inc: 0 0 0; - --suc: 0 0 0; - --wac: 0 0 0; - --erc: 0 0 0; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: .2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; - --p: 0.6569 0.196 275.75; - --s: 0.748 0.26 342.55; - --a: 0.7451 0.167 183.61; - --n: 0.313815 0.021108 254.139175; - --nc: 0.746477 0.0216 264.435964; - --b1: 0.253267 0.015896 252.417568; - --b2: 0.232607 0.013807 253.100675; - --b3: 0.211484 0.01165 254.087939; - --bc: 0.746477 0.0216 264.435964; -} - -[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0px; - padding-top: 0.5rem; - padding-right: 0.75rem; - padding-bottom: 0.5rem; - padding-left: 0.75rem; - font-size: 1rem; - line-height: 1.5rem; - --tw-shadow: 0 0 #0000; -} - -[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - border-color: #2563eb; -} - -input::-moz-placeholder, textarea::-moz-placeholder { - color: #6b7280; - opacity: 1; -} - -input::placeholder,textarea::placeholder { - color: #6b7280; - opacity: 1; -} - -::-webkit-datetime-edit-fields-wrapper { - padding: 0; -} - -::-webkit-date-and-time-value { - min-height: 1.5em; - text-align: inherit; -} - -::-webkit-datetime-edit { - display: inline-flex; -} - -::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { - padding-top: 0; - padding-bottom: 0; -} - -select { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); - background-position: right 0.5rem center; - background-repeat: no-repeat; - background-size: 1.5em 1.5em; - padding-right: 2.5rem; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; -} - -[multiple],[size]:where(select:not([size="1"])) { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: 0.75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -[type='checkbox'],[type='radio'] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: 0; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - display: inline-block; - vertical-align: middle; - background-origin: border-box; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-shrink: 0; - height: 1rem; - width: 1rem; - color: #2563eb; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - --tw-shadow: 0 0 #0000; -} - -[type='checkbox'] { - border-radius: 0px; -} - -[type='radio'] { - border-radius: 100%; -} - -[type='checkbox']:focus,[type='radio']:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 2px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); -} - -[type='checkbox']:checked,[type='radio']:checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type='checkbox']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - [type='checkbox']:checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='radio']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - [type='radio']:checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='checkbox']:indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -@media (forced-colors: active) { - [type='checkbox']:indeterminate { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='file'] { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -[type='file']:focus { - outline: 1px solid ButtonText; - outline: 1px auto -webkit-focus-ring-color; -} - -.\!container { - width: 100% !important; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .\!container { - max-width: 640px !important; - } - - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .\!container { - max-width: 768px !important; - } - - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .\!container { - max-width: 1024px !important; - } - - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .\!container { - max-width: 1280px !important; - } - - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .\!container { - max-width: 1536px !important; - } - - .container { - max-width: 1536px; - } -} - -.alert { - display: grid; - width: 100%; - grid-auto-flow: row; - align-content: flex-start; - align-items: center; - justify-items: center; - gap: 1rem; - text-align: center; - border-radius: var(--rounded-box, 1rem); - border-width: 1px; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - padding: 1rem; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --alert-bg: var(--fallback-b2,oklch(var(--b2)/1)); - --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1)); - background-color: var(--alert-bg); -} - -@media (min-width: 640px) { - .alert { - grid-auto-flow: column; - grid-template-columns: auto minmax(auto,1fr); - justify-items: start; - text-align: start; - } -} - -.avatar.placeholder > div { - display: flex; - align-items: center; - justify-content: center; -} - -.badge { - display: inline-flex; - align-items: center; - justify-content: center; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; - height: 1.25rem; - font-size: 0.875rem; - line-height: 1.25rem; - width: -moz-fit-content; - width: fit-content; - padding-left: 0.563rem; - padding-right: 0.563rem; - border-radius: var(--rounded-badge, 1.9rem); - border-width: 1px; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -@media (hover:hover) { - .label a:hover { - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - } - - .menu li > *:not(ul, .menu-title, details, .btn):active, -.menu li > *:not(ul, .menu-title, details, .btn).active, -.menu li > details > summary:active { - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); - } - - .radio-primary:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - } - - .tab:hover { - --tw-text-opacity: 1; - } - - .tabs-boxed .tab-active:not(.tab-disabled):not([disabled]):hover, - .tabs-boxed :is(input:checked):hover { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - } - - .table tr.hover:hover, - .table tr.hover:nth-child(even):hover { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - } - - .table-zebra tr.hover:hover, - .table-zebra tr.hover:nth-child(even):hover { - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } -} - -.btn { - display: inline-flex; - height: 3rem; - min-height: 3rem; - flex-shrink: 0; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - border-radius: var(--rounded-btn, 0.5rem); - border-color: transparent; - border-color: oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity)); - padding-left: 1rem; - padding-right: 1rem; - text-align: center; - font-size: 0.875rem; - line-height: 1em; - gap: 0.5rem; - font-weight: 600; - text-decoration-line: none; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - border-width: var(--border-btn, 1px); - animation: button-pop var(--animation-btn, 0.25s) ease-out; - transition-property: color, background-color, border-color, opacity, box-shadow, transform; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: var(--fallback-bc,oklch(var(--bc)/1)); - background-color: oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity)); - --tw-bg-opacity: 1; - --tw-border-opacity: 1; -} - -.btn-disabled, - .btn[disabled], - .btn:disabled { - pointer-events: none; -} - -:where(.btn:is(input[type="checkbox"])), -:where(.btn:is(input[type="radio"])) { - width: auto; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.btn:is(input[type="checkbox"]):after, -.btn:is(input[type="radio"]):after { - --tw-content: attr(aria-label); - content: var(--tw-content); -} - -.card { - position: relative; - display: flex; - flex-direction: column; - border-radius: var(--rounded-box, 1rem); -} - -.card:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.card-body { - display: flex; - flex: 1 1 auto; - flex-direction: column; - padding: var(--padding-card, 2rem); - gap: 0.5rem; -} - -.card-body :where(p) { - flex-grow: 1; -} - -.card-actions { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: 0.5rem; -} - -.card figure { - display: flex; - align-items: center; - justify-content: center; -} - -.card.image-full { - display: grid; -} - -.card.image-full:before { - position: relative; - content: ""; - z-index: 10; - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - opacity: 0.75; -} - -.card.image-full:before, - .card.image-full > * { - grid-column-start: 1; - grid-row-start: 1; -} - -.card.image-full > figure img { - height: 100%; - -o-object-fit: cover; - object-fit: cover; -} - -.card.image-full > .card-body { - position: relative; - z-index: 20; - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); -} - -.checkbox { - flex-shrink: 0; - --chkbg: var(--fallback-bc,oklch(var(--bc)/1)); - --chkfg: var(--fallback-b1,oklch(var(--b1)/1)); - height: 1.5rem; - width: 1.5rem; - cursor: pointer; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border-radius: var(--rounded-btn, 0.5rem); - border-width: 1px; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0.2; -} - -.divider { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; - margin-top: 1rem; - margin-bottom: 1rem; - height: 1rem; - white-space: nowrap; -} - -.divider:before, - .divider:after { - height: 0.125rem; - width: 100%; - flex-grow: 1; - --tw-content: ''; - content: var(--tw-content); - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); -} - -.\!drawer { - position: relative !important; - display: grid !important; - grid-auto-columns: max-content auto !important; - width: 100% !important; -} - -.drawer { - position: relative; - display: grid; - grid-auto-columns: max-content auto; - width: 100%; -} - -.dropdown { - position: relative; - display: inline-block; -} - -.dropdown > *:not(summary):focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.dropdown .dropdown-content { - position: absolute; -} - -.dropdown:is(:not(details)) .dropdown-content { - visibility: hidden; - opacity: 0; - transform-origin: top; - --tw-scale-x: .95; - --tw-scale-y: .95; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; -} - -.dropdown-end .dropdown-content { - inset-inline-end: 0px; -} - -.dropdown-left .dropdown-content { - bottom: auto; - inset-inline-end: 100%; - top: 0px; - transform-origin: right; -} - -.dropdown-right .dropdown-content { - bottom: auto; - inset-inline-start: 100%; - top: 0px; - transform-origin: left; -} - -.dropdown-bottom .dropdown-content { - bottom: auto; - top: 100%; - transform-origin: top; -} - -.dropdown-top .dropdown-content { - bottom: 100%; - top: auto; - transform-origin: bottom; -} - -.dropdown-end.dropdown-right .dropdown-content { - bottom: 0px; - top: auto; -} - -.dropdown-end.dropdown-left .dropdown-content { - bottom: 0px; - top: auto; -} - -.dropdown.dropdown-open .dropdown-content, -.dropdown:not(.dropdown-hover):focus .dropdown-content, -.dropdown:focus-within .dropdown-content { - visibility: visible; - opacity: 1; -} - -@media (hover: hover) { - .dropdown.dropdown-hover:hover .dropdown-content { - visibility: visible; - opacity: 1; - } - - .btm-nav > *.disabled:hover, - .btm-nav > *[disabled]:hover { - pointer-events: none; - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - .btn:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn:hover { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - } - - @supports not (color: oklch(0 0 0)) { - .btn:hover { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .btn.glass:hover { - --glass-opacity: 25%; - --glass-border-opacity: 15%; - } - - .btn-ghost:hover { - border-color: transparent; - } - - @supports (color: oklch(0 0 0)) { - .btn-ghost:hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - } - } - - .btn-outline:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity))); - } - - .btn-outline.btn-primary:hover { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-primary:hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - - .btn-outline.btn-secondary:hover { - --tw-text-opacity: 1; - color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-secondary:hover { - background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); - } - } - - .btn-outline.btn-accent:hover { - --tw-text-opacity: 1; - color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-accent:hover { - background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); - } - } - - .btn-outline.btn-success:hover { - --tw-text-opacity: 1; - color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-success:hover { - background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); - } - } - - .btn-outline.btn-info:hover { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-info:hover { - background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - } - } - - .btn-outline.btn-warning:hover { - --tw-text-opacity: 1; - color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-warning:hover { - background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); - } - } - - .btn-outline.btn-error:hover { - --tw-text-opacity: 1; - color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-error:hover { - background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); - } - } - - .btn-disabled:hover, - .btn[disabled]:hover, - .btn:disabled:hover { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn:is(input[type="checkbox"]:checked):hover, .btn:is(input[type="radio"]:checked):hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - - .dropdown.dropdown-hover:hover .dropdown-content { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - } - - :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { - cursor: pointer; - outline: 2px solid transparent; - outline-offset: 2px; - } - - @supports (color: oklch(0 0 0)) { - :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(.active, .btn):hover, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(.active, .btn):hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); - } - } - - .tab[disabled], - .tab[disabled]:hover { - cursor: not-allowed; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } -} - -.dropdown:is(details) summary::-webkit-details-marker { - display: none; -} - -.file-input { - height: 3rem; - flex-shrink: 1; - padding-inline-end: 1rem; - font-size: 1rem; - line-height: 2; - line-height: 1.5rem; - overflow: hidden; - border-radius: var(--rounded-btn, 0.5rem); - border-width: 1px; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.file-input::file-selector-button { - margin-inline-end: 1rem; - display: inline-flex; - height: 100%; - flex-shrink: 0; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - padding-left: 1rem; - padding-right: 1rem; - text-align: center; - font-size: 0.875rem; - line-height: 1.25rem; - line-height: 1em; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; - border-style: solid; - --tw-border-opacity: 1; - border-color: var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - font-weight: 600; - text-transform: uppercase; - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); - text-decoration-line: none; - border-width: var(--border-btn, 1px); - animation: button-pop var(--animation-btn, 0.25s) ease-out; -} - -.footer { - display: grid; - width: 100%; - grid-auto-flow: row; - place-items: start; - -moz-column-gap: 1rem; - column-gap: 1rem; - row-gap: 2.5rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.footer > * { - display: grid; - place-items: start; - gap: 0.5rem; -} - -@media (min-width: 48rem) { - .footer { - grid-auto-flow: column; - } - - .footer-center { - grid-auto-flow: row dense; - } -} - -.form-control { - display: flex; - flex-direction: column; -} - -.label { - display: flex; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - align-items: center; - justify-content: space-between; - padding-left: 0.25rem; - padding-right: 0.25rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.hero { - display: grid; - width: 100%; - place-items: center; - background-size: cover; - background-position: center; -} - -.hero > * { - grid-column-start: 1; - grid-row-start: 1; -} - -.hero-content { - z-index: 0; - display: flex; - align-items: center; - justify-content: center; - max-width: 80rem; - gap: 1rem; - padding: 1rem; -} - -.indicator { - position: relative; - display: inline-flex; - width: -moz-max-content; - width: max-content; -} - -.indicator :where(.indicator-item) { - z-index: 1; - position: absolute; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - white-space: nowrap; -} - -.input { - flex-shrink: 1; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - height: 3rem; - padding-left: 1rem; - padding-right: 1rem; - font-size: 1rem; - line-height: 2; - line-height: 1.5rem; - border-radius: var(--rounded-btn, 0.5rem); - border-width: 1px; - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.input[type="number"]::-webkit-inner-spin-button, -.input-md[type="number"]::-webkit-inner-spin-button { - margin-top: -1rem; - margin-bottom: -1rem; - margin-inline-end: -1rem; -} - -.input-xs[type="number"]::-webkit-inner-spin-button { - margin-top: -0.25rem; - margin-bottom: -0.25rem; - margin-inline-end: -0px; -} - -.input-sm[type="number"]::-webkit-inner-spin-button { - margin-top: 0px; - margin-bottom: 0px; - margin-inline-end: -0px; -} - -.join { - display: inline-flex; - align-items: stretch; - border-radius: var(--rounded-btn, 0.5rem); -} - -.join :where(.join-item) { - border-start-end-radius: 0; - border-end-end-radius: 0; - border-end-start-radius: 0; - border-start-start-radius: 0; -} - -.join .join-item:not(:first-child):not(:last-child), - .join *:not(:first-child):not(:last-child) .join-item { - border-start-end-radius: 0; - border-end-end-radius: 0; - border-end-start-radius: 0; - border-start-start-radius: 0; -} - -.join .join-item:first-child:not(:last-child), - .join *:first-child:not(:last-child) .join-item { - border-start-end-radius: 0; - border-end-end-radius: 0; -} - -.join .dropdown .join-item:first-child:not(:last-child), - .join *:first-child:not(:last-child) .dropdown .join-item { - border-start-end-radius: inherit; - border-end-end-radius: inherit; -} - -.join :where(.join-item:first-child:not(:last-child)), - .join :where(*:first-child:not(:last-child) .join-item) { - border-end-start-radius: inherit; - border-start-start-radius: inherit; -} - -.join .join-item:last-child:not(:first-child), - .join *:last-child:not(:first-child) .join-item { - border-end-start-radius: 0; - border-start-start-radius: 0; -} - -.join :where(.join-item:last-child:not(:first-child)), - .join :where(*:last-child:not(:first-child) .join-item) { - border-start-end-radius: inherit; - border-end-end-radius: inherit; -} - -@supports not selector(:has(*)) { - :where(.join *) { - border-radius: inherit; - } -} - -@supports selector(:has(*)) { - :where(.join *:has(.join-item)) { - border-radius: inherit; - } -} - -.link { - cursor: pointer; - text-decoration-line: underline; -} - -.menu { - display: flex; - flex-direction: column; - flex-wrap: wrap; - font-size: 0.875rem; - line-height: 1.25rem; - padding: 0.5rem; -} - -.menu :where(li ul) { - position: relative; - white-space: nowrap; - margin-inline-start: 1rem; - padding-inline-start: 0.5rem; -} - -.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), .menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: 0.5rem; - grid-auto-columns: minmax(auto, max-content) auto max-content; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.menu li.disabled { - cursor: not-allowed; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - color: var(--fallback-bc,oklch(var(--bc)/0.3)); -} - -.menu :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; -} - -:where(.menu li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; -} - -:where(.menu li) .badge { - justify-self: end; -} - -.modal { - pointer-events: none; - position: fixed; - inset: 0px; - margin: 0px; - display: grid; - height: 100%; - max-height: none; - width: 100%; - max-width: none; - justify-items: center; - padding: 0px; - opacity: 0; - overscroll-behavior: contain; - z-index: 999; - background-color: transparent; - color: inherit; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-property: transform, opacity, visibility; - overflow-y: hidden; -} - -:where(.modal) { - align-items: center; -} - -.modal-box { - max-height: calc(100vh - 5em); - grid-column-start: 1; - grid-row-start: 1; - width: 91.666667%; - max-width: 32rem; - --tw-scale-x: .9; - --tw-scale-y: .9; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-bottom-right-radius: var(--rounded-box, 1rem); - border-bottom-left-radius: var(--rounded-box, 1rem); - border-top-left-radius: var(--rounded-box, 1rem); - border-top-right-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - padding: 1.5rem; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; - box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px; - overflow-y: auto; - overscroll-behavior: contain; -} - -.modal-open, -.modal:target, -.modal-toggle:checked + .modal, -.modal[open] { - pointer-events: auto; - visibility: visible; - opacity: 1; -} - -.modal-action { - display: flex; - margin-top: 1.5rem; - justify-content: flex-end; -} - -.modal-toggle { - position: fixed; - height: 0px; - width: 0px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - opacity: 0; -} - -:root:has(:is(.modal-open, .modal:target, .modal-toggle:checked + .modal, .modal[open])) { - overflow: hidden; -} - -.navbar { - display: flex; - align-items: center; - padding: var(--navbar-padding, 0.5rem); - min-height: 4rem; - width: 100%; -} - -:where(.navbar > *:not(script, style)) { - display: inline-flex; - align-items: center; -} - -.navbar-start { - width: 50%; - justify-content: flex-start; -} - -.navbar-center { - flex-shrink: 0; -} - -.navbar-end { - width: 50%; - justify-content: flex-end; -} - -.progress { - position: relative; - width: 100%; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - overflow: hidden; - height: 0.5rem; - border-radius: var(--rounded-box, 1rem); - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.radio { - flex-shrink: 0; - --chkbg: var(--bc); - height: 1.5rem; - width: 1.5rem; - cursor: pointer; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border-radius: 9999px; - border-width: 1px; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0.2; -} - -.range { - height: 1.5rem; - width: 100%; - cursor: pointer; - -moz-appearance: none; - appearance: none; - -webkit-appearance: none; - --range-shdw: var(--fallback-bc,oklch(var(--bc)/1)); - overflow: hidden; - border-radius: var(--rounded-box, 1rem); - background-color: transparent; -} - -.range:focus { - outline: none; -} - -.select { - display: inline-flex; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - height: 3rem; - min-height: 3rem; - padding-left: 1rem; - padding-right: 2.5rem; - font-size: 0.875rem; - line-height: 1.25rem; - line-height: 2; - border-radius: var(--rounded-btn, 0.5rem); - border-width: 1px; - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), - linear-gradient(135deg, currentColor 50%, transparent 50%); - background-position: calc(100% - 20px) calc(1px + 50%), - calc(100% - 16.1px) calc(1px + 50%); - background-size: 4px 4px, - 4px 4px; - background-repeat: no-repeat; -} - -.select[multiple] { - height: auto; -} - -.stack { - display: inline-grid; - place-items: center; - align-items: flex-end; -} - -.stack > * { - grid-column-start: 1; - grid-row-start: 1; - transform: translateY(10%) scale(0.9); - z-index: 1; - width: 100%; - opacity: 0.6; -} - -.stack > *:nth-child(2) { - transform: translateY(5%) scale(0.95); - z-index: 2; - opacity: 0.8; -} - -.stack > *:nth-child(1) { - transform: translateY(0) scale(1); - z-index: 3; - opacity: 1; -} - -.stats { - display: inline-grid; - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -:where(.stats) { - grid-auto-flow: column; - overflow-x: auto; -} - -.stat { - display: inline-grid; - width: 100%; - grid-template-columns: repeat(1, 1fr); - -moz-column-gap: 1rem; - column-gap: 1rem; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0.1; - padding-left: 1.5rem; - padding-right: 1.5rem; - padding-top: 1rem; - padding-bottom: 1rem; -} - -.stat-title { - grid-column-start: 1; - white-space: nowrap; - color: var(--fallback-bc,oklch(var(--bc)/0.6)); -} - -.stat-value { - grid-column-start: 1; - white-space: nowrap; - font-size: 2.25rem; - line-height: 2.5rem; - font-weight: 800; -} - -.stat-desc { - grid-column-start: 1; - white-space: nowrap; - font-size: 0.75rem; - line-height: 1rem; - color: var(--fallback-bc,oklch(var(--bc)/0.6)); -} - -.steps .step { - display: grid; - grid-template-columns: repeat(1, minmax(0, 1fr)); - grid-template-columns: auto; - grid-template-rows: repeat(2, minmax(0, 1fr)); - grid-template-rows: 40px 1fr; - place-items: center; - text-align: center; - min-width: 4rem; -} - -.tabs { - display: grid; - align-items: flex-end; -} - -.tabs-lifted:has(.tab-content[class^="rounded-"]) .tab:first-child:not(.tab-active), - .tabs-lifted:has(.tab-content[class*=" rounded-"]) .tab:first-child:not(.tab-active) { - border-bottom-color: transparent; -} - -.tab { - position: relative; - grid-row-start: 1; - display: inline-flex; - height: 2rem; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - text-align: center; - font-size: 0.875rem; - line-height: 1.25rem; - line-height: 2; - --tab-padding: 1rem; - --tw-text-opacity: 0.5; - --tab-color: var(--fallback-bc,oklch(var(--bc)/1)); - --tab-bg: var(--fallback-b1,oklch(var(--b1)/1)); - --tab-border-color: var(--fallback-b3,oklch(var(--b3)/1)); - color: var(--tab-color); - padding-inline-start: var(--tab-padding, 1rem); - padding-inline-end: var(--tab-padding, 1rem); -} - -.tab:is(input[type="radio"]) { - width: auto; - border-bottom-right-radius: 0px; - border-bottom-left-radius: 0px; -} - -.tab:is(input[type="radio"]):after { - --tw-content: attr(aria-label); - content: var(--tw-content); -} - -.tab:not(input):empty { - cursor: default; - grid-column-start: span 9999; -} - -:checked + .tab-content:nth-child(2), - .tab-active + .tab-content:nth-child(2) { - border-start-start-radius: 0px; -} - -input.tab:checked + .tab-content, -.tab-active + .tab-content { - display: block; -} - -.table { - position: relative; - width: 100%; - border-radius: var(--rounded-box, 1rem); - text-align: left; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.table :where(.table-pin-rows thead tr) { - position: sticky; - top: 0px; - z-index: 1; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.table :where(.table-pin-rows tfoot tr) { - position: sticky; - bottom: 0px; - z-index: 1; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.table :where(.table-pin-cols tr th) { - position: sticky; - left: 0px; - right: 0px; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.timeline { - position: relative; - display: flex; -} - -:where(.timeline > li) { - position: relative; - display: grid; - flex-shrink: 0; - align-items: center; - 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) - ); -} - -.timeline > li > hr { - width: 100%; - border-width: 0px; -} - -:where(.timeline > li > hr):first-child { - grid-column-start: 1; - grid-row-start: 2; -} - -:where(.timeline > li > hr):last-child { - grid-column-start: 3; - grid-column-end: none; - grid-row-start: 2; - grid-row-end: auto; -} - -.timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 2; - margin: 0.25rem; - align-self: flex-end; - justify-self: center; -} - -.timeline-middle { - grid-column-start: 2; - grid-row-start: 2; -} - -.timeline-end { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; -} - -.toggle { - flex-shrink: 0; - --tglbg: var(--fallback-b1,oklch(var(--b1)/1)); - --handleoffset: 1.5rem; - --handleoffsetcalculator: calc(var(--handleoffset) * -1); - --togglehandleborder: 0 0; - height: 1.5rem; - width: 3rem; - cursor: pointer; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border-radius: var(--rounded-badge, 1.9rem); - border-width: 1px; - border-color: currentColor; - background-color: currentColor; - color: var(--fallback-bc,oklch(var(--bc)/0.5)); - transition: background, - box-shadow var(--animation-input, 0.2s) ease-out; - box-shadow: var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset, - 0 0 0 2px var(--tglbg) inset, - var(--togglehandleborder); -} - -.alert-info { - border-color: var(--fallback-in,oklch(var(--in)/0.2)); - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - --alert-bg: var(--fallback-in,oklch(var(--in)/1)); - --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1)); -} - -.badge-primary { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.badge-secondary { - --tw-border-opacity: 1; - border-color: var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); -} - -.badge-success { - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); -} - -.badge-warning { - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); -} - -.badge-outline { - border-color: currentColor; - --tw-border-opacity: 0.5; - background-color: transparent; - color: currentColor; -} - -.badge-outline.badge-neutral { - --tw-text-opacity: 1; - color: var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity))); -} - -.badge-outline.badge-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -.badge-outline.badge-secondary { - --tw-text-opacity: 1; - color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity))); -} - -.badge-outline.badge-accent { - --tw-text-opacity: 1; - color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); -} - -.badge-outline.badge-info { - --tw-text-opacity: 1; - color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); -} - -.badge-outline.badge-success { - --tw-text-opacity: 1; - color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); -} - -.badge-outline.badge-warning { - --tw-text-opacity: 1; - color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity))); -} - -.badge-outline.badge-error { - --tw-text-opacity: 1; - color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); -} - -.btm-nav > *:where(.active) { - border-top-width: 2px; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); -} - -.btm-nav > *.disabled, - .btm-nav > *[disabled] { - pointer-events: none; - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.btm-nav > * .label { - font-size: 1rem; - line-height: 1.5rem; -} - -.btn:active:hover, - .btn:active:focus { - animation: button-pop 0s ease-out; - transform: scale(var(--btn-focus-scale, 0.97)); -} - -@supports not (color: oklch(0 0 0)) { - .btn { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - - .btn-primary { - --btn-color: var(--fallback-p); - } - - .btn-neutral { - --btn-color: var(--fallback-n); - } - - .btn-info { - --btn-color: var(--fallback-in); - } - - .btn-success { - --btn-color: var(--fallback-su); - } - - .btn-warning { - --btn-color: var(--fallback-wa); - } - - .btn-error { - --btn-color: var(--fallback-er); - } -} - -@supports (color: color-mix(in oklab, black, black)) { - .btn-active { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b3)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b3)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - - .btn-outline.btn-primary.btn-active { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - - .btn-outline.btn-secondary.btn-active { - background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black); - } - - .btn-outline.btn-accent.btn-active { - background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); - } - - .btn-outline.btn-success.btn-active { - background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black); - } - - .btn-outline.btn-info.btn-active { - background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - } - - .btn-outline.btn-warning.btn-active { - background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black); - } - - .btn-outline.btn-error.btn-active { - background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black); - } -} - -.btn:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; -} - -.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -@supports (color: oklch(0 0 0)) { - .btn-primary { - --btn-color: var(--p); - } - - .btn-neutral { - --btn-color: var(--n); - } - - .btn-info { - --btn-color: var(--in); - } - - .btn-success { - --btn-color: var(--su); - } - - .btn-warning { - --btn-color: var(--wa); - } - - .btn-error { - --btn-color: var(--er); - } -} - -.btn-neutral { - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); - outline-color: var(--fallback-n,oklch(var(--n)/1)); -} - -.btn-info { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - outline-color: var(--fallback-in,oklch(var(--in)/1)); -} - -.btn-success { - --tw-text-opacity: 1; - color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); - outline-color: var(--fallback-su,oklch(var(--su)/1)); -} - -.btn-warning { - --tw-text-opacity: 1; - color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); - outline-color: var(--fallback-wa,oklch(var(--wa)/1)); -} - -.btn-error { - --tw-text-opacity: 1; - color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); - outline-color: var(--fallback-er,oklch(var(--er)/1)); -} - -.btn.glass { - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; -} - -.btn.glass.btn-active { - --glass-opacity: 25%; - --glass-border-opacity: 15%; -} - -.btn-ghost { - border-width: 1px; - border-color: transparent; - background-color: transparent; - color: currentColor; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; -} - -.btn-ghost.btn-active { - border-color: transparent; - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.btn-link.btn-active { - border-color: transparent; - background-color: transparent; - text-decoration-line: underline; -} - -.btn-outline { - border-color: currentColor; - background-color: transparent; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.btn-outline.btn-active { - --tw-border-opacity: 1; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity))); -} - -.btn-outline.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -.btn-outline.btn-primary.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.btn-outline.btn-secondary { - --tw-text-opacity: 1; - color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity))); -} - -.btn-outline.btn-secondary.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity))); -} - -.btn-outline.btn-accent { - --tw-text-opacity: 1; - color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); -} - -.btn-outline.btn-accent.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); -} - -.btn-outline.btn-success { - --tw-text-opacity: 1; - color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); -} - -.btn-outline.btn-success.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity))); -} - -.btn-outline.btn-info { - --tw-text-opacity: 1; - color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); -} - -.btn-outline.btn-info.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); -} - -.btn-outline.btn-warning { - --tw-text-opacity: 1; - color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity))); -} - -.btn-outline.btn-warning.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity))); -} - -.btn-outline.btn-error { - --tw-text-opacity: 1; - color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); -} - -.btn-outline.btn-error.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); -} - -.btn.btn-disabled, - .btn[disabled], - .btn:disabled { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.btn:is(input[type="checkbox"]:checked), -.btn:is(input[type="radio"]:checked) { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.btn:is(input[type="checkbox"]:checked):focus-visible, .btn:is(input[type="radio"]:checked):focus-visible { - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -@keyframes button-pop { - 0% { - transform: scale(var(--btn-focus-scale, 0.98)); - } - - 40% { - transform: scale(1.02); - } - - 100% { - transform: scale(1); - } -} - -.card :where(figure:first-child) { - overflow: hidden; - border-start-start-radius: inherit; - border-start-end-radius: inherit; - border-end-start-radius: unset; - border-end-end-radius: unset; -} - -.card :where(figure:last-child) { - overflow: hidden; - border-start-start-radius: unset; - border-start-end-radius: unset; - border-end-start-radius: inherit; - border-end-end-radius: inherit; -} - -.card:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; -} - -.card.bordered { - border-width: 1px; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); -} - -.card.compact .card-body { - padding: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.card-title { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 1.25rem; - line-height: 1.75rem; - font-weight: 600; -} - -.card.image-full :where(figure) { - overflow: hidden; - border-radius: inherit; -} - -.checkbox:focus { - box-shadow: none; -} - -.checkbox:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/1)); -} - -.checkbox:checked, - .checkbox[checked="true"], - .checkbox[aria-checked="true"] { - background-repeat: no-repeat; - animation: checkmark var(--animation-input, 0.2s) ease-out; - background-color: var(--chkbg); - background-image: linear-gradient(-45deg, transparent 65%, var(--chkbg) 65.99%), - linear-gradient(45deg, transparent 75%, var(--chkbg) 75.99%), - linear-gradient(-45deg, var(--chkbg) 40%, transparent 40.99%), - linear-gradient( - 45deg, - var(--chkbg) 30%, - var(--chkfg) 30.99%, - var(--chkfg) 40%, - transparent 40.99% - ), - linear-gradient(-45deg, var(--chkfg) 50%, var(--chkbg) 50.99%); -} - -.checkbox:indeterminate { - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - background-repeat: no-repeat; - animation: checkmark var(--animation-input, 0.2s) ease-out; - background-image: linear-gradient(90deg, transparent 80%, var(--chkbg) 80%), - linear-gradient(-90deg, transparent 80%, var(--chkbg) 80%), - linear-gradient(0deg, var(--chkbg) 43%, var(--chkfg) 43%, var(--chkfg) 57%, var(--chkbg) 57%); -} - -.checkbox:disabled { - cursor: not-allowed; - border-color: transparent; - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - opacity: 0.2; -} - -@keyframes checkmark { - 0% { - background-position-y: 5px; - } - - 50% { - background-position-y: -2px; - } - - 100% { - background-position-y: 0; - } -} - -.divider:not(:empty) { - gap: 1rem; -} - -.drawer-toggle:focus-visible ~ .drawer-content label.drawer-button { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; -} - -.dropdown.dropdown-open .dropdown-content, -.dropdown:focus .dropdown-content, -.dropdown:focus-within .dropdown-content { - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.file-input-bordered { - --tw-border-opacity: 0.2; -} - -.file-input:focus { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.file-input-disabled, - .file-input[disabled] { - cursor: not-allowed; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - --tw-text-opacity: 0.2; -} - -.file-input-disabled::-moz-placeholder, .file-input[disabled]::-moz-placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.file-input-disabled::placeholder, - .file-input[disabled]::placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.file-input-disabled::file-selector-button, .file-input[disabled]::file-selector-button { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.footer-title { - margin-bottom: 0.5rem; - font-weight: 700; - text-transform: uppercase; - opacity: 0.6; -} - -.label-text { - font-size: 0.875rem; - line-height: 1.25rem; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -.label-text-alt { - font-size: 0.75rem; - line-height: 1rem; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -.input input { - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - background-color: transparent; -} - -.input input:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.input[list]::-webkit-calendar-picker-indicator { - line-height: 1em; -} - -.input-bordered { - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.input:focus, - .input:focus-within { - box-shadow: none; - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.input-ghost { - --tw-bg-opacity: 0.05; -} - -.input-ghost:focus, - .input-ghost:focus-within { - --tw-bg-opacity: 1; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - box-shadow: none; -} - -.input-primary { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); -} - -.input-primary:focus, - .input-primary:focus-within { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -.input-error { - --tw-border-opacity: 1; - border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity))); -} - -.input-error:focus, - .input-error:focus-within { - --tw-border-opacity: 1; - border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity))); - outline-color: var(--fallback-er,oklch(var(--er)/1)); -} - -.input-disabled, - .input:disabled, - .input[disabled] { - cursor: not-allowed; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - color: var(--fallback-bc,oklch(var(--bc)/0.4)); -} - -.input-disabled::-moz-placeholder, .input:disabled::-moz-placeholder, .input[disabled]::-moz-placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.input-disabled::placeholder, - .input:disabled::placeholder, - .input[disabled]::placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.input::-webkit-date-and-time-value { - text-align: inherit; -} - -.join > :where(*:not(:first-child)) { - margin-top: 0px; - margin-bottom: 0px; - margin-inline-start: -1px; -} - -.join-item:focus { - isolation: isolate; -} - -.link-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -@supports (color:color-mix(in oklab,black,black)) { - @media (hover:hover) { - .link-primary:hover { - color: color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,black); - } - } -} - -.link:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.link:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; -} - -.loading { - pointer-events: none; - display: inline-block; - aspect-ratio: 1 / 1; - width: 1.5rem; - background-color: currentColor; - -webkit-mask-size: 100%; - mask-size: 100%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: center; - mask-position: center; - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); -} - -.loading-spinner { - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E"); -} - -.loading-dots { - -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_qM83%7Banimation:spinner_8HQG 1.05s infinite%7D.spinner_oXPr%7Banimation-delay:.1s%7D.spinner_ZTLf%7Banimation-delay:.2s%7D@keyframes spinner_8HQG%7B0%25,57.14%25%7Banimation-timing-function:cubic-bezier(0.33,.66,.66,1);transform:translate(0)%7D28.57%25%7Banimation-timing-function:cubic-bezier(0.33,0,.66,.33);transform:translateY(-6px)%7D100%25%7Btransform:translate(0)%7D%7D%3C/style%3E%3Ccircle class='spinner_qM83' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_oXPr' cx='12' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_ZTLf' cx='20' cy='12' r='3'/%3E%3C/svg%3E"); - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_qM83%7Banimation:spinner_8HQG 1.05s infinite%7D.spinner_oXPr%7Banimation-delay:.1s%7D.spinner_ZTLf%7Banimation-delay:.2s%7D@keyframes spinner_8HQG%7B0%25,57.14%25%7Banimation-timing-function:cubic-bezier(0.33,.66,.66,1);transform:translate(0)%7D28.57%25%7Banimation-timing-function:cubic-bezier(0.33,0,.66,.33);transform:translateY(-6px)%7D100%25%7Btransform:translate(0)%7D%7D%3C/style%3E%3Ccircle class='spinner_qM83' cx='4' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_oXPr' cx='12' cy='12' r='3'/%3E%3Ccircle class='spinner_qM83 spinner_ZTLf' cx='20' cy='12' r='3'/%3E%3C/svg%3E"); -} - -.loading-sm { - width: 1.25rem; -} - -.loading-md { - width: 1.5rem; -} - -.loading-lg { - width: 2.5rem; -} - -:where(.menu li:empty) { - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - opacity: 0.1; - margin: 0.5rem 1rem; - height: 1px; -} - -.menu :where(li ul):before { - position: absolute; - bottom: 0.75rem; - inset-inline-start: 0px; - top: 0.75rem; - width: 1px; - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - opacity: 0.1; - content: ""; -} - -.menu :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), -.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - border-radius: var(--rounded-btn, 0.5rem); - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - text-align: start; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 200ms; - text-wrap: balance; -} - -:where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title)):is(summary):not(.active, .btn):focus-visible, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn).focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):not(summary, .active, .btn):focus, :where(.menu li:not(.menu-title, .disabled) > details > summary:not(.menu-title)):is(summary):not(.active, .btn):focus-visible { - cursor: pointer; - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - outline: 2px solid transparent; - outline-offset: 2px; -} - -.menu li > *:not(ul, .menu-title, details, .btn):active, -.menu li > *:not(ul, .menu-title, details, .btn).active, -.menu li > details > summary:active { - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); -} - -.menu :where(li > details > summary)::-webkit-details-marker { - display: none; -} - -.menu :where(li > details > summary):after, -.menu :where(li > .menu-dropdown-toggle):after { - justify-self: end; - display: block; - margin-top: -0.5rem; - height: 0.5rem; - width: 0.5rem; - transform: rotate(45deg); - transition-property: transform, margin-top; - transition-duration: 0.3s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - content: ""; - transform-origin: 75% 75%; - box-shadow: 2px 2px; - pointer-events: none; -} - -.menu :where(li > details[open] > summary):after, -.menu :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - transform: rotate(225deg); - margin-top: 0; -} - -.mockup-phone .display { - overflow: hidden; - border-radius: 40px; - margin-top: -25px; -} - -.mockup-browser .mockup-browser-toolbar .input { - position: relative; - margin-left: auto; - margin-right: auto; - display: block; - height: 1.75rem; - width: 24rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - padding-left: 2rem; - direction: ltr; -} - -.mockup-browser .mockup-browser-toolbar .input:before { - content: ""; - position: absolute; - left: 0.5rem; - top: 50%; - aspect-ratio: 1 / 1; - height: 0.75rem; - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-radius: 9999px; - border-width: 2px; - border-color: currentColor; - opacity: 0.6; -} - -.mockup-browser .mockup-browser-toolbar .input:after { - content: ""; - position: absolute; - left: 1.25rem; - top: 50%; - height: 0.5rem; - --tw-translate-y: 25%; - --tw-rotate: -45deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-radius: 9999px; - border-width: 1px; - border-color: currentColor; - opacity: 0.6; -} - -.modal:not(dialog:not(.modal-open)), - .modal::backdrop { - background-color: #0006; - animation: modal-pop 0.2s ease-out; -} - -.modal-backdrop { - z-index: -1; - grid-column-start: 1; - grid-row-start: 1; - display: grid; - align-self: stretch; - justify-self: stretch; - color: transparent; -} - -.modal-open .modal-box, -.modal-toggle:checked + .modal .modal-box, -.modal:target .modal-box, -.modal[open] .modal-box { - --tw-translate-y: 0px; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.modal-action > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} - -@keyframes modal-pop { - 0% { - opacity: 0; - } -} - -.progress::-moz-progress-bar { - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); -} - -.progress-primary::-moz-progress-bar { - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); -} - -.progress:indeterminate { - --progress-color: var(--fallback-bc,oklch(var(--bc)/1)); - background-image: repeating-linear-gradient( - 90deg, - var(--progress-color) -1%, - var(--progress-color) 10%, - transparent 10%, - transparent 90% - ); - background-size: 200%; - background-position-x: 15%; - animation: progress-loading 5s ease-in-out infinite; -} - -.progress-primary:indeterminate { - --progress-color: var(--fallback-p,oklch(var(--p)/1)); -} - -.progress::-webkit-progress-bar { - border-radius: var(--rounded-box, 1rem); - background-color: transparent; -} - -.progress::-webkit-progress-value { - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); -} - -.progress-primary::-webkit-progress-value { - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); -} - -.progress:indeterminate::-moz-progress-bar { - background-color: transparent; - background-image: repeating-linear-gradient( - 90deg, - var(--progress-color) -1%, - var(--progress-color) 10%, - transparent 10%, - transparent 90% - ); - background-size: 200%; - background-position-x: 15%; - animation: progress-loading 5s ease-in-out infinite; -} - -@keyframes progress-loading { - 50% { - background-position-x: -115%; - } -} - -.radio:focus { - box-shadow: none; -} - -.radio:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/1)); -} - -.radio:checked, - .radio[aria-checked="true"] { - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); - background-image: none; - animation: radiomark var(--animation-input, 0.2s) ease-out; - box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; -} - -.radio-primary { - --chkbg: var(--p); - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); -} - -.radio-primary:focus-visible { - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -.radio-primary:checked, - .radio-primary[aria-checked="true"] { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -.radio:disabled { - cursor: not-allowed; - opacity: 0.2; -} - -@keyframes radiomark { - 0% { - box-shadow: 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } - - 50% { - box-shadow: 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } - - 100% { - box-shadow: 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset, - 0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset; - } -} - -.range:focus-visible::-webkit-slider-thumb { - --focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 2rem var(--range-shdw) inset; -} - -.range:focus-visible::-moz-range-thumb { - --focus-shadow: 0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset, 0 0 0 2rem var(--range-shdw) inset; -} - -.range::-webkit-slider-runnable-track { - height: 0.5rem; - width: 100%; - border-radius: var(--rounded-box, 1rem); - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); -} - -.range::-moz-range-track { - height: 0.5rem; - width: 100%; - border-radius: var(--rounded-box, 1rem); - background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); -} - -.range::-webkit-slider-thumb { - position: relative; - height: 1.5rem; - width: 1.5rem; - border-radius: var(--rounded-box, 1rem); - border-style: none; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - appearance: none; - -webkit-appearance: none; - top: 50%; - color: var(--range-shdw); - transform: translateY(-50%); - --filler-size: 100rem; - --filler-offset: 0.6rem; - box-shadow: 0 0 0 3px var(--range-shdw) inset, - var(--focus-shadow, 0 0), - calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size); -} - -.range::-moz-range-thumb { - position: relative; - height: 1.5rem; - width: 1.5rem; - border-radius: var(--rounded-box, 1rem); - border-style: none; - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - top: 50%; - color: var(--range-shdw); - --filler-size: 100rem; - --filler-offset: 0.5rem; - box-shadow: 0 0 0 3px var(--range-shdw) inset, - var(--focus-shadow, 0 0), - calc(var(--filler-size) * -1 - var(--filler-offset)) 0 0 var(--filler-size); -} - -@keyframes rating-pop { - 0% { - transform: translateY(-0.125em); - } - - 40% { - transform: translateY(-0.125em); - } - - 100% { - transform: translateY(0); - } -} - -.select-bordered { - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.select:focus { - box-shadow: none; - border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.select-disabled, - .select:disabled, - .select[disabled] { - cursor: not-allowed; - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.select-disabled::-moz-placeholder, .select:disabled::-moz-placeholder, .select[disabled]::-moz-placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.select-disabled::placeholder, - .select:disabled::placeholder, - .select[disabled]::placeholder { - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); - --tw-placeholder-opacity: 0.2; -} - -.select-multiple, - .select[multiple], - .select[size].select:not([size="1"]) { - background-image: none; - padding-right: 1rem; -} - -[dir="rtl"] .select { - background-position: calc(0% + 12px) calc(1px + 50%), - calc(0% + 16px) calc(1px + 50%); -} - -@keyframes skeleton { - from { - background-position: 150%; - } - - to { - background-position: -50%; - } -} - -:where(.stats) > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-right-width: calc(1px * var(--tw-divide-x-reverse)); - border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - --tw-divide-y-reverse: 0; - border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); -} - -:is([dir="rtl"] .stats > :not([hidden]) ~ :not([hidden])) { - --tw-divide-x-reverse: 1; -} - -.steps .step:before { - top: 0px; - grid-column-start: 1; - grid-row-start: 1; - height: 0.5rem; - width: 100%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - content: ""; - margin-inline-start: -100%; -} - -.steps .step:after { - content: counter(step); - counter-increment: step; - z-index: 1; - position: relative; - grid-column-start: 1; - grid-row-start: 1; - display: grid; - height: 2rem; - width: 2rem; - place-items: center; - place-self: center; - border-radius: 9999px; - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -.steps .step:first-child:before { - content: none; -} - -.steps .step[data-content]:after { - content: attr(data-content); -} - -.tabs-lifted > .tab:focus-visible { - border-end-end-radius: 0; - border-end-start-radius: 0; -} - -.tab.tab-active:not(.tab-disabled):not([disabled]), - .tab:is(input:checked) { - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 1; - --tw-text-opacity: 1; -} - -.tab:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -.tab:focus-visible { - outline: 2px solid currentColor; - outline-offset: -5px; -} - -.tab-disabled, - .tab[disabled] { - cursor: not-allowed; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; -} - -.tabs-bordered > .tab { - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0.2; - border-style: solid; - border-bottom-width: calc(var(--tab-border, 1px) + 1px); -} - -.tabs-lifted > .tab { - border: var(--tab-border, 1px) solid transparent; - border-width: 0 0 var(--tab-border, 1px) 0; - border-start-start-radius: var(--tab-radius, 0.5rem); - border-start-end-radius: var(--tab-radius, 0.5rem); - border-bottom-color: var(--tab-border-color); - padding-inline-start: var(--tab-padding, 1rem); - padding-inline-end: var(--tab-padding, 1rem); - padding-top: var(--tab-border, 1px); -} - -.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]), - .tabs-lifted > .tab:is(input:checked) { - background-color: var(--tab-bg); - border-width: var(--tab-border, 1px) var(--tab-border, 1px) 0 var(--tab-border, 1px); - border-inline-start-color: var(--tab-border-color); - border-inline-end-color: var(--tab-border-color); - border-top-color: var(--tab-border-color); - padding-inline-start: calc(var(--tab-padding, 1rem) - var(--tab-border, 1px)); - padding-inline-end: calc(var(--tab-padding, 1rem) - var(--tab-border, 1px)); - padding-bottom: var(--tab-border, 1px); - padding-top: 0; -} - -.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):before, .tabs-lifted > .tab:is(input:checked):before { - z-index: 1; - content: ""; - display: block; - position: absolute; - width: calc(100% + var(--tab-radius, 0.5rem) * 2); - height: var(--tab-radius, 0.5rem); - bottom: 0; - background-size: var(--tab-radius, 0.5rem); - background-position: top left, - top right; - background-repeat: no-repeat; - --tab-grad: calc(69% - var(--tab-border, 1px)); - --radius-start: radial-gradient( - circle at top left, - transparent var(--tab-grad), - var(--tab-border-color) calc(var(--tab-grad) + 0.25px), - var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), - var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px) - ); - --radius-end: radial-gradient( - circle at top right, - transparent var(--tab-grad), - var(--tab-border-color) calc(var(--tab-grad) + 0.25px), - var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)), - var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px) - ); - background-image: var(--radius-start), var(--radius-end); -} - -.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before, .tabs-lifted > .tab:is(input:checked):first-child:before { - background-image: var(--radius-end); - background-position: top right; -} - -[dir="rtl"] .tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before, [dir="rtl"] .tabs-lifted > .tab:is(input:checked):first-child:before { - background-image: var(--radius-start); - background-position: top left; -} - -.tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before, .tabs-lifted > .tab:is(input:checked):last-child:before { - background-image: var(--radius-start); - background-position: top left; -} - -[dir="rtl"] .tabs-lifted > .tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before, [dir="rtl"] .tabs-lifted > .tab:is(input:checked):last-child:before { - background-image: var(--radius-end); - background-position: top right; -} - -.tabs-lifted - > .tab-active:not(.tab-disabled):not([disabled]) - + .tabs-lifted - .tab-active:not(.tab-disabled):not([disabled]):before, .tabs-lifted > .tab:is(input:checked) + .tabs-lifted .tab:is(input:checked):before { - background-image: var(--radius-end); - background-position: top right; -} - -.tabs-boxed { - border-radius: var(--rounded-btn, 0.5rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - padding: 0.25rem; -} - -.tabs-boxed .tab { - border-radius: var(--rounded-btn, 0.5rem); -} - -.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]), - .tabs-boxed :is(input:checked) { - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - -:is([dir="rtl"] .table) { - text-align: right; -} - -.table :where(th, td) { - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - vertical-align: middle; -} - -.table tr.active, - .table tr.active:nth-child(even), - .table-zebra tbody tr:nth-child(even) { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); -} - -.table-zebra tr.active, - .table-zebra tr.active:nth-child(even), - .table-zebra-zebra tbody tr:nth-child(even) { - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); -} - -.table :where(thead, tbody) :where(tr:not(:last-child)), - .table :where(thead, tbody) :where(tr:first-child:last-child) { - border-bottom-width: 1px; - --tw-border-opacity: 1; - border-bottom-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); -} - -.table :where(thead, tfoot) { - white-space: nowrap; - font-size: 0.75rem; - line-height: 1rem; - font-weight: 700; - color: var(--fallback-bc,oklch(var(--bc)/0.6)); -} - -.timeline hr { - height: 0.25rem; -} - -:where(.timeline hr) { - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); -} - -:where(.timeline:has(.timeline-middle) hr):first-child { - border-start-end-radius: var(--rounded-badge, 1.9rem); - border-end-end-radius: var(--rounded-badge, 1.9rem); - border-start-start-radius: 0px; - border-end-start-radius: 0px; -} - -:where(.timeline:has(.timeline-middle) hr):last-child { - border-start-start-radius: var(--rounded-badge, 1.9rem); - border-end-start-radius: var(--rounded-badge, 1.9rem); - border-start-end-radius: 0px; - border-end-end-radius: 0px; -} - -:where(.timeline:not(:has(.timeline-middle)) :first-child hr:last-child) { - border-start-start-radius: var(--rounded-badge, 1.9rem); - border-end-start-radius: var(--rounded-badge, 1.9rem); - border-start-end-radius: 0px; - border-end-end-radius: 0px; -} - -:where(.timeline:not(:has(.timeline-middle)) :last-child hr:first-child) { - border-start-end-radius: var(--rounded-badge, 1.9rem); - border-end-end-radius: var(--rounded-badge, 1.9rem); - border-start-start-radius: 0px; - border-end-start-radius: 0px; -} - -.timeline-box { - border-radius: var(--rounded-box, 1rem); - border-width: 1px; - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - padding-left: 1rem; - padding-right: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -@keyframes toast-pop { - 0% { - transform: scale(0.9); - opacity: 0; - } - - 100% { - transform: scale(1); - opacity: 1; - } -} - -[dir="rtl"] .toggle { - --handleoffsetcalculator: calc(var(--handleoffset) * 1); -} - -.toggle:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.toggle:hover { - background-color: currentColor; -} - -.toggle:checked, - .toggle[checked="true"], - .toggle[aria-checked="true"] { - background-image: none; - --handleoffsetcalculator: var(--handleoffset); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -[dir="rtl"] .toggle:checked, [dir="rtl"] .toggle[checked="true"], [dir="rtl"] .toggle[aria-checked="true"] { - --handleoffsetcalculator: calc(var(--handleoffset) * -1); -} - -.toggle:indeterminate { - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, - calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, - 0 0 0 2px var(--tglbg) inset; -} - -[dir="rtl"] .toggle:indeterminate { - box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, - calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, - 0 0 0 2px var(--tglbg) inset; -} - -.toggle:disabled { - cursor: not-allowed; - --tw-border-opacity: 1; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - background-color: transparent; - opacity: 0.3; - --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset, - var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset; -} - -.glass, - .glass.btn-active { - border: none; - -webkit-backdrop-filter: blur(var(--glass-blur, 40px)); - backdrop-filter: blur(var(--glass-blur, 40px)); - background-color: transparent; - background-image: linear-gradient( - 135deg, - rgb(255 255 255 / var(--glass-opacity, 30%)) 0%, - rgb(0 0 0 / 0%) 100% - ), - linear-gradient( - var(--glass-reflex-degree, 100deg), - rgb(255 255 255 / var(--glass-reflex-opacity, 10%)) 25%, - rgb(0 0 0 / 0%) 25% - ); - box-shadow: 0 0 0 1px rgb(255 255 255 / var(--glass-border-opacity, 10%)) inset, - 0 0 0 2px rgb(0 0 0 / 5%); - text-shadow: 0 1px rgb(0 0 0 / var(--glass-text-shadow-opacity, 5%)); -} - -@media (hover: hover) { - .glass.btn-active { - border: none; - -webkit-backdrop-filter: blur(var(--glass-blur, 40px)); - backdrop-filter: blur(var(--glass-blur, 40px)); - background-color: transparent; - background-image: linear-gradient( - 135deg, - rgb(255 255 255 / var(--glass-opacity, 30%)) 0%, - rgb(0 0 0 / 0%) 100% - ), - linear-gradient( - var(--glass-reflex-degree, 100deg), - rgb(255 255 255 / var(--glass-reflex-opacity, 10%)) 25%, - rgb(0 0 0 / 0%) 25% - ); - box-shadow: 0 0 0 1px rgb(255 255 255 / var(--glass-border-opacity, 10%)) inset, - 0 0 0 2px rgb(0 0 0 / 5%); - text-shadow: 0 1px rgb(0 0 0 / var(--glass-text-shadow-opacity, 5%)); - } -} - -.badge-xs { - height: 0.75rem; - font-size: 0.75rem; - line-height: .75rem; - padding-left: 0.313rem; - padding-right: 0.313rem; -} - -.badge-sm { - height: 1rem; - font-size: 0.75rem; - line-height: 1rem; - padding-left: 0.438rem; - padding-right: 0.438rem; -} - -.btm-nav-xs > *:where(.active) { - border-top-width: 1px; -} - -.btm-nav-sm > *:where(.active) { - border-top-width: 2px; -} - -.btm-nav-md > *:where(.active) { - border-top-width: 2px; -} - -.btm-nav-lg > *:where(.active) { - border-top-width: 4px; -} - -.btn-xs { - height: 1.5rem; - min-height: 1.5rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - font-size: 0.75rem; -} - -.btn-sm { - height: 2rem; - min-height: 2rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - font-size: 0.875rem; -} - -.btn-square:where(.btn-xs) { - height: 1.5rem; - width: 1.5rem; - padding: 0px; -} - -.btn-square:where(.btn-sm) { - height: 2rem; - width: 2rem; - padding: 0px; -} - -.btn-circle:where(.btn-xs) { - height: 1.5rem; - width: 1.5rem; - border-radius: 9999px; - padding: 0px; -} - -.btn-circle:where(.btn-sm) { - height: 2rem; - width: 2rem; - border-radius: 9999px; - padding: 0px; -} - -[type="checkbox"].checkbox-sm { - height: 1.25rem; - width: 1.25rem; -} - -.indicator :where(.indicator-item) { - bottom: auto; - inset-inline-end: 0px; - inset-inline-start: auto; - top: 0px; - --tw-translate-y: -50%; - --tw-translate-x: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -:is([dir="rtl"] .indicator :where(.indicator-item)) { - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-start) { - inset-inline-end: auto; - inset-inline-start: 0px; - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -:is([dir="rtl"] .indicator :where(.indicator-item.indicator-start)) { - --tw-translate-x: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-center) { - inset-inline-end: 50%; - inset-inline-start: 50%; - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -:is([dir="rtl"] .indicator :where(.indicator-item.indicator-center)) { - --tw-translate-x: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-end) { - inset-inline-end: 0px; - inset-inline-start: auto; - --tw-translate-x: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -:is([dir="rtl"] .indicator :where(.indicator-item.indicator-end)) { - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-bottom) { - bottom: 0px; - top: auto; - --tw-translate-y: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-middle) { - bottom: 50%; - top: 50%; - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.indicator :where(.indicator-item.indicator-top) { - bottom: auto; - top: 0px; - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.input-xs { - height: 1.5rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - font-size: 0.75rem; - line-height: 1rem; - line-height: 1.625; -} - -.input-sm { - height: 2rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - font-size: 0.875rem; - line-height: 2rem; -} - -.join.join-vertical { - flex-direction: column; -} - -.join.join-vertical .join-item:first-child:not(:last-child), - .join.join-vertical *:first-child:not(:last-child) .join-item { - border-end-start-radius: 0; - border-end-end-radius: 0; - border-start-start-radius: inherit; - border-start-end-radius: inherit; -} - -.join.join-vertical .join-item:last-child:not(:first-child), - .join.join-vertical *:last-child:not(:first-child) .join-item { - border-start-start-radius: 0; - border-start-end-radius: 0; - border-end-start-radius: inherit; - border-end-end-radius: inherit; -} - -.join.join-horizontal { - flex-direction: row; -} - -.join.join-horizontal .join-item:first-child:not(:last-child), - .join.join-horizontal *:first-child:not(:last-child) .join-item { - border-end-end-radius: 0; - border-start-end-radius: 0; - border-end-start-radius: inherit; - border-start-start-radius: inherit; -} - -.join.join-horizontal .join-item:last-child:not(:first-child), - .join.join-horizontal *:last-child:not(:first-child) .join-item { - border-end-start-radius: 0; - border-start-start-radius: 0; - border-end-end-radius: inherit; - border-start-end-radius: inherit; -} - -.menu-horizontal { - display: inline-flex; - flex-direction: row; -} - -.menu-horizontal > li:not(.menu-title) > details > ul { - position: absolute; -} - -.select-sm { - height: 2rem; - min-height: 2rem; - padding-left: 0.75rem; - padding-right: 2rem; - font-size: 0.875rem; - line-height: 2rem; -} - -[dir="rtl"] .select-sm { - padding-left: 2rem; - padding-right: 0.75rem; -} - -.stats-vertical { - grid-auto-flow: row; -} - -.steps-horizontal .step { - display: grid; - grid-template-columns: repeat(1, minmax(0, 1fr)); - grid-template-rows: repeat(2, minmax(0, 1fr)); - place-items: center; - text-align: center; -} - -.steps-vertical .step { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - grid-template-rows: repeat(1, minmax(0, 1fr)); -} - -.tabs-md :where(.tab) { - height: 2rem; - font-size: 0.875rem; - line-height: 1.25rem; - line-height: 2; - --tab-padding: 1rem; -} - -.tabs-lg :where(.tab) { - height: 3rem; - font-size: 1.125rem; - line-height: 1.75rem; - line-height: 2; - --tab-padding: 1.25rem; -} - -.tabs-sm :where(.tab) { - height: 1.5rem; - font-size: 0.875rem; - line-height: .75rem; - --tab-padding: 0.75rem; -} - -.tabs-xs :where(.tab) { - height: 1.25rem; - font-size: 0.75rem; - line-height: .75rem; - --tab-padding: 0.5rem; -} - -.timeline-vertical { - flex-direction: column; -} - -.timeline-compact .timeline-start, .timeline-horizontal.timeline-compact .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; -} - -.timeline-compact li:has(.timeline-start) .timeline-end, .timeline-horizontal.timeline-compact li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; -} - -.timeline-vertical.timeline-compact > li { - --timeline-col-start: 0; -} - -.timeline-vertical.timeline-compact .timeline-start { - grid-column-start: 3; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 4; - align-self: center; - justify-self: start; -} - -.timeline-vertical.timeline-compact li:has(.timeline-start) .timeline-end { - grid-column-start: auto; - grid-row-start: none; -} - -:where(.timeline-vertical > li) { - --timeline-row-start: minmax(0, 1fr); - --timeline-row-end: minmax(0, 1fr); - justify-items: center; -} - -.timeline-vertical > li > hr { - height: 100%; -} - -:where(.timeline-vertical > li > hr):first-child { - grid-column-start: 2; - grid-row-start: 1; -} - -:where(.timeline-vertical > li > hr):last-child { - grid-column-start: 2; - grid-column-end: auto; - grid-row-start: 3; - grid-row-end: none; -} - -.timeline-vertical .timeline-start { - grid-column-start: 1; - grid-column-end: 2; - grid-row-start: 1; - grid-row-end: 4; - align-self: center; - justify-self: end; -} - -.timeline-vertical .timeline-end { - grid-column-start: 3; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 4; - align-self: center; - justify-self: start; -} - -.timeline-vertical:where(.timeline-snap-icon) > li { - --timeline-col-start: minmax(0, 1fr); - --timeline-row-start: 0.5rem; -} - -.timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 2; - align-self: flex-end; - justify-self: center; -} - -.timeline-horizontal .timeline-end { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - align-self: flex-start; - justify-self: center; -} - -:where(.timeline-snap-icon) > li, -.timeline-horizontal:where(.timeline-snap-icon) > li { - --timeline-col-start: 0.5rem; - --timeline-row-start: minmax(0, 1fr); -} - -.tooltip { - position: relative; - display: inline-block; - --tooltip-offset: calc(100% + 1px + var(--tooltip-tail, 0px)); -} - -.tooltip:before { - position: absolute; - pointer-events: none; - z-index: 1; - content: var(--tw-content); - --tw-content: attr(data-tip); -} - -.tooltip:before, .tooltip-top:before { - transform: translateX(-50%); - top: auto; - left: 50%; - right: auto; - bottom: var(--tooltip-offset); -} - -.tooltip-bottom:before { - transform: translateX(-50%); - top: var(--tooltip-offset); - left: 50%; - right: auto; - bottom: auto; -} - -.card-compact .card-body { - padding: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.card-compact .card-title { - margin-bottom: 0.25rem; -} - -.card-normal .card-body { - padding: var(--padding-card, 2rem); - font-size: 1rem; - line-height: 1.5rem; -} - -.card-normal .card-title { - margin-bottom: 0.75rem; -} - -.join.join-vertical > :where(*:not(:first-child)) { - margin-left: 0px; - margin-right: 0px; - margin-top: -1px; -} - -.join.join-horizontal > :where(*:not(:first-child)) { - margin-top: 0px; - margin-bottom: 0px; - margin-inline-start: -1px; -} - -.menu-horizontal > li:not(.menu-title) > details > ul { - margin-inline-start: 0px; - margin-top: 1rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-inline-end: 0.5rem; -} - -.menu-horizontal > li > details > ul:before { - content: none; -} - -:where(.menu-horizontal > li:not(.menu-title) > details > ul) { - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.menu-sm :where(li:not(.menu-title) > *:not(ul, details, .menu-title)), .menu-sm :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - border-radius: var(--rounded-btn, 0.5rem); - padding-left: 0.75rem; - padding-right: 0.75rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.menu-sm .menu-title { - padding-left: 0.75rem; - padding-right: 0.75rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.modal-top :where(.modal-box) { - width: 100%; - max-width: none; - --tw-translate-y: -2.5rem; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-bottom-right-radius: var(--rounded-box, 1rem); - border-bottom-left-radius: var(--rounded-box, 1rem); - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -.modal-middle :where(.modal-box) { - width: 91.666667%; - max-width: 32rem; - --tw-translate-y: 0px; - --tw-scale-x: .9; - --tw-scale-y: .9; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-top-left-radius: var(--rounded-box, 1rem); - border-top-right-radius: var(--rounded-box, 1rem); - border-bottom-right-radius: var(--rounded-box, 1rem); - border-bottom-left-radius: var(--rounded-box, 1rem); -} - -.modal-bottom :where(.modal-box) { - width: 100%; - max-width: none; - --tw-translate-y: 2.5rem; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - border-top-left-radius: var(--rounded-box, 1rem); - border-top-right-radius: var(--rounded-box, 1rem); - border-bottom-right-radius: 0px; - border-bottom-left-radius: 0px; -} - -.stats-vertical > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-right-width: calc(0px * var(--tw-divide-x-reverse)); - border-left-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); -} - -.stats-vertical { - overflow-y: auto; -} - -.steps-horizontal .step { - grid-template-rows: 40px 1fr; - grid-template-columns: auto; - min-width: 4rem; -} - -.steps-horizontal .step:before { - height: 0.5rem; - width: 100%; - --tw-translate-x: 0px; - --tw-translate-y: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - content: ""; - margin-inline-start: -100%; -} - -:is([dir="rtl"] .steps-horizontal .step):before { - --tw-translate-x: 0px; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.steps-vertical .step { - gap: 0.5rem; - grid-template-columns: 40px 1fr; - grid-template-rows: auto; - min-height: 4rem; - justify-items: start; -} - -.steps-vertical .step:before { - height: 100%; - width: 0.5rem; - --tw-translate-x: -50%; - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); - margin-inline-start: 50%; -} - -:is([dir="rtl"] .steps-vertical .step):before { - --tw-translate-x: 50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.timeline-vertical > li > hr { - width: 0.25rem; -} - -:where(.timeline-vertical:has(.timeline-middle) > li > hr):first-child { - border-bottom-right-radius: var(--rounded-badge, 1.9rem); - border-bottom-left-radius: var(--rounded-badge, 1.9rem); - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -:where(.timeline-vertical:has(.timeline-middle) > li > hr):last-child { - border-top-left-radius: var(--rounded-badge, 1.9rem); - border-top-right-radius: var(--rounded-badge, 1.9rem); - border-bottom-right-radius: 0px; - border-bottom-left-radius: 0px; -} - -:where(.timeline-vertical:not(:has(.timeline-middle)) :first-child > hr:last-child) { - border-top-left-radius: var(--rounded-badge, 1.9rem); - border-top-right-radius: var(--rounded-badge, 1.9rem); - border-bottom-right-radius: 0px; - border-bottom-left-radius: 0px; -} - -:where(.timeline-vertical:not(:has(.timeline-middle)) :last-child > hr:first-child) { - border-bottom-right-radius: var(--rounded-badge, 1.9rem); - border-bottom-left-radius: var(--rounded-badge, 1.9rem); - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -:where(.timeline-horizontal:has(.timeline-middle) > li > hr):first-child { - border-start-end-radius: var(--rounded-badge, 1.9rem); - border-end-end-radius: var(--rounded-badge, 1.9rem); - border-start-start-radius: 0px; - border-end-start-radius: 0px; -} - -:where(.timeline-horizontal:has(.timeline-middle) > li > hr):last-child { - border-start-start-radius: var(--rounded-badge, 1.9rem); - border-end-start-radius: var(--rounded-badge, 1.9rem); - border-start-end-radius: 0px; - border-end-end-radius: 0px; -} - -.tooltip { - position: relative; - display: inline-block; - text-align: center; - --tooltip-tail: 0.1875rem; - --tooltip-color: var(--fallback-n,oklch(var(--n)/1)); - --tooltip-text-color: var(--fallback-nc,oklch(var(--nc)/1)); - --tooltip-tail-offset: calc(100% + 0.0625rem - var(--tooltip-tail)); -} - -.tooltip:before, -.tooltip:after { - opacity: 0; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-delay: 100ms; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -.tooltip:after { - position: absolute; - content: ""; - border-style: solid; - border-width: var(--tooltip-tail, 0); - width: 0; - height: 0; - display: block; -} - -.tooltip:before { - max-width: 20rem; - border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - font-size: 0.875rem; - line-height: 1.25rem; - background-color: var(--tooltip-color); - color: var(--tooltip-text-color); - width: -moz-max-content; - width: max-content; -} - -.tooltip.tooltip-open:before { - opacity: 1; - transition-delay: 75ms; -} - -.tooltip.tooltip-open:after { - opacity: 1; - transition-delay: 75ms; -} - -.tooltip:hover:before { - opacity: 1; - transition-delay: 75ms; -} - -.tooltip:hover:after { - opacity: 1; - transition-delay: 75ms; -} - -.tooltip:has(:focus-visible):after, -.tooltip:has(:focus-visible):before { - opacity: 1; - transition-delay: 75ms; -} - -.tooltip:not([data-tip]):hover:before, -.tooltip:not([data-tip]):hover:after { - visibility: hidden; - opacity: 0; -} - -.tooltip:after, .tooltip-top:after { - transform: translateX(-50%); - border-color: var(--tooltip-color) transparent transparent transparent; - top: auto; - left: 50%; - right: auto; - bottom: var(--tooltip-tail-offset); -} - -.tooltip-bottom:after { - transform: translateX(-50%); - border-color: transparent transparent var(--tooltip-color) transparent; - top: var(--tooltip-tail-offset); - left: 50%; - right: auto; - bottom: auto; -} - -.fade-out { - opacity: 0; - transition: opacity 150ms ease-in-out; -} - -.visible { - visibility: visible; -} - -.invisible { - visibility: hidden; -} - -.static { - position: static; -} - -.fixed { - position: fixed; -} - -.absolute { - position: absolute; -} - -.relative { - position: relative; -} - -.left-2 { - left: 0.5rem; -} - -.right-0 { - right: 0px; -} - -.right-5 { - right: 1.25rem; -} - -.top-0 { - top: 0px; -} - -.top-2 { - top: 0.5rem; -} - -.top-5 { - top: 1.25rem; -} - -.z-0 { - z-index: 0; -} - -.z-10 { - z-index: 10; -} - -.z-50 { - z-index: 50; -} - -.z-\[1\] { - z-index: 1; -} - -.z-\[5000\] { - z-index: 5000; -} - -.z-\[6000\] { - z-index: 6000; -} - -.m-0 { - margin: 0px; -} - -.m-2 { - margin: 0.5rem; -} - -.m-5 { - margin: 1.25rem; -} - -.m-auto { - margin: auto; -} - -.mx-1 { - margin-left: 0.25rem; - margin-right: 0.25rem; -} - -.mx-4 { - margin-left: 1rem; - margin-right: 1rem; -} - -.mx-5 { - margin-left: 1.25rem; - margin-right: 1.25rem; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.my-10 { - margin-top: 2.5rem; - margin-bottom: 2.5rem; -} - -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - -.my-3 { - margin-top: 0.75rem; - margin-bottom: 0.75rem; -} - -.my-4 { - margin-top: 1rem; - margin-bottom: 1rem; -} - -.my-5 { - margin-top: 1.25rem; - margin-bottom: 1.25rem; -} - -.mb-1 { - margin-bottom: 0.25rem; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.mb-3 { - margin-bottom: 0.75rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-5 { - margin-bottom: 1.25rem; -} - -.mb-6 { - margin-bottom: 1.5rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-2 { - margin-left: 0.5rem; -} - -.ml-4 { - margin-left: 1rem; -} - -.ml-auto { - margin-left: auto; -} - -.mr-2 { - margin-right: 0.5rem; -} - -.mr-4 { - margin-right: 1rem; -} - -.mt-1 { - margin-top: 0.25rem; -} - -.mt-10 { - margin-top: 2.5rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - -.mt-3 { - margin-top: 0.75rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mt-5 { - margin-top: 1.25rem; -} - -.mt-6 { - margin-top: 1.5rem; -} - -.mt-8 { - margin-top: 2rem; -} - -.block { - display: block; -} - -.inline-block { - display: inline-block; -} - -.inline { - display: inline; -} - -.flex { - display: flex; -} - -.inline-flex { - display: inline-flex; -} - -.table { - display: table; -} - -.grid { - display: grid; -} - -.contents { - display: contents; -} - -.hidden { - display: none; -} - -.h-4 { - height: 1rem; -} - -.h-48 { - height: 12rem; -} - -.h-5 { - height: 1.25rem; -} - -.h-6 { - height: 1.5rem; -} - -.h-8 { - height: 2rem; -} - -.h-96 { - height: 24rem; -} - -.h-\[250px\] { - height: 250px; -} - -.h-\[25rem\] { - height: 25rem; -} - -.h-fit { - height: -moz-fit-content; - height: fit-content; -} - -.h-full { - height: 100%; -} - -.max-h-96 { - max-height: 24rem; -} - -.min-h-80 { - min-height: 20rem; -} - -.min-h-screen { - min-height: 100vh; -} - -.w-1\/2 { - width: 50%; -} - -.w-10\/12 { - width: 83.333333%; -} - -.w-4 { - width: 1rem; -} - -.w-4\/12 { - width: 33.333333%; -} - -.w-5 { - width: 1.25rem; -} - -.w-52 { - width: 13rem; -} - -.w-6 { - width: 1.5rem; -} - -.w-64 { - width: 16rem; -} - -.w-8 { - width: 2rem; -} - -.w-80 { - width: 20rem; -} - -.w-96 { - width: 24rem; -} - -.w-auto { - width: auto; -} - -.w-full { - width: 100%; -} - -.min-w-52 { - min-width: 13rem; -} - -.min-w-full { - min-width: 100%; -} - -.max-w-2xl { - max-width: 42rem; -} - -.max-w-3xl { - max-width: 48rem; -} - -.max-w-5xl { - max-width: 64rem; -} - -.max-w-md { - max-width: 28rem; -} - -.max-w-sm { - max-width: 24rem; -} - -.max-w-xs { - max-width: 20rem; -} - -.flex-1 { - flex: 1 1 0%; -} - -.flex-shrink-0 { - flex-shrink: 0; -} - -.shrink-0 { - flex-shrink: 0; -} - -.translate-x-full { - --tw-translate-x: 100%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.cursor-not-allowed { - cursor: not-allowed; -} - -.cursor-pointer { - cursor: pointer; -} - -.resize { - resize: both; -} - -.grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); -} - -.grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.flex-row { - flex-direction: row; -} - -.flex-col { - flex-direction: column; -} - -.flex-col-reverse { - flex-direction: column-reverse; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.items-center { - align-items: center; -} - -.justify-start { - justify-content: flex-start; -} - -.justify-end { - justify-content: flex-end; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.gap-2 { - gap: 0.5rem; -} - -.gap-3 { - gap: 0.75rem; -} - -.gap-4 { - gap: 1rem; -} - -.gap-5 { - gap: 1.25rem; -} - -.gap-6 { - gap: 1.5rem; -} - -.space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.75rem * var(--tw-space-x-reverse)); - margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-y-1 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); -} - -.space-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); -} - -.space-y-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-8 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(2rem * var(--tw-space-y-reverse)); -} - -.divide-y > :not([hidden]) ~ :not([hidden]) { - --tw-divide-y-reverse: 0; - border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); -} - -.divide-base-300 > :not([hidden]) ~ :not([hidden]) { - --tw-divide-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-divide-opacity, 1))); -} - -.justify-self-end { - justify-self: end; -} - -.justify-self-center { - justify-self: center; -} - -.overflow-hidden { - overflow: hidden; -} - -.overflow-x-auto { - overflow-x: auto; -} - -.overflow-y-auto { - overflow-y: auto; -} - -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.text-ellipsis { - text-overflow: ellipsis; -} - -.whitespace-nowrap { - white-space: nowrap; -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-box { - border-radius: var(--rounded-box, 1rem); -} - -.rounded-full { - border-radius: 9999px; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.rounded-md { - border-radius: 0.375rem; -} - -.rounded-t-none { - border-top-left-radius: 0px; - border-top-right-radius: 0px; -} - -.border { - border-width: 1px; -} - -.border-2 { - border-width: 2px; -} - -.border-b { - border-bottom-width: 1px; -} - -.border-dashed { - border-style: dashed; -} - -.border-base-300 { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity, 1))); -} - -.border-blue-300 { - --tw-border-opacity: 1; - border-color: rgb(147 197 253 / var(--tw-border-opacity, 1)); -} - -.border-error { - --tw-border-opacity: 1; - border-color: var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity, 1))); -} - -.border-gray-500 { - --tw-border-opacity: 1; - border-color: rgb(107 114 128 / var(--tw-border-opacity, 1)); -} - -.border-red-300 { - --tw-border-opacity: 1; - border-color: rgb(252 165 165 / var(--tw-border-opacity, 1)); -} - -.border-sky-500 { - --tw-border-opacity: 1; - border-color: rgb(14 165 233 / var(--tw-border-opacity, 1)); -} - -.border-opacity-30 { - --tw-border-opacity: 0.3; -} - -.bg-base-100 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); -} - -.bg-base-200 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); -} - -.bg-base-300 { - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity, 1))); -} - -.bg-blue-100 { - --tw-bg-opacity: 1; - background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1)); -} - -.bg-blue-500 { - --tw-bg-opacity: 1; - background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1)); -} - -.bg-blue-600 { - --tw-bg-opacity: 1; - background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); -} - -.bg-blue-900 { - --tw-bg-opacity: 1; - background-color: rgb(30 58 138 / var(--tw-bg-opacity, 1)); -} - -.bg-gray-100 { - --tw-bg-opacity: 1; - background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); -} - -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); -} - -.bg-gray-50 { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1)); -} - -.bg-green-50 { - --tw-bg-opacity: 1; - background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1)); -} - -.bg-green-500 { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity, 1)); -} - -.bg-neutral { - --tw-bg-opacity: 1; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity, 1))); -} - -.bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1)); -} - -.bg-red-50 { - --tw-bg-opacity: 1; - background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1)); -} - -.bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); -} - -.bg-secondary-content { - --tw-bg-opacity: 1; - background-color: var(--fallback-sc,oklch(var(--sc)/var(--tw-bg-opacity, 1))); -} - -.stroke-current { - stroke: currentColor; -} - -.stroke-info { - stroke: var(--fallback-in,oklch(var(--in)/1)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.p-0 { - padding: 0px; -} - -.p-2 { - padding: 0.5rem; -} - -.p-3 { - padding: 0.75rem; -} - -.p-4 { - padding: 1rem; -} - -.p-5 { - padding: 1.25rem; -} - -.px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-20 { - padding-top: 5rem; - padding-bottom: 5rem; -} - -.py-3 { - padding-top: 0.75rem; - padding-bottom: 0.75rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.py-5 { - padding-top: 1.25rem; - padding-bottom: 1.25rem; -} - -.py-6 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.pl-4 { - padding-left: 1rem; -} - -.pl-6 { - padding-left: 1.5rem; -} - -.pr-10 { - padding-right: 2.5rem; -} - -.text-left { - text-align: left; -} - -.text-center { - text-align: center; -} - -.font-mono { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; -} - -.text-5xl { - font-size: 3rem; - line-height: 1; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - -.font-black { - font-weight: 900; -} - -.font-bold { - font-weight: 700; -} - -.font-medium { - font-weight: 500; -} - -.font-semibold { - font-weight: 600; -} - -.normal-case { - text-transform: none; -} - -.italic { - font-style: italic; -} - -.text-accent { - --tw-text-opacity: 1; - color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity, 1))); -} - -.text-accent-content { - --tw-text-opacity: 1; - color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity, 1))); -} - -.text-base-content { - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity, 1))); -} - -.text-base-content\/60 { - color: var(--fallback-bc,oklch(var(--bc)/0.6)); -} - -.text-base-content\/70 { - color: var(--fallback-bc,oklch(var(--bc)/0.7)); -} - -.text-base-content\/80 { - color: var(--fallback-bc,oklch(var(--bc)/0.8)); -} - -.text-blue-600 { - --tw-text-opacity: 1; - color: rgb(37 99 235 / var(--tw-text-opacity, 1)); -} - -.text-blue-700 { - --tw-text-opacity: 1; - color: rgb(29 78 216 / var(--tw-text-opacity, 1)); -} - -.text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity, 1)); -} - -.text-gray-600 { - --tw-text-opacity: 1; - color: rgb(75 85 99 / var(--tw-text-opacity, 1)); -} - -.text-gray-700 { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity, 1)); -} - -.text-green-500 { - --tw-text-opacity: 1; - color: rgb(34 197 94 / var(--tw-text-opacity, 1)); -} - -.text-neutral-content { - --tw-text-opacity: 1; - color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity, 1))); -} - -.text-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity, 1))); -} - -.text-red-500 { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity, 1)); -} - -.text-red-700 { - --tw-text-opacity: 1; - color: rgb(185 28 28 / var(--tw-text-opacity, 1)); -} - -.text-secondary { - --tw-text-opacity: 1; - color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity, 1))); -} - -.text-success { - --tw-text-opacity: 1; - color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity, 1))); -} - -.text-warning { - --tw-text-opacity: 1; - color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity, 1))); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity, 1)); -} - -.underline { - text-decoration-line: underline; -} - -.decoration-dotted { - text-decoration-style: dotted; -} - -.placeholder-base-content\/70::-moz-placeholder { - color: var(--fallback-bc,oklch(var(--bc)/0.7)); -} - -.placeholder-base-content\/70::placeholder { - color: var(--fallback-bc,oklch(var(--bc)/0.7)); -} - -.opacity-0 { - opacity: 0; -} - -.opacity-50 { - opacity: 0.5; -} - -.shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-2xl { - --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); - --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-sm { - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-xl { - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.grayscale { - --tw-grayscale: grayscale(100%); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.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, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-colors { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-shadow { - transition-property: box-shadow; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-transform { - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-200 { - transition-duration: 200ms; -} - -.duration-300 { - transition-duration: 300ms; -} - -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -@tailwind daisyui; - -/* - -@layer components { - .btn-primary { - @apply py-2 px-4 bg-blue-200; - } -} - -*/ - -/* Leaflet Panel Styles */ - -.leaflet-right-panel { - margin-top: 80px; - /* Give space for controls above */ - margin-right: 10px; - transform: none; - transition: right 0.3s ease-in-out; - z-index: 400; - background: white; - border-radius: 4px; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); -} - -.leaflet-right-panel.controls-shifted { - right: 310px; -} - -.leaflet-control-button { - background-color: white !important; - color: #374151 !important; -} - -.leaflet-control-button:hover { - background-color: #f3f4f6 !important; -} - -/* Drawer Panel Styles */ - -.leaflet-drawer { - position: absolute; - top: 0; - right: 0; - width: 338px; - height: 100%; - background: rgba(255, 255, 255, 0.5); - transform: translateX(100%); - transition: transform 0.3s ease-in-out; - z-index: 450; - box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); -} - -.leaflet-drawer.open { - transform: translateX(0); -} - -/* Controls transition */ - -.leaflet-control-layers, -.leaflet-control-button, -.toggle-panel-button { - transition: right 0.3s ease-in-out; - z-index: 500; -} - -.controls-shifted { - right: 338px !important; -} - -/* Selection Tool Styles */ - -.leaflet-control-custom { - background-color: white; - border-radius: 4px; - width: 30px; - height: 30px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); -} - -.leaflet-control-custom:hover { - background-color: #f3f4f6; -} - -#selection-tool-button.active { - background-color: #60a5fa; - color: white; -} - -/* Cancel Selection Button */ - -#cancel-selection-button { - margin-bottom: 1rem; - width: 100%; -} - -@media (hover: hover) { - .hover\:btn-ghost:hover:hover { - border-color: transparent; - } - - @supports (color: oklch(0 0 0)) { - .hover\:btn-ghost:hover:hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - } - } - - .hover\:btn-ghost:hover:hover { - border-color: transparent; - } - - @supports (color: oklch(0 0 0)) { - .hover\:btn-ghost:hover:hover { - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); - } - } - - .hover\:btn-info:hover.btn-outline:hover { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .hover\:btn-info:hover.btn-outline:hover { - background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - } - } - - .hover\:btn-info:hover.btn-outline:hover { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .hover\:btn-info:hover.btn-outline:hover { - background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - } - } -} - -@supports not (color: oklch(0 0 0)) { - .hover\:btn-info:hover { - --btn-color: var(--fallback-in); - } -} - -@supports (color: color-mix(in oklab, black, black)) { - .hover\:btn-info:hover.btn-outline.btn-active { - background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black); - } -} - -@supports (color: oklch(0 0 0)) { - .hover\:btn-info:hover { - --btn-color: var(--in); - } -} - -.hover\:btn-info:hover { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); - outline-color: var(--fallback-in,oklch(var(--in)/1)); -} - -.hover\:btn-ghost:hover { - border-width: 1px; - border-color: transparent; - background-color: transparent; - color: currentColor; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; -} - -.hover\:btn-ghost:hover.btn-active { - border-color: transparent; - background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); -} - -.hover\:btn-info:hover.btn-outline { - --tw-text-opacity: 1; - color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); -} - -.hover\:btn-info:hover.btn-outline.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); -} - -.hover\:input-primary:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); -} - -.hover\:input-primary:hover:focus,.hover\:input-primary:hover:focus-within { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - -.focus\:input-ghost:focus { - --tw-bg-opacity: 0.05; -} - -.focus\:input-ghost:focus:focus,.focus\:input-ghost:focus:focus-within { - --tw-bg-opacity: 1; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - box-shadow: none; -} - -@media not all and (min-width: 768px) { - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; - } - - .max-md\:timeline-compact.timeline-vertical > li { - --timeline-col-start: 0; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; - } - - .max-md\:timeline-compact.timeline-vertical > li { - --timeline-col-start: 0; - } - - .max-md\:timeline-compact.timeline-vertical .timeline-start { - grid-column-start: 3; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 4; - align-self: center; - justify-self: start; - } - - .max-md\:timeline-compact,.max-md\:timeline-compact -.timeline-horizontal { - --timeline-row-start: 0; - } - - .max-md\:timeline-compact .timeline-start,.max-md\:timeline-compact .timeline-horizontal .timeline-start { - grid-column-start: 1; - grid-column-end: 4; - grid-row-start: 3; - grid-row-end: 4; - margin: 0.25rem; - align-self: flex-start; - justify-self: center; - } - - .max-md\:timeline-compact li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end { - grid-column-start: none; - grid-row-start: auto; - } - - .max-md\:timeline-compact.timeline-vertical > li { - --timeline-col-start: 0; - } - - .max-md\:timeline-compact.timeline-vertical .timeline-start { - grid-column-start: 3; - grid-column-end: 4; - grid-row-start: 1; - grid-row-end: 4; - align-self: center; - justify-self: start; - } - - .max-md\:timeline-compact.timeline-vertical li:has(.timeline-start) .timeline-end { - grid-column-start: auto; - grid-row-start: none; - } -} - -@media (min-width: 1024px) { - .lg\:stats-horizontal { - grid-auto-flow: column; - } - - .lg\:stats-horizontal > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-right-width: calc(1px * var(--tw-divide-x-reverse)); - border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - --tw-divide-y-reverse: 0; - border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); - } - - .lg\:stats-horizontal { - overflow-x: auto; - } - - :is([dir="rtl"] .lg\:stats-horizontal) { - --tw-divide-x-reverse: 1; - } -} - -.last\:border-0:last-child { - border-width: 0px; -} - -.hover\:scale-105:hover { - --tw-scale-x: 1.05; - --tw-scale-y: 1.05; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.hover\:cursor-pointer:hover { - cursor: pointer; -} - -.hover\:bg-accent:hover { - --tw-bg-opacity: 1; - background-color: var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity, 1))); -} - -.hover\:bg-base-200:hover { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity, 1))); -} - -.hover\:bg-base-300:hover { - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity, 1))); -} - -.hover\:text-accent-content:hover { - --tw-text-opacity: 1; - color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity, 1))); -} - -.hover\:underline:hover { - text-decoration-line: underline; -} - -.hover\:no-underline:hover { - text-decoration-line: none; -} - -.hover\:shadow-2xl:hover { - --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); - --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.hover\:shadow-lg:hover { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.hover\:shadow-blue-500\/50:hover { - --tw-shadow-color: rgb(59 130 246 / 0.5); - --tw-shadow: var(--tw-shadow-colored); -} - -.focus\:border-primary:focus { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity, 1))); -} - -.focus\:bg-base-100:focus { - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity, 1))); -} - -.group:hover .group-hover\:opacity-100 { - opacity: 1; -} - -@media (min-width: 640px) { - .sm\:inline { - display: inline; - } - - .sm\:w-1\/12 { - width: 8.333333%; - } - - .sm\:w-2\/12 { - width: 16.666667%; - } - - .sm\:w-6\/12 { - width: 50%; - } - - .sm\:grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .sm\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .sm\:flex-row { - flex-direction: row; - } - - .sm\:items-end { - align-items: flex-end; - } - - .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - } - - .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); - } -} - -@media (min-width: 768px) { - .md\:h-64 { - height: 16rem; - } - - .md\:min-h-64 { - min-height: 16rem; - } - - .md\:w-1\/12 { - width: 8.333333%; - } - - .md\:w-2\/12 { - width: 16.666667%; - } - - .md\:w-2\/3 { - width: 66.666667%; - } - - .md\:w-3\/12 { - width: 25%; - } - - .md\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .md\:flex-row { - flex-direction: row; - } - - .md\:items-end { - align-items: flex-end; - } - - .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - } - - .md\:text-end { - text-align: end; - } -} - -@media (min-width: 1024px) { - .lg\:flex { - display: flex; - } - - .lg\:hidden { - display: none; - } - - .lg\:w-1\/12 { - width: 8.333333%; - } - - .lg\:w-1\/2 { - width: 50%; - } - - .lg\:w-2\/12 { - width: 16.666667%; - } - - .lg\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .lg\:grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .lg\:flex-row { - flex-direction: row; - } - - .lg\:flex-row-reverse { - flex-direction: row-reverse; - } - - .lg\:space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); - } - - .lg\:text-left { - text-align: left; - } -} - -@media (prefers-color-scheme: dark) { - .dark\:bg-gray-800 { - --tw-bg-opacity: 1; - background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1)); - } -} From 88e3f53cc5209e85471fb268e4a56f20b381c614 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 19:48:39 +0200 Subject: [PATCH 05/12] Remove old code --- app/services/tracks/generator.rb | 29 ++++++----------- app/services/tracks/segmentation.rb | 4 +-- app/services/tracks/track_builder.rb | 47 +++++----------------------- db/schema.rb | 4 ++- 4 files changed, 20 insertions(+), 64 deletions(-) diff --git a/app/services/tracks/generator.rb b/app/services/tracks/generator.rb index f39b9f8b..b93f863c 100644 --- a/app/services/tracks/generator.rb +++ b/app/services/tracks/generator.rb @@ -40,20 +40,17 @@ class Tracks::Generator def call clean_existing_tracks if should_clean_tracks? - # Get timestamp range for SQL query start_timestamp, end_timestamp = get_timestamp_range - + Rails.logger.debug "Generator: querying points for user #{user.id} in #{mode} mode" - - # Use optimized SQL segmentation with pre-calculated distances - untracked_only = (mode == :incremental) + segments = Track.get_segments_with_points( user.id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, - untracked_only: untracked_only + untracked_only: mode == :incremental ) Rails.logger.debug "Generator: created #{segments.size} segments via SQL" @@ -110,23 +107,14 @@ class Tracks::Generator user.tracked_points.where(timestamp: day_range).order(:timestamp) end - def create_track_from_segment_optimized(segment_data) + def create_track_from_segment(segment_data) points = segment_data[:points] pre_calculated_distance = segment_data[:pre_calculated_distance] - + Rails.logger.debug "Generator: processing segment with #{points.size} points" return unless points.size >= 2 - track = create_track_from_points_optimized(points, pre_calculated_distance) - Rails.logger.debug "Generator: created track #{track&.id}" - track - end - - def create_track_from_segment(segment) - Rails.logger.debug "Generator: processing segment with #{segment.size} points" - return unless segment.size >= 2 - - track = create_track_from_points(segment) + track = create_track_from_points(points, pre_calculated_distance) Rails.logger.debug "Generator: created track #{track&.id}" track end @@ -194,7 +182,6 @@ class Tracks::Generator scope.destroy_all end - # Get timestamp range for SQL query based on mode def get_timestamp_range case mode when :bulk @@ -204,22 +191,24 @@ class Tracks::Generator # Get full range for user first_point = user.tracked_points.order(:timestamp).first last_point = user.tracked_points.order(:timestamp).last + [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] end when :daily day = start_at&.to_date || Date.current + [day.beginning_of_day.to_i, day.end_of_day.to_i] when :incremental # For incremental, we need all untracked points up to end_at first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first end_timestamp = end_at ? end_at.to_i : Time.current.to_i + [first_point&.timestamp || 0, end_timestamp] else raise ArgumentError, "Unknown mode: #{mode}" end end - # Threshold methods from safe_settings def distance_threshold_meters @distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i end diff --git a/app/services/tracks/segmentation.rb b/app/services/tracks/segmentation.rb index 9f0842cc..e5b4477d 100644 --- a/app/services/tracks/segmentation.rb +++ b/app/services/tracks/segmentation.rb @@ -86,14 +86,12 @@ module Tracks::Segmentation end def calculate_km_distance_between_points(point1, point2) - # OPTIMIZED: Use PostGIS for more accurate distance calculation (same as track distance) - # This maintains consistency with track distance calculations distance_meters = Point.connection.select_value( 'SELECT ST_Distance(ST_GeomFromEWKT($1)::geography, ST_GeomFromEWKT($2)::geography)', nil, [point1.lonlat, point2.lonlat] ) - + distance_meters.to_f / 1000.0 # Convert meters to kilometers end diff --git a/app/services/tracks/track_builder.rb b/app/services/tracks/track_builder.rb index 95bbcae3..0ccd82b0 100644 --- a/app/services/tracks/track_builder.rb +++ b/app/services/tracks/track_builder.rb @@ -49,7 +49,7 @@ module Tracks::TrackBuilder extend ActiveSupport::Concern - def create_track_from_points(points) + def create_track_from_points(points, pre_calculated_distance) return nil if points.size < 2 track = Track.new( @@ -59,54 +59,20 @@ module Tracks::TrackBuilder original_path: build_path(points) ) - # Calculate track statistics - track.distance = calculate_track_distance(points) - track.duration = calculate_duration(points) - track.avg_speed = calculate_average_speed(track.distance, track.duration) - - # Calculate elevation statistics - elevation_stats = calculate_elevation_stats(points) - track.elevation_gain = elevation_stats[:gain] - track.elevation_loss = elevation_stats[:loss] - track.elevation_max = elevation_stats[:max] - track.elevation_min = elevation_stats[:min] - - if track.save - Point.where(id: points.map(&:id)).update_all(track_id: track.id) - - track - else - Rails.logger.error "Failed to create track for user #{user.id}: #{track.errors.full_messages.join(', ')}" - - nil - end - end - - # Optimized version that uses pre-calculated distance from SQL - def create_track_from_points_optimized(points, pre_calculated_distance) - return nil if points.size < 2 - - track = Track.new( - user_id: user.id, - start_at: Time.zone.at(points.first.timestamp), - end_at: Time.zone.at(points.last.timestamp), - original_path: build_path(points) - ) - - # Use pre-calculated distance from SQL instead of recalculating - track.distance = pre_calculated_distance.round - track.duration = calculate_duration(points) + track.distance = pre_calculated_distance.round + track.duration = calculate_duration(points) track.avg_speed = calculate_average_speed(track.distance, track.duration) # Calculate elevation statistics (no DB queries needed) elevation_stats = calculate_elevation_stats(points) track.elevation_gain = elevation_stats[:gain] track.elevation_loss = elevation_stats[:loss] - track.elevation_max = elevation_stats[:max] - track.elevation_min = elevation_stats[:min] + track.elevation_max = elevation_stats[:max] + track.elevation_min = elevation_stats[:min] if track.save Point.where(id: points.map(&:id)).update_all(track_id: track.id) + track else Rails.logger.error "Failed to create track for user #{user.id}: #{track.errors.full_messages.join(', ')}" @@ -133,6 +99,7 @@ module Tracks::TrackBuilder # Speed in meters per second, then convert to km/h for storage speed_mps = distance_in_meters.to_f / duration_seconds + (speed_mps * 3.6).round(2) # m/s to km/h end diff --git a/db/schema.rb b/db/schema.rb index 903fe090..25efe3c2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_07_03_193657) do +ActiveRecord::Schema[8.0].define(version: 2025_07_23_164055) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" enable_extension "postgis" @@ -146,6 +146,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_03_193657) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.geography "lonlat", limit: {srid: 4326, type: "st_point", geographic: true} + t.index "(((geodata -> 'properties'::text) ->> 'osm_id'::text))", name: "index_places_on_geodata_osm_id" t.index ["lonlat"], name: "index_places_on_lonlat", using: :gist end @@ -202,6 +203,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_03_193657) do t.index ["timestamp"], name: "index_points_on_timestamp" t.index ["track_id"], name: "index_points_on_track_id" t.index ["trigger"], name: "index_points_on_trigger" + t.index ["user_id", "timestamp", "track_id"], name: "idx_points_track_generation" t.index ["user_id"], name: "index_points_on_user_id" t.index ["visit_id"], name: "index_points_on_visit_id" end From 7ea149bd4e9f471eb3e4108846c8d0a194562164 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:02:38 +0200 Subject: [PATCH 06/12] Fix specs --- CHANGELOG.md | 4 ++++ app/services/tracks/generator.rb | 2 +- app/views/stats/index.html.erb | 2 +- spec/services/tracks/track_builder_spec.rb | 28 ++++++++++++---------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a51f35d3..cda4369e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Track generation is now significantly faster and less resource intensive. +## Fixed + +- Distance on the stats page is now rounded. #1548 + # [0.30.2] - 2025-07-22 diff --git a/app/services/tracks/generator.rb b/app/services/tracks/generator.rb index b93f863c..62a4fab3 100644 --- a/app/services/tracks/generator.rb +++ b/app/services/tracks/generator.rb @@ -58,7 +58,7 @@ class Tracks::Generator tracks_created = 0 segments.each do |segment_data| - track = create_track_from_segment_optimized(segment_data) + track = create_track_from_segment(segment_data) tracks_created += 1 if track end diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index af42647b..bd06de8e 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -41,7 +41,7 @@

<% cache [current_user, 'year_distance_stat', year], skip_digest: true do %> - <%= number_with_delimiter year_distance_stat(year, current_user) %><%= current_user.safe_settings.distance_unit %> + <%= number_with_delimiter year_distance_stat(year, current_user).round %> <%= current_user.safe_settings.distance_unit %> <% end %>

<% if DawarichSettings.reverse_geocoding_enabled? %> diff --git a/spec/services/tracks/track_builder_spec.rb b/spec/services/tracks/track_builder_spec.rb index 5046e60f..2213f5a2 100644 --- a/spec/services/tracks/track_builder_spec.rb +++ b/spec/services/tracks/track_builder_spec.rb @@ -39,21 +39,23 @@ RSpec.describe Tracks::TrackBuilder do ] end + let(:pre_calculated_distance) { 1500 } # 1500 meters + it 'creates a track with correct attributes' do - track = builder.create_track_from_points(points) + track = builder.create_track_from_points(points, pre_calculated_distance) expect(track).to be_persisted expect(track.user).to eq(user) expect(track.start_at).to be_within(1.second).of(Time.zone.at(points.first.timestamp)) expect(track.end_at).to be_within(1.second).of(Time.zone.at(points.last.timestamp)) - expect(track.distance).to be > 0 + expect(track.distance).to eq(1500) expect(track.duration).to eq(90.minutes.to_i) expect(track.avg_speed).to be > 0 expect(track.original_path).to be_present end it 'calculates elevation statistics correctly' do - track = builder.create_track_from_points(points) + track = builder.create_track_from_points(points, pre_calculated_distance) expect(track.elevation_gain).to eq(10) # 110 - 100 expect(track.elevation_loss).to eq(5) # 110 - 105 @@ -62,7 +64,7 @@ RSpec.describe Tracks::TrackBuilder do end it 'associates points with the track' do - track = builder.create_track_from_points(points) + track = builder.create_track_from_points(points, pre_calculated_distance) points.each(&:reload) expect(points.map(&:track)).to all(eq(track)) @@ -73,12 +75,12 @@ RSpec.describe Tracks::TrackBuilder do let(:single_point) { [create(:point, user: user)] } it 'returns nil for single point' do - result = builder.create_track_from_points(single_point) + result = builder.create_track_from_points(single_point, 1000) expect(result).to be_nil end it 'returns nil for empty array' do - result = builder.create_track_from_points([]) + result = builder.create_track_from_points([], 1000) expect(result).to be_nil end end @@ -100,7 +102,7 @@ RSpec.describe Tracks::TrackBuilder do /Failed to create track for user #{user.id}/ ) - result = builder.create_track_from_points(points) + result = builder.create_track_from_points(points, 1000) expect(result).to be_nil end end @@ -120,7 +122,7 @@ RSpec.describe Tracks::TrackBuilder do ).and_call_original result = builder.build_path(points) - expect(result).to respond_to(:as_text) + expect(result).to be_a(RGeo::Geographic::SphericalLineStringImpl) end end @@ -134,7 +136,7 @@ RSpec.describe Tracks::TrackBuilder do before do # Mock Point.total_distance to return distance in meters - allow(Point).to receive(:total_distance).and_return(1500) # 1500 meters + allow(Point).to receive(:total_distance).with(points, :m).and_return(1500) # 1500 meters end it 'stores distance in meters regardless of user unit preference' do @@ -143,7 +145,7 @@ RSpec.describe Tracks::TrackBuilder do end it 'rounds distance to nearest meter' do - allow(Point).to receive(:total_distance).and_return(1500.7) + allow(Point).to receive(:total_distance).with(points, :m).and_return(1500.7) result = builder.calculate_track_distance(points) expect(result).to eq(1501) # Rounded to nearest meter end @@ -312,13 +314,15 @@ RSpec.describe Tracks::TrackBuilder do ] end + let(:pre_calculated_distance) { 2000 } + it 'creates a complete track end-to-end' do - expect { builder.create_track_from_points(points) }.to change(Track, :count).by(1) + expect { builder.create_track_from_points(points, pre_calculated_distance) }.to change(Track, :count).by(1) track = Track.last expect(track.user).to eq(user) expect(track.points).to match_array(points) - expect(track.distance).to be > 0 + expect(track.distance).to eq(2000) expect(track.duration).to eq(1.hour.to_i) expect(track.elevation_gain).to eq(20) end From 2e46069fcc6af32756f098da85629d9007460d82 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:08:24 +0200 Subject: [PATCH 07/12] Clean up code a bit --- app/models/concerns/distanceable.rb | 11 ++---- app/models/track.rb | 58 +++++++++++------------------ 2 files changed, 25 insertions(+), 44 deletions(-) diff --git a/app/models/concerns/distanceable.rb b/app/models/concerns/distanceable.rb index f9c28477..dc668169 100644 --- a/app/models/concerns/distanceable.rb +++ b/app/models/concerns/distanceable.rb @@ -5,7 +5,6 @@ module Distanceable module ClassMethods def total_distance(points = nil, unit = :km) - # Handle method being called directly on relation vs with array if points.nil? calculate_distance_for_relation(unit) else @@ -50,20 +49,17 @@ module Distanceable return 0 if points.length < 2 - # OPTIMIZED: Single SQL query instead of N individual queries total_meters = calculate_batch_distances(points).sum total_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] end - # Optimized batch distance calculation using single SQL query def calculate_batch_distances(points) return [] if points.length < 2 point_pairs = points.each_cons(2).to_a return [] if point_pairs.empty? - # Create a VALUES clause with all point pairs values_clause = point_pairs.map.with_index do |(p1, p2), index| "(#{index}, ST_GeomFromEWKT('#{p1.lonlat}')::geography, ST_GeomFromEWKT('#{p2.lonlat}')::geography)" end.join(', ') @@ -71,13 +67,13 @@ module Distanceable # Single query to calculate all distances results = connection.execute(<<-SQL.squish) WITH point_pairs AS ( - SELECT + SELECT pair_id, point1, point2 FROM (VALUES #{values_clause}) AS t(pair_id, point1, point2) ) - SELECT + SELECT pair_id, ST_Distance(point1, point2) as distance_meters FROM point_pairs @@ -85,7 +81,7 @@ module Distanceable SQL # Return array of distances in meters - results.map { |row| row['distance_meters'].to_f } + results.map { |row| row['distance_meters'].to_i } end end @@ -94,7 +90,6 @@ module Distanceable raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end - # Extract coordinates based on what type other_point is other_lonlat = extract_point(other_point) return nil if other_lonlat.nil? diff --git a/app/models/track.rb b/app/models/track.rb index b79de93e..f02071d2 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -25,16 +25,15 @@ class Track < ApplicationRecord .first end - # Optimized SQL segmentation using PostgreSQL window functions def self.segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: false) time_threshold_seconds = time_threshold_minutes * 60 - + where_clause = if untracked_only "WHERE user_id = $1 AND timestamp BETWEEN $2 AND $3 AND track_id IS NULL" else "WHERE user_id = $1 AND timestamp BETWEEN $2 AND $3" end - + sql = <<~SQL WITH points_with_gaps AS ( SELECT @@ -44,21 +43,21 @@ class Track < ApplicationRecord LAG(lonlat) OVER (ORDER BY timestamp) as prev_lonlat, LAG(timestamp) OVER (ORDER BY timestamp) as prev_timestamp, ST_Distance( - lonlat::geography, + lonlat::geography, LAG(lonlat) OVER (ORDER BY timestamp)::geography ) as distance_meters, (timestamp - LAG(timestamp) OVER (ORDER BY timestamp)) as time_diff_seconds - FROM points + FROM points #{where_clause} ORDER BY timestamp ), segment_breaks AS ( SELECT *, - CASE + CASE WHEN prev_lonlat IS NULL THEN 1 WHEN time_diff_seconds > $4 THEN 1 WHEN distance_meters > $5 THEN 1 - ELSE 0 + ELSE 0 END as is_break FROM points_with_gaps ), @@ -67,7 +66,7 @@ class Track < ApplicationRecord SUM(is_break) OVER (ORDER BY timestamp ROWS UNBOUNDED PRECEDING) as segment_id FROM segment_breaks ) - SELECT + SELECT segment_id, array_agg(id ORDER BY timestamp) as point_ids, count(*) as point_count, @@ -79,7 +78,7 @@ class Track < ApplicationRecord HAVING count(*) >= 2 ORDER BY segment_id SQL - + results = Point.connection.exec_query( sql, 'segment_points_in_sql', @@ -104,15 +103,18 @@ class Track < ApplicationRecord # Get actual Point objects for each segment with pre-calculated distances def self.get_segments_with_points(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: false) - segments_data = segment_points_in_sql(user_id, start_timestamp, end_timestamp, time_threshold_minutes, distance_threshold_meters, untracked_only: untracked_only) - - # Get all point IDs we need - all_point_ids = segments_data.flat_map { |seg| seg[:point_ids] } - - # Single query to get all points - points_by_id = Point.where(id: all_point_ids).index_by(&:id) - - # Build segments with actual Point objects + segments_data = segment_points_in_sql( + user_id, + start_timestamp, + end_timestamp, + time_threshold_minutes, + distance_threshold_meters, + untracked_only: untracked_only + ) + + point_ids = segments_data.flat_map { |seg| seg[:point_ids] } + points_by_id = Point.where(id: point_ids).index_by(&:id) + segments_data.map do |seg_data| { points: seg_data[:point_ids].map { |id| points_by_id[id] }.compact, @@ -126,7 +128,7 @@ class Track < ApplicationRecord # Parse PostgreSQL array format like "{1,2,3}" into Ruby array def self.parse_postgres_array(pg_array_string) return [] if pg_array_string.nil? || pg_array_string.empty? - + # Remove curly braces and split by comma pg_array_string.gsub(/[{}]/, '').split(',').map(&:to_i) end @@ -151,23 +153,7 @@ class Track < ApplicationRecord def broadcast_track_update(action) TracksChannel.broadcast_to(user, { action: action, - track: serialize_track_data + track: TrackSerializer.new(self).call }) end - - def serialize_track_data - { - id: id, - start_at: start_at.iso8601, - end_at: end_at.iso8601, - distance: distance.to_i, - avg_speed: avg_speed.to_f, - duration: duration, - elevation_gain: elevation_gain, - elevation_loss: elevation_loss, - elevation_max: elevation_max, - elevation_min: elevation_min, - original_path: original_path.to_s - } - end end From bf199de2a0d6acaf9da6d38c7e3fc171f8d28055 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:17:07 +0200 Subject: [PATCH 08/12] Fix non-selfhosted users export and import --- CHANGELOG.md | 1 + app/controllers/settings/users_controller.rb | 2 +- app/services/tracks/generator.rb | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cda4369e..2046bacf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Fixed - Distance on the stats page is now rounded. #1548 +- Non-selfhosted users can now export and import their account data. # [0.30.2] - 2025-07-22 diff --git a/app/controllers/settings/users_controller.rb b/app/controllers/settings/users_controller.rb index d8696617..e114daa7 100644 --- a/app/controllers/settings/users_controller.rb +++ b/app/controllers/settings/users_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Settings::UsersController < ApplicationController - before_action :authenticate_self_hosted! + before_action :authenticate_self_hosted!, only: [:export, :import] before_action :authenticate_admin!, except: [:export, :import] before_action :authenticate_user!, only: [:export, :import] diff --git a/app/services/tracks/generator.rb b/app/services/tracks/generator.rb index 62a4fab3..f4cbb7bf 100644 --- a/app/services/tracks/generator.rb +++ b/app/services/tracks/generator.rb @@ -57,12 +57,12 @@ class Tracks::Generator tracks_created = 0 - segments.each do |segment_data| - track = create_track_from_segment(segment_data) + segments.each do |segment| + track = create_track_from_segment(segment) tracks_created += 1 if track end - Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in optimized #{mode} mode" + Rails.logger.info "Generated #{tracks_created} tracks for user #{user.id} in #{mode} mode" tracks_created end @@ -188,7 +188,6 @@ class Tracks::Generator if start_at && end_at [start_at.to_i, end_at.to_i] else - # Get full range for user first_point = user.tracked_points.order(:timestamp).first last_point = user.tracked_points.order(:timestamp).last From 51dd2e0a4b60864a73e2d7d1f25b722779454de1 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:21:33 +0200 Subject: [PATCH 09/12] Fix auth rules for export and import --- app/controllers/settings/users_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/settings/users_controller.rb b/app/controllers/settings/users_controller.rb index e114daa7..f00a28ce 100644 --- a/app/controllers/settings/users_controller.rb +++ b/app/controllers/settings/users_controller.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true class Settings::UsersController < ApplicationController - before_action :authenticate_self_hosted!, only: [:export, :import] + before_action :authenticate_self_hosted!, except: [:export, :import] before_action :authenticate_admin!, except: [:export, :import] - before_action :authenticate_user!, only: [:export, :import] + before_action :authenticate_user! def index @users = User.order(created_at: :desc) From 94f6dbe18e0a3cedeaf36f165826b97ba79029da Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:27:55 +0200 Subject: [PATCH 10/12] Extract timestamp range calculation to separate methods --- app/services/tracks/generator.rb | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/app/services/tracks/generator.rb b/app/services/tracks/generator.rb index f4cbb7bf..be22d021 100644 --- a/app/services/tracks/generator.rb +++ b/app/services/tracks/generator.rb @@ -184,30 +184,35 @@ class Tracks::Generator def get_timestamp_range case mode - when :bulk - if start_at && end_at - [start_at.to_i, end_at.to_i] - else - first_point = user.tracked_points.order(:timestamp).first - last_point = user.tracked_points.order(:timestamp).last - - [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] - end - when :daily - day = start_at&.to_date || Date.current - - [day.beginning_of_day.to_i, day.end_of_day.to_i] - when :incremental - # For incremental, we need all untracked points up to end_at - first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first - end_timestamp = end_at ? end_at.to_i : Time.current.to_i - - [first_point&.timestamp || 0, end_timestamp] + when :bulk then bulk_timestamp_range + when :daily then daily_timestamp_range + when :incremental then incremental_timestamp_range else raise ArgumentError, "Unknown mode: #{mode}" end end + def bulk_timestamp_range + return [start_at.to_i, end_at.to_i] if start_at && end_at + + first_point = user.tracked_points.order(:timestamp).first + last_point = user.tracked_points.order(:timestamp).last + + [first_point&.timestamp || 0, last_point&.timestamp || Time.current.to_i] + end + + def daily_timestamp_range + day = start_at&.to_date || Date.current + [day.beginning_of_day.to_i, day.end_of_day.to_i] + end + + def incremental_timestamp_range + first_point = user.tracked_points.where(track_id: nil).order(:timestamp).first + end_timestamp = end_at ? end_at.to_i : Time.current.to_i + + [first_point&.timestamp || 0, end_timestamp] + end + def distance_threshold_meters @distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i end From 79202091878e22b72e7bf0334b4aac39870db989 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:35:42 +0200 Subject: [PATCH 11/12] Return tailwind.css --- app/assets/builds/tailwind.css | 6 ++++++ app/models/concerns/distanceable.rb | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 app/assets/builds/tailwind.css diff --git a/app/assets/builds/tailwind.css b/app/assets/builds/tailwind.css new file mode 100644 index 00000000..5efebdd7 --- /dev/null +++ b/app/assets/builds/tailwind.css @@ -0,0 +1,6 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:Inter var,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}:root,[data-theme]{background-color:var(--fallback-b1,oklch(var(--b1)/1));color:var(--fallback-bc,oklch(var(--bc)/1))}@supports not (color:oklch(0 0 0)){:root{color-scheme:light;--fallback-p:#491eff;--fallback-pc:#d4dbff;--fallback-s:#ff41c7;--fallback-sc:#fff9fc;--fallback-a:#00cfbd;--fallback-ac:#00100d;--fallback-n:#2b3440;--fallback-nc:#d7dde4;--fallback-b1:#fff;--fallback-b2:#e5e6e6;--fallback-b3:#e5e6e6;--fallback-bc:#1f2937;--fallback-in:#00b3f0;--fallback-inc:#000;--fallback-su:#00ca92;--fallback-suc:#000;--fallback-wa:#ffc22d;--fallback-wac:#000;--fallback-er:#ff6f70;--fallback-erc:#000}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--fallback-p:#7582ff;--fallback-pc:#050617;--fallback-s:#ff71cf;--fallback-sc:#190211;--fallback-a:#00c7b5;--fallback-ac:#000e0c;--fallback-n:#2a323c;--fallback-nc:#a6adbb;--fallback-b1:#1d232a;--fallback-b2:#191e24;--fallback-b3:#15191e;--fallback-bc:#a6adbb;--fallback-in:#00b3f0;--fallback-inc:#000;--fallback-su:#00ca92;--fallback-suc:#000;--fallback-wa:#ffc22d;--fallback-wac:#000;--fallback-er:#ff6f70;--fallback-erc:#000}}}html{-webkit-tap-highlight-color:transparent}:root{color-scheme:light;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.89824 0.06192 275.75;--ac:0.15352 0.0368 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.4912 0.3096 275.75;--s:0.6971 0.329 342.55;--sc:0.9871 0.0106 342.55;--a:0.7676 0.184 183.61;--n:0.321785 0.02476 255.701624;--nc:0.894994 0.011585 252.096176;--b1:1 0 0;--b2:0.961151 0 0;--b3:0.924169 0.00108 197.137559;--bc:0.278078 0.029596 256.847952}@media (prefers-color-scheme:dark){:root{color-scheme:dark;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.13138 0.0392 275.75;--sc:0.1496 0.052 342.55;--ac:0.14902 0.0334 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.6569 0.196 275.75;--s:0.748 0.26 342.55;--a:0.7451 0.167 183.61;--n:0.313815 0.021108 254.139175;--nc:0.746477 0.0216 264.435964;--b1:0.253267 0.015896 252.417568;--b2:0.232607 0.013807 253.100675;--b3:0.211484 0.01165 254.087939;--bc:0.746477 0.0216 264.435964}}[data-theme=light]{color-scheme:light;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.89824 0.06192 275.75;--ac:0.15352 0.0368 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.4912 0.3096 275.75;--s:0.6971 0.329 342.55;--sc:0.9871 0.0106 342.55;--a:0.7676 0.184 183.61;--n:0.321785 0.02476 255.701624;--nc:0.894994 0.011585 252.096176;--b1:1 0 0;--b2:0.961151 0 0;--b3:0.924169 0.00108 197.137559;--bc:0.278078 0.029596 256.847952}[data-theme=dark]{color-scheme:dark;--in:0.7206 0.191 231.6;--su:64.8% 0.150 160;--wa:0.8471 0.199 83.87;--er:0.7176 0.221 22.18;--pc:0.13138 0.0392 275.75;--sc:0.1496 0.052 342.55;--ac:0.14902 0.0334 183.61;--inc:0 0 0;--suc:0 0 0;--wac:0 0 0;--erc:0 0 0;--rounded-box:1rem;--rounded-btn:0.5rem;--rounded-badge:1.9rem;--animation-btn:0.25s;--animation-input:.2s;--btn-focus-scale:0.95;--border-btn:1px;--tab-border:1px;--tab-radius:0.5rem;--p:0.6569 0.196 275.75;--s:0.748 0.26 342.55;--a:0.7451 0.167 183.61;--n:0.313815 0.021108 254.139175;--nc:0.746477 0.0216 264.435964;--b1:0.253267 0.015896 252.417568;--b2:0.232607 0.013807 253.100675;--b3:0.211484 0.01165 254.087939;--bc:0.746477 0.0216 264.435964}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate{background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}@media (forced-colors:active) {[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:transparent}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.alert{align-content:flex-start;align-items:center;border-radius:var(--rounded-box,1rem);border-width:1px;display:grid;gap:1rem;grid-auto-flow:row;justify-items:center;text-align:center;width:100%;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));padding:1rem;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--alert-bg:var(--fallback-b2,oklch(var(--b2)/1));--alert-bg-mix:var(--fallback-b1,oklch(var(--b1)/1));background-color:var(--alert-bg)}@media (min-width:640px){.alert{grid-auto-flow:column;grid-template-columns:auto minmax(auto,1fr);justify-items:start;text-align:start}}.avatar.placeholder>div{align-items:center;display:flex;justify-content:center}.badge{align-items:center;border-radius:var(--rounded-badge,1.9rem);border-width:1px;display:inline-flex;font-size:.875rem;height:1.25rem;justify-content:center;line-height:1.25rem;padding-left:.563rem;padding-right:.563rem;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);width:-moz-fit-content;width:fit-content;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}@media (hover:hover){.label a:hover{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.menu li>:not(ul,.menu-title,details,.btn).active,.menu li>:not(ul,.menu-title,details,.btn):active,.menu li>details>summary:active{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.radio-primary:hover{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.tab:hover{--tw-text-opacity:1}.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]):hover,.tabs-boxed :is(input:checked):hover{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.table tr.hover:hover,.table tr.hover:nth-child(2n):hover{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}.table-zebra tr.hover:hover,.table-zebra tr.hover:nth-child(2n):hover{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}}.btn{align-items:center;animation:button-pop var(--animation-btn,.25s) ease-out;border-color:transparent;border-color:oklch(var(--btn-color,var(--b2))/var(--tw-border-opacity));border-radius:var(--rounded-btn,.5rem);border-width:var(--border-btn,1px);cursor:pointer;display:inline-flex;flex-shrink:0;flex-wrap:wrap;font-size:.875rem;font-weight:600;gap:.5rem;height:3rem;justify-content:center;line-height:1em;min-height:3rem;padding-left:1rem;padding-right:1rem;text-align:center;text-decoration-line:none;transition-duration:.2s;transition-property:color,background-color,border-color,opacity,box-shadow,transform;transition-timing-function:cubic-bezier(0,0,.2,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);background-color:oklch(var(--btn-color,var(--b2))/var(--tw-bg-opacity));box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:var(--fallback-bc,oklch(var(--bc)/1));--tw-bg-opacity:1;--tw-border-opacity:1}.btn-disabled,.btn:disabled,.btn[disabled]{pointer-events:none}:where(.btn:is(input[type=checkbox])),:where(.btn:is(input[type=radio])){-webkit-appearance:none;-moz-appearance:none;appearance:none;width:auto}.btn:is(input[type=checkbox]):after,.btn:is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.card{border-radius:var(--rounded-box,1rem);display:flex;flex-direction:column;position:relative}.card:focus{outline:2px solid transparent;outline-offset:2px}.card-body{display:flex;flex:1 1 auto;flex-direction:column;gap:.5rem;padding:var(--padding-card,2rem)}.card-body :where(p){flex-grow:1}.card-actions{align-items:flex-start;display:flex;flex-wrap:wrap;gap:.5rem}.card figure{align-items:center;display:flex;justify-content:center}.card.image-full{display:grid}.card.image-full:before{border-radius:var(--rounded-box,1rem);content:"";position:relative;z-index:10;--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));opacity:.75}.card.image-full:before,.card.image-full>*{grid-column-start:1;grid-row-start:1}.card.image-full>figure img{height:100%;-o-object-fit:cover;object-fit:cover}.card.image-full>.card-body{position:relative;z-index:20;--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.checkbox{flex-shrink:0;--chkbg:var(--fallback-bc,oklch(var(--bc)/1));--chkfg:var(--fallback-b1,oklch(var(--b1)/1));-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:var(--rounded-btn,.5rem);border-width:1px;cursor:pointer;height:1.5rem;width:1.5rem;--tw-border-opacity:0.2}.divider{align-items:center;align-self:stretch;display:flex;flex-direction:row;height:1rem;margin-bottom:1rem;margin-top:1rem;white-space:nowrap}.divider:after,.divider:before{flex-grow:1;height:.125rem;width:100%;--tw-content:"";background-color:var(--fallback-bc,oklch(var(--bc)/.1));content:var(--tw-content)}.\!drawer{display:grid!important;grid-auto-columns:max-content auto!important;position:relative!important;width:100%!important}.drawer{display:grid;grid-auto-columns:max-content auto;position:relative;width:100%}.dropdown{display:inline-block;position:relative}.dropdown>:not(summary):focus{outline:2px solid transparent;outline-offset:2px}.dropdown .dropdown-content{position:absolute}.dropdown:is(:not(details)) .dropdown-content{opacity:0;transform-origin:top;visibility:hidden;--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.dropdown-end .dropdown-content{inset-inline-end:0}.dropdown-left .dropdown-content{bottom:auto;inset-inline-end:100%;top:0;transform-origin:right}.dropdown-right .dropdown-content{bottom:auto;inset-inline-start:100%;top:0;transform-origin:left}.dropdown-bottom .dropdown-content{bottom:auto;top:100%;transform-origin:top}.dropdown-top .dropdown-content{bottom:100%;top:auto;transform-origin:bottom}.dropdown-end.dropdown-left .dropdown-content,.dropdown-end.dropdown-right .dropdown-content{bottom:0;top:auto}.dropdown.dropdown-open .dropdown-content,.dropdown:focus-within .dropdown-content,.dropdown:not(.dropdown-hover):focus .dropdown-content{opacity:1;visibility:visible}@media (hover:hover){.dropdown.dropdown-hover:hover .dropdown-content{opacity:1;visibility:visible}.btm-nav>.disabled:hover,.btm-nav>[disabled]:hover{pointer-events:none;--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btn:hover{--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn:hover{background-color:color-mix(in oklab,oklch(var(--btn-color,var(--b2))/var(--tw-bg-opacity,1)) 90%,#000);border-color:color-mix(in oklab,oklch(var(--btn-color,var(--b2))/var(--tw-border-opacity,1)) 90%,#000)}}@supports not (color:oklch(0 0 0)){.btn:hover{background-color:var(--btn-color,var(--fallback-b2));border-color:var(--btn-color,var(--fallback-b2))}}.btn.glass:hover{--glass-opacity:25%;--glass-border-opacity:15%}.btn-ghost:hover{border-color:transparent}@supports (color:oklch(0 0 0)){.btn-ghost:hover{background-color:var(--fallback-bc,oklch(var(--bc)/.2))}}.btn-outline:hover{--tw-border-opacity:1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}.btn-outline.btn-primary:hover{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-primary:hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}}.btn-outline.btn-secondary:hover{--tw-text-opacity:1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-secondary:hover{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000)}}.btn-outline.btn-accent:hover{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-accent:hover{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000)}}.btn-outline.btn-success:hover{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-success:hover{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000)}}.btn-outline.btn-info:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-info:hover{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}.btn-outline.btn-warning:hover{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-warning:hover{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000)}}.btn-outline.btn-error:hover{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.btn-outline.btn-error:hover{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000)}}.btn-disabled:hover,.btn:disabled:hover,.btn[disabled]:hover{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}@supports (color:color-mix(in oklab,black,black)){.btn:is(input[type=checkbox]:checked):hover,.btn:is(input[type=radio]:checked):hover{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}}.dropdown.dropdown-hover:hover .dropdown-content{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(.active,.btn):hover,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{cursor:pointer;outline:2px solid transparent;outline-offset:2px}@supports (color:oklch(0 0 0)){:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(.active,.btn):hover,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.active,.btn):hover{background-color:var(--fallback-bc,oklch(var(--bc)/.1))}}.tab[disabled],.tab[disabled]:hover{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));cursor:not-allowed;--tw-text-opacity:0.2}}.dropdown:is(details) summary::-webkit-details-marker{display:none}.file-input{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:var(--rounded-btn,.5rem);border-width:1px;flex-shrink:1;font-size:1rem;height:3rem;line-height:2;line-height:1.5rem;overflow:hidden;padding-inline-end:1rem;--tw-border-opacity:0;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.file-input::file-selector-button{align-items:center;border-style:solid;cursor:pointer;display:inline-flex;flex-shrink:0;flex-wrap:wrap;font-size:.875rem;height:100%;justify-content:center;line-height:1.25rem;line-height:1em;margin-inline-end:1rem;padding-left:1rem;padding-right:1rem;text-align:center;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-border-opacity:1;border-color:var(--fallback-n,oklch(var(--n)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));font-weight:600;text-transform:uppercase;--tw-text-opacity:1;animation:button-pop var(--animation-btn,.25s) ease-out;border-width:var(--border-btn,1px);color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));text-decoration-line:none}.footer{-moz-column-gap:1rem;column-gap:1rem;font-size:.875rem;grid-auto-flow:row;line-height:1.25rem;row-gap:2.5rem;width:100%}.footer,.footer>*{display:grid;place-items:start}.footer>*{gap:.5rem}@media (min-width:48rem){.footer{grid-auto-flow:column}.footer-center{grid-auto-flow:row dense}}.form-control{flex-direction:column}.form-control,.label{display:flex}.label{align-items:center;justify-content:space-between;padding:.5rem .25rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.hero{background-position:50%;background-size:cover;display:grid;place-items:center;width:100%}.hero>*{grid-column-start:1;grid-row-start:1}.hero-content{align-items:center;display:flex;gap:1rem;justify-content:center;max-width:80rem;padding:1rem;z-index:0}.indicator{display:inline-flex;position:relative;width:-moz-max-content;width:max-content}.indicator :where(.indicator-item){position:absolute;white-space:nowrap;z-index:1}.input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:transparent;border-radius:var(--rounded-btn,.5rem);border-width:1px;flex-shrink:1;font-size:1rem;height:3rem;line-height:2;line-height:1.5rem;padding-left:1rem;padding-right:1rem;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.input-md[type=number]::-webkit-inner-spin-button,.input[type=number]::-webkit-inner-spin-button{margin-bottom:-1rem;margin-top:-1rem;margin-inline-end:-1rem}.input-xs[type=number]::-webkit-inner-spin-button{margin-bottom:-.25rem;margin-top:-.25rem;margin-inline-end:0}.input-sm[type=number]::-webkit-inner-spin-button{margin-bottom:0;margin-top:0;margin-inline-end:0}.join{align-items:stretch;border-radius:var(--rounded-btn,.5rem);display:inline-flex}.join :where(.join-item){border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:0;border-start-start-radius:0}.join .join-item:not(:first-child):not(:last-child),.join :not(:first-child):not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:0;border-start-start-radius:0}.join .join-item:first-child:not(:last-child),.join :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-start-end-radius:0}.join .dropdown .join-item:first-child:not(:last-child),.join :first-child:not(:last-child) .dropdown .join-item{border-end-end-radius:inherit;border-start-end-radius:inherit}.join :where(.join-item:first-child:not(:last-child)),.join :where(:first-child:not(:last-child) .join-item){border-end-start-radius:inherit;border-start-start-radius:inherit}.join .join-item:last-child:not(:first-child),.join :last-child:not(:first-child) .join-item{border-end-start-radius:0;border-start-start-radius:0}.join :where(.join-item:last-child:not(:first-child)),.join :where(:last-child:not(:first-child) .join-item){border-end-end-radius:inherit;border-start-end-radius:inherit}@supports not selector(:has(*)){:where(.join *){border-radius:inherit}}@supports selector(:has(*)){:where(.join :has(.join-item)){border-radius:inherit}}.link{cursor:pointer;text-decoration-line:underline}.menu{display:flex;flex-direction:column;flex-wrap:wrap;font-size:.875rem;line-height:1.25rem;padding:.5rem}.menu :where(li ul){margin-inline-start:1rem;padding-inline-start:.5rem;position:relative;white-space:nowrap}.menu :where(li:not(.menu-title)>:not(ul,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){align-content:flex-start;align-items:center;display:grid;gap:.5rem;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;-webkit-user-select:none;-moz-user-select:none;user-select:none}.menu li.disabled{color:var(--fallback-bc,oklch(var(--bc)/.3));cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;user-select:none}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}:where(.menu li){align-items:stretch;display:flex;flex-direction:column;flex-shrink:0;flex-wrap:wrap;position:relative}:where(.menu li) .badge{justify-self:end}.modal{background-color:transparent;color:inherit;display:grid;height:100%;inset:0;justify-items:center;margin:0;max-height:none;max-width:none;opacity:0;overflow-y:hidden;overscroll-behavior:contain;padding:0;pointer-events:none;position:fixed;transition-duration:.2s;transition-property:transform,opacity,visibility;transition-timing-function:cubic-bezier(0,0,.2,1);width:100%;z-index:999}:where(.modal){align-items:center}.modal-box{grid-column-start:1;grid-row-start:1;max-height:calc(100vh - 5em);max-width:32rem;width:91.666667%;--tw-scale-x:.9;--tw-scale-y:.9;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));box-shadow:0 25px 50px -12px rgba(0,0,0,.25);overflow-y:auto;overscroll-behavior:contain;padding:1.5rem;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}.modal-open,.modal-toggle:checked+.modal,.modal:target,.modal[open]{opacity:1;pointer-events:auto;visibility:visible}.modal-action{display:flex;justify-content:flex-end;margin-top:1.5rem}.modal-toggle{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:0;opacity:0;position:fixed;width:0}:root:has(:is(.modal-open,.modal:target,.modal-toggle:checked+.modal,.modal[open])){overflow:hidden}.navbar{align-items:center;display:flex;min-height:4rem;padding:var(--navbar-padding,.5rem);width:100%}:where(.navbar>:not(script,style)){align-items:center;display:inline-flex}.navbar-start{justify-content:flex-start;width:50%}.navbar-center{flex-shrink:0}.navbar-end{justify-content:flex-end;width:50%}.progress{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-radius:var(--rounded-box,1rem);height:.5rem;overflow:hidden;position:relative;width:100%}.radio{flex-shrink:0;--chkbg:var(--bc);-webkit-appearance:none;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));border-radius:9999px;border-width:1px;width:1.5rem;--tw-border-opacity:0.2}.radio,.range{-moz-appearance:none;appearance:none;cursor:pointer;height:1.5rem}.range{-webkit-appearance:none;width:100%;--range-shdw:var(--fallback-bc,oklch(var(--bc)/1));background-color:transparent;border-radius:var(--rounded-box,1rem);overflow:hidden}.range:focus{outline:none}.select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-color:transparent;border-radius:var(--rounded-btn,.5rem);border-width:1px;cursor:pointer;display:inline-flex;font-size:.875rem;height:3rem;line-height:1.25rem;line-height:2;min-height:3rem;padding-left:1rem;padding-right:2.5rem;-webkit-user-select:none;-moz-user-select:none;user-select:none;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));background-image:linear-gradient(45deg,transparent 50%,currentColor 0),linear-gradient(135deg,currentColor 50%,transparent 0);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}.select[multiple]{height:auto}.stack{display:inline-grid;place-items:center;align-items:flex-end}.stack>*{grid-column-start:1;grid-row-start:1;opacity:.6;transform:translateY(10%) scale(.9);width:100%;z-index:1}.stack>:nth-child(2){opacity:.8;transform:translateY(5%) scale(.95);z-index:2}.stack>:first-child{opacity:1;transform:translateY(0) scale(1);z-index:3}.stats{border-radius:var(--rounded-box,1rem);display:inline-grid;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}:where(.stats){grid-auto-flow:column;overflow-x:auto}.stat{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));-moz-column-gap:1rem;column-gap:1rem;display:inline-grid;grid-template-columns:repeat(1,1fr);width:100%;--tw-border-opacity:0.1;padding:1rem 1.5rem}.stat-title{color:var(--fallback-bc,oklch(var(--bc)/.6))}.stat-title,.stat-value{grid-column-start:1;white-space:nowrap}.stat-value{font-size:2.25rem;font-weight:800;line-height:2.5rem}.stat-desc{color:var(--fallback-bc,oklch(var(--bc)/.6));font-size:.75rem;grid-column-start:1;line-height:1rem;white-space:nowrap}.steps .step{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));grid-template-columns:auto;grid-template-rows:repeat(2,minmax(0,1fr));grid-template-rows:40px 1fr;min-width:4rem;place-items:center;text-align:center}.tabs{align-items:flex-end;display:grid}.tabs-lifted:has(.tab-content[class*=" rounded-"]) .tab:first-child:not(.tab-active),.tabs-lifted:has(.tab-content[class^=rounded-]) .tab:first-child:not(.tab-active){border-bottom-color:transparent}.tab{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;display:inline-flex;flex-wrap:wrap;font-size:.875rem;grid-row-start:1;height:2rem;justify-content:center;line-height:1.25rem;line-height:2;position:relative;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;--tab-padding:1rem;--tw-text-opacity:0.5;--tab-color:var(--fallback-bc,oklch(var(--bc)/1));--tab-bg:var(--fallback-b1,oklch(var(--b1)/1));--tab-border-color:var(--fallback-b3,oklch(var(--b3)/1));color:var(--tab-color);padding-inline-end:var(--tab-padding,1rem);padding-inline-start:var(--tab-padding,1rem)}.tab:is(input[type=radio]){border-bottom-left-radius:0;border-bottom-right-radius:0;width:auto}.tab:is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:not(input):empty{cursor:default;grid-column-start:span 9999}.tab-active+.tab-content:nth-child(2),:checked+.tab-content:nth-child(2){border-start-start-radius:0}.tab-active+.tab-content,input.tab:checked+.tab-content{display:block}.table{border-radius:var(--rounded-box,1rem);font-size:.875rem;line-height:1.25rem;position:relative;text-align:left;width:100%}.table :where(.table-pin-rows thead tr){position:sticky;top:0;z-index:1;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.table :where(.table-pin-rows tfoot tr){bottom:0;position:sticky;z-index:1;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.table :where(.table-pin-cols tr th){left:0;position:sticky;right:0;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.timeline{display:flex;position:relative}:where(.timeline>li){align-items:center;display:grid;flex-shrink:0;grid-template-columns:var(--timeline-col-start,minmax(0,1fr)) auto var( + --timeline-col-end,minmax(0,1fr) + );grid-template-rows:var(--timeline-row-start,minmax(0,1fr)) auto var( + --timeline-row-end,minmax(0,1fr) + );position:relative}.timeline>li>hr{border-width:0;width:100%}:where(.timeline>li>hr):first-child{grid-column-start:1;grid-row-start:2}:where(.timeline>li>hr):last-child{grid-column-end:none;grid-column-start:3;grid-row-end:auto;grid-row-start:2}.timeline-start{align-self:flex-end;grid-column-end:4;grid-column-start:1;grid-row-end:2;grid-row-start:1;justify-self:center;margin:.25rem}.timeline-middle{grid-column-start:2;grid-row-start:2}.timeline-end{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.toggle{flex-shrink:0;--tglbg:var(--fallback-b1,oklch(var(--b1)/1));--handleoffset:1.5rem;--handleoffsetcalculator:calc(var(--handleoffset)*-1);--togglehandleborder:0 0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:currentColor;border-color:currentColor;border-radius:var(--rounded-badge,1.9rem);border-width:1px;box-shadow:var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset,var(--togglehandleborder);color:var(--fallback-bc,oklch(var(--bc)/.5));cursor:pointer;height:1.5rem;transition:background,box-shadow var(--animation-input,.2s) ease-out;width:3rem}.alert-info{border-color:var(--fallback-in,oklch(var(--in)/.2));--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));--alert-bg:var(--fallback-in,oklch(var(--in)/1));--alert-bg-mix:var(--fallback-b1,oklch(var(--b1)/1))}.badge-primary{background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.badge-primary,.badge-secondary{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.badge-secondary{background-color:var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity)));border-color:var(--fallback-s,oklch(var(--s)/var(--tw-border-opacity)));color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}.badge-success{background-color:var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity)));color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}.badge-success,.badge-warning{border-color:transparent;--tw-bg-opacity:1;--tw-text-opacity:1}.badge-warning{background-color:var(--fallback-wa,oklch(var(--wa)/var(--tw-bg-opacity)));color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}.badge-outline{border-color:currentColor;--tw-border-opacity:0.5;background-color:transparent;color:currentColor}.badge-outline.badge-neutral{--tw-text-opacity:1;color:var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity)))}.badge-outline.badge-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}.badge-outline.badge-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}.badge-outline.badge-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}.badge-outline.badge-info{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.badge-outline.badge-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}.badge-outline.badge-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}.badge-outline.badge-error{--tw-text-opacity:1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}.btm-nav>:where(.active){border-top-width:2px;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)))}.btm-nav>.disabled,.btm-nav>[disabled]{pointer-events:none;--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btm-nav>* .label{font-size:1rem;line-height:1.5rem}.btn:active:focus,.btn:active:hover{animation:button-pop 0s ease-out;transform:scale(var(--btn-focus-scale,.97))}@supports not (color:oklch(0 0 0)){.btn{background-color:var(--btn-color,var(--fallback-b2));border-color:var(--btn-color,var(--fallback-b2))}.btn-primary{--btn-color:var(--fallback-p)}.btn-neutral{--btn-color:var(--fallback-n)}.btn-info{--btn-color:var(--fallback-in)}.btn-success{--btn-color:var(--fallback-su)}.btn-warning{--btn-color:var(--fallback-wa)}.btn-error{--btn-color:var(--fallback-er)}}@supports (color:color-mix(in oklab,black,black)){.btn-active{background-color:color-mix(in oklab,oklch(var(--btn-color,var(--b3))/var(--tw-bg-opacity,1)) 90%,#000);border-color:color-mix(in oklab,oklch(var(--btn-color,var(--b3))/var(--tw-border-opacity,1)) 90%,#000)}.btn-outline.btn-primary.btn-active{background-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 90%,#000)}.btn-outline.btn-secondary.btn-active{background-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-s,oklch(var(--s)/1)) 90%,#000)}.btn-outline.btn-accent.btn-active{background-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-a,oklch(var(--a)/1)) 90%,#000)}.btn-outline.btn-success.btn-active{background-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-su,oklch(var(--su)/1)) 90%,#000)}.btn-outline.btn-info.btn-active{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}.btn-outline.btn-warning.btn-active{background-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-wa,oklch(var(--wa)/1)) 90%,#000)}.btn-outline.btn-error.btn-active{background-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-er,oklch(var(--er)/1)) 90%,#000)}}.btn:focus-visible{outline-offset:2px;outline-style:solid;outline-width:2px}.btn-primary{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}@supports (color:oklch(0 0 0)){.btn-primary{--btn-color:var(--p)}.btn-neutral{--btn-color:var(--n)}.btn-info{--btn-color:var(--in)}.btn-success{--btn-color:var(--su)}.btn-warning{--btn-color:var(--wa)}.btn-error{--btn-color:var(--er)}}.btn-neutral{--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));outline-color:var(--fallback-n,oklch(var(--n)/1))}.btn-info{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));outline-color:var(--fallback-in,oklch(var(--in)/1))}.btn-success{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));outline-color:var(--fallback-su,oklch(var(--su)/1))}.btn-warning{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));outline-color:var(--fallback-wa,oklch(var(--wa)/1))}.btn-error{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));outline-color:var(--fallback-er,oklch(var(--er)/1))}.btn.glass{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.btn.glass.btn-active{--glass-opacity:25%;--glass-border-opacity:15%}.btn-ghost{background-color:transparent;border-color:transparent;border-width:1px;color:currentColor;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.btn-ghost.btn-active{background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-color:transparent}.btn-link.btn-active{background-color:transparent;border-color:transparent;text-decoration-line:underline}.btn-outline{background-color:transparent;border-color:currentColor;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.btn-outline.btn-active{--tw-border-opacity:1;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)))}.btn-outline.btn-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}.btn-outline.btn-primary.btn-active{--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.btn-outline.btn-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)))}.btn-outline.btn-secondary.btn-active{--tw-text-opacity:1;color:var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)))}.btn-outline.btn-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)))}.btn-outline.btn-accent.btn-active{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)))}.btn-outline.btn-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)))}.btn-outline.btn-success.btn-active{--tw-text-opacity:1;color:var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)))}.btn-outline.btn-info{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.btn-outline.btn-info.btn-active{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}.btn-outline.btn-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)))}.btn-outline.btn-warning.btn-active{--tw-text-opacity:1;color:var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)))}.btn-outline.btn-error{--tw-text-opacity:1;color:var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)))}.btn-outline.btn-error.btn-active{--tw-text-opacity:1;color:var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)))}.btn.btn-disabled,.btn:disabled,.btn[disabled]{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.btn:is(input[type=checkbox]:checked),.btn:is(input[type=radio]:checked){--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.btn:is(input[type=checkbox]:checked):focus-visible,.btn:is(input[type=radio]:checked):focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}@keyframes button-pop{0%{transform:scale(var(--btn-focus-scale,.98))}40%{transform:scale(1.02)}to{transform:scale(1)}}.card :where(figure:first-child){border-end-end-radius:unset;border-end-start-radius:unset;border-start-end-radius:inherit;border-start-start-radius:inherit;overflow:hidden}.card :where(figure:last-child){border-end-end-radius:inherit;border-end-start-radius:inherit;border-start-end-radius:unset;border-start-start-radius:unset;overflow:hidden}.card:focus-visible{outline:2px solid currentColor;outline-offset:2px}.card.bordered{border-width:1px;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}.card.compact .card-body{font-size:.875rem;line-height:1.25rem;padding:1rem}.card-title{align-items:center;display:flex;font-size:1.25rem;font-weight:600;gap:.5rem;line-height:1.75rem}.card.image-full :where(figure){border-radius:inherit;overflow:hidden}.checkbox:focus{box-shadow:none}.checkbox:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/1));outline-offset:2px;outline-style:solid;outline-width:2px}.checkbox:checked,.checkbox[aria-checked=true],.checkbox[checked=true]{animation:checkmark var(--animation-input,.2s) ease-out;background-color:var(--chkbg);background-image:linear-gradient(-45deg,transparent 65%,var(--chkbg) 65.99%),linear-gradient(45deg,transparent 75%,var(--chkbg) 75.99%),linear-gradient(-45deg,var(--chkbg) 40%,transparent 40.99%),linear-gradient(45deg,var(--chkbg) 30%,var(--chkfg) 30.99%,var(--chkfg) 40%,transparent 40.99%),linear-gradient(-45deg,var(--chkfg) 50%,var(--chkbg) 50.99%);background-repeat:no-repeat}.checkbox:indeterminate{--tw-bg-opacity:1;animation:checkmark var(--animation-input,.2s) ease-out;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-image:linear-gradient(90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(-90deg,transparent 80%,var(--chkbg) 80%),linear-gradient(0deg,var(--chkbg) 43%,var(--chkfg) 43%,var(--chkfg) 57%,var(--chkbg) 57%);background-repeat:no-repeat}.checkbox:disabled{border-color:transparent;cursor:not-allowed;--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));opacity:.2}@keyframes checkmark{0%{background-position-y:5px}50%{background-position-y:-2px}to{background-position-y:0}}.divider:not(:empty){gap:1rem}.drawer-toggle:focus-visible~.drawer-content label.drawer-button{outline-offset:2px;outline-style:solid;outline-width:2px}.dropdown.dropdown-open .dropdown-content,.dropdown:focus .dropdown-content,.dropdown:focus-within .dropdown-content{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.file-input-bordered{--tw-border-opacity:0.2}.file-input:focus{outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.file-input-disabled,.file-input[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));--tw-text-opacity:0.2}.file-input-disabled::-moz-placeholder,.file-input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.file-input-disabled::placeholder,.file-input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.file-input-disabled::file-selector-button,.file-input[disabled]::file-selector-button{--tw-border-opacity:0;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-bg-opacity:0.2;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.footer-title{font-weight:700;margin-bottom:.5rem;opacity:.6;text-transform:uppercase}.label-text{font-size:.875rem;line-height:1.25rem}.label-text,.label-text-alt{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.label-text-alt{font-size:.75rem;line-height:1rem}.input input{--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));background-color:transparent}.input input:focus{outline:2px solid transparent;outline-offset:2px}.input[list]::-webkit-calendar-picker-indicator{line-height:1em}.input-bordered{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}.input:focus,.input:focus-within{border-color:var(--fallback-bc,oklch(var(--bc)/.2));box-shadow:none;outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.input-ghost{--tw-bg-opacity:0.05}.input-ghost:focus,.input-ghost:focus-within{--tw-bg-opacity:1;--tw-text-opacity:1;box-shadow:none;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}.input-primary{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.input-primary:focus,.input-primary:focus-within{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}.input-error{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))}.input-error:focus,.input-error:focus-within{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)));outline-color:var(--fallback-er,oklch(var(--er)/1))}.input-disabled,.input:disabled,.input[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/.4))}.input-disabled::-moz-placeholder,.input:disabled::-moz-placeholder,.input[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.input-disabled::placeholder,.input:disabled::placeholder,.input[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.input::-webkit-date-and-time-value{text-align:inherit}.join>:where(:not(:first-child)){margin-bottom:0;margin-top:0;margin-inline-start:-1px}.join-item:focus{isolation:isolate}.link-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){@media (hover:hover){.link-primary:hover{color:color-mix(in oklab,var(--fallback-p,oklch(var(--p)/1)) 80%,#000)}}}.link:focus{outline:2px solid transparent;outline-offset:2px}.link:focus-visible{outline:2px solid currentColor;outline-offset:2px}.loading{aspect-ratio:1/1;background-color:currentColor;display:inline-block;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;pointer-events:none;width:1.5rem}.loading,.loading-spinner{-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' stroke='%23000'%3E%3Cstyle%3E@keyframes spinner_zKoa{to{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%25{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%25{stroke-dasharray:42 150;stroke-dashoffset:-16}95%25,to{stroke-dasharray:42 150;stroke-dashoffset:-59}}%3C/style%3E%3Cg style='transform-origin:center;animation:spinner_zKoa 2s linear infinite'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' class='spinner_V8m1' style='stroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite'/%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' stroke='%23000'%3E%3Cstyle%3E@keyframes spinner_zKoa{to{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%25{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%25{stroke-dasharray:42 150;stroke-dashoffset:-16}95%25,to{stroke-dasharray:42 150;stroke-dashoffset:-59}}%3C/style%3E%3Cg style='transform-origin:center;animation:spinner_zKoa 2s linear infinite'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' class='spinner_V8m1' style='stroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite'/%3E%3C/g%3E%3C/svg%3E")}.loading-dots{-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cstyle%3E@keyframes spinner_8HQG{0%25,57.14%25{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%25{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}%3C/style%3E%3Ccircle cx='4' cy='12' r='3' class='spinner_qM83'/%3E%3Ccircle cx='12' cy='12' r='3' class='spinner_qM83' style='animation-delay:.1s'/%3E%3Ccircle cx='20' cy='12' r='3' class='spinner_qM83' style='animation-delay:.2s'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cstyle%3E@keyframes spinner_8HQG{0%25,57.14%25{animation-timing-function:cubic-bezier(.33,.66,.66,1);transform:translate(0)}28.57%25{animation-timing-function:cubic-bezier(.33,0,.66,.33);transform:translateY(-6px)}to{transform:translate(0)}}.spinner_qM83{animation:spinner_8HQG 1.05s infinite}%3C/style%3E%3Ccircle cx='4' cy='12' r='3' class='spinner_qM83'/%3E%3Ccircle cx='12' cy='12' r='3' class='spinner_qM83' style='animation-delay:.1s'/%3E%3Ccircle cx='20' cy='12' r='3' class='spinner_qM83' style='animation-delay:.2s'/%3E%3C/svg%3E")}.loading-sm{width:1.25rem}.loading-md{width:1.5rem}.loading-lg{width:2.5rem}:where(.menu li:empty){--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));height:1px;margin:.5rem 1rem;opacity:.1}.menu :where(li ul):before{bottom:.75rem;inset-inline-start:0;position:absolute;top:.75rem;width:1px;--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));content:"";opacity:.1}.menu :where(li:not(.menu-title)>:not(ul,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn,.5rem);padding:.5rem 1rem;text-align:start;text-wrap:balance;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1)}:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):is(summary):not(.active,.btn):focus-visible,:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(summary,.active,.btn).focus,:where(.menu li:not(.menu-title,.disabled)>:not(ul,details,.menu-title)):not(summary,.active,.btn):focus,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):is(summary):not(.active,.btn):focus-visible,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn).focus,:where(.menu li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(summary,.active,.btn):focus{background-color:var(--fallback-bc,oklch(var(--bc)/.1));cursor:pointer;--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));outline:2px solid transparent;outline-offset:2px}.menu li>:not(ul,.menu-title,details,.btn).active,.menu li>:not(ul,.menu-title,details,.btn):active,.menu li>details>summary:active{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)))}.menu :where(li>details>summary)::-webkit-details-marker{display:none}.menu :where(li>.menu-dropdown-toggle):after,.menu :where(li>details>summary):after{box-shadow:2px 2px;content:"";display:block;height:.5rem;justify-self:end;margin-top:-.5rem;pointer-events:none;transform:rotate(45deg);transform-origin:75% 75%;transition-duration:.3s;transition-property:transform,margin-top;transition-timing-function:cubic-bezier(.4,0,.2,1);width:.5rem}.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after,.menu :where(li>details[open]>summary):after{margin-top:0;transform:rotate(225deg)}.mockup-phone .display{border-radius:40px;margin-top:-25px;overflow:hidden}.mockup-browser .mockup-browser-toolbar .input{display:block;height:1.75rem;margin-left:auto;margin-right:auto;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:24rem;--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));direction:ltr;padding-left:2rem}.mockup-browser .mockup-browser-toolbar .input:before{aspect-ratio:1/1;height:.75rem;left:.5rem;--tw-translate-y:-50%;border-color:currentColor;border-radius:9999px;border-width:2px}.mockup-browser .mockup-browser-toolbar .input:after,.mockup-browser .mockup-browser-toolbar .input:before{content:"";opacity:.6;position:absolute;top:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mockup-browser .mockup-browser-toolbar .input:after{height:.5rem;left:1.25rem;--tw-translate-y:25%;--tw-rotate:-45deg;border-color:currentColor;border-radius:9999px;border-width:1px}.modal::backdrop,.modal:not(dialog:not(.modal-open)){animation:modal-pop .2s ease-out;background-color:#0006}.modal-backdrop{align-self:stretch;color:transparent;display:grid;grid-column-start:1;grid-row-start:1;justify-self:stretch;z-index:-1}.modal-open .modal-box,.modal-toggle:checked+.modal .modal-box,.modal:target .modal-box,.modal[open] .modal-box{--tw-translate-y:0px;--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-action>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}@keyframes modal-pop{0%{opacity:0}}.progress::-moz-progress-bar{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}.progress-primary::-moz-progress-bar{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}.progress:indeterminate{--progress-color:var(--fallback-bc,oklch(var(--bc)/1));animation:progress-loading 5s ease-in-out infinite;background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-position-x:15%;background-size:200%}.progress-primary:indeterminate{--progress-color:var(--fallback-p,oklch(var(--p)/1))}.progress::-webkit-progress-bar{background-color:transparent;border-radius:var(--rounded-box,1rem)}.progress::-webkit-progress-value{border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)))}.progress-primary::-webkit-progress-value{--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)))}.progress:indeterminate::-moz-progress-bar{animation:progress-loading 5s ease-in-out infinite;background-color:transparent;background-image:repeating-linear-gradient(90deg,var(--progress-color) -1%,var(--progress-color) 10%,transparent 10%,transparent 90%);background-position-x:15%;background-size:200%}@keyframes progress-loading{50%{background-position-x:-115%}}.radio:focus{box-shadow:none}.radio:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/1));outline-offset:2px;outline-style:solid;outline-width:2px}.radio:checked,.radio[aria-checked=true]{--tw-bg-opacity:1;animation:radiomark var(--animation-input,.2s) ease-out;background-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));background-image:none;box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}.radio-primary{--chkbg:var(--p);--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.radio-primary:focus-visible{outline-color:var(--fallback-p,oklch(var(--p)/1))}.radio-primary:checked,.radio-primary[aria-checked=true]{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}.radio:disabled{cursor:not-allowed;opacity:.2}@keyframes radiomark{0%{box-shadow:0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 12px var(--fallback-b1,oklch(var(--b1)/1)) inset}50%{box-shadow:0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 3px var(--fallback-b1,oklch(var(--b1)/1)) inset}to{box-shadow:0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 4px var(--fallback-b1,oklch(var(--b1)/1)) inset}}.range:focus-visible::-webkit-slider-thumb{--focus-shadow:0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 2rem var(--range-shdw) inset}.range:focus-visible::-moz-range-thumb{--focus-shadow:0 0 0 6px var(--fallback-b1,oklch(var(--b1)/1)) inset,0 0 0 2rem var(--range-shdw) inset}.range::-webkit-slider-runnable-track{background-color:var(--fallback-bc,oklch(var(--bc)/.1));border-radius:var(--rounded-box,1rem);height:.5rem;width:100%}.range::-moz-range-track{background-color:var(--fallback-bc,oklch(var(--bc)/.1));border-radius:var(--rounded-box,1rem);height:.5rem;width:100%}.range::-webkit-slider-thumb{border-radius:var(--rounded-box,1rem);border-style:none;height:1.5rem;position:relative;width:1.5rem;--tw-bg-opacity:1;appearance:none;-webkit-appearance:none;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));color:var(--range-shdw);top:50%;transform:translateY(-50%);--filler-size:100rem;--filler-offset:0.6rem;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow,0 0),calc(var(--filler-size)*-1 - var(--filler-offset)) 0 0 var(--filler-size)}.range::-moz-range-thumb{border-radius:var(--rounded-box,1rem);border-style:none;height:1.5rem;position:relative;width:1.5rem;--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));color:var(--range-shdw);top:50%;--filler-size:100rem;--filler-offset:0.5rem;box-shadow:0 0 0 3px var(--range-shdw) inset,var(--focus-shadow,0 0),calc(var(--filler-size)*-1 - var(--filler-offset)) 0 0 var(--filler-size)}@keyframes rating-pop{0%{transform:translateY(-.125em)}40%{transform:translateY(-.125em)}to{transform:translateY(0)}}.select-bordered,.select:focus{border-color:var(--fallback-bc,oklch(var(--bc)/.2))}.select:focus{box-shadow:none;outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.select-disabled,.select:disabled,.select[disabled]{cursor:not-allowed;--tw-border-opacity:1;border-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));--tw-text-opacity:0.2}.select-disabled::-moz-placeholder,.select:disabled::-moz-placeholder,.select[disabled]::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.select-disabled::placeholder,.select:disabled::placeholder,.select[disabled]::placeholder{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity)));--tw-placeholder-opacity:0.2}.select-multiple,.select[multiple],.select[size].select:not([size="1"]){background-image:none;padding-right:1rem}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}:where(.stats)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(0px*(1 - var(--tw-divide-y-reverse))) calc(1px*var(--tw-divide-x-reverse)) calc(0px*var(--tw-divide-y-reverse)) calc(1px*(1 - var(--tw-divide-x-reverse)))}:is([dir=rtl] .stats>:not([hidden])~:not([hidden])){--tw-divide-x-reverse:1}.steps .step:before{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));content:"";height:.5rem;margin-inline-start:-100%;top:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));width:100%}.steps .step:after,.steps .step:before{grid-column-start:1;grid-row-start:1;--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));--tw-text-opacity:1}.steps .step:after{border-radius:9999px;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));content:counter(step);counter-increment:step;display:grid;height:2rem;place-items:center;place-self:center;position:relative;width:2rem;z-index:1}.steps .step:first-child:before{content:none}.steps .step[data-content]:after{content:attr(data-content)}.tabs-lifted>.tab:focus-visible{border-end-end-radius:0;border-end-start-radius:0}.tab.tab-active:not(.tab-disabled):not([disabled]),.tab:is(input:checked){border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity:1;--tw-text-opacity:1}.tab:focus{outline:2px solid transparent;outline-offset:2px}.tab:focus-visible{outline:2px solid currentColor;outline-offset:-5px}.tab-disabled,.tab[disabled]{color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));cursor:not-allowed;--tw-text-opacity:0.2}.tabs-bordered>.tab{border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));--tw-border-opacity:0.2;border-bottom-width:calc(var(--tab-border, 1px) + 1px);border-style:solid}.tabs-lifted>.tab{border:var(--tab-border,1px) solid transparent;border-bottom-color:var(--tab-border-color);border-start-end-radius:var(--tab-radius,.5rem);border-start-start-radius:var(--tab-radius,.5rem);border-width:0 0 var(--tab-border,1px) 0;padding-inline-end:var(--tab-padding,1rem);padding-inline-start:var(--tab-padding,1rem);padding-top:var(--tab-border,1px)}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]),.tabs-lifted>.tab:is(input:checked){background-color:var(--tab-bg);border-inline-end-color:var(--tab-border-color);border-inline-start-color:var(--tab-border-color);border-top-color:var(--tab-border-color);border-width:var(--tab-border,1px) var(--tab-border,1px) 0 var(--tab-border,1px);padding-inline-end:calc(var(--tab-padding, 1rem) - var(--tab-border, 1px));padding-bottom:var(--tab-border,1px);padding-inline-start:calc(var(--tab-padding, 1rem) - var(--tab-border, 1px));padding-top:0}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):before,.tabs-lifted>.tab:is(input:checked):before{background-position:0 0,100% 0;background-repeat:no-repeat;background-size:var(--tab-radius,.5rem);bottom:0;content:"";display:block;height:var(--tab-radius,.5rem);position:absolute;width:calc(100% + var(--tab-radius, .5rem)*2);z-index:1;--tab-grad:calc(69% - var(--tab-border, 1px));--radius-start:radial-gradient(circle at top left,transparent var(--tab-grad),var(--tab-border-color) calc(var(--tab-grad) + 0.25px),var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)),var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px));--radius-end:radial-gradient(circle at top right,transparent var(--tab-grad),var(--tab-border-color) calc(var(--tab-grad) + 0.25px),var(--tab-border-color) calc(var(--tab-grad) + var(--tab-border, 1px)),var(--tab-bg) calc(var(--tab-grad) + var(--tab-border, 1px) + 0.25px));background-image:var(--radius-start),var(--radius-end)}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before,.tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-end);background-position:100% 0}[dir=rtl] .tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):first-child:before,[dir=rtl] .tabs-lifted>.tab:is(input:checked):first-child:before{background-image:var(--radius-start);background-position:0 0}.tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before,.tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-start);background-position:0 0}[dir=rtl] .tabs-lifted>.tab.tab-active:not(.tab-disabled):not([disabled]):last-child:before,[dir=rtl] .tabs-lifted>.tab:is(input:checked):last-child:before{background-image:var(--radius-end);background-position:100% 0}.tabs-lifted>.tab-active:not(.tab-disabled):not([disabled])+.tabs-lifted .tab-active:not(.tab-disabled):not([disabled]):before,.tabs-lifted>.tab:is(input:checked)+.tabs-lifted .tab:is(input:checked):before{background-image:var(--radius-end);background-position:100% 0}.tabs-boxed{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));padding:.25rem}.tabs-boxed,.tabs-boxed .tab{border-radius:var(--rounded-btn,.5rem)}.tabs-boxed .tab-active:not(.tab-disabled):not([disabled]),.tabs-boxed :is(input:checked){--tw-bg-opacity:1;background-color:var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity)));--tw-text-opacity:1;color:var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)))}:is([dir=rtl] .table){text-align:right}.table :where(th,td){padding:.75rem 1rem;vertical-align:middle}.table tr.active,.table tr.active:nth-child(2n),.table-zebra tbody tr:nth-child(2n){--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)))}.table-zebra tr.active,.table-zebra tr.active:nth-child(2n),.table-zebra-zebra tbody tr:nth-child(2n){--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}.table :where(thead,tbody) :where(tr:first-child:last-child),.table :where(thead,tbody) :where(tr:not(:last-child)){border-bottom-width:1px;--tw-border-opacity:1;border-bottom-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)))}.table :where(thead,tfoot){color:var(--fallback-bc,oklch(var(--bc)/.6));font-size:.75rem;font-weight:700;line-height:1rem;white-space:nowrap}.timeline hr{height:.25rem}:where(.timeline hr){--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)))}:where(.timeline:has(.timeline-middle) hr):first-child{border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}:where(.timeline:has(.timeline-middle) hr):last-child{border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}:where(.timeline:not(:has(.timeline-middle)) :first-child hr:last-child){border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}:where(.timeline:not(:has(.timeline-middle)) :last-child hr:first-child){border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}.timeline-box{border-radius:var(--rounded-box,1rem);border-width:1px;--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity)));--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));padding:.5rem 1rem;--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@keyframes toast-pop{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}[dir=rtl] .toggle{--handleoffsetcalculator:calc(var(--handleoffset)*1)}.toggle:focus-visible{outline-color:var(--fallback-bc,oklch(var(--bc)/.2));outline-offset:2px;outline-style:solid;outline-width:2px}.toggle:hover{background-color:currentColor}.toggle:checked,.toggle[aria-checked=true],.toggle[checked=true]{background-image:none;--handleoffsetcalculator:var(--handleoffset);--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}[dir=rtl] .toggle:checked,[dir=rtl] .toggle[aria-checked=true],[dir=rtl] .toggle[checked=true]{--handleoffsetcalculator:calc(var(--handleoffset)*-1)}.toggle:indeterminate{--tw-text-opacity:1;box-shadow:calc(var(--handleoffset)/2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset)/-2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}[dir=rtl] .toggle:indeterminate{box-shadow:calc(var(--handleoffset)/2) 0 0 2px var(--tglbg) inset,calc(var(--handleoffset)/-2) 0 0 2px var(--tglbg) inset,0 0 0 2px var(--tglbg) inset}.toggle:disabled{cursor:not-allowed;--tw-border-opacity:1;background-color:transparent;border-color:var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));opacity:.3;--togglehandleborder:0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset,var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset}.glass,.glass.btn-active{-webkit-backdrop-filter:blur(var(--glass-blur,40px));backdrop-filter:blur(var(--glass-blur,40px));background-color:transparent;background-image:linear-gradient(135deg,rgb(255 255 255/var(--glass-opacity,30%)) 0,transparent 100%),linear-gradient(var(--glass-reflex-degree,100deg),rgb(255 255 255/var(--glass-reflex-opacity,10%)) 25%,transparent 25%);border:none;box-shadow:0 0 0 1px rgb(255 255 255/var(--glass-border-opacity,10%)) inset,0 0 0 2px rgb(0 0 0/5%);text-shadow:0 1px rgb(0 0 0/var(--glass-text-shadow-opacity,5%))}@media (hover:hover){.glass.btn-active{-webkit-backdrop-filter:blur(var(--glass-blur,40px));backdrop-filter:blur(var(--glass-blur,40px));background-color:transparent;background-image:linear-gradient(135deg,rgb(255 255 255/var(--glass-opacity,30%)) 0,transparent 100%),linear-gradient(var(--glass-reflex-degree,100deg),rgb(255 255 255/var(--glass-reflex-opacity,10%)) 25%,transparent 25%);border:none;box-shadow:0 0 0 1px rgb(255 255 255/var(--glass-border-opacity,10%)) inset,0 0 0 2px rgb(0 0 0/5%);text-shadow:0 1px rgb(0 0 0/var(--glass-text-shadow-opacity,5%))}}.badge-xs{font-size:.75rem;height:.75rem;line-height:.75rem;padding-left:.313rem;padding-right:.313rem}.badge-sm{font-size:.75rem;height:1rem;line-height:1rem;padding-left:.438rem;padding-right:.438rem}.btm-nav-xs>:where(.active){border-top-width:1px}.btm-nav-sm>:where(.active){border-top-width:2px}.btm-nav-md>:where(.active){border-top-width:2px}.btm-nav-lg>:where(.active){border-top-width:4px}.btn-xs{font-size:.75rem;height:1.5rem;min-height:1.5rem;padding-left:.5rem;padding-right:.5rem}.btn-sm{font-size:.875rem;height:2rem;min-height:2rem;padding-left:.75rem;padding-right:.75rem}.btn-square:where(.btn-xs){height:1.5rem;padding:0;width:1.5rem}.btn-square:where(.btn-sm){height:2rem;padding:0;width:2rem}.btn-circle:where(.btn-xs){border-radius:9999px;height:1.5rem;padding:0;width:1.5rem}.btn-circle:where(.btn-sm){border-radius:9999px;height:2rem;padding:0;width:2rem}[type=checkbox].checkbox-sm{height:1.25rem;width:1.25rem}.indicator :where(.indicator-item){bottom:auto;inset-inline-end:0;inset-inline-start:auto;top:0;--tw-translate-y:-50%;--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item)){--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-start){inset-inline-end:auto;inset-inline-start:0;--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-start)){--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-center){inset-inline-end:50%;inset-inline-start:50%;--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-center)){--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-end){inset-inline-end:0;inset-inline-start:auto;--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .indicator :where(.indicator-item.indicator-end)){--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-bottom){bottom:0;top:auto;--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-middle){bottom:50%;top:50%;--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.indicator :where(.indicator-item.indicator-top){bottom:auto;top:0;--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.input-xs{font-size:.75rem;height:1.5rem;line-height:1rem;line-height:1.625;padding-left:.5rem;padding-right:.5rem}.input-sm{font-size:.875rem;height:2rem;line-height:2rem;padding-left:.75rem;padding-right:.75rem}.join.join-vertical{flex-direction:column}.join.join-vertical .join-item:first-child:not(:last-child),.join.join-vertical :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:0;border-start-end-radius:inherit;border-start-start-radius:inherit}.join.join-vertical .join-item:last-child:not(:first-child),.join.join-vertical :last-child:not(:first-child) .join-item{border-end-end-radius:inherit;border-end-start-radius:inherit;border-start-end-radius:0;border-start-start-radius:0}.join.join-horizontal{flex-direction:row}.join.join-horizontal .join-item:first-child:not(:last-child),.join.join-horizontal :first-child:not(:last-child) .join-item{border-end-end-radius:0;border-end-start-radius:inherit;border-start-end-radius:0;border-start-start-radius:inherit}.join.join-horizontal .join-item:last-child:not(:first-child),.join.join-horizontal :last-child:not(:first-child) .join-item{border-end-end-radius:inherit;border-end-start-radius:0;border-start-end-radius:inherit;border-start-start-radius:0}.menu-horizontal{display:inline-flex;flex-direction:row}.menu-horizontal>li:not(.menu-title)>details>ul{position:absolute}.select-sm{font-size:.875rem;height:2rem;line-height:2rem;min-height:2rem;padding-left:.75rem;padding-right:2rem}[dir=rtl] .select-sm{padding-left:2rem;padding-right:.75rem}.stats-vertical{grid-auto-flow:row}.steps-horizontal .step{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));place-items:center;text-align:center}.steps-vertical .step{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(1,minmax(0,1fr))}.tabs-md :where(.tab){font-size:.875rem;height:2rem;line-height:1.25rem;line-height:2;--tab-padding:1rem}.tabs-lg :where(.tab){font-size:1.125rem;height:3rem;line-height:1.75rem;line-height:2;--tab-padding:1.25rem}.tabs-sm :where(.tab){font-size:.875rem;height:1.5rem;line-height:.75rem;--tab-padding:0.75rem}.tabs-xs :where(.tab){font-size:.75rem;height:1.25rem;line-height:.75rem;--tab-padding:0.5rem}.timeline-vertical{flex-direction:column}.timeline-compact .timeline-start,.timeline-horizontal.timeline-compact .timeline-start{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.timeline-compact li:has(.timeline-start) .timeline-end,.timeline-horizontal.timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:none;grid-row-start:auto}.timeline-vertical.timeline-compact>li{--timeline-col-start:0}.timeline-vertical.timeline-compact .timeline-start{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.timeline-vertical.timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:auto;grid-row-start:none}:where(.timeline-vertical>li){--timeline-row-start:minmax(0,1fr);--timeline-row-end:minmax(0,1fr);justify-items:center}.timeline-vertical>li>hr{height:100%}:where(.timeline-vertical>li>hr):first-child{grid-column-start:2;grid-row-start:1}:where(.timeline-vertical>li>hr):last-child{grid-column-end:auto;grid-column-start:2;grid-row-end:none;grid-row-start:3}.timeline-vertical .timeline-start{align-self:center;grid-column-end:2;grid-column-start:1;grid-row-end:4;grid-row-start:1;justify-self:end}.timeline-vertical .timeline-end{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.timeline-vertical:where(.timeline-snap-icon)>li{--timeline-col-start:minmax(0,1fr);--timeline-row-start:0.5rem}.timeline-horizontal .timeline-start{align-self:flex-end;grid-column-end:4;grid-column-start:1;grid-row-end:2;grid-row-start:1;justify-self:center}.timeline-horizontal .timeline-end{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center}.timeline-horizontal:where(.timeline-snap-icon)>li,:where(.timeline-snap-icon)>li{--timeline-col-start:0.5rem;--timeline-row-start:minmax(0,1fr)}.tooltip{--tooltip-offset:calc(100% + 1px + var(--tooltip-tail, 0px))}.tooltip:before{content:var(--tw-content);pointer-events:none;position:absolute;z-index:1;--tw-content:attr(data-tip)}.tooltip-top:before,.tooltip:before{bottom:var(--tooltip-offset);left:50%;right:auto;top:auto;transform:translateX(-50%)}.tooltip-bottom:before{bottom:auto;left:50%;right:auto;top:var(--tooltip-offset);transform:translateX(-50%)}.card-compact .card-body{font-size:.875rem;line-height:1.25rem;padding:1rem}.card-compact .card-title{margin-bottom:.25rem}.card-normal .card-body{font-size:1rem;line-height:1.5rem;padding:var(--padding-card,2rem)}.card-normal .card-title{margin-bottom:.75rem}.join.join-vertical>:where(:not(:first-child)){margin-left:0;margin-right:0;margin-top:-1px}.join.join-horizontal>:where(:not(:first-child)){margin-bottom:0;margin-top:0;margin-inline-start:-1px}.menu-horizontal>li:not(.menu-title)>details>ul{margin-inline-start:0;margin-top:1rem;padding-bottom:.5rem;padding-inline-end:.5rem;padding-top:.5rem}.menu-horizontal>li>details>ul:before{content:none}:where(.menu-horizontal>li:not(.menu-title)>details>ul){border-radius:var(--rounded-box,1rem);--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.menu-sm :where(li:not(.menu-title)>:not(ul,details,.menu-title)),.menu-sm :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--rounded-btn,.5rem);font-size:.875rem;line-height:1.25rem;padding:.25rem .75rem}.menu-sm .menu-title{padding:.5rem .75rem}.modal-top :where(.modal-box){max-width:none;width:100%;--tw-translate-y:-2.5rem;--tw-scale-x:1;--tw-scale-y:1;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:0;border-top-right-radius:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-middle :where(.modal-box){max-width:32rem;width:91.666667%;--tw-translate-y:0px;--tw-scale-x:.9;--tw-scale-y:.9;border-bottom-left-radius:var(--rounded-box,1rem);border-bottom-right-radius:var(--rounded-box,1rem);border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-bottom :where(.modal-box){max-width:none;width:100%;--tw-translate-y:2.5rem;--tw-scale-x:1;--tw-scale-y:1;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-box,1rem);border-top-right-radius:var(--rounded-box,1rem);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.stats-vertical>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(1px*(1 - var(--tw-divide-y-reverse))) calc(0px*var(--tw-divide-x-reverse)) calc(1px*var(--tw-divide-y-reverse)) calc(0px*(1 - var(--tw-divide-x-reverse)))}.stats-vertical{overflow-y:auto}.steps-horizontal .step{grid-template-columns:auto;grid-template-rows:40px 1fr;min-width:4rem}.steps-horizontal .step:before{height:.5rem;width:100%;--tw-translate-x:0px;--tw-translate-y:0px;content:"";margin-inline-start:-100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .steps-horizontal .step):before{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.steps-vertical .step{gap:.5rem;grid-template-columns:40px 1fr;grid-template-rows:auto;justify-items:start;min-height:4rem}.steps-vertical .step:before{height:100%;width:.5rem;--tw-translate-x:-50%;--tw-translate-y:-50%;margin-inline-start:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is([dir=rtl] .steps-vertical .step):before{--tw-translate-x:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.timeline-vertical>li>hr{width:.25rem}:where(.timeline-vertical:has(.timeline-middle)>li>hr):first-child{border-bottom-left-radius:var(--rounded-badge,1.9rem);border-bottom-right-radius:var(--rounded-badge,1.9rem);border-top-left-radius:0;border-top-right-radius:0}:where(.timeline-vertical:has(.timeline-middle)>li>hr):last-child{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-badge,1.9rem);border-top-right-radius:var(--rounded-badge,1.9rem)}:where(.timeline-vertical:not(:has(.timeline-middle)) :first-child>hr:last-child){border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--rounded-badge,1.9rem);border-top-right-radius:var(--rounded-badge,1.9rem)}:where(.timeline-vertical:not(:has(.timeline-middle)) :last-child>hr:first-child){border-bottom-left-radius:var(--rounded-badge,1.9rem);border-bottom-right-radius:var(--rounded-badge,1.9rem);border-top-left-radius:0;border-top-right-radius:0}:where(.timeline-horizontal:has(.timeline-middle)>li>hr):first-child{border-end-end-radius:var(--rounded-badge,1.9rem);border-end-start-radius:0;border-start-end-radius:var(--rounded-badge,1.9rem);border-start-start-radius:0}:where(.timeline-horizontal:has(.timeline-middle)>li>hr):last-child{border-end-end-radius:0;border-end-start-radius:var(--rounded-badge,1.9rem);border-start-end-radius:0;border-start-start-radius:var(--rounded-badge,1.9rem)}.tooltip{display:inline-block;position:relative;text-align:center;--tooltip-tail:0.1875rem;--tooltip-color:var(--fallback-n,oklch(var(--n)/1));--tooltip-text-color:var(--fallback-nc,oklch(var(--nc)/1));--tooltip-tail-offset:calc(100% + 0.0625rem - var(--tooltip-tail))}.tooltip:after,.tooltip:before{opacity:0;transition-delay:.1s;transition-duration:.2s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.tooltip:after{border-style:solid;border-width:var(--tooltip-tail,0);content:"";display:block;height:0;position:absolute;width:0}.tooltip:before{background-color:var(--tooltip-color);border-radius:.25rem;color:var(--tooltip-text-color);font-size:.875rem;line-height:1.25rem;max-width:20rem;padding:.25rem .5rem;width:-moz-max-content;width:max-content}.tooltip.tooltip-open:after,.tooltip.tooltip-open:before,.tooltip:hover:after,.tooltip:hover:before{opacity:1;transition-delay:75ms}.tooltip:has(:focus-visible):after,.tooltip:has(:focus-visible):before{opacity:1;transition-delay:75ms}.tooltip:not([data-tip]):hover:after,.tooltip:not([data-tip]):hover:before{opacity:0;visibility:hidden}.tooltip-top:after,.tooltip:after{border-color:var(--tooltip-color) transparent transparent transparent;bottom:var(--tooltip-tail-offset);left:50%;right:auto;top:auto;transform:translateX(-50%)}.tooltip-bottom:after{border-color:transparent transparent var(--tooltip-color) transparent;bottom:auto;left:50%;right:auto;top:var(--tooltip-tail-offset);transform:translateX(-50%)}.fade-out{opacity:0;transition:opacity .15s ease-in-out}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.left-2{left:.5rem}.right-0{right:0}.right-5{right:1.25rem}.top-0{top:0}.top-2{top:.5rem}.top-5{top:1.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.z-\[1\]{z-index:1}.z-\[5000\]{z-index:5000}.z-\[6000\]{z-index:6000}.m-0{margin:0}.m-2{margin:.5rem}.m-5{margin:1.25rem}.m-auto{margin:auto}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-bottom:2.5rem;margin-top:2.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-4{margin-bottom:1rem;margin-top:1rem}.my-5{margin-bottom:1.25rem;margin-top:1.25rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[250px\]{height:250px}.h-\[25rem\]{height:25rem}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.max-h-96{max-height:24rem}.min-h-80{min-height:20rem}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-10\/12{width:83.333333%}.w-4{width:1rem}.w-4\/12{width:33.333333%}.w-5{width:1.25rem}.w-52{width:13rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-80{width:20rem}.w-96{width:24rem}.w-auto{width:auto}.w-full{width:100%}.min-w-52{min-width:13rem}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.translate-x-full{--tw-translate-x:100%}.transform,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-base-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-divide-opacity,1)))}.justify-self-end{justify-self:end}.justify-self-center{justify-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-box{border-radius:var(--rounded-box,1rem)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-dashed{border-style:dashed}.border-base-300{--tw-border-opacity:1;border-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity,1)))}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.border-error{--tw-border-opacity:1;border-color:var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity,1)))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity,1))}.border-sky-500{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity,1))}.border-opacity-30{--tw-border-opacity:0.3}.bg-base-100{--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity,1)))}.bg-base-200{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity,1)))}.bg-base-300{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity,1)))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-blue-900{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-neutral{--tw-bg-opacity:1;background-color:var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity,1)))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-secondary-content{--tw-bg-opacity:1;background-color:var(--fallback-sc,oklch(var(--sc)/var(--tw-bg-opacity,1)))}.stroke-current{stroke:currentColor}.stroke-info{stroke:var(--fallback-in,oklch(var(--in)/1))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-20{padding-bottom:5rem;padding-top:5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-10{padding-right:2.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.normal-case{text-transform:none}.italic{font-style:italic}.text-accent{--tw-text-opacity:1;color:var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity,1)))}.text-accent-content{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity,1)))}.text-base-content{--tw-text-opacity:1;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity,1)))}.text-base-content\/60{color:var(--fallback-bc,oklch(var(--bc)/.6))}.text-base-content\/70{color:var(--fallback-bc,oklch(var(--bc)/.7))}.text-base-content\/80{color:var(--fallback-bc,oklch(var(--bc)/.8))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-neutral-content{--tw-text-opacity:1;color:var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity,1)))}.text-primary{--tw-text-opacity:1;color:var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity,1)))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-secondary{--tw-text-opacity:1;color:var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity,1)))}.text-success{--tw-text-opacity:1;color:var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity,1)))}.text-warning{--tw-text-opacity:1;color:var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity,1)))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.decoration-dotted{text-decoration-style:dotted}.placeholder-base-content\/70::-moz-placeholder{color:var(--fallback-bc,oklch(var(--bc)/.7))}.placeholder-base-content\/70::placeholder{color:var(--fallback-bc,oklch(var(--bc)/.7))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.grayscale{--tw-grayscale:grayscale(100%)}.filter,.grayscale{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-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-shadow{transition-duration:.15s;transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@tailwind daisyui;.leaflet-right-panel{background:#fff;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.3);margin-right:10px;margin-top:80px;transform:none;transition:right .3s ease-in-out;z-index:400}.leaflet-right-panel.controls-shifted{right:310px}.leaflet-control-button{background-color:#fff!important;color:#374151!important}.leaflet-control-button:hover{background-color:#f3f4f6!important}.leaflet-drawer{background:hsla(0,0%,100%,.5);box-shadow:-2px 0 5px rgba(0,0,0,.1);height:100%;position:absolute;right:0;top:0;transform:translateX(100%);transition:transform .3s ease-in-out;width:338px;z-index:450}.leaflet-drawer.open{transform:translateX(0)}.leaflet-control-button,.leaflet-control-layers,.toggle-panel-button{transition:right .3s ease-in-out;z-index:500}.controls-shifted{right:338px!important}.leaflet-control-custom{align-items:center;background-color:#fff;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.3);cursor:pointer;display:flex;height:30px;justify-content:center;width:30px}.leaflet-control-custom:hover{background-color:#f3f4f6}#selection-tool-button.active{background-color:#60a5fa;color:#fff}#cancel-selection-button{margin-bottom:1rem;width:100%}@media (hover:hover){.hover\:btn-ghost:hover:hover{border-color:transparent}@supports (color:oklch(0 0 0)){.hover\:btn-ghost:hover:hover{background-color:var(--fallback-bc,oklch(var(--bc)/.2))}}.hover\:btn-info:hover.btn-outline:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}@supports (color:color-mix(in oklab,black,black)){.hover\:btn-info:hover.btn-outline:hover{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}}@supports not (color:oklch(0 0 0)){.hover\:btn-info:hover{--btn-color:var(--fallback-in)}}@supports (color:color-mix(in oklab,black,black)){.hover\:btn-info:hover.btn-outline.btn-active{background-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000);border-color:color-mix(in oklab,var(--fallback-in,oklch(var(--in)/1)) 90%,#000)}}@supports (color:oklch(0 0 0)){.hover\:btn-info:hover{--btn-color:var(--in)}}.hover\:btn-info:hover{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));outline-color:var(--fallback-in,oklch(var(--in)/1))}.hover\:btn-ghost:hover{background-color:transparent;border-color:transparent;border-width:1px;color:currentColor;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline-color:currentColor}.hover\:btn-ghost:hover.btn-active{background-color:var(--fallback-bc,oklch(var(--bc)/.2));border-color:transparent}.hover\:btn-info:hover.btn-outline{--tw-text-opacity:1;color:var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)))}.hover\:btn-info:hover.btn-outline.btn-active{--tw-text-opacity:1;color:var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)))}.hover\:input-primary:hover{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)))}.hover\:input-primary:hover:focus,.hover\:input-primary:hover:focus-within{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity)));outline-color:var(--fallback-p,oklch(var(--p)/1))}.focus\:input-ghost:focus{--tw-bg-opacity:0.05}.focus\:input-ghost:focus:focus,.focus\:input-ghost:focus:focus-within{--tw-bg-opacity:1;--tw-text-opacity:1;box-shadow:none;color:var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)))}@media not all and (min-width:768px){.max-md\:timeline-compact,.max-md\:timeline-compact +.timeline-horizontal{--timeline-row-start:0}.max-md\:timeline-compact .timeline-horizontal .timeline-start,.max-md\:timeline-compact .timeline-start{align-self:flex-start;grid-column-end:4;grid-column-start:1;grid-row-end:4;grid-row-start:3;justify-self:center;margin:.25rem}.max-md\:timeline-compact .timeline-horizontal li:has(.timeline-start) .timeline-end,.max-md\:timeline-compact li:has(.timeline-start) .timeline-end{grid-column-start:none;grid-row-start:auto}.max-md\:timeline-compact.timeline-vertical>li{--timeline-col-start:0}.max-md\:timeline-compact.timeline-vertical .timeline-start{align-self:center;grid-column-end:4;grid-column-start:3;grid-row-end:4;grid-row-start:1;justify-self:start}.max-md\:timeline-compact.timeline-vertical li:has(.timeline-start) .timeline-end{grid-column-start:auto;grid-row-start:none}}@media (min-width:1024px){.lg\:stats-horizontal{grid-auto-flow:column}.lg\:stats-horizontal>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;--tw-divide-y-reverse:0;border-width:calc(0px*(1 - var(--tw-divide-y-reverse))) calc(1px*var(--tw-divide-x-reverse)) calc(0px*var(--tw-divide-y-reverse)) calc(1px*(1 - var(--tw-divide-x-reverse)))}.lg\:stats-horizontal{overflow-x:auto}:is([dir=rtl] .lg\:stats-horizontal){--tw-divide-x-reverse:1}}.last\:border-0:last-child{border-width:0}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-accent:hover{--tw-bg-opacity:1;background-color:var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity,1)))}.hover\:bg-base-200:hover{--tw-bg-opacity:1;background-color:var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity,1)))}.hover\:bg-base-300:hover{--tw-bg-opacity:1;background-color:var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity,1)))}.hover\:text-accent-content:hover{--tw-text-opacity:1;color:var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity,1)))}.hover\:underline:hover{text-decoration-line:underline}.hover\:no-underline:hover{text-decoration-line:none}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-blue-500\/50:hover{--tw-shadow-color:rgba(59,130,246,.5);--tw-shadow:var(--tw-shadow-colored)}.focus\:border-primary:focus{--tw-border-opacity:1;border-color:var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity,1)))}.focus\:bg-base-100:focus{--tw-bg-opacity:1;background-color:var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity,1)))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:inline{display:inline}.sm\:w-1\/12{width:8.333333%}.sm\:w-2\/12{width:16.666667%}.sm\:w-6\/12{width:50%}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}}@media (min-width:768px){.md\:h-64{height:16rem}.md\:min-h-64{min-height:16rem}.md\:w-1\/12{width:8.333333%}.md\:w-2\/12{width:16.666667%}.md\:w-2\/3{width:66.666667%}.md\:w-3\/12{width:25%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-end{align-items:flex-end}.md\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.md\:text-end{text-align:end}}@media (min-width:1024px){.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-1\/12{width:8.333333%}.lg\:w-1\/2{width:50%}.lg\:w-2\/12{width:16.666667%}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:flex-row-reverse{flex-direction:row-reverse}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.lg\:text-left{text-align:left}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}} \ No newline at end of file diff --git a/app/models/concerns/distanceable.rb b/app/models/concerns/distanceable.rb index dc668169..add1b7df 100644 --- a/app/models/concerns/distanceable.rb +++ b/app/models/concerns/distanceable.rb @@ -60,18 +60,25 @@ module Distanceable point_pairs = points.each_cons(2).to_a return [] if point_pairs.empty? - values_clause = point_pairs.map.with_index do |(p1, p2), index| - "(#{index}, ST_GeomFromEWKT('#{p1.lonlat}')::geography, ST_GeomFromEWKT('#{p2.lonlat}')::geography)" + # Create parameterized placeholders for VALUES clause + values_placeholders = point_pairs.map.with_index do |_, index| + base_idx = index * 3 + "($#{base_idx + 1}, ST_GeomFromEWKT($#{base_idx + 2})::geography, ST_GeomFromEWKT($#{base_idx + 3})::geography)" end.join(', ') - # Single query to calculate all distances - results = connection.execute(<<-SQL.squish) + # Flatten parameters: [pair_id, lonlat1, lonlat2, pair_id, lonlat1, lonlat2, ...] + params = point_pairs.flat_map.with_index do |(p1, p2), index| + [index, p1.lonlat, p2.lonlat] + end + + # Single query to calculate all distances using parameterized query + results = connection.exec_params(<<-SQL.squish, params) WITH point_pairs AS ( SELECT pair_id, point1, point2 - FROM (VALUES #{values_clause}) AS t(pair_id, point1, point2) + FROM (VALUES #{values_placeholders}) AS t(pair_id, point1, point2) ) SELECT pair_id, From 9fb251fa4a4c5aad28a273a7bb0197ae898b18a0 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 23 Jul 2025 20:52:02 +0200 Subject: [PATCH 12/12] Sanitize input in distanceable --- app/models/concerns/distanceable.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/models/concerns/distanceable.rb b/app/models/concerns/distanceable.rb index add1b7df..532cfa7f 100644 --- a/app/models/concerns/distanceable.rb +++ b/app/models/concerns/distanceable.rb @@ -60,10 +60,9 @@ module Distanceable point_pairs = points.each_cons(2).to_a return [] if point_pairs.empty? - # Create parameterized placeholders for VALUES clause - values_placeholders = point_pairs.map.with_index do |_, index| - base_idx = index * 3 - "($#{base_idx + 1}, ST_GeomFromEWKT($#{base_idx + 2})::geography, ST_GeomFromEWKT($#{base_idx + 3})::geography)" + # Create parameterized placeholders for VALUES clause using ? placeholders + values_placeholders = point_pairs.map do |_| + "(?, ST_GeomFromEWKT(?)::geography, ST_GeomFromEWKT(?)::geography)" end.join(', ') # Flatten parameters: [pair_id, lonlat1, lonlat2, pair_id, lonlat1, lonlat2, ...] @@ -72,7 +71,7 @@ module Distanceable end # Single query to calculate all distances using parameterized query - results = connection.exec_params(<<-SQL.squish, params) + sql_with_params = ActiveRecord::Base.sanitize_sql_array([<<-SQL.squish] + params) WITH point_pairs AS ( SELECT pair_id, @@ -86,9 +85,11 @@ module Distanceable FROM point_pairs ORDER BY pair_id SQL + + results = connection.select_all(sql_with_params) # Return array of distances in meters - results.map { |row| row['distance_meters'].to_i } + results.map { |row| row['distance_meters'].to_f } end end