diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index 5747a11bf..ab0f5aa5d 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -86,7 +86,7 @@ jobs: # Compute metadata - name: Extract metadata id: meta - uses: docker/metadata-action@dbef88086f6cef02e264edb7dbf63250c17cef6c # v5.5.0 + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: bunkerity/${{ inputs.IMAGE }} # Build cached image diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 416cfc102..da038282e 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -137,7 +137,7 @@ jobs: - name: Extract metadata if: inputs.TEST == true id: meta - uses: docker/metadata-action@dbef88086f6cef02e264edb7dbf63250c17cef6c # v5.5.0 + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: ghcr.io/bunkerity/${{ inputs.LINUX }}-tests:${{ inputs.RELEASE }} - name: Build test image diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index ee576a2b7..6ef4ed8f7 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -65,7 +65,7 @@ jobs: # Compute metadata - name: Extract metadata id: meta - uses: docker/metadata-action@dbef88086f6cef02e264edb7dbf63250c17cef6c # v5.5.0 + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: bunkerity/${{ inputs.IMAGE }} # Build and push diff --git a/.github/workflows/push-packagecloud.yml b/.github/workflows/push-packagecloud.yml index d5959f0df..10592a44f 100644 --- a/.github/workflows/push-packagecloud.yml +++ b/.github/workflows/push-packagecloud.yml @@ -42,7 +42,7 @@ jobs: - name: Check out repository code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install ruby - uses: ruby/setup-ruby@bd03e04863f52d169e18a2b190e8fa6b84938215 # v1.170.0 + uses: ruby/setup-ruby@22fdc77bf4148f810455b226c90fb81b5cbc00a7 # v1.171.0 with: ruby-version: "3.0" - name: Install packagecloud diff --git a/.github/workflows/test-core-linux.yml b/.github/workflows/test-core-linux.yml index 2a619c7f5..8f42a28a6 100644 --- a/.github/workflows/test-core-linux.yml +++ b/.github/workflows/test-core-linux.yml @@ -36,7 +36,7 @@ jobs: ' | sudo tee /etc/apt/preferences.d/mozilla-firefox sudo apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox - name: Download geckodriver - uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 + uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 with: max_attempts: 3 timeout_minutes: 20 diff --git a/.github/workflows/tests-ui-linux.yml b/.github/workflows/tests-ui-linux.yml index 7ee82670c..8b6f42903 100644 --- a/.github/workflows/tests-ui-linux.yml +++ b/.github/workflows/tests-ui-linux.yml @@ -33,7 +33,7 @@ jobs: ' | sudo tee /etc/apt/preferences.d/mozilla-firefox sudo apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox - name: Download geckodriver - uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 + uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 with: max_attempts: 3 timeout_minutes: 20 diff --git a/CHANGELOG.md b/CHANGELOG.md index f3729cea1..1f7d6c44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,27 @@ ## v1.5.6 - YYYY/MM/DD +- [BUGFIX] Fix issues with the antibot feature ([#866](https://github.com/bunkerity/bunkerweb/issues/866), [#870](https://github.com/bunkerity/bunkerweb/issues/870)) +- [UI] Add bans management page in the web UI +- [UI] Add blocked requests page in the web UI +- [UI] Add the possibility to clone a service in the web UI +- [UI] Add the possibility to set a service as draft in the web UI +- [FEATURE] Add setting REDIS_SSL_VERIFY to activate/disable the SSL certificate verification when using Redis +- [FEATURE] Add Redis Sentinel fallback to master automatically if no slaves are available +- [FEATURE] Add Redis Sentinel support for bwcli +- [FEATURE] Add new Metrics core plugin that will allow metrics collection and retrieval of internal metrics +- [FEATURE] Add setting DATABASE_LOG_LEVEL to control SQLAlchemy loggers separately from the main one +- [MISC] Add a better custom certificate cache handling - [MISC] Updated Linux base images in Dockerfiles +- [MISC] Add recommended dialects to databases string +- [DOCUMENTATION] Update web UI's setup wizard instructions in the documentation - [DEPS] Updated stream-lua-nginx-module to v0.0.14 - [DEPS] Updated lua-nginx-module version to v0.10.26 - [DEPS] Updated libmaxminddb version to v1.9.1 - [DEPS] Updated lua-resty-core to v0.1.28 - [DEPS] Updated zlib version to v1.3.1 +- [DEPS] Updated ModSecurity version to v3.0.12 +- [DEPS] Updated lua-resty-mlcache version to v2.6.1 ## v1.5.5 - 2024/01/12 diff --git a/docs/plugins.md b/docs/plugins.md index 3a3dee47f..c99184e4d 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -260,10 +260,39 @@ The first step is to install the plugin by putting the plugin files inside the c ## Writing a plugin +### Structure + !!! tip "Existing plugins" If the documentation is not enough, you can have a look at the existing source code of [official plugins](https://github.com/bunkerity/bunkerweb-plugins) and the [core plugins](https://github.com/bunkerity/bunkerweb/tree/v1.5.6/src/common/core) (already included in BunkerWeb but they are plugins, technically speaking). +What a plugin structure looks like : +``` +plugin / + confs / conf_type.conf + ui / actions.py + template.html + plugin.lua + plugin.json +``` + +- **conf_type.conf** : add a [custom NGINX configurations.](/quickstart-guide/#custom-configurations) + +- **actions.py** : script to execute on flask server. +This script is running on flask context, you have access to lib and utils like `jinja2`, `requests`, etc... + +- **template.html** : custom plugin page you can access from ui. + +- **plugin.lua** : code to execute on NGINX using [NGING LUA modile.](https://github.com/openresty/lua-nginx-module) + +- **plugin.json** : metadata, settings and jobs for your settings. + +!!! info "Optional files" + + Files like `confs` and `ui` ones are optional. Add them only to fit your needs. + +### Getting started + The first step is to create a folder that will contain the plugin : ```shell @@ -515,9 +544,9 @@ BunkerWeb uses an internal job scheduler for periodic tasks like renewing certif ### Plugin page -Plugin pages are used to display information about your plugin and interact with the user inside the plugins section of the [web UI](web-ui.md). +Everything related to the web UI is located inside the subfolder **ui** as we seen in the [previous structure section.](#structure) -Everything related to the web UI is located inside a subfolder named **ui** at the root directory of your plugin. A template file named **template.html** and located inside the **ui** subfolder contains the client code and logic to display your page. Another file named **actions.py** and also located inside the **ui** subfolder contains code that will be executed when the user is interacting with your page (filling a form for example). +#### Basic example !!! info "Jinja 2 template" The **template.html** file is a Jinja2 template, please refer to the [Jinja2 documentation](https://jinja.palletsprojects.com) if needed. @@ -555,3 +584,40 @@ def myplugin() : !!! info "Python libraries" You can use Python libraries that are already available like : `Flask`, `Flask-Login`, `Flask-WTF`, `beautifulsoup4`, `docker`, `Jinja2`, `python-magic` and `requests`. To see the full list, you can have a look at the Web UI [requirements.txt](https://github.com/bunkerity/bunkerweb/blobsrc/ui/requirements.txt). If you need external libraries, you can install them inside the **ui** folder of your plugin and then use the classical **import** directive. + +#### Utils + +To easily update the content of a template inside the UI with JSON, a **SetupPlugin class** is available in `src/ui/static/js/plugins/setup.js` and will be executed when the plugin template is load. + +For example, in case **actions.py** return this JSON : +```python +def plugin(): + return {"message": "ok", "data": {"name": "test"}} +``` + +I need to add this on my **template.html** : +```html + +``` + +!!! info "Check core plugins" + + Core plugins are using this utils. Feel free to look at them in order to see in details how each `key` is working. + + +| key | Type | Description | +| :--------: | :----: | :------------------------------------------------------------------------------------------- | +| `name ` | string | Replace `name` by the JSON key to extract the related value. | +| `el` | element| Select element you want the value to be updated. | +| `value` | any | Default value on template load or in case retrieving JSON failed. | +| `type` | string | Define the script behavior with the incoming value. Available : `text`, `list` and `status`. | +| `textEl` | element| Optional additionnal text content when type is `status`. | +| `listNames`| string | List of data keys when type is `list`. | diff --git a/docs/requirements.in b/docs/requirements.in index b8b8f6f54..e597685a9 100644 --- a/docs/requirements.in +++ b/docs/requirements.in @@ -1,5 +1,5 @@ mike==2.0.0 mkdocs==1.5.3 -mkdocs-material[imaging]==9.5.5 +mkdocs-material[imaging]==9.5.6 mkdocs-print-site-plugin==2.3.6 pytablewriter==1.2.0 diff --git a/docs/requirements.txt b/docs/requirements.txt index e90dbfb4d..d49a2ba8a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -311,9 +311,9 @@ mkdocs==1.5.3 \ # -r requirements.in # mike # mkdocs-material -mkdocs-material==9.5.5 \ - --hash=sha256:4480d9580faf42fed0123d0465502bfc1c0c239ecc9c4d66159cf0459ea1b4ae \ - --hash=sha256:ac50b2431a79a3b160fdefbba37c9132485f1a69166aba115ad49fafdbbbc5df +mkdocs-material==9.5.6 \ + --hash=sha256:5b24df36d8ac6cecd611241ce6f6423ccde3e1ad89f8360c3f76d5565fc2d82a \ + --hash=sha256:e115b90fccf5cd7f5d15b0c2f8e6246b21041628b8f590630e7fca66ed7fcf6c # via # -r requirements.in # mkdocs-material @@ -415,9 +415,9 @@ pillow==10.2.0 \ # via # cairosvg # mkdocs-material -platformdirs==4.1.0 \ - --hash=sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380 \ - --hash=sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420 +platformdirs==4.2.0 \ + --hash=sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068 \ + --hash=sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768 # via mkdocs pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ @@ -445,9 +445,9 @@ python-dateutil==2.8.2 \ # via # ghp-import # typepy -pytz==2023.3.post1 \ - --hash=sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b \ - --hash=sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7 +pytz==2023.4 \ + --hash=sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40 \ + --hash=sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a # via typepy pyyaml==6.0.1 \ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ @@ -643,9 +643,9 @@ typepy==1.3.2 \ # pytablewriter # tabledata # typepy -urllib3==2.1.0 \ - --hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \ - --hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54 +urllib3==2.2.0 \ + --hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \ + --hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224 # via requests verspec==0.1.0 \ --hash=sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31 \ diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index ed966699a..9f1ee4e16 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -119,6 +119,10 @@ Here is how you can access the logs, depending on your integration : Don't forget that BunkerWeb runs as an unprivileged user for obvious security reasons. Double-check the permissions of files and folders used by BunkerWeb, especially if you use custom configurations (more info [here](quickstart-guide.md#custom-configurations)). You will need to set at least **RW** rights on files and **_RWX_** on folders. +## Disable security checks + +For debugging purposes, you may need to temporarily disable the security checks made by BunkerWeb. One quick way of doing it is by adding everyone in the whitelist (e.g. `WHITELIST_IP=0.0.0.0/0`). + ## ModSecurity The default BunkerWeb configuration of ModSecurity is to load the Core Rule Set in anomaly scoring mode with a paranoia level (PL) of 1 : diff --git a/misc/update-version.sh b/misc/update-version.sh index 720684c76..3da699e97 100755 --- a/misc/update-version.sh +++ b/misc/update-version.sh @@ -37,6 +37,7 @@ shopt -u globstar # linux sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/linux/scripts/*.sh # db +sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/common/db/Database.py sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/common/db/model.py # github sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" .github/ISSUE_TEMPLATE/bug_report.yml diff --git a/src/autoconf/IngressController.py b/src/autoconf/IngressController.py index c49f5039c..137107d26 100644 --- a/src/autoconf/IngressController.py +++ b/src/autoconf/IngressController.py @@ -232,6 +232,7 @@ class IngressController(Controller): obj = event["object"] metadata = obj.metadata if obj else None annotations = metadata.annotations if metadata else None + data = obj.data if obj else None if not obj: return False if obj.kind == "Pod": @@ -242,6 +243,8 @@ class IngressController(Controller): return annotations and "bunkerweb.io/CONFIG_TYPE" in annotations if obj.kind == "Service": return True + if obj.kind == "Secret": + return data and "tls.crt" in data and "tls.key" in data return False def __watch(self, watch_type): @@ -255,6 +258,8 @@ class IngressController(Controller): what = self.__corev1.list_config_map_for_all_namespaces elif watch_type == "service": what = self.__corev1.list_service_for_all_namespaces + elif watch_type == "secret": + what = self.__corev1.list_secret_for_all_namespaces else: raise Exception(f"Unsupported watch_type {watch_type}") @@ -328,7 +333,7 @@ class IngressController(Controller): def process_events(self): self._set_autoconf_load_db() - watch_types = ("pod", "ingress", "configmap", "service") + watch_types = ("pod", "ingress", "configmap", "service", "secret") threads = [Thread(target=self.__watch, args=(watch_type,)) for watch_type in watch_types] for thread in threads: thread.start() diff --git a/src/bw/lua/bunkerweb/helpers.lua b/src/bw/lua/bunkerweb/helpers.lua index 3c41fa82f..09b1d6dc3 100644 --- a/src/bw/lua/bunkerweb/helpers.lua +++ b/src/bw/lua/bunkerweb/helpers.lua @@ -185,7 +185,7 @@ helpers.fill_ctx = function(no_ref) end data.remote_addr = var.remote_addr data.server_name = var.server_name - data.local_time = var.local_time + data.time_local = var.time_local if data.kind == "http" then data.uri = var.uri data.request_uri = var.request_uri @@ -196,6 +196,7 @@ helpers.fill_ctx = function(no_ref) data.http_content_length = var.http_content_length data.http_origin = var.http_origin data.http_version = req.http_version() + data.start_time = req.start_time() data.scheme = var.scheme end -- IP data : global diff --git a/src/bw/lua/bunkerweb/plugin.lua b/src/bw/lua/bunkerweb/plugin.lua index 3db038311..7230a9b62 100644 --- a/src/bw/lua/bunkerweb/plugin.lua +++ b/src/bw/lua/bunkerweb/plugin.lua @@ -93,4 +93,19 @@ function plugin:ret(ret, msg, status, redirect, data) return { ret = ret, msg = msg, status = status, redirect = redirect, data = data } end +function plugin:set_metric(kind, key, value) + if self.ctx and self.ctx.bw then + if not self.ctx.bw.metrics then + self.ctx.bw.metrics = {} + end + if not self.ctx.bw.metrics[self.id] then + self.ctx.bw.metrics[self.id] = {} + end + if not self.ctx.bw.metrics[self.id][kind] then + self.ctx.bw.metrics[self.id][kind] = {} + end + self.ctx.bw.metrics[self.id][kind][key] = value + end +end + return plugin diff --git a/src/bw/lua/bunkerweb/utils.lua b/src/bw/lua/bunkerweb/utils.lua index e4e528666..89e7d94e0 100644 --- a/src/bw/lua/bunkerweb/utils.lua +++ b/src/bw/lua/bunkerweb/utils.lua @@ -786,6 +786,7 @@ utils.get_phases = function() "preread", "log_stream", "log_default", + "timer" } end diff --git a/src/common/confs/init-worker-lua.conf b/src/common/confs/init-worker-lua.conf index 01d3f29cc..8105ba073 100644 --- a/src/common/confs/init-worker-lua.conf +++ b/src/common/confs/init-worker-lua.conf @@ -13,10 +13,15 @@ init_worker_by_lua_block { local ngx = ngx local INFO = ngx.INFO local ERR = ngx.ERR + local WARN = ngx.WARN local NOTICE = ngx.NOTICE + local worker = ngx.worker + local worker_id = worker.id + local timer_at = ngx.timer.at local require_plugin = helpers.require_plugin local new_plugin = helpers.new_plugin local call_plugin = helpers.call_plugin + local tostring = tostring -- Don't go further we are in loading state local is_loading, err = require "bunkerweb.utils".get_variable("IS_LOADING", false) @@ -27,6 +32,71 @@ init_worker_by_lua_block { return end + -- Recurrent timer + local recurrent_timer + recurrent_timer = function(premature) + + local worker_id = tostring(worker.id()) + local logger = require "bunkerweb.logger":new("TIMER-" .. worker_id) + + -- Worker exiting + if premature then + logger:log(WARN, "worker is exiting, disabling timer") + return + end + logger:log(INFO, "timer started") + + -- Get plugins order + local order, err = datastore:get("plugins_order", true) + if not order then + logger:log(ERR, "can't get plugins order from datastore : " .. err) + return + end + + -- Call timer() methods + logger:log(INFO, "calling timer() methods of plugins ...") + for i, plugin_id in ipairs(order.timer) do + -- Require call + local plugin_lua, err = require_plugin(plugin_id) + if plugin_lua == false then + logger:log(ERR, err) + elseif plugin_lua == nil then + logger:log(INFO, err) + else + -- Check if plugin has timer method + if plugin_lua.timer ~= nil then + -- New call + local ok, plugin_obj = new_plugin(plugin_lua) + if not ok then + logger:log(ERR, plugin_obj) + else + local ok, ret = call_plugin(plugin_obj, "timer") + if not ok then + logger:log(ERR, ret) + elseif not ret.ret then + logger:log(ERR, plugin_id .. ":timer() call failed : " .. ret.msg) + else + logger:log(INFO, plugin_id .. ":timer() call successful : " .. ret.msg) + end + end + else + logger:log(INFO, "skipped execution of " .. plugin_id .. " because method timer() is not defined") + end + end + end + logger:log(INFO, "called timer() methods of plugins") + local hdl + hdl, err = timer_at(5, recurrent_timer) + if not hdl then + logger:log(ERR, "can't create timer : " .. err) + end + end + local hdl + hdl, err = timer_at(0, recurrent_timer) + if not hdl then + logger:log(ERR, "can't create timer : " .. err) + end + -- Instantiate lock local lock = require "resty.lock":new("worker_lock", { timeout = 10 }) if not lock then diff --git a/src/common/core/antibot/antibot.lua b/src/common/core/antibot/antibot.lua index d7ef474f2..10ddce3db 100644 --- a/src/common/core/antibot/antibot.lua +++ b/src/common/core/antibot/antibot.lua @@ -152,6 +152,7 @@ function antibot:access() if ok == nil then return self:ret(false, "check challenge error : " .. err, HTTP_INTERNAL_SERVER_ERROR) elseif not ok then + self:set_metric("counters", "failed_challenges", 1) self.logger:log(ngx.WARN, "client failed challenge : " .. err) end if redirect then diff --git a/src/common/core/antibot/ui/actions.py b/src/common/core/antibot/ui/actions.py index c34b8c821..52b4f80f6 100644 --- a/src/common/core/antibot/ui/actions.py +++ b/src/common/core/antibot/ui/actions.py @@ -1 +1,11 @@ -# Spoofing an action file +def antibot(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("antibot") + + if data.get("counter_failed_challenges") is None: + data["counter_failed_challenges"] = 0 + + return data + + except: + return {"counter_failed_challenges": 0} diff --git a/src/common/core/antibot/ui/template.html b/src/common/core/antibot/ui/template.html index 46a45e762..338068f13 100644 --- a/src/common/core/antibot/ui/template.html +++ b/src/common/core/antibot/ui/template.html @@ -1,4 +1,11 @@ {% extends "base.html" %} {% block content %} +
@@ -9,12 +16,9 @@

- {{ antibot_info or "Anti-bot technology is designed to detect and - mitigate suspicious or malicious bots, preventing them from reaching an - organization's websites or IT ecosystem." }} -

+ >

@@ -29,14 +33,12 @@ > Challenges

-
- {{ antibot_count or "unknown" }} -
+
"unknown"

- total number + total failed +

@@ -66,4 +68,20 @@ + + {% endblock %} diff --git a/src/common/core/authbasic/ui/actions.py b/src/common/core/authbasic/ui/actions.py deleted file mode 100644 index c34b8c821..000000000 --- a/src/common/core/authbasic/ui/actions.py +++ /dev/null @@ -1 +0,0 @@ -# Spoofing an action file diff --git a/src/common/core/authbasic/ui/template.html b/src/common/core/authbasic/ui/template.html deleted file mode 100644 index 112e6d4b1..000000000 --- a/src/common/core/authbasic/ui/template.html +++ /dev/null @@ -1,65 +0,0 @@ -{% extends "base.html" %} {% block content %} - -
- -
-
INFO
- -
-

- {{ authbasic_info or "Basic Auth is a method for an HTTP user agent - (e.g. a web browser) to provide a user name and password when making a - request." }} -

-
-
- - -
- -
-

- AUTH BASIC -

-
- {{ authbasic_count or "unknown" }} -
- -

- passed credentials -

-
- - - - -
-
- -{% endblock %} diff --git a/src/common/core/badbehavior/badbehavior.lua b/src/common/core/badbehavior/badbehavior.lua index 8c0c7e971..dacd77051 100644 --- a/src/common/core/badbehavior/badbehavior.lua +++ b/src/common/core/badbehavior/badbehavior.lua @@ -49,6 +49,7 @@ function badbehavior:log() if not ok then return self:ret(false, "can't create increase timer : " .. err) end + self:set_metric("counters", tostring(ngx.status), 1) return self:ret(true, "success") end diff --git a/src/common/core/badbehavior/ui/actions.py b/src/common/core/badbehavior/ui/actions.py index c34b8c821..648c1b38f 100644 --- a/src/common/core/badbehavior/ui/actions.py +++ b/src/common/core/badbehavior/ui/actions.py @@ -1 +1,13 @@ -# Spoofing an action file +def badbehavior(**kwargs): + try: + # Here we will have a list { 'counter_403': X, 'counter_401': Y ... } + data = kwargs["app"].config["INSTANCES"].get_metrics("badbehavior") + format_data = [] + # Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...] + for key, value in data.items(): + format_data[key] = {"code": int(key.split("_")[1]), "count": value} + + return {"items": format_data} + + except: + return {"items": []} diff --git a/src/common/core/badbehavior/ui/template.html b/src/common/core/badbehavior/ui/template.html index 865a12119..3ce5e9fd2 100644 --- a/src/common/core/badbehavior/ui/template.html +++ b/src/common/core/badbehavior/ui/template.html @@ -1,72 +1,31 @@ -{% extends "base.html" %} {% block content %} {% set items = [{"code" : 400, -"count" : 24}, {"code" : 403, "count" : 845}, {"code" : 402, "count" : 12}]%} +{% extends "base.html" %} {% block content %} +
-
-
-
INFO
+
+
INFO
-
-

- {{ bad_behavior_info or "Ban IP generating too much 'bad' HTTP status - code in a period of time." }} -

-
+
+

- -
-

- BAD BEHAVIOR -

-
- {{ bad_behavior_count or "unknown" }} -
- -

- total ip bans -

-
- - - - -
- - {% if items|length != 0 %} -
@@ -91,29 +50,41 @@
    - {% for item in items %}
  • - {{item['code']}} -

    + >

    - {{item['count']}} -

    + >

  • - {% endfor %}
- {% endif %} -
+ +
{% endblock %} diff --git a/src/common/core/blacklist/blacklist.lua b/src/common/core/blacklist/blacklist.lua index 63136ed5c..e4cffb8cf 100644 --- a/src/common/core/blacklist/blacklist.lua +++ b/src/common/core/blacklist/blacklist.lua @@ -132,12 +132,14 @@ function blacklist:access() if not ok then self.logger:log(ERR, "error while checking cache : " .. cached) elseif cached and cached ~= "ok" then + local data = self:get_data(cached) + self:set_metric("counters", "failed_" .. data.id, 1) return self:ret( true, k .. " is in cached blacklist (info : " .. cached .. ")", get_deny_status(), nil, - self:get_data(cached) + data ) end if ok and cached then @@ -161,12 +163,14 @@ function blacklist:access() self.logger:log(ERR, "error while adding element to cache : " .. err) end if blacklisted ~= "ok" then + local data = self:get_data(blacklisted) + self:set_metric("counters", "failed_" .. data.id, 1) return self:ret( true, k .. " is blacklisted (info : " .. blacklisted .. ")", get_deny_status(), nil, - self:get_data(blacklisted) + data ) end end diff --git a/src/common/core/blacklist/ui/actions.py b/src/common/core/blacklist/ui/actions.py index c34b8c821..86a9feb87 100644 --- a/src/common/core/blacklist/ui/actions.py +++ b/src/common/core/blacklist/ui/actions.py @@ -1 +1,23 @@ -# Spoofing an action file +def blacklist(**kwargs): + keys = [ + "counter_blacklist_url", + "counter_blacklist_ip", + "counter_blacklist_rdns", + "counter_blacklist_asn", + "counter_blacklist_usa", + ] + + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("blacklist") + + for key in keys: + if data.get(key) is None: + data[key] = 0 + + return data + + except: + data = {} + for key in keys: + data[key] = 0 + return data diff --git a/src/common/core/blacklist/ui/template.html b/src/common/core/blacklist/ui/template.html index 487f4f243..e2c75a2a7 100644 --- a/src/common/core/blacklist/ui/template.html +++ b/src/common/core/blacklist/ui/template.html @@ -1,4 +1,11 @@ {% extends "base.html" %} {% block content %} +
@@ -10,11 +17,9 @@

- {{ blacklist_info or "Deny access based on internal and external - IP/network/rDNS/ASN blacklists." }} -

+ >

@@ -30,9 +35,7 @@ > URL

-
- {{ blacklist_url_count or "unknown" }} -
+

@@ -72,9 +75,7 @@ > IP

-
- {{ blacklist_ip_count or "unknown" }} -
+

@@ -112,9 +113,7 @@ > RDNS

-
- {{ blacklist_rdns_count or "unknown" }} -
+

@@ -160,9 +159,7 @@ > ASN

-
- {{ blacklist_asn_count or "unknown" }} -
+

@@ -200,9 +197,7 @@ > User Agent

-
- {{ blacklist_user_agent_count or "unknown" }} -
+

@@ -231,6 +226,41 @@

- + + {% endblock %} diff --git a/src/common/core/bunkernet/bunkernet.lua b/src/common/core/bunkernet/bunkernet.lua index accf7de56..495200f8d 100644 --- a/src/common/core/bunkernet/bunkernet.lua +++ b/src/common/core/bunkernet/bunkernet.lua @@ -10,6 +10,8 @@ local ngx = ngx local ERR = ngx.ERR local NOTICE = ngx.NOTICE local WARN = ngx.WARN +local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR +local HTTP_OK = ngx.HTTP_OK local timer_at = ngx.timer.at local get_phase = ngx.get_phase local get_version = utils.get_version @@ -28,6 +30,7 @@ local open = io.open local encode = cjson.encode local decode = cjson.decode local http_new = http.new +local match = string.match function bunkernet:initialize(ctx) -- Call parent initialize @@ -308,4 +311,28 @@ function bunkernet:report(ip, reason, reason_data, method, url, headers) return self:request("POST", "/report", data) end +function bunkernet:api() + -- Match request + if not match(self.ctx.bw.uri, "^/bunkernet/ping$") or self.ctx.bw.request_method ~= "POST" then + return self:ret(false, "success") + end + -- Check id + if not self.bunkernet_id then + return self:ret(true, "missing instance ID", HTTP_INTERNAL_SERVER_ERROR) + end + -- Send ping request + local ok, err, status, _ = self:ping() + if not ok then + return self:ret(true, "error while sending request to API : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + if status ~= 200 then + return self:ret( + true, + "received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id, + HTTP_INTERNAL_SERVER_ERROR + ) + end + return self:ret(true, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful", HTTP_OK) +end + return bunkernet diff --git a/src/common/core/bunkernet/ui/actions.py b/src/common/core/bunkernet/ui/actions.py index c34b8c821..201551572 100644 --- a/src/common/core/bunkernet/ui/actions.py +++ b/src/common/core/bunkernet/ui/actions.py @@ -1 +1,8 @@ -# Spoofing an action file +def bunkernet(): + return { + "message": "ok", + "data": { + "info": "test", + "status": "active", + }, + } diff --git a/src/common/core/bunkernet/ui/template.html b/src/common/core/bunkernet/ui/template.html index 7a458b6a0..be65a1807 100644 --- a/src/common/core/bunkernet/ui/template.html +++ b/src/common/core/bunkernet/ui/template.html @@ -1,59 +1,34 @@ {% extends "base.html" %} {% block content %} +
- {% if bunkernet_status %}
STATUS
- +

- Active -

+ >

- {% else %} -
-
-
STATUS
- - - -
-

- Inactive -

-
- {% endif %}
@@ -66,11 +41,9 @@

- {{ bunkernet_info or "BunkerNet is a crowdsourced database of malicious - requests shared between all BunkerWeb instances over the world. " }} -

+ >

@@ -180,6 +153,25 @@ + + + + {% endblock %} diff --git a/src/common/core/country/country.lua b/src/common/core/country/country.lua index 8bb767fd6..3cfc06bfd 100644 --- a/src/common/core/country/country.lua +++ b/src/common/core/country/country.lua @@ -34,6 +34,7 @@ function country:access() .. ")" ) end + self:set_metric("counters", "failed_country", 1) return self:ret( true, "client IP " @@ -85,6 +86,7 @@ function country:access() if not ok then return self:ret(false, "error while adding item to cache : " .. err) end + self:set_metric("counters", "failed_country", 1) return self:ret( true, "client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country_data .. ")", @@ -105,6 +107,7 @@ function country:access() if not ok then return self:ret(false, "error while adding item to cache : " .. err) end + self:set_metric("counters", "failed_country", 1) return self:ret( true, "client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country_data .. ")", diff --git a/src/common/core/country/ui/actions.py b/src/common/core/country/ui/actions.py index c34b8c821..1f3356eb4 100644 --- a/src/common/core/country/ui/actions.py +++ b/src/common/core/country/ui/actions.py @@ -1 +1,22 @@ -# Spoofing an action file +def country(): + return { + "message": "ok", + "data": { + "info": "test", + "blacklist_count": 3, + "whitelist_count": 23, + }, + } + + +def country(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("country") + + if data.get("counter_failed_country") is None: + data["counter_failed_country"] = 0 + + return data + + except: + return {"counter_failed_country": 0} diff --git a/src/common/core/country/ui/template.html b/src/common/core/country/ui/template.html index 7bc88ea32..abea388b0 100644 --- a/src/common/core/country/ui/template.html +++ b/src/common/core/country/ui/template.html @@ -1,22 +1,24 @@ {% extends "base.html" %} {% block content %} +
-
-
-
INFO
+
+
INFO
-
-

- {{ country_info or "The country security feature allows you to apply - policy based on the country of the IP address of clients (blacklist / - whitelist)." }} -

-
+
+

@@ -31,13 +33,11 @@ > Country

-
- {{ country_blacklist_count or "unknown" }} -
+

blacklist request blockedrequest blocked

@@ -58,54 +58,27 @@
-
- -
-

- Country -

-
- {{ country_blacklist_count or "unknown" }} -
- -

- whitelist request passed -

-
- - - - -
+ - {% endblock %} diff --git a/src/common/core/customcert/ui/actions.py b/src/common/core/customcert/ui/actions.py deleted file mode 100644 index c34b8c821..000000000 --- a/src/common/core/customcert/ui/actions.py +++ /dev/null @@ -1 +0,0 @@ -# Spoofing an action file diff --git a/src/common/core/customcert/ui/template.html b/src/common/core/customcert/ui/template.html deleted file mode 100644 index 9c9a66134..000000000 --- a/src/common/core/customcert/ui/template.html +++ /dev/null @@ -1,87 +0,0 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" : -"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"}, -{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"}, -{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%} - -
- -
-
INFO
- -
-

- {{ custom_certificate_info or "Custom certificates allow you to get - HTTPS / SSL / TLS on your requests." }} -

-
-
- - - {% if items|length != 0 %} - -
-
-
- CUSTOM CERTIFICATE LIST -
-
-
- -
- -

- Server name -

-

- CN -

-

- Expiry date -

- - - -
    - {% for item in items %} -
  • -

    - {{item['server_name']}} -

    -

    - {{item['cn']}} -

    -

    - {{item['expire']}} -

    -
  • - {% endfor %} -
- -
- -
-
- {% endif %} -
- -{% endblock %} diff --git a/src/common/core/db/ui/actions.py b/src/common/core/db/ui/actions.py index c34b8c821..9e8b64c7b 100644 --- a/src/common/core/db/ui/actions.py +++ b/src/common/core/db/ui/actions.py @@ -1 +1,10 @@ -# Spoofing an action file +def db(): + return { + "message": "ok", + "data": { + "info": "test", + "driver": "SQLite", + "version": "13.2", + "size": "14.8", + }, + } diff --git a/src/common/core/db/ui/template.html b/src/common/core/db/ui/template.html index cc7b879de..c7c44fb52 100644 --- a/src/common/core/db/ui/template.html +++ b/src/common/core/db/ui/template.html @@ -1,4 +1,11 @@ {% extends "base.html" %} {% block content %} +
@@ -10,50 +17,47 @@

+
+
+ + +
+
DB
+ +
+

- {{ db_info or "BunkerWeb securely stores its current configuration in - a backend database, which contains essential data for smooth - operation." }} + DRIVER + + +

+
+
+

+ VERSION + +

+ - -
-
DB
- -
-

- DRIVER - - {{ db_driver or "unknown" }} - -

-
-
-

- VERSION - - {{ db_version or "unknown" }} - -

-
-
- -
@@ -64,9 +68,7 @@ > SIZE

-
- {{ db_count or "unknown" }} -
+

@@ -102,6 +104,31 @@

- + + {% endblock %} diff --git a/src/common/core/dnsbl/dnsbl.lua b/src/common/core/dnsbl/dnsbl.lua index 545c9a1b9..dc1f7aa14 100644 --- a/src/common/core/dnsbl/dnsbl.lua +++ b/src/common/core/dnsbl/dnsbl.lua @@ -91,6 +91,7 @@ function dnsbl:access() if cached == "ok" then return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (not blacklisted)") end + self:set_metric("counters", "failed_dnsbl", 1) return self:ret( true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")", diff --git a/src/common/core/dnsbl/ui/actions.py b/src/common/core/dnsbl/ui/actions.py index c34b8c821..7a30d4fb0 100644 --- a/src/common/core/dnsbl/ui/actions.py +++ b/src/common/core/dnsbl/ui/actions.py @@ -1 +1,11 @@ -# Spoofing an action file +def dnsbl(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("dnsbl") + + if data.get("counter_failed_dnsbl") is None: + data["counter_failed_dnsbl"] = 0 + + return data + + except: + return {"counter_failed_dnsbl": 0} diff --git a/src/common/core/dnsbl/ui/template.html b/src/common/core/dnsbl/ui/template.html index 234ab3677..95b022b0e 100644 --- a/src/common/core/dnsbl/ui/template.html +++ b/src/common/core/dnsbl/ui/template.html @@ -1,6 +1,11 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" : -"www.example.com", "status" : "ok"}, {"server_name" : "app1.com", "status" : -"ok"}, {"server_name" : "test.2.fr", "status" : "ko"} ]%} +{% extends "base.html" %} {% block content %} +
@@ -11,87 +16,70 @@

- {{ dnsbl_info or "Deny access based on external DNSBL servers." }} -

+ >

- {% if items|length != 0 %}
-
-
DNSBL LIST
-
-
- -
- -

+

+

+ DNSBL +

+
+ +

+ request blocked - Server name -

-

- Status -

- - -
    - {% for item in items %} -
  • -

    - {{item['server_name']}} -

    - {% if item['status'] == "ko"%} -
    - - - -
    - {% else %} -
    - - - -
    - {% endif %} -
  • - {% endfor %} -
- -
- +

+ + + +
- {% endif %} + +
{% endblock %} diff --git a/src/common/core/errors/errors.lua b/src/common/core/errors/errors.lua index 8692d3a3d..8747ab9de 100644 --- a/src/common/core/errors/errors.lua +++ b/src/common/core/errors/errors.lua @@ -3,6 +3,7 @@ local plugin = require "bunkerweb.plugin" local ngx = ngx local subsystem = ngx.config.subsystem +local tostring = tostring local template local render = nil @@ -69,6 +70,11 @@ function errors:initialize(ctx) } end +function errors:log() + self:set_metric("counters", tostring(ngx.status), 1) + return self:ret(true, "success") +end + function errors:render_template(code) -- Render template render("error.html", { diff --git a/src/common/core/errors/ui/actions.py b/src/common/core/errors/ui/actions.py index c34b8c821..4b7f885cf 100644 --- a/src/common/core/errors/ui/actions.py +++ b/src/common/core/errors/ui/actions.py @@ -1 +1,13 @@ -# Spoofing an action file +def errors(**kwargs): + try: + # Here we will have a list { 'counter_403': X, 'counter_401': Y ... } + data = kwargs["app"].config["INSTANCES"].get_metrics("errors") + format_data = [] + # Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...] + for key, value in data.items(): + format_data[key] = {"code": int(key.split("_")[1]), "count": value} + + return {"items": format_data} + + except: + return {"items": []} diff --git a/src/common/core/errors/ui/template.html b/src/common/core/errors/ui/template.html index 87aa4a7d9..76a12d9c5 100644 --- a/src/common/core/errors/ui/template.html +++ b/src/common/core/errors/ui/template.html @@ -1,6 +1,11 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"count" : 74, -"code" : "403"}, {"count" : 82, "code" : "404"}, {"count" : "32", "code" : -"400"} ]%} +{% extends "base.html" %} {% block content %} +
@@ -11,17 +16,16 @@

- {{ dnsbl_info or "Deny access based on external DNSBL servers." }} -

+ >

- {% if items|length != 0 %}
ERRORS LIST
@@ -43,29 +47,42 @@
- {% endif %} + + {% endblock %} diff --git a/src/common/core/greylist/greylist.lua b/src/common/core/greylist/greylist.lua index 2e26327d9..5b0e46491 100644 --- a/src/common/core/greylist/greylist.lua +++ b/src/common/core/greylist/greylist.lua @@ -151,6 +151,7 @@ function greylist:access() end -- Return + self:set_metric("counters", "failed_greylist", 1) return self:ret(true, "not in greylist", get_deny_status()) end diff --git a/src/common/core/greylist/ui/actions.py b/src/common/core/greylist/ui/actions.py index c34b8c821..3bd3bb452 100644 --- a/src/common/core/greylist/ui/actions.py +++ b/src/common/core/greylist/ui/actions.py @@ -1 +1,11 @@ -# Spoofing an action file +def greylist(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("greylist") + + if data.get("counter_failed_greylist") is None: + data["counter_failed_greylist"] = 0 + + return data + + except: + return {"counter_failed_greylist": 0} diff --git a/src/common/core/greylist/ui/template.html b/src/common/core/greylist/ui/template.html index 16cd1917c..1f45133cc 100644 --- a/src/common/core/greylist/ui/template.html +++ b/src/common/core/greylist/ui/template.html @@ -1,236 +1,83 @@ {% extends "base.html" %} {% block content %} +
-
- -
-
INFO
+ +
+
INFO
-
-

+

+
+
+ +
+ +
+

+ GREYLIST +

+
+ +

+ request blocked - {{ greylist_info or "Allow access while keeping security features - based on internal and external IP/network/rDNS/ASN greylists. " }} -

-
-
- -
- -
- -
-

- URL -

-
- {{ greylist_url_count or "unknown" }} -
- -

- - denied -

-
- -
-

- IP -

-
- {{ greylist_ip_count or "unknown" }} -
- -

- - denied - -

-
- - - - -
- -
- -
-

- RDNS -

-
- {{ greylist_rdns_count or "unknown" }} -
- -

- - denied - -

-
- - - - -
- -
- -
-

- ASN -

-
- {{ greylist_asn_count or "unknown" }} -
- -

- - denied - -

-
- - - - -
- -
- -
-

- User Agent -

-
- {{ greylist_user_agent_count or "unknown" }} -
- -

- - denied - -

-
- - - - -
+
- {% endblock %} diff --git a/src/common/core/letsencrypt/ui/actions.py b/src/common/core/letsencrypt/ui/actions.py deleted file mode 100644 index c34b8c821..000000000 --- a/src/common/core/letsencrypt/ui/actions.py +++ /dev/null @@ -1 +0,0 @@ -# Spoofing an action file diff --git a/src/common/core/letsencrypt/ui/template.html b/src/common/core/letsencrypt/ui/template.html deleted file mode 100644 index 385117144..000000000 --- a/src/common/core/letsencrypt/ui/template.html +++ /dev/null @@ -1,86 +0,0 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" : -"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"}, -{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"}, -{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%} - -
- -
-
INFO
- -
-

- {{ lets_encrypt_info or "Let's Encrypt certificates for secure HTTP - requests." }} -

-
-
- - - {% if items|length != 0 %} -
-
-
- LET'S ENCRYPT LIST -
-
-
- -
- -

- Server name -

-

- CN -

-

- Expiry date -

- - - -
    - {% for item in items %} -
  • -

    - {{item['server_name']}} -

    -

    - {{item['cn']}} -

    -

    - {{item['expire']}} -

    -
  • - {% endfor %} -
- -
- -
-
- {% endif %} -
- -{% endblock %} diff --git a/src/common/core/limit/limit.lua b/src/common/core/limit/limit.lua index 11ec29a75..59d2b49fb 100644 --- a/src/common/core/limit/limit.lua +++ b/src/common/core/limit/limit.lua @@ -158,6 +158,7 @@ function limit:access() end -- Limit reached if limited then + self:set_metric("counters", "limited_uri_" .. self.ctx.bw.uri, 1) return self:ret( true, "client IP " diff --git a/src/common/core/limit/ui/actions.py b/src/common/core/limit/ui/actions.py index c34b8c821..04ab8a9c7 100644 --- a/src/common/core/limit/ui/actions.py +++ b/src/common/core/limit/ui/actions.py @@ -1 +1,13 @@ -# Spoofing an action file +def limit(**kwargs): + try: + # Here we will have a list { 'limit_uri_url1': X, 'limit_uri_url2': Y ... } + data = kwargs["app"].config["INSTANCES"].get_metrics("limit") + format_data = [] + # Format to fit [{url: "url1", count: X}, {url: "url2", count: Y} ...] + for key, value in data.items(): + format_data[key] = {"url": key.replace("limit_uri_", ""), "count": value} + + return {"items": format_data} + + except: + return {"items": []} diff --git a/src/common/core/limit/ui/template.html b/src/common/core/limit/ui/template.html index 79f49b214..be2e1be90 100644 --- a/src/common/core/limit/ui/template.html +++ b/src/common/core/limit/ui/template.html @@ -1,6 +1,11 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"url" : -"http://www.example.com", "count" : 24},{"url" : "http://www.example.com", -"count" : 24} ]%} +{% extends "base.html" %} {% block content %} +
@@ -11,17 +16,16 @@

- {{ limit_info or "Limit maximum number of requests and connections." }} -

+ >

- {% if items|length != 0 %}
@@ -46,29 +50,41 @@
    - {% for item in items %}
  • - {{item['url']}} -

    + >

    - {{item['count']}} -

    + >

  • - {% endfor %}
- {% endif %} - + + {% endblock %} diff --git a/src/common/core/metrics/metrics.lua b/src/common/core/metrics/metrics.lua index 1588dc182..e63d2a8e4 100644 --- a/src/common/core/metrics/metrics.lua +++ b/src/common/core/metrics/metrics.lua @@ -3,22 +3,36 @@ local class = require "middleclass" local datastore = require "bunkerweb.datastore" local plugin = require "bunkerweb.plugin" local utils = require "bunkerweb.utils" +local lrucache = require "resty.lrucache" local metrics = class("metrics", plugin) +local lru, err_lru = lrucache.new(100000) +if not lru then + require "bunkerweb.logger":new("METRICS"):log(ERR, "failed to instantiate LRU cache : " .. err_lru) +end + local ngx = ngx local shared = ngx.shared local subsystem = ngx.config.subsystem local ERR = ngx.ERR local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR local HTTP_OK = ngx.HTTP_OK +local worker = ngx.worker +local worker_id = worker.id local get_reason = utils.get_reason local get_country = utils.get_country +local has_variable = utils.has_variable local encode = cjson.encode local decode = cjson.decode local match = string.match +local time = os.time +local tonumber = tonumber +local tostring = tostring +local table_insert = table.insert +local table_remove = table.remove function metrics:initialize(ctx) -- Call parent initialize @@ -32,9 +46,9 @@ function metrics:initialize(ctx) self.metrics_datastore = datastore:new(dict) end -function metrics:log() +function metrics:log(bypass_checks) -- Don't go further if metrics is not enabled - if self.variables["USE_METRICS"] == "no" then + if not bypass_checks and self.variables["USE_METRICS"] == "no" then return self:ret(true, "metrics are disabled") end -- Store blocked requests @@ -50,58 +64,169 @@ function metrics:log() end end local request = { - date = os.time(), + date = self.ctx.bw.start_time or time(), ip = self.ctx.bw.remote_addr, country = country, method = self.ctx.bw.request_method, url = self.ctx.bw.request_uri, status = ngx.status, - ["user-agent"] = self.ctx.bw.http_user_agent or "", + user_agent = self.ctx.bw.http_user_agent or "", reason = reason, data = data, } - local ok - ok, err = self.metrics_datastore:safe_rpush("metrics_requests", encode(request)) - if not ok then - self.logger:log(ERR, "can't save request to datastore : " .. err) + -- Get current requests + local requests = lru:get("requests") + if not requests then + requests = {} + end + -- Add current request + table_insert(requests, request) + -- Remove old requests + local nb_delete = #requests - tonumber(self.variables["METRICS_MAX_BLOCKED_REQUESTS"]) + while nb_delete > 0 do + table_remove(requests, 1) + nb_delete = nb_delete - 1 + end + -- Update worker cache + lru:set("requests", requests) + end + -- Get metrics from plugins + local all_metrics = self.ctx.bw.metrics + if all_metrics then + -- Loop on plugins + for plugin, plugin_metrics in pairs(all_metrics) do + -- Loop on kinds + for kind, kind_metrics in pairs(plugin_metrics) do + -- Increment counters + if kind == "counters" then + for metric_key, metric_value in pairs(kind_metrics) do + local lru_key = plugin .. "_counter_" .. metric_key + local metric_counter = lru:get(lru_key) + if not metric_counter then + metric_counter = metric_value + else + metric_counter = metric_counter + metric_value + end + lru:set(lru_key, metric_counter) + end + end + end end end return self:ret(true, "success") end function metrics:log_default() - return self:log() + local is_needed, err = has_variable("USE_METRICS", "yes") + if is_needed == nil then + return self:ret(false, "can't check USE_METRICS variable : " .. err) + end + if is_needed then + return self:log(true) + end + return self:ret(true, "metrics not used") +end + +function metrics:timer() + -- Check if metrics is used + local is_needed, err = has_variable("USE_METRICS", "yes") + if is_needed == nil then + return self:ret(false, "can't check USE_METRICS variable : " .. err) + end + if not is_needed then + return self:ret(true, "metrics not used") + end + + local ret = true + local ret_err = "metrics updated" + local wid = tostring(worker_id()) + + -- Purpose of following code is to populate the LRU cache. + -- In case of a reload, everything in LRU cache is removed + -- so we need to copy it from SHM cache if it exists. + local setup = lru:get("setup") + if not setup then + for _, key in ipairs(self.metrics_datastore:keys()) do + if key:match("_" .. wid .. "$") then + local value, err = self.metrics_datastore:get(key) + if not value and err ~= "not found" then + ret = false + ret_err = err + self.logger:log(ERR, "error while checking " .. key .. " : " .. err) + end + if value then + local ok, decoded = pcall(decode, value) + if ok then + value = decoded + end + lru:set(key:gsub("_" .. wid .. "$", ""), value) + end + end + end + lru:set("setup", true) + end + -- Loop on all keys + for _, key in ipairs(lru:get_keys()) do + -- Get LRU data + local value = lru:get(key) + if type(value) == "table" then + value = encode(value) + end + -- Push to dict + local ok, err = self.metrics_datastore:set(key .. "_" .. wid, value) + if not ok then + ret = false + ret_err = err + self.logger:log(ERR, "can't update " .. key .. "_" .. wid .. " : " .. err) + end + end + -- Done + return self:ret(ret, ret_err) end function metrics:api() -- Match request - if not match(self.ctx.bw.uri, "^/metrics/requests$") or self.ctx.bw.request_method ~= "GET" then + if not match(self.ctx.bw.uri, "^/metrics/.+$") or self.ctx.bw.request_method ~= "GET" then return self:ret(false, "success") end - - -- Get requests metrics - local len, err = self.metrics_datastore:llen("metrics_requests") - if not len then - return self:ret(true, "error while getting length of metrics_requests : " .. err, HTTP_INTERNAL_SERVER_ERROR) - end - local i = 0 - local data = {} - while i < len do - local request - request, err = self.metrics_datastore:lpop("metrics_requests") - if request then - table.insert(data, decode(request)) - else - return self:ret(true, "error while getting metrics_requests : " .. err, HTTP_INTERNAL_SERVER_ERROR) + -- Extract filter parameter + local filter = self.ctx.bw.uri:gsub("^/metrics/", "") + -- Loop on keys + local metrics_data = {} + for _, key in ipairs(self.metrics_datastore:keys()) do + -- Check if key starts with our filter + if key:match("^" .. filter .. "_") then + -- Get the value + local data, err = self.metrics_datastore:get(key) + if not data then + return self:ret(true, "error while fetching requests : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + local metric_key = key:gsub("_[0-9]+$", ""):gsub("^" .. filter .. "_", "") + if metric_key == "" then + metric_key = filter + end + -- Table case + local ok, decoded = pcall(decode, data) + if ok then + data = decoded + end + if type(data) == "table" then + if not metrics_data[metric_key] then + metrics_data[metric_key] = {} + end + for _, metric_value in ipairs(data) do + table_insert(metrics_data[metric_key], metric_value) + end + -- Counter case + else + if not metrics_data[metric_key] then + metrics_data[metric_key] = 0 + end + metrics_data[metric_key] = metrics_data[metric_key] + data + end end - local ok - ok, err = self.metrics_datastore:safe_rpush("metrics_requests", request) - if not ok then - self.logger:log(ERR, "can't save request to datastore : " .. err) - end - i = i + 1 end - return self:ret(true, data, HTTP_OK) + return self:ret(true, metrics_data, HTTP_OK) end return metrics diff --git a/src/common/core/metrics/plugin.json b/src/common/core/metrics/plugin.json index 3ef006c87..f3d0d6341 100644 --- a/src/common/core/metrics/plugin.json +++ b/src/common/core/metrics/plugin.json @@ -6,7 +6,7 @@ "stream": "partial", "settings": { "USE_METRICS": { - "context": "global", + "context": "multisite", "default": "yes", "help": "Enable collection and retrieval of internal metrics.", "id": "use-metrics", @@ -22,6 +22,15 @@ "label": "Metrics memory size", "regex": "^\\d+[kKmMgG]?$", "type": "text" + }, + "METRICS_MAX_BLOCKED_REQUESTS": { + "context": "global", + "default": "100", + "help": "Maximum number of blocked requests to store (per worker).", + "id": "metrics-max-blocked-requests", + "label": "Metrics max blocked requests", + "regex": "^\\d+$", + "type": "text" } } } diff --git a/src/common/core/misc/misc.lua b/src/common/core/misc/misc.lua index 40c16fbaf..8546466f3 100644 --- a/src/common/core/misc/misc.lua +++ b/src/common/core/misc/misc.lua @@ -23,10 +23,18 @@ function misc:access() -- Check if method is allowed for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do if method == allowed_method then + self:set_metric("counters", "failed_method", 1) return self:ret(true, "method " .. method .. " is allowed") end end return self:ret(true, "method " .. method .. " is not allowed", HTTP_NOT_ALLOWED) end +function misc:log_default() + if self.variables["DISABLE_DEFAULT_SERVER"] == "yes" then + self:set_metric("counters", "failed_default", 1) + end + return self:ret(true, "success") +end + return misc diff --git a/src/common/core/misc/ui/actions.py b/src/common/core/misc/ui/actions.py index c34b8c821..10598b407 100644 --- a/src/common/core/misc/ui/actions.py +++ b/src/common/core/misc/ui/actions.py @@ -1 +1,14 @@ -# Spoofing an action file +def misc(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("misc") + + if "counter_failed_default" not in data: + data["counter_failed_default"] = 0 + + if "counter_failed_method" not in data: + data["counter_failed_method"] = 0 + + return data + + except: + return {"counter_failed_default": 0, "counter_failed_method": 0} diff --git a/src/common/core/misc/ui/template.html b/src/common/core/misc/ui/template.html index 1eab61124..a22092192 100644 --- a/src/common/core/misc/ui/template.html +++ b/src/common/core/misc/ui/template.html @@ -1,4 +1,11 @@ {% extends "base.html" %} {% block content %} +
@@ -10,10 +17,9 @@

- {{ misc_info or "Miscellaneous settings (methods, servers...)." }} -

+ >

@@ -29,9 +35,10 @@ > DEFAULT SERVER DISABLED

-
- {{ default_server_count or "unknown" }} -
+

@@ -74,9 +81,10 @@ > DISALLOWED METHODS

-
- {{ disallowed_methods_count or "unknown" }} -
+

@@ -103,6 +111,26 @@

- + + {% endblock %} diff --git a/src/common/core/modsecurity/ui/actions.py b/src/common/core/modsecurity/ui/actions.py deleted file mode 100644 index c34b8c821..000000000 --- a/src/common/core/modsecurity/ui/actions.py +++ /dev/null @@ -1 +0,0 @@ -# Spoofing an action file diff --git a/src/common/core/modsecurity/ui/template.html b/src/common/core/modsecurity/ui/template.html deleted file mode 100644 index 767291b20..000000000 --- a/src/common/core/modsecurity/ui/template.html +++ /dev/null @@ -1,64 +0,0 @@ -{% extends "base.html" %} {% block content %} - -
- -
-
INFO
- -
-

- {{ modsec_info or "ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs." }} -

-
-
- - -
- -
-

- MODSECURITY -

-
{{ modsec_count or "unknown" }}
- -

- request blocked - -

-
- - - - -
-
- -{% endblock %} diff --git a/src/common/core/order.json b/src/common/core/order.json index ae866457f..cbcfeb2e0 100644 --- a/src/common/core/order.json +++ b/src/common/core/order.json @@ -32,7 +32,7 @@ "antibot" ], "headers": ["headers", "cors", "reverseproxy", "clientcache", "antibot"], - "log": ["badbehavior", "bunkernet", "metrics"], + "log": ["badbehavior", "bunkernet", "errors", "metrics"], "preread": [ "whitelist", "blacklist", @@ -42,5 +42,6 @@ "reversescan" ], "log_stream": ["badbehavior", "bunkernet"], - "log_default": ["badbehavior", "bunkernet", "metrics"] + "log_default": ["badbehavior", "bunkernet", "errors", "misc", "metrics"], + "timer": ["metrics"] } diff --git a/src/common/core/redis/redis.lua b/src/common/core/redis/redis.lua index ff6d4e838..aa9ced7d5 100644 --- a/src/common/core/redis/redis.lua +++ b/src/common/core/redis/redis.lua @@ -5,6 +5,9 @@ local redis = class("redis", plugin) local ngx = ngx local NOTICE = ngx.NOTICE +local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR +local HTTP_OK = ngx.HTTP_OK +local match = string.match function redis:initialize(ctx) -- Call parent initialize @@ -34,4 +37,46 @@ function redis:init_worker() return self:ret(true, "success") end +function redis:api() + if self.ctx.bw.uri == "/redis/ping" and self.ctx.bw.request_method == "POST" then + -- Check redis connection + local ok, err = self.clusterstore:connect(true) + if not ok then + return self:ret(true, "redis connect error : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + -- Send ping + local ok, err = self.clusterstore:call("ping") + self.clusterstore:close() + if err then + return self:ret(true, "error while sending ping command to redis server : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + if not ok then + return self:ret(true, "redis ping command failed", HTTP_INTERNAL_SERVER_ERROR) + end + return self:ret(true, "success", HTTP_OK) + end + if self.ctx.bw.uri == "/redis/stats" and self.ctx.bw.request_method == "GET" then + -- Connect to redis + local ok, err = self.clusterstore:connect(true) + if not ok then + return self:ret(true, "redis connect error : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + -- Get number of keys + local nb_keys, err = self.clusterstore:call("dbsize") + self.clusterstore:close() + if err then + return self:ret(true, "error while sending dbsize command to redis server : " .. err, HTTP_INTERNAL_SERVER_ERROR) + end + if not ok then + return self:ret(true, "redis dbsize command failed", HTTP_INTERNAL_SERVER_ERROR) + end + -- Return data + local data = { + redis_nb_keys = nb_keys + } + return self:ret(true, data, HTTP_OK) + end + return self:ret(false, "success") +end + return redis diff --git a/src/common/core/redis/ui/actions.py b/src/common/core/redis/ui/actions.py index c34b8c821..064d1b3d3 100644 --- a/src/common/core/redis/ui/actions.py +++ b/src/common/core/redis/ui/actions.py @@ -1 +1,8 @@ -# Spoofing an action file +def redis(): + return { + "message": "ok", + "data": { + "info": "test", + "status": "active", + }, + } diff --git a/src/common/core/redis/ui/template.html b/src/common/core/redis/ui/template.html index 5539b8cbf..683670611 100644 --- a/src/common/core/redis/ui/template.html +++ b/src/common/core/redis/ui/template.html @@ -1,60 +1,34 @@ {% extends "base.html" %} {% block content %} +
- {% if redis_status %}
STATUS
- +

- Active -

+ >

- {% else %} -
-
-
STATUS
- - - -
-

- Inactive -

-
- {% endif %} -
@@ -66,11 +40,9 @@

- {{ redis_info or "Redis server configuration when using BunkerWeb in - cluster mode. " }} -

+ >

@@ -179,7 +151,25 @@ - + + + + {% endblock %} diff --git a/src/common/core/selfsigned/ui/actions.py b/src/common/core/selfsigned/ui/actions.py deleted file mode 100644 index c34b8c821..000000000 --- a/src/common/core/selfsigned/ui/actions.py +++ /dev/null @@ -1 +0,0 @@ -# Spoofing an action file diff --git a/src/common/core/selfsigned/ui/template.html b/src/common/core/selfsigned/ui/template.html deleted file mode 100644 index 9507df4f2..000000000 --- a/src/common/core/selfsigned/ui/template.html +++ /dev/null @@ -1,86 +0,0 @@ -{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" : -"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"}, -{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"}, -{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%} - -
- -
-
INFO
- -
-

- {{ lets_encrypt_info or "Selfsigned certificates for secure HTTP - requests." }} -

-
-
- - - {% if items|length != 0 %} -
-
-
- SELFSIGNED LIST -
-
-
- -
- -

- Server name -

-

- CN -

-

- Expiry date -

- - - -
    - {% for item in items %} -
  • -

    - {{item['server_name']}} -

    -

    - {{item['cn']}} -

    -

    - {{item['expire']}} -

    -
  • - {% endfor %} -
- -
- -
-
- {% endif %} -
- -{% endblock %} diff --git a/src/common/core/ui/confs/modsec/ui.conf b/src/common/core/ui/confs/modsec/ui.conf index 2d273d2a6..3c98b3662 100644 --- a/src/common/core/ui/confs/modsec/ui.conf +++ b/src/common/core/ui/confs/modsec/ui.conf @@ -1,4 +1,5 @@ {% if USE_UI == "yes" +%} SecRule REQUEST_FILENAME "@rx /services$" "id:7771,ctl:ruleRemoveByTag=attack-rce,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-generic,nolog" SecRule REQUEST_FILENAME "@rx /global_config$" "id:7772,ctl:ruleRemoveByTag=platform-pgsql,nolog" +SecRule REQUEST_FILENAME "@rx /configs$" "id:7773,ctl:ruleRemoveByTag=language-shell,ctl:ruleRemoveByTag=attack-lfi,nolog" {% endif +%} diff --git a/src/common/core/whitelist/ui/actions.py b/src/common/core/whitelist/ui/actions.py index c34b8c821..19aeeb591 100644 --- a/src/common/core/whitelist/ui/actions.py +++ b/src/common/core/whitelist/ui/actions.py @@ -1 +1,11 @@ -# Spoofing an action file +def whitelist(**kwargs): + try: + data = kwargs["app"].config["INSTANCES"].get_metrics("whitelist") + + if "counter_passed_whitelist" not in data: + data["counter_passed_whitelist"] = 0 + + return data + + except: + return {"counter_passed_whitelist": 0} diff --git a/src/common/core/whitelist/ui/template.html b/src/common/core/whitelist/ui/template.html index 58a0366f7..c71c6b106 100644 --- a/src/common/core/whitelist/ui/template.html +++ b/src/common/core/whitelist/ui/template.html @@ -1,60 +1,76 @@ {% extends "base.html" %} {% block content %} +
-
- -
-
INFO
+ +
+
INFO
-
-

+

+
+
+ +
+ +
+

+ WHITELIST +

+
+ +

+ request passed - {{ whitelist_info or "Allow access based on internal and external - IP/network/rDNS/ASN whitelists." }} -

-
-
- -
- -
- -
-

- URL -

-
- {{ whitelist_url_count or "unknown" }} -
- -

- - passed -

+ -
- -
-

- IP -

-
- {{ whitelist_ip_count or "unknown" }} -
- -

- - passed - -

-
- - - - -
- -
- -
-

- RDNS -

-
- {{ whitelist_rdns_count or "unknown" }} -
- -

- - passed - -

-
- - - - -
- -
- -
-

- ASN -

-
- {{ whitelist_asn_count or "unknown" }} -
- -

- - passed - -

-
- - - - -
- -
- -
-

- User Agent -

-
- {{ whitelist_user_agent_count or "unknown" }} -
- -

- - passed - -

-
- - - - -
+
- {% endblock %} diff --git a/src/common/core/whitelist/whitelist.lua b/src/common/core/whitelist/whitelist.lua index f7f39cb74..880c27de0 100644 --- a/src/common/core/whitelist/whitelist.lua +++ b/src/common/core/whitelist/whitelist.lua @@ -137,6 +137,7 @@ function whitelist:access() ngx_var.is_whitelisted = "yes" self.ctx.bw.is_whitelisted = "yes" env_set("is_whitelisted", "yes") + self:set_metric("counters", "passed_whitelist", 1) return self:ret(true, err, OK) end -- Perform checks @@ -155,7 +156,8 @@ function whitelist:access() ngx_var.is_whitelisted = "yes" self.ctx.bw.is_whitelisted = "yes" env_set("is_whitelisted", "yes") - return self:ret(true, k .. " is whitelisted (info : " .. whitelisted .. ")", ngx.OK) + self:set_metric("counters", "passed_whitelist", 1) + return self:ret(true, k .. " is whitelisted (info : " .. whitelisted .. ")", OK) end end end diff --git a/src/common/db/Database.py b/src/common/db/Database.py index 83c3c4352..8a0d91471 100644 --- a/src/common/db/Database.py +++ b/src/common/db/Database.py @@ -274,7 +274,7 @@ class Database: def get_metadata(self) -> Dict[str, str]: """Get the metadata from the database""" - data = {"version": "1.5.4", "integration": "unknown"} + data = {"version": "1.5.6", "integration": "unknown"} with self.__db_session() as session: with suppress(ProgrammingError, OperationalError): metadata = session.query(Metadata).with_entities(Metadata.version, Metadata.integration).filter_by(id=1).first() diff --git a/src/common/db/requirements.in b/src/common/db/requirements.in index 07911b2b7..1dfa1d5e6 100644 --- a/src/common/db/requirements.in +++ b/src/common/db/requirements.in @@ -1,4 +1,4 @@ -cryptography==42.0.1 +cryptography==42.0.2 psycopg[binary,pool]==3.1.17 PyMySQL==1.1.0 sqlalchemy==2.0.25 diff --git a/src/common/db/requirements.txt b/src/common/db/requirements.txt index ba871348f..fd5ebd863 100644 --- a/src/common/db/requirements.txt +++ b/src/common/db/requirements.txt @@ -58,39 +58,39 @@ cffi==1.16.0 \ --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 # via cryptography -cryptography==42.0.1 \ - --hash=sha256:0b7cacc142260ada944de070ce810c3e2a438963ee3deb45aa26fd2cee94c9a4 \ - --hash=sha256:126e0ba3cc754b200a2fb88f67d66de0d9b9e94070c5bc548318c8dab6383cb6 \ - --hash=sha256:160fa08dfa6dca9cb8ad9bd84e080c0db6414ba5ad9a7470bc60fb154f60111e \ - --hash=sha256:16b9260d04a0bfc8952b00335ff54f471309d3eb9d7e8dbfe9b0bd9e26e67881 \ - --hash=sha256:25ec6e9e81de5d39f111a4114193dbd39167cc4bbd31c30471cebedc2a92c323 \ - --hash=sha256:265bdc693570b895eb641410b8fc9e8ddbce723a669236162b9d9cfb70bd8d77 \ - --hash=sha256:2dff7a32880a51321f5de7869ac9dde6b1fca00fc1fef89d60e93f215468e824 \ - --hash=sha256:2fe16624637d6e3e765530bc55caa786ff2cbca67371d306e5d0a72e7c3d0407 \ - --hash=sha256:32ea63ceeae870f1a62e87f9727359174089f7b4b01e4999750827bf10e15d60 \ - --hash=sha256:351db02c1938c8e6b1fee8a78d6b15c5ccceca7a36b5ce48390479143da3b411 \ - --hash=sha256:430100abed6d3652208ae1dd410c8396213baee2e01a003a4449357db7dc9e14 \ - --hash=sha256:4d84673c012aa698555d4710dcfe5f8a0ad76ea9dde8ef803128cc669640a2e0 \ - --hash=sha256:50aecd93676bcca78379604ed664c45da82bc1241ffb6f97f6b7392ed5bc6f04 \ - --hash=sha256:6ac8924085ed8287545cba89dc472fc224c10cc634cdf2c3e2866fe868108e77 \ - --hash=sha256:6bfd823b336fdcd8e06285ae8883d3d2624d3bdef312a0e2ef905f332f8e9302 \ - --hash=sha256:727387886c9c8de927c360a396c5edcb9340d9e960cda145fca75bdafdabd24c \ - --hash=sha256:7911586fc69d06cd0ab3f874a169433db1bc2f0e40988661408ac06c4527a986 \ - --hash=sha256:802d6f83233cf9696b59b09eb067e6b4d5ae40942feeb8e13b213c8fad47f1aa \ - --hash=sha256:8d7efb6bf427d2add2f40b6e1e8e476c17508fa8907234775214b153e69c2e11 \ - --hash=sha256:9544492e8024f29919eac2117edd8c950165e74eb551a22c53f6fdf6ba5f4cb8 \ - --hash=sha256:95d900d19a370ae36087cc728e6e7be9c964ffd8cbcb517fd1efb9c9284a6abc \ - --hash=sha256:9d61fcdf37647765086030d81872488e4cb3fafe1d2dda1d487875c3709c0a49 \ - --hash=sha256:ab6b302d51fbb1dd339abc6f139a480de14d49d50f65fdc7dff782aa8631d035 \ - --hash=sha256:b512f33c6ab195852595187af5440d01bb5f8dd57cb7a91e1e009a17f1b7ebca \ - --hash=sha256:cb2861a9364fa27d24832c718150fdbf9ce6781d7dc246a516435f57cfa31fe7 \ - --hash=sha256:d3594947d2507d4ef7a180a7f49a6db41f75fb874c2fd0e94f36b89bfd678bf2 \ - --hash=sha256:d3902c779a92151f134f68e555dd0b17c658e13429f270d8a847399b99235a3f \ - --hash=sha256:d50718dd574a49d3ef3f7ef7ece66ef281b527951eb2267ce570425459f6a404 \ - --hash=sha256:e5edf189431b4d51f5c6fb4a95084a75cef6b4646c934eb6e32304fc720e1453 \ - --hash=sha256:e6edc3a568667daf7d349d7e820783426ee4f1c0feab86c29bd1d6fe2755e009 \ - --hash=sha256:ed1b2130f5456a09a134cc505a17fc2830a1a48ed53efd37dcc904a23d7b82fa \ - --hash=sha256:fd33f53809bb363cf126bebe7a99d97735988d9b0131a2be59fbf83e1259a5b7 +cryptography==42.0.2 \ + --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ + --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ + --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ + --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ + --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ + --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ + --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ + --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ + --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ + --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ + --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ + --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ + --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ + --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ + --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ + --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ + --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ + --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ + --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ + --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ + --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ + --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ + --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ + --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ + --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ + --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ + --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ + --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ + --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ + --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ + --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ + --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f # via -r requirements.in greenlet==3.0.3 \ --hash=sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67 \ diff --git a/src/common/gen/requirements.txt b/src/common/gen/requirements.txt index 43dd3a69d..c9bffec7e 100644 --- a/src/common/gen/requirements.txt +++ b/src/common/gen/requirements.txt @@ -298,9 +298,9 @@ six==1.16.0 \ # via # kubernetes # python-dateutil -urllib3==2.1.0 \ - --hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \ - --hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54 +urllib3==2.2.0 \ + --hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \ + --hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224 # via # docker # kubernetes diff --git a/src/deps/requirements-deps.txt b/src/deps/requirements-deps.txt index ebb1028cb..c1718aa59 100644 --- a/src/deps/requirements-deps.txt +++ b/src/deps/requirements-deps.txt @@ -181,9 +181,9 @@ toposort==1.10 \ --hash=sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd \ --hash=sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87 # via pip-compile-multi -urllib3==2.1.0 \ - --hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \ - --hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54 +urllib3==2.2.0 \ + --hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \ + --hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224 # via requests wheel==0.42.0 \ --hash=sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d \ diff --git a/src/scheduler/requirements.in b/src/scheduler/requirements.in index 73ebd0a32..712e03165 100644 --- a/src/scheduler/requirements.in +++ b/src/scheduler/requirements.in @@ -1,6 +1,6 @@ certbot==2.8.0 configobj==5.0.8 -cryptography==42.0.1 +cryptography==42.0.2 maxminddb==2.5.2 python-magic==0.4.27 schedule==1.2.1 diff --git a/src/scheduler/requirements.txt b/src/scheduler/requirements.txt index 738bcc34e..cf1cf430e 100644 --- a/src/scheduler/requirements.txt +++ b/src/scheduler/requirements.txt @@ -172,39 +172,39 @@ configobj==5.0.8 \ # via # -r requirements.in # certbot -cryptography==42.0.1 \ - --hash=sha256:0b7cacc142260ada944de070ce810c3e2a438963ee3deb45aa26fd2cee94c9a4 \ - --hash=sha256:126e0ba3cc754b200a2fb88f67d66de0d9b9e94070c5bc548318c8dab6383cb6 \ - --hash=sha256:160fa08dfa6dca9cb8ad9bd84e080c0db6414ba5ad9a7470bc60fb154f60111e \ - --hash=sha256:16b9260d04a0bfc8952b00335ff54f471309d3eb9d7e8dbfe9b0bd9e26e67881 \ - --hash=sha256:25ec6e9e81de5d39f111a4114193dbd39167cc4bbd31c30471cebedc2a92c323 \ - --hash=sha256:265bdc693570b895eb641410b8fc9e8ddbce723a669236162b9d9cfb70bd8d77 \ - --hash=sha256:2dff7a32880a51321f5de7869ac9dde6b1fca00fc1fef89d60e93f215468e824 \ - --hash=sha256:2fe16624637d6e3e765530bc55caa786ff2cbca67371d306e5d0a72e7c3d0407 \ - --hash=sha256:32ea63ceeae870f1a62e87f9727359174089f7b4b01e4999750827bf10e15d60 \ - --hash=sha256:351db02c1938c8e6b1fee8a78d6b15c5ccceca7a36b5ce48390479143da3b411 \ - --hash=sha256:430100abed6d3652208ae1dd410c8396213baee2e01a003a4449357db7dc9e14 \ - --hash=sha256:4d84673c012aa698555d4710dcfe5f8a0ad76ea9dde8ef803128cc669640a2e0 \ - --hash=sha256:50aecd93676bcca78379604ed664c45da82bc1241ffb6f97f6b7392ed5bc6f04 \ - --hash=sha256:6ac8924085ed8287545cba89dc472fc224c10cc634cdf2c3e2866fe868108e77 \ - --hash=sha256:6bfd823b336fdcd8e06285ae8883d3d2624d3bdef312a0e2ef905f332f8e9302 \ - --hash=sha256:727387886c9c8de927c360a396c5edcb9340d9e960cda145fca75bdafdabd24c \ - --hash=sha256:7911586fc69d06cd0ab3f874a169433db1bc2f0e40988661408ac06c4527a986 \ - --hash=sha256:802d6f83233cf9696b59b09eb067e6b4d5ae40942feeb8e13b213c8fad47f1aa \ - --hash=sha256:8d7efb6bf427d2add2f40b6e1e8e476c17508fa8907234775214b153e69c2e11 \ - --hash=sha256:9544492e8024f29919eac2117edd8c950165e74eb551a22c53f6fdf6ba5f4cb8 \ - --hash=sha256:95d900d19a370ae36087cc728e6e7be9c964ffd8cbcb517fd1efb9c9284a6abc \ - --hash=sha256:9d61fcdf37647765086030d81872488e4cb3fafe1d2dda1d487875c3709c0a49 \ - --hash=sha256:ab6b302d51fbb1dd339abc6f139a480de14d49d50f65fdc7dff782aa8631d035 \ - --hash=sha256:b512f33c6ab195852595187af5440d01bb5f8dd57cb7a91e1e009a17f1b7ebca \ - --hash=sha256:cb2861a9364fa27d24832c718150fdbf9ce6781d7dc246a516435f57cfa31fe7 \ - --hash=sha256:d3594947d2507d4ef7a180a7f49a6db41f75fb874c2fd0e94f36b89bfd678bf2 \ - --hash=sha256:d3902c779a92151f134f68e555dd0b17c658e13429f270d8a847399b99235a3f \ - --hash=sha256:d50718dd574a49d3ef3f7ef7ece66ef281b527951eb2267ce570425459f6a404 \ - --hash=sha256:e5edf189431b4d51f5c6fb4a95084a75cef6b4646c934eb6e32304fc720e1453 \ - --hash=sha256:e6edc3a568667daf7d349d7e820783426ee4f1c0feab86c29bd1d6fe2755e009 \ - --hash=sha256:ed1b2130f5456a09a134cc505a17fc2830a1a48ed53efd37dcc904a23d7b82fa \ - --hash=sha256:fd33f53809bb363cf126bebe7a99d97735988d9b0131a2be59fbf83e1259a5b7 +cryptography==42.0.2 \ + --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ + --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ + --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ + --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ + --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ + --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ + --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ + --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ + --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ + --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ + --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ + --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ + --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ + --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ + --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ + --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ + --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ + --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ + --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ + --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ + --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ + --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ + --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ + --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ + --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ + --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ + --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ + --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ + --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ + --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ + --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ + --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f # via # -r requirements.in # acme @@ -303,9 +303,9 @@ python-magic==0.4.27 \ --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 # via -r requirements.in -pytz==2023.3.post1 \ - --hash=sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b \ - --hash=sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7 +pytz==2023.4 \ + --hash=sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40 \ + --hash=sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a # via # acme # certbot @@ -328,9 +328,9 @@ six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via configobj -urllib3==2.1.0 \ - --hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \ - --hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54 +urllib3==2.2.0 \ + --hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \ + --hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224 # via requests zipp==3.17.0 \ --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \ diff --git a/src/ui/main.py b/src/ui/main.py index 1c31f81a2..5bef5ef59 100755 --- a/src/ui/main.py +++ b/src/ui/main.py @@ -41,7 +41,6 @@ from tarfile import CompressionError, HeaderError, ReadError, TarError, open as from threading import Thread from tempfile import NamedTemporaryFile from time import sleep, time -from traceback import format_exc from zipfile import BadZipFile, ZipFile from src.Instances import Instances @@ -191,7 +190,6 @@ try: WTF_CSRF_SSL_STRICT=False, USER=USER, SEND_FILE_MAX_AGE_DEFAULT=86400, - PLUGIN_ARGS={}, RELOADING=False, LAST_RELOAD=0, TO_FLASH=[], @@ -1188,21 +1186,6 @@ def plugins(): return redirect(url_for("loading", next=url_for("plugins"), message="Reloading plugins")) - plugin_args = app.config["PLUGIN_ARGS"] - app.config["PLUGIN_ARGS"] = {} - - if request.args.get("plugin_id", False): - plugin_id = request.args.get("plugin_id") - page = db.get_plugin_template(plugin_id) - - if page: - return render_template( - Environment(loader=FileSystemLoader(join(sep, "usr", "share", "bunkerweb", "ui", "templates") + "/")).from_string(page.decode("utf-8")), - dark_mode=app.config["DARK_MODE"], - username=current_user.get_id(), - **(app.jinja_env.globals | (plugin_args["args"] if plugin_args.get("plugin", None) == plugin_id else {})), - ) - plugins = app.config["CONFIG"].get_plugins() plugins_internal = 0 plugins_external = 0 @@ -1218,7 +1201,6 @@ def plugins(): plugins=plugins, plugins_internal=plugins_internal, plugins_external=plugins_external, - plugins_errors=db.get_plugins_errors(), username=current_user.get_id(), dark_mode=app.config["DARK_MODE"], ) @@ -1276,22 +1258,44 @@ def upload_plugin(): @app.route("/plugins/", methods=["GET", "POST"]) @login_required -def custom_plugin(plugin): +def custom_plugin(plugin: str): + plugins = app.config["CONFIG"].get_plugins() + + curr_plugin = {} + for plug in plugins: + if plug["id"] == plugin: + curr_plugin = plug + break + + message = "" if not plugin_id_rx.match(plugin): - flash( - f"Invalid plugin id, {plugin} (must be between 1 and 64 characters, only letters, numbers, underscores and hyphens)", - "error", - ) - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + message = f'Invalid plugin id, "{plugin}" (must be between 1 and 64 characters, only letters, numbers, underscores and hyphens)' + app.logger.error(message) + if request.method == "GET": + return message, 400 + return {"message": f'Invalid plugin id, "{plugin}" (must be between 1 and 64 characters, only letters, numbers, underscores and hyphens)'}, 400 + + if request.method == "GET": + page = db.get_plugin_template(plugin) + + if page: + return render_template( + Environment(loader=FileSystemLoader(join(sep, "usr", "share", "bunkerweb", "ui", "templates") + "/")).from_string(page.decode("utf-8")), + dark_mode=app.config["DARK_MODE"], + username=current_user.get_id(), + current_endpoint=plugin, + plugin=curr_plugin, + **app.jinja_env.globals, + ) + + message = f'The plugin "{plugin}" does not have a template' + app.logger.error(message) + return message, 404 module = db.get_plugin_actions(plugin) if module is None: - flash( - f"The actions.py file for the plugin {plugin} does not exist", - "error", - ) - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + return {"message": f'The actions.py file for the plugin "{plugin}" does not exist'}, 404 try: # Try to import the custom plugin @@ -1302,32 +1306,28 @@ def custom_plugin(plugin): loader = SourceFileLoader("actions", temp.name) actions = loader.load_module() except: - flash( - f"An error occurred while importing the plugin {plugin}:
{format_exc()}", - "error", - ) - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + message = f'An error occurred while importing the plugin "{plugin}", see logs for more details' + app.logger.exception(message) + return {"message": message}, 500 - error = False + error = None res = None try: # Try to get the custom plugin custom function and call it method = getattr(actions, plugin) - res = method() + if request.args: + res = method(app=app, args=request.args.to_dict()) + elif request.is_json: + res = method(app=app, args=request.json) + else: + res = method(app=app) except AttributeError: - flash( - f"The plugin {plugin} does not have a {plugin} method", - "error", - ) - error = True - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + message = f'The plugin "{plugin}" does not have a "{plugin}" method, see logs for more details' + error = 404 except: - flash( - f"An error occurred while executing the plugin {plugin}:
{format_exc()}", - "error", - ) - error = True + message = f'An error occurred while executing the plugin "{plugin}", see logs for more details' + error = 500 finally: if sbin_nginx_path.is_file(): # Remove the custom plugin from the shared library @@ -1335,13 +1335,15 @@ def custom_plugin(plugin): sys_modules.pop("actions") del actions - if request.method != "POST" or error is True or res is None or isinstance(res, dict) is False: - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + if message or not isinstance(res, dict) and not res: + message = message or f'The plugin "{plugin}" did not return a valid response' + if error: + app.logger.exception(message) + else: + app.logger.error(message) - app.config["PLUGIN_ARGS"] = {"plugin": plugin, "args": res} - - flash(f"Your action {plugin} has been executed") - return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin))) + app.logger.info(f"Plugin {plugin} action executed successfully") + return jsonify({"message": "ok", "data": res}), 200 @app.route("/cache", methods=["GET"]) @@ -1580,9 +1582,11 @@ def logs_container(container_id): logs.append( { "content": log, - "type": "error" - if "[error]" in log_lower or "[crit]" in log_lower or "[alert]" in log_lower or "❌" in log_lower - else ("warn" if "[warn]" in log_lower or "⚠️" in log_lower else ("info" if "[info]" in log_lower or "ℹ️" in log_lower else "message")), + "type": ( + "error" + if "[error]" in log_lower or "[crit]" in log_lower or "[alert]" in log_lower or "❌" in log_lower + else ("warn" if "[warn]" in log_lower or "⚠️" in log_lower else ("info" if "[info]" in log_lower or "ℹ️" in log_lower else "message")) + ), } ) diff --git a/src/ui/src/Instances.py b/src/ui/src/Instances.py index f1d062e9a..de4cc70c9 100644 --- a/src/ui/src/Instances.py +++ b/src/ui/src/Instances.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 - from os import sep from os.path import join from pathlib import Path @@ -118,6 +117,9 @@ class Instance: def reports(self) -> Tuple[bool, dict[str, Any]]: return self.apiCaller.send_to_apis("GET", "/metrics/requests", response=True) + def metrics(self, plugin_id) -> Tuple[bool, dict[str, Any]]: + return self.apiCaller.send_to_apis("GET", f"/metrics/{plugin_id}", response=True) + class Instances: def __init__(self, docker_client, kubernetes_client, integration: str): @@ -352,15 +354,72 @@ class Instances: resp, instance_reports = instance.reports() if not resp: return [] - return instance_reports[instance.name if instance.name != "local" else "127.0.0.1"].get("msg", []) + return instance_reports[instance.name if instance.name != "local" else "127.0.0.1"].get("msg", {"requests": []})["requests"] reports: List[dict[str, Any]] = [] for instance in self.get_instances(): - resp, instance_reports = instance.reports() + try: + resp, instance_reports = instance.reports() + except: + continue + if not resp: continue - reports.extend(instance_reports[instance.name if instance.name != "local" else "127.0.0.1"].get("msg", [])) + reports.extend(instance_reports[instance.name if instance.name != "local" else "127.0.0.1"].get("msg", {"requests": []})["requests"]) reports.sort(key=lambda x: x["date"], reverse=True) return reports + + def get_metrics(self, plugin_id: str): + # Get metrics from all instances + metrics = {} + for instance in self.get_instances(): + try: + resp, instance_metrics = instance.metrics(plugin_id) + except: + continue + + # filters + if not resp: + continue + + if instance.name not in instance_metrics or instance_metrics[instance.name]["msg"] is None or instance_metrics[instance.name]["msg"] is not dict or instance_metrics[instance.name]["status"] != "success": + continue + + metric_data = instance_metrics[instance.name]["msg"] + + # Update metrics looking for value type + for key, value in metric_data.items(): + if key not in metrics: + metrics[key] = value + continue + + # Case value is number, add it to the existing value + if isinstance(value, (int, float)): + metrics[key] += value + continue + # Case value is string, replace the existing value + elif isinstance(value, str): + metrics[key] = value + continue + # Case value is list, extend it to the existing value + if isinstance(value, list): + metrics[key].extend(value) + continue + # Case value is a dict, loop on it and update the existing value + if isinstance(value, dict): + for k, v in value.items(): + if k not in metrics[key]: + metrics[key][k] = v + continue + if isinstance(v, (int, float)): + metrics[key][k] += v + continue + if isinstance(v, list): + metrics[key][k].extend(v) + continue + if isinstance(v, str): + metrics[key][k] = v + continue + return metrics diff --git a/src/ui/static/css/dashboard.css b/src/ui/static/css/dashboard.css index ca56c62f0..de36b8a95 100644 --- a/src/ui/static/css/dashboard.css +++ b/src/ui/static/css/dashboard.css @@ -1 +1 @@ -/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e9ecef}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Open Sans;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;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-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-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}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#ced4da}input::placeholder,textarea::placeholder{opacity:1;color:#ced4da}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,: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:#5e72e480;--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: }.\!container{width:100%!important;margin-right:auto!important;margin-left:auto!important;padding-right:1.5rem!important;padding-left:1.5rem!important}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:1.5rem;padding-left:1.5rem}@media (min-width:340px){.\!container{max-width:340px!important}.container{max-width:340px}}@media (min-width:576px){.\!container{max-width:576px!important}.container{max-width:576px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:992px){.\!container{max-width:992px!important}.container{max-width:992px}}@media (min-width:1200px){.\!container{max-width:1200px!important}.container{max-width:1200px}}@media (min-width:1320px){.\!container{max-width:1320px!important}.container{max-width:1320px}}@media (min-width:1920px){.\!container{max-width:1920px!important}.container{max-width:1920px}}a{letter-spacing:-.025rem}hr{margin:1rem 0;border:0;opacity:.25}img{max-width:none}label{display:inline-block}p{line-height:1.625;font-weight:400;margin-bottom:1rem}small{font-size:.875em}svg{display:inline}table{border-collapse:inherit}h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;color:#344767}h1,h2,h3,h4{letter-spacing:-.05rem}h1,h2,h3{font-weight:700}h4,h5,h6{font-weight:600}h1{font-size:3rem;line-height:1.25}h2{font-size:2.25rem;line-height:1.3}h3{font-size:1.875rem}h3,h4{line-height:1.375}h4{font-size:1.5rem}h5{font-size:1.25rem;line-height:1.375}h6{font-size:1rem;line-height:1.625}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-y-0{top:0;bottom:0}.-left-full{left:-100%}.-right-0{right:0}.-right-1{right:-.25rem}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-1\.5{bottom:.375rem}.bottom-2{bottom:.5rem}.bottom-24{bottom:6rem}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.bottom-7{bottom:1.75rem}.bottom-8{bottom:2rem}.left-0{left:0}.left-1{left:.25rem}.left-16{left:4rem}.left-20{left:5rem}.left-24{left:6rem}.left-32{left:8rem}.left-4{left:1rem}.left-40{left:10rem}.left-48{left:12rem}.left-8{left:2rem}.left-auto{left:auto}.left-full{left:100%}.right-0{right:0}.right-2{right:.5rem}.right-20{right:5rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.right-7{right:1.75rem}.right-8{right:2rem}.right-\[3\.25rem\]{right:3.25rem}.top-0{top:0}.top-1{top:.25rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-10{top:2.5rem}.top-12{top:3rem}.top-14{top:3.5rem}.top-16{top:4rem}.top-2{top:.5rem}.top-24{top:6rem}.top-3{top:.75rem}.top-36{top:9rem}.top-4{top:1rem}.top-6{top:1.5rem}.top-7{top:1.75rem}.top-8{top:2rem}.top-\[38\%\]{top:38%}.top-\[4\.5rem\]{top:4.5rem}.top-\[52\%\]{top:52%}.top-\[55\%\]{top:55%}.top-\[8\.2rem\]{top:8.2rem}.right-12{right:3rem}.-z-10{z-index:-10}.z-0{z-index:0}.z-10{z-index:10}.z-100{z-index:100}.z-110{z-index:110}.z-20{z-index:20}.z-990{z-index:990}.z-\[10000\]{z-index:10000}.z-\[1000\]{z-index:1000}.z-\[1001\]{z-index:1001}.z-\[20\]{z-index:20}.z-sticky{z-index:1020}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-11{grid-column:span 11/span 11}.col-span-12{grid-column:span 12/span 12}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.col-span-9{grid-column:span 9/span 9}.float-right{float:right}.float-left{float:left}.m-0{margin:0}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mx-0{margin-left:0;margin-right:0}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-1\.5{margin-left:.375rem;margin-right:.375rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-2\.5{margin-left:.625rem;margin-right:.625rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.ml-0{margin-left:0}.ml-1{margin-left:.25rem}.ml-12{margin-left:3rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-12{margin-right:3rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-\[15vh\]{margin-top:15vh}.mt-\[4\.5rem\]{margin-top:4.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.list-item{display:list-item}.hidden{display:none}.h-1{height:.25rem}.h-1\/3{height:33.333333%}.h-12{height:3rem}.h-14{height:3.5rem}.h-19{height:4.75rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-4\.5{height:1.125rem}.h-40{height:10rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-5\.5{height:1.375rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-\[2\.5rem\]{height:2.5rem}.h-\[250px\]{height:250px}.h-\[3\.5rem\]{height:3.5rem}.h-\[4rem\]{height:4rem}.h-\[90vh\]{height:90vh}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-sidenav{height:calc(100vh - 450px)}.max-h-100{max-height:25rem}.max-h-135{max-height:33.75rem}.max-h-30{max-height:7.5rem}.max-h-80{max-height:20rem}.max-h-90{max-height:22.5rem}.max-h-\[250px\]{max-height:250px}.max-h-\[400px\]{max-height:400px}.max-h-\[70vh\]{max-height:70vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-12{min-height:3rem}.min-h-20{min-height:5rem}.min-h-52{min-height:13rem}.min-h-6{min-height:1.5rem}.min-h-\[100px\]{min-height:100px}.min-h-\[350px\]{min-height:350px}.min-h-\[55vh\]{min-height:55vh}.min-h-\[75px\]{min-height:75px}.min-h-\[85vh\]{min-height:85vh}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-10{width:2.5rem}.w-11\/12{width:91.666667%}.w-12{width:3rem}.w-28{width:7rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-4\.5{width:1.125rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\.5{width:1.375rem}.w-50{width:12.5rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-90{width:22.5rem}.w-\[2\.5rem\]{width:2.5rem}.w-\[50vw\]{width:50vw}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0}.min-w-\[1150px\]{min-width:1150px}.min-w-\[1200px\]{min-width:1200px}.min-w-\[250px\]{min-width:250px}.min-w-\[350px\]{min-width:350px}.min-w-\[400px\]{min-width:400px}.min-w-\[500px\]{min-width:500px}.min-w-\[600px\]{min-width:600px}.min-w-\[800px\]{min-width:800px}.min-w-\[900px\]{min-width:900px}.max-w-180{max-width:45rem}.max-w-40{max-width:10rem}.max-w-60{max-width:15rem}.max-w-64{max-width:16rem}.max-w-\[1920px\]{max-width:1920px}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-\[450px\]{max-width:450px}.max-w-\[550px\]{max-width:550px}.max-w-\[650px\]{max-width:650px}.max-w-\[700px\]{max-width:700px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-auto{flex:1 1 auto}.grow{flex-grow:1}.basis-full{flex-basis:100%}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x:-0.25rem}.-translate-x-1,.-translate-x-1\.5{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))}.-translate-x-1\.5{--tw-translate-x:-0.375rem}.-translate-x-36{--tw-translate-x:-9rem}.-translate-x-36,.-translate-x-40{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))}.-translate-x-40{--tw-translate-x:-10rem}.-translate-x-48{--tw-translate-x:-12rem}.-translate-x-48,.-translate-x-52{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))}.-translate-x-52{--tw-translate-x:-13rem}.-translate-x-56{--tw-translate-x:-14rem}.-translate-x-56,.-translate-x-60{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))}.-translate-x-60{--tw-translate-x:-15rem}.-translate-x-64{--tw-translate-x:-16rem}.-translate-x-64,.-translate-x-72{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))}.-translate-x-72{--tw-translate-x:-18rem}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.-translate-y-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))}.-translate-y-0{--tw-translate-y:-0px}.-translate-y-0\.4{--tw-translate-y:-0.1rem}.-translate-y-0\.4,.-translate-y-0\.5{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))}.-translate-y-0\.5{--tw-translate-y:-0.125rem}.-translate-y-1{--tw-translate-y:-0.25rem}.-translate-y-1,.-translate-y-1\.5{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))}.-translate-y-1\.5{--tw-translate-y:-0.375rem}.-translate-y-12{--tw-translate-y:-3rem}.-translate-y-12,.-translate-y-16{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))}.-translate-y-16{--tw-translate-y:-4rem}.-translate-y-2{--tw-translate-y:-0.5rem}.-translate-y-2,.-translate-y-20{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))}.-translate-y-20{--tw-translate-y:-5rem}.-translate-y-24{--tw-translate-y:-6rem}.-translate-y-24,.-translate-y-28{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))}.-translate-y-28{--tw-translate-y:-7rem}.-translate-y-36{--tw-translate-y:-9rem}.-translate-y-36,.-translate-y-4{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))}.-translate-y-4{--tw-translate-y:-1rem}.-translate-y-6{--tw-translate-y:-1.5rem}.-translate-y-6,.-translate-y-8{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))}.-translate-y-8{--tw-translate-y:-2rem}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-0\.5{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))}.translate-x-0\.5{--tw-translate-x:0.125rem}.translate-x-1{--tw-translate-x:0.25rem}.translate-x-1,.translate-x-16{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))}.translate-x-16{--tw-translate-x:4rem}.translate-x-2{--tw-translate-x:0.5rem}.translate-x-2,.translate-x-24{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))}.translate-x-24{--tw-translate-x:6rem}.translate-x-3{--tw-translate-x:0.75rem}.translate-x-3,.translate-x-32{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))}.translate-x-32{--tw-translate-x:8rem}.translate-x-4{--tw-translate-x:1rem}.translate-x-4,.translate-x-40{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))}.translate-x-40{--tw-translate-x:10rem}.translate-x-48{--tw-translate-x:12rem}.translate-x-48,.translate-x-52{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))}.translate-x-52{--tw-translate-x:13rem}.translate-x-56{--tw-translate-x:14rem}.translate-x-56,.translate-x-60{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))}.translate-x-60{--tw-translate-x:15rem}.translate-x-90{--tw-translate-x:22.5rem}.translate-x-90,.translate-x-\[3rem\]{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))}.translate-x-\[3rem\]{--tw-translate-x:3rem}.translate-y-0{--tw-translate-y:0px}.translate-y-0,.translate-y-0\.5{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))}.translate-y-0\.5{--tw-translate-y:0.125rem}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-1,.translate-y-16{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))}.translate-y-16{--tw-translate-y:4rem}.translate-y-2{--tw-translate-y:0.5rem}.-rotate-12,.translate-y-2{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))}.-rotate-12{--tw-rotate:-12deg}.rotate-12{--tw-rotate:12deg}.rotate-12,.rotate-180{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))}.rotate-180{--tw-rotate:180deg}.rotate-90{--tw-rotate:90deg}.rotate-90,.scale-105{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))}.scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.scale-110,.scale-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))}.scale-50{--tw-scale-x:.5;--tw-scale-y:.5}.scale-75{--tw-scale-x:.75;--tw-scale-y:.75}.scale-75,.scale-90{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))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-\[0\.55\]{--tw-scale-x:0.55;--tw-scale-y:0.55}.scale-\[0\.55\],.scale-\[0\.6\]{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))}.scale-\[0\.6\]{--tw-scale-x:0.6;--tw-scale-y:0.6}.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-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.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}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-2{row-gap:.5rem}.gap-y-4{row-gap:1rem}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded,.rounded-1{border-radius:.25rem}.rounded-1\.4{border-radius:.35rem}.rounded-10{border-radius:2.5rem}.rounded-2xl{border-radius:1rem}.rounded-circle{border-radius:50%}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-none{border-radius:0}.rounded-xl{border-radius:.75rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-lg{border-bottom-left-radius:.5rem}.rounded-b-lg,.rounded-r-lg{border-bottom-right-radius:.5rem}.rounded-r-lg{border-top-right-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-gray-100{--tw-border-opacity:1;border-color:rgb(235 239 244/var(--tw-border-opacity))}.border-gray-100\/50{border-color:#ebeff480}.border-gray-200{--tw-border-opacity:1;border-color:rgb(233 236 239/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity))}.border-primary{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgb(73 80 87/var(--tw-border-opacity))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(17 113 239/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(45 206 137/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(233 236 239/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(210 214 218/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(248 249 250/var(--tw-bg-opacity))}.bg-gray-600\/50{background-color:#6c757d80}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.bg-lime-600{--tw-bg-opacity:1;background-color:rgb(101 163 13/var(--tw-bg-opacity))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(251 99 64/var(--tw-bg-opacity))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(245 96 54/var(--tw-bg-opacity))}.bg-primary{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.bg-primary\/20{background-color:#0b557733}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(245 57 57/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(245 54 92/var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity))}.bg-secondary{--tw-bg-opacity:1;background-color:rgb(46 172 104/var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.bg-sky-700{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.bg-slate-800\/10{background-color:#3a416f1a}.bg-transparent{background-color:initial}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(251 177 64/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-tl{background-image:linear-gradient(to top left,var(--tw-gradient-stops))}.bg-none{background-image:none}.from-\[\#075577\]{--tw-gradient-from:#075577 var(--tw-gradient-from-position);--tw-gradient-to:#07557700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-\[\#0b5577\]{--tw-gradient-from:#0b5577 var(--tw-gradient-from-position);--tw-gradient-to:#0b557700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-black\/40{--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#0006 var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-\[\#116D70\]{--tw-gradient-to:#116d70 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.bg-150{background-size:150%}.bg-contain{background-size:contain}.bg-clip-border{background-clip:initial}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.bg-left{background-position:0}.bg-x-25{background-position:25% 0}.bg-no-repeat{background-repeat:no-repeat}.fill-amber-500{fill:#f59e0b}.fill-blue-500{fill:#5e72e4}.fill-gray-500{fill:#adb5bd}.fill-gray-600{fill:#6c757d}.fill-gray-700{fill:#495057}.fill-green-500{fill:#22c55e}.fill-primary{fill:#0b5577}.fill-red-500{fill:#f53939}.fill-red-700{fill:#b91c1c}.fill-sky-500{fill:#0ea5e9}.fill-slate-800{fill:#3a416f}.fill-white{fill:#fff}.fill-yellow-500{fill:#fbb140}.stroke-amber-500{stroke:#f59e0b}.stroke-blue-400{stroke:#60a5fa}.stroke-blue-500{stroke:#5e72e4}.stroke-emerald-600{stroke:#059669}.stroke-gray-100{stroke:#ebeff4}.stroke-gray-100\/50{stroke:#ebeff480}.stroke-gray-600{stroke:#6c757d}.stroke-gray-700{stroke:#495057}.stroke-green-500{stroke:#22c55e}.stroke-green-700{stroke:#15803d}.stroke-orange-500{stroke:#fb6340}.stroke-pink-600{stroke:#db2777}.stroke-red-500{stroke:#f53939}.stroke-sky-500{stroke:#0ea5e9}.stroke-stone-500{stroke:#78716c}.stroke-white{stroke:#fff}.stroke-yellow-500{stroke:#fbb140}.stroke-0{stroke-width:0}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-0{padding-left:0;padding-right:0}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-28{padding-left:7rem;padding-right:7rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.pb-0{padding-bottom:0}.pb-10{padding-bottom:2.5rem}.pb-16{padding-bottom:4rem}.pb-2{padding-bottom:.5rem}.pb-24{padding-bottom:6rem}.pb-28{padding-bottom:7rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-0{padding-left:0}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-0{padding-right:0}.pr-8{padding-right:2rem}.pt-1{padding-top:.25rem}.pt-10{padding-top:2.5rem}.pt-2{padding-top:.5rem}.pt-20{padding-top:5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.pt-9{padding-top:2.25rem}.text-left{text-align:left}.text-center{text-align:center}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-sans{font-family:Open Sans}.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-6xl{font-size:3.75rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-9xl{font-size:6rem;line-height:1}.text-\[0\.7rem\]{font-size:.7rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-5\.6{line-height:1.4rem}.leading-6{line-height:1.5rem}.leading-8{line-height:2rem}.leading-default{line-height:1.6}.leading-none{line-height:1}.leading-normal{line-height:1.5}.leading-tight{line-height:1.25}.tracking-\[0\.20rem\]{letter-spacing:.2rem}.tracking-normal{letter-spacing:0}.tracking-tight-rem{letter-spacing:-.025rem}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-blue-500{--tw-text-opacity:1;color:rgb(94 114 228/var(--tw-text-opacity))}.text-emerald-500{--tw-text-opacity:1;color:rgb(45 206 137/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity))}.text-gray-100\/50{color:#ebeff480}.text-gray-300{--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(248 249 250/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(108 117 125/var(--tw-text-opacity))}.text-gray-600\/90{color:#6c757de6}.text-gray-700{--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.text-gray-700\/80{color:#495057cc}.text-gray-800{--tw-text-opacity:1;color:rgb(37 47 64/var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}.text-primary{--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity))}.text-secondary{--tw-text-opacity:1;color:rgb(46 172 104/var(--tw-text-opacity))}.text-sky-500{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity:1;color:rgb(62 161 236/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(103 116 142/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(52 71 103/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity:1;color:rgb(251 177 64/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-3xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-3xl{--tw-shadow:0 8px 26px -4px #14141426,0 8px 9px -5px #1414140f;--tw-shadow-colored:0 8px 26px -4px var(--tw-shadow-color),0 8px 9px -5px var(--tw-shadow-color)}.shadow-\[8px_8px_12px_rgb\(0\2c 0\2c 0\2c 0\.2\)\]{--tw-shadow:8px 8px 12px #0003;--tw-shadow-colored:8px 8px 12px var(--tw-shadow-color)}.shadow-\[8px_8px_12px_rgb\(0\2c 0\2c 0\2c 0\.2\)\],.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 #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.shadow-none,.shadow-sm{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 .25rem .375rem -.0625rem #1414141f,0 .125rem .25rem -.0625rem #14141412;--tw-shadow-colored:0 .25rem .375rem -.0625rem var(--tw-shadow-color),0 .125rem .25rem -.0625rem var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 0 2rem 0 #8898aa26;--tw-shadow-colored:0 0 2rem 0 var(--tw-shadow-color)}.shadow-xl,.shadow-xs{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.outline-secondary{outline-color:#2eac68}.blur{--tw-blur:blur(8px)}.blur,.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:ease;transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:ease;transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:ease;transition-duration:.15s}.delay-200{transition-delay:.2s}.duration-200{transition-duration:.2s}.duration-250{transition-duration:.25s}.duration-300{transition-duration:.3s}.duration-700{transition-duration:.7s}.ease-in{transition-timing-function:ease-in}.ease-in-out{transition-timing-function:ease-in-out}.flex-wrap-inherit{flex-wrap:inherit}@font-face{font-family:Open Sans;src:url(../webfonts/OpenSans.ttf)}*{font-family:Open Sans,sans-serif!important}.ace_content,.ace_editor,.ace_editor div{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,source-code-pro,monospace!important;font-size:16px!important;font-weight:400!important}.sr-only{display:none}.close-btn{display:inline-block;cursor:pointer;border-radius:.5rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.close-btn,.close-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.close-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.close-btn:focus,.close-btn:hover{background-color:#fffc}.close-btn:active{opacity:.85}.close-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.close-btn:disabled,.close-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.close-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .close-btn){--tw-bg-opacity:1;background-color:rgb(233 236 239/var(--tw-bg-opacity));--tw-brightness:brightness(.9)}:is(.dark .close-btn),:is(.dark .close-btn:hover){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)}:is(.dark .close-btn:hover){--tw-brightness:brightness(.75)}:is(.dark .close-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .close-btn:disabled),:is(.dark .close-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .close-btn:hover:disabled){--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));--tw-bg-opacity:1}.valid-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.valid-btn,.valid-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.valid-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.valid-btn:focus,.valid-btn:hover{background-color:#22c55ecc}.valid-btn:active{opacity:.85}.valid-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.valid-btn:disabled,.valid-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.valid-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .valid-btn){--tw-brightness:brightness(.9);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)}:is(.dark .valid-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .valid-btn:disabled),:is(.dark .valid-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .valid-btn:hover:disabled){--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));--tw-bg-opacity:1}.delete-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(245 57 57/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.delete-btn,.delete-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.delete-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.delete-btn:focus,.delete-btn:hover{background-color:#f53939cc}.delete-btn:active{opacity:.85}.delete-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.delete-btn:disabled,.delete-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.delete-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .delete-btn){--tw-brightness:brightness(.9);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)}:is(.dark .delete-btn:disabled){border-color:#49505700;--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .delete-btn:hover:disabled){--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));border-color:#49505700;--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}.edit-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(251 177 64/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.edit-btn,.edit-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.edit-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.edit-btn:focus,.edit-btn:hover{background-color:#fbb140cc}.edit-btn:active{opacity:.85}.edit-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.edit-btn:disabled,.edit-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.edit-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .edit-btn){--tw-brightness:brightness(.9);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)}:is(.dark .edit-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .edit-btn:disabled),:is(.dark .edit-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .edit-btn:hover:disabled){--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));--tw-bg-opacity:1}.info-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.info-btn,.info-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.info-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.info-btn:focus,.info-btn:hover{background-color:#0ea5e9cc}.info-btn:active{opacity:.85}.info-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.info-btn:disabled,.info-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.info-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .info-btn){--tw-brightness:brightness(.9);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)}:is(.dark .info-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .info-btn:disabled),:is(.dark .info-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .info-btn:hover:disabled){--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));--tw-bg-opacity:1}.checkbox{position:relative;z-index:10;float:left;margin-top:.25rem;height:1.25rem;width:1.25rem;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.35rem;border-width:1px;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(255 255 255/var(--tw-bg-opacity));background-size:contain;background-position:50%;background-repeat:no-repeat;vertical-align:top;font-size:1rem;line-height:1.5rem;transition-property:all;transition-timing-function:ease;transition-duration:.25s}.checkbox,.checkbox:disabled{--tw-border-opacity:1;--tw-bg-opacity:1}.checkbox:disabled{cursor:default;border-color:rgb(206 212 218/var(--tw-border-opacity));background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.checkbox[data-checked=true]{z-index:0;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.checkbox:disabled[data-checked=true]{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}:is(.dark .checkbox){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .checkbox:disabled){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .checkbox[data-checked=true]){--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .checkbox:disabled[data-checked=true]){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.custom-select-btn{display:flex;width:100%;align-items:center;justify-content:space-between;border-radius:.5rem;border-width:1px;border-style:solid;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));background-clip:padding-box;padding:.25rem .375rem;text-align:left;vertical-align:middle;font-size:.875rem;font-weight:400;line-height:1.4rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.3s;transition-timing-function:ease-in-out}.custom-select-btn::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.custom-select-btn::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.custom-select-btn:focus{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.custom-select-btn:disabled{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));opacity:.75}:is(.dark .custom-select-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity));opacity:.9}:is(.dark .custom-select-btn),:is(.dark .custom-select-btn:disabled){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .custom-select-btn:disabled){border-color:rgb(37 47 64/var(--tw-border-opacity));background-color:rgb(37 47 64/var(--tw-bg-opacity))}@media (min-width:768px){.custom-select-btn{padding:.5rem .75rem}}.custom-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;min-height:38px;cursor:pointer;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.custom-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .custom-dropdown-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity))}.active.custom-dropdown-btn,:is(.dark .custom-dropdown-btn){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.active.custom-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;min-height:38px;cursor:pointer;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(11 85 119/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.custom-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .active.custom-dropdown-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(11 85 119/var(--tw-bg-opacity));color:rgb(210 214 218/var(--tw-text-opacity))}.regular-input,:is(.dark .active.custom-dropdown-btn){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.regular-input{display:block;width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.5rem;border-width:1px;border-style:solid;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(255 255 255/var(--tw-bg-opacity));background-clip:padding-box;padding:.25rem .375rem;font-size:.875rem;font-weight:400;line-height:1.4rem;color:rgb(73 80 87/var(--tw-text-opacity));outline:2px solid #0000;outline-offset:2px;transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.regular-input::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.regular-input::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.regular-input:focus{border-color:#d2d6da00;--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,0 0 #0000)}.regular-input:valid:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity))}.regular-input:invalid:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))}.regular-input:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));opacity:.75}:is(.dark .regular-input){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity));opacity:.9}:is(.dark .regular-input),:is(.dark .regular-input:disabled){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .regular-input:disabled){border-color:rgb(37 47 64/var(--tw-border-opacity));background-color:rgb(37 47 64/var(--tw-bg-opacity))}@media (min-width:768px){.regular-input{padding:.5rem .75rem}}.input-title{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .input-title){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.popover-settings-container{position:absolute;left:0;bottom:0;z-index:50;--tw-translate-y:-1.75rem;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:.375rem;--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity));padding:.75rem;transition-property:all;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:ease;transition-duration:.5s}:is(.dark .popover-settings-container){--tw-brightness:brightness(.9);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)}.popover-settings-text{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .popover-settings-text){--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity));opacity:.9}.popover-settings-svg{margin-left:.5rem;height:1.25rem;width:1.25rem;cursor:pointer;fill:#5e72e4}.popover-settings-svg:hover{--tw-brightness:brightness(.75);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)}.active.settings-tabs-tab-btn{position:relative;z-index:10;margin-top:.25rem;margin-bottom:.25rem;cursor:pointer;border-radius:0;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);--tw-brightness:brightness(.9);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-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.settings-tabs-tab-btn:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .active.settings-tabs-tab-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .active.settings-tabs-tab-btn:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}.settings-tabs-tab-btn{position:relative;margin-top:.25rem;margin-bottom:.25rem;cursor:pointer;border-radius:0;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-tab-btn:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .settings-tabs-tab-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .settings-tabs-tab-btn:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}.settings-tabs-name{padding-left:.75rem;padding-right:.5rem;--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .settings-tabs-name){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.settings-tabs-popover-container{position:absolute;top:60px;left:0;z-index:50;min-width:150px;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity));padding:.75rem;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:ease;transition-duration:.15s}:is(.dark .settings-tabs-popover-container){--tw-brightness:brightness(.9);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)}.settings-tabs-popover-text{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.settings-tabs-mobile-btn{margin-top:.25rem;margin-bottom:.25rem;display:flex;width:100%;cursor:pointer;align-items:center;justify-content:space-between;border-radius:.5rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-mobile-btn:hover{--tw-translate-y:-1px;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:rgb(248 249 250/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .settings-tabs-mobile-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .settings-tabs-mobile-btn:hover){--tw-brightness:brightness(.95);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)}.settings-tabs-mobile-btn-text{--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .settings-tabs-mobile-btn-text){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.active.settings-tabs-mobile-dropdown-btn{position:relative;z-index:1000;margin-top:0;margin-bottom:0;cursor:pointer;border-radius:0;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.settings-tabs-mobile-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .active.settings-tabs-mobile-dropdown-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .active.settings-tabs-mobile-dropdown-btn:hover){--tw-brightness:brightness(.9);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)}.first.settings-tabs-mobile-dropdown-btn{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-width:1px}.settings-tabs-mobile-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;display:flex;cursor:pointer;justify-content:space-between;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-mobile-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .settings-tabs-mobile-dropdown-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .settings-tabs-mobile-dropdown-btn:hover){--tw-brightness:brightness(.9);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)}.first-letter\:absolute:first-letter{position:absolute}.first-letter\:w-full:first-letter{width:100%}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.before\:float-left:before{content:var(--tw-content);float:left}.before\:pr-2:before{content:var(--tw-content);padding-right:.5rem}.before\:text-white:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:top-px:after{content:var(--tw-content);top:1px}.after\:float-right:after{content:var(--tw-content);float:right}.after\:h-4:after{content:var(--tw-content);height:1rem}.after\:w-4:after{content:var(--tw-content);width:1rem}.after\:translate-x-px:after{content:var(--tw-content);--tw-translate-x:1px;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))}.after\:rounded-circle:after{content:var(--tw-content);border-radius:50%}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.after\:pl-2:after{content:var(--tw-content);padding-left:.5rem}.after\:text-gray-600:after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(108 117 125/var(--tw-text-opacity))}.after\:shadow-2xl:after{content:var(--tw-content);--tw-shadow:0 .3125rem .625rem 0 #0000001f;--tw-shadow-colored:0 .3125rem .625rem 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)}.after\:duration-300:after{content:var(--tw-content);transition-duration:.3s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.after\:content-\[\'\/\'\]:after{--tw-content:"/";content:var(--tw-content)}.checked\:z-0:checked{z-index:0}.checked\:border-primary:checked{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.checked\:bg-primary:checked{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.checked\:bg-none:checked{background-image:none}.checked\:bg-right:checked{background-position:100%}.checked\:after\:translate-x-5:checked:after{--tw-translate-x:1.25rem}.checked\:after\:translate-x-5:checked:after,.checked\:after\:translate-x-5\.3:checked:after{content:var(--tw-content);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))}.checked\:after\:translate-x-5\.3:checked:after{--tw-translate-x:1.3rem}.valid\:\!border-red-500:valid{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.hover\:-translate-y-0:hover{--tw-translate-y:-0px}.hover\:-translate-y-0:hover,.hover\:-translate-y-0\.4:hover{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\:-translate-y-0\.4:hover{--tw-translate-y:-0.1rem}.hover\:-translate-y-0\.5:hover{--tw-translate-y:-0.125rem}.hover\:-translate-y-0\.5:hover,.hover\:-translate-y-px:hover{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\:-translate-y-px:hover{--tw-translate-y:-1px}.hover\:scale-102:hover{--tw-scale-x:1.02;--tw-scale-y:1.02;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\:rounded-lg:hover{border-radius:.5rem}.hover\:bg-emerald-500\/80:hover{background-color:#2dce89cc}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity))}.hover\:bg-gray-100\/10:hover{background-color:#ebeff41a}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(210 214 218/var(--tw-bg-opacity))}.hover\:bg-green-500\/80:hover{background-color:#22c55ecc}.hover\:bg-primary\/30:hover{background-color:#0b55774d}.hover\:bg-primary\/5:hover{background-color:#0b55770d}.hover\:bg-primary\/80:hover{background-color:#0b5577cc}.hover\:bg-red-500\/80:hover{background-color:#f53939cc}.hover\:bg-sky-500\/80:hover{background-color:#0ea5e9cc}.hover\:bg-yellow-500\/80:hover{background-color:#fbb140cc}.hover\:italic:hover{font-style:italic}.hover\:no-underline:hover{text-decoration-line:none}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px 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\:brightness-75:hover{--tw-brightness:brightness(.75)}.hover\:brightness-75:hover,.hover\:brightness-90:hover{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)}.hover\:brightness-90:hover{--tw-brightness:brightness(.9)}.hover\:brightness-95:hover{--tw-brightness:brightness(.95);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)}.focus\:\!border-red-500:focus{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.focus\:border-green-500:focus{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.focus\:border-primary:focus{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.focus\:bg-emerald-500\/80:focus{background-color:#2dce89cc}.focus\:bg-green-500\/80:focus{background-color:#22c55ecc}.focus\:bg-primary\/80:focus{background-color:#0b5577cc}.focus\:bg-red-500\/80:focus{background-color:#f53939cc}.focus\:bg-sky-500\/80:focus{background-color:#0ea5e9cc}.focus\:bg-yellow-500\/80:focus{background-color:#fbb140cc}.focus\:\!ring-red-500:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))!important}.focus\:valid\:\!border-red-500:valid:focus{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.focus\:valid\:border-green-500:valid:focus{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.focus\:valid\:\!ring-red-500:valid:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))!important}.focus\:invalid\:border-red-500:invalid:focus{--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity))}.focus\:file\:invalid\:border-red-500:invalid::file-selector-button:focus{--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity))}.active\:\!border-red-500:active{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.active\:opacity-85:active{opacity:.85}.active\:valid\:\!border-red-500:valid:active{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:border-gray-400:disabled{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity))}.disabled\:border-gray-400\/0:disabled{border-color:#ced4da00}.disabled\:bg-gray-400:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.disabled\:text-gray-700:disabled{--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.disabled\:opacity-75:disabled{opacity:.75}.disabled\:hover\:translate-y-0:hover:disabled{--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))}.disabled\:hover\:border-gray-400\/0:hover:disabled{border-color:#ced4da00}.disabled\:hover\:bg-gray-400:hover:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.group:hover .group-hover\:z-10{z-index:10}.group:hover .group-hover\:block{display:block}.group:hover .group-hover\:opacity-100{opacity:1}:is(.dark .dark\:inline){display:inline}:is(.dark .dark\:hidden){display:none}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgb(73 80 87/var(--tw-border-opacity))}:is(.dark .dark\:border-slate-600){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity))}:is(.dark .dark\:border-slate-800){--tw-border-opacity:1;border-color:rgb(58 65 111/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-300){--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-500\/90){background-color:#22c55ee6}:is(.dark .dark\:bg-primary){--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary\/50){background-color:#0b557780}:is(.dark .dark\:bg-red-500\/90){background-color:#f53939e6}:is(.dark .dark\:bg-slate-700){--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-700\/50){background-color:#34476780}:is(.dark .dark\:bg-slate-850){--tw-bg-opacity:1;background-color:rgb(17 28 68/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-900){--tw-bg-opacity:1;background-color:rgb(5 17 57/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-900\/30){background-color:#0511394d}:is(.dark .dark\:bg-slate-800){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gradient-to-r){background-image:linear-gradient(to right,var(--tw-gradient-stops))}:is(.dark .dark\:from-transparent){--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}:is(.dark .dark\:via-white){--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fff var(--tw-gradient-via-position),var(--tw-gradient-to)}:is(.dark .dark\:to-transparent){--tw-gradient-to:#0000 var(--tw-gradient-to-position)}:is(.dark .dark\:fill-blue-500){fill:#5e72e4}:is(.dark .dark\:fill-gray-300){fill:#d2d6da}:is(.dark .dark\:fill-gray-500){fill:#adb5bd}:is(.dark .dark\:fill-gray-600){fill:#6c757d}:is(.dark .dark\:stroke-amber-500){stroke:#f59e0b}:is(.dark .dark\:stroke-gray-400){stroke:#ced4da}:is(.dark .dark\:stroke-gray-600){stroke:#6c757d}:is(.dark .dark\:stroke-red-500){stroke:#f53939}:is(.dark .dark\:text-gray-100){--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgb(233 236 239/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgb(206 212 218/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}:is(.dark .dark\:text-red-500){--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:text-white\/80){color:#fffc}:is(.dark .dark\:text-white\/90){color:#ffffffe6}:is(.dark .dark\:opacity-60){opacity:.6}:is(.dark .dark\:opacity-75){opacity:.75}:is(.dark .dark\:opacity-80){opacity:.8}:is(.dark .dark\:opacity-90){opacity:.9}:is(.dark .dark\:shadow-dark-xl){--tw-shadow:0 2px 2px 0 #00000024,0 3px 1px -2px #0003,0 1px 5px 0 #0000001f;--tw-shadow-colored:0 2px 2px 0 var(--tw-shadow-color),0 3px 1px -2px var(--tw-shadow-color),0 1px 5px 0 var(--tw-shadow-color)}:is(.dark .dark\:shadow-dark-xl),:is(.dark .dark\:shadow-none){box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .dark\:shadow-none){--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}:is(.dark .dark\:brightness-110){--tw-brightness:brightness(1.1)}:is(.dark .dark\:brightness-110),:is(.dark .dark\:brightness-125){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)}:is(.dark .dark\:brightness-125){--tw-brightness:brightness(1.25)}:is(.dark .dark\:brightness-150){--tw-brightness:brightness(1.5)}:is(.dark .dark\:brightness-150),:is(.dark .dark\:brightness-90){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)}:is(.dark .dark\:brightness-90){--tw-brightness:brightness(.9)}:is(.dark .dark\:brightness-95){--tw-brightness:brightness(.95)}:is(.dark .dark\:brightness-95),:is(.dark .dark\:brightness-\[0\.885\]){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)}:is(.dark .dark\:brightness-\[0\.885\]){--tw-brightness:brightness(0.885)}:is(.dark .dark\:after\:text-gray-500):after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}:is(.dark .dark\:checked\:border-primary:checked){--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}:is(.dark .dark\:checked\:bg-primary:checked){--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-primary\/20:hover){background-color:#0b557733}:is(.dark .dark\:hover\:bg-primary\/60:hover){background-color:#0b557799}:is(.dark .dark\:hover\:bg-slate-700\/50:hover){background-color:#34476780}:is(.dark .dark\:hover\:bg-slate-800:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:brightness-100:hover){--tw-brightness:brightness(1)}:is(.dark .dark\:hover\:brightness-100:hover),:is(.dark .dark\:hover\:brightness-105:hover){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)}:is(.dark .dark\:hover\:brightness-105:hover){--tw-brightness:brightness(1.05)}:is(.dark .dark\:hover\:brightness-90:hover){--tw-brightness:brightness(.9)}:is(.dark .dark\:hover\:brightness-110:hover),:is(.dark .dark\:hover\:brightness-90:hover){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)}:is(.dark .dark\:hover\:brightness-110:hover){--tw-brightness:brightness(1.1)}:is(.dark .dark\:disabled\:border-gray-700\/0:disabled){border-color:#49505700}:is(.dark .dark\:disabled\:border-gray-800:disabled){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity))}:is(.dark .dark\:disabled\:bg-gray-700:disabled){--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .dark\:disabled\:bg-gray-800:disabled){--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity))}:is(.dark .dark\:disabled\:text-gray-300:disabled){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .dark\:disabled\:hover\:translate-y-0:hover:disabled){--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))}:is(.dark .dark\:disabled\:hover\:border-gray-700\/0:hover:disabled){border-color:#49505700}:is(.dark .dark\:disabled\:hover\:bg-gray-700:hover:disabled){--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}@media (min-width:576px){.sm\:right-24{right:6rem}.sm\:right-40{right:10rem}.sm\:right-6{right:1.5rem}.sm\:top-2{top:.5rem}.sm\:top-8{top:2rem}.sm\:top-\[4\.5rem\]{top:4.5rem}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:col-start-5{grid-column-start:5}.sm\:mx-4{margin-left:1rem;margin-right:1rem}.sm\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.sm\:ml-1{margin-left:.25rem}.sm\:mr-16{margin-right:4rem}.sm\:inline{display:inline}.sm\:h-10{height:2.5rem}.sm\:h-14{height:3.5rem}.sm\:h-7{height:1.75rem}.sm\:max-h-125{max-height:31.25rem}.sm\:max-h-28{max-height:7rem}.sm\:w-36{width:9rem}.sm\:w-50{width:12.5rem}.sm\:w-7{width:1.75rem}.sm\:min-w-\[500px\]{min-width:500px}.sm\:translate-x-0{--tw-translate-x:0px}.sm\:scale-100,.sm\:translate-x-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))}.sm\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-end{justify-content:flex-end}.sm\:justify-items-start{justify-items:start}.sm\:gap-4{gap:1rem}.sm\:p-3{padding:.75rem}.sm\:px-12{padding-left:3rem;padding-right:3rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-0{padding-top:0;padding-bottom:0}.sm\:pt-6{padding-top:1.5rem}.sm\:text-left{text-align:left}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-7xl{font-size:4.5rem;line-height:1}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.5rem}}@media (min-width:768px){.md\:right-8{right:2rem}.md\:right-\[3\.75rem\]{right:3.75rem}.md\:top-\[40\%\]{top:40%}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-span-7{grid-column:span 7/span 7}.md\:col-span-8{grid-column:span 8/span 8}.md\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.md\:my-3{margin-top:.75rem;margin-bottom:.75rem}.md\:mb-0{margin-bottom:0}.md\:mb-3{margin-bottom:.75rem}.md\:mb-8{margin-bottom:2rem}.md\:mr-3{margin-right:.75rem}.md\:mt-0{margin-top:0}.md\:mt-6{margin-top:1.5rem}.md\:block{display:block}.md\:hidden{display:none}.md\:h-16{height:4rem}.md\:max-h-160{max-height:40rem}.md\:min-h-75-screen{min-height:75vh}.md\:w-1\/2{width:50%}.md\:w-60{width:15rem}.md\:max-w-\[350px\]{max-width:350px}.md\:max-w-\[400px\]{max-width:400px}.md\:max-w-\[600px\]{max-width:600px}.md\:-translate-y-20{--tw-translate-y:-5rem;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))}.md\:justify-end{justify-content:flex-end}.md\:gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.md\:px-3{padding-left:.75rem;padding-right:.75rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:py-2{padding-top:.5rem;padding-bottom:.5rem}.md\:py-4{padding-top:1rem;padding-bottom:1rem}.md\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width:992px){.lg\:relative{position:relative}.lg\:bottom-2{bottom:.5rem}.lg\:left-48{left:12rem}.lg\:top-24{top:6rem}.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:mx-0{margin-left:0;margin-right:0}.lg\:mx-8{margin-left:2rem;margin-right:2rem}.lg\:my-3{margin-top:.75rem;margin-bottom:.75rem}.lg\:mt-0{margin-top:0}.lg\:mt-4{margin-top:1rem}.lg\:mt-8{margin-top:2rem}.lg\:block{display:block}.lg\:inline{display:inline}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-24{height:6rem}.lg\:h-36{height:9rem}.lg\:h-9{height:2.25rem}.lg\:max-h-\[550px\]{max-height:550px}.lg\:w-36{width:9rem}.lg\:w-80{width:20rem}.lg\:w-9{width:2.25rem}.lg\:w-\[400px\]{width:400px}.lg\:max-w-\[700px\]{max-width:700px}.lg\:translate-x-0{--tw-translate-x:0px}.lg\:translate-x-0,.lg\:translate-y-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))}.lg\:translate-y-0{--tw-translate-y:0px}.lg\:flex-row{flex-direction:row}.lg\:flex-nowrap{flex-wrap:nowrap}.lg\:justify-start{justify-content:flex-start}.lg\:justify-end{justify-content:flex-end}.lg\:justify-between{justify-content:space-between}.lg\:gap-6{gap:1.5rem}.lg\:gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.lg\:bg-gray-50{--tw-bg-opacity:1;background-color:rgb(248 249 250/var(--tw-bg-opacity))}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:pb-1{padding-bottom:.25rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-6{padding-top:1.5rem}.lg\:text-left{text-align:left}.lg\:text-base{font-size:1rem;line-height:1.5rem}.lg\:text-sm{font-size:.875rem;line-height:1.5rem}}@media (min-width:1200px){.xl\:left-0{left:0}.xl\:right-24{right:6rem}.xl\:right-6{right:1.5rem}.xl\:ml-6{margin-left:1.5rem}.xl\:ml-68{margin-left:17rem}.xl\:hidden{display:none}.xl\:h-44{height:11rem}.xl\:max-h-\[550px\]{max-height:550px}.xl\:w-1\/3{width:33.333333%}.xl\:w-44{width:11rem}.xl\:w-\[500px\]{width:500px}.xl\:max-w-\[1200px\]{max-width:1200px}.xl\:max-w-\[500px\]{max-width:500px}.xl\:max-w-\[600px\]{max-width:600px}.xl\:translate-x-0{--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))}.xl\:p-1{padding:.25rem}.xl\:p-1\.5{padding:.375rem}.xl\:pl-75{padding-left:18.75rem}.xl\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width:1320px){.\32xl\:col-span-3{grid-column:span 3/span 3}.\32xl\:col-span-4{grid-column:span 4/span 4}.\32xl\:col-span-6{grid-column:span 6/span 6}.\32xl\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.\32xl\:my-3{margin-top:.75rem;margin-bottom:.75rem}.\32xl\:max-w-\[1500px\]{max-width:1500px}.\32xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}.\32xl\:text-5xl{font-size:3rem;line-height:1}.\32xl\:text-8xl{font-size:5rem;line-height:1}.\32xl\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1920px){.\33xl\:col-span-2{grid-column:span 2/span 2}.\33xl\:col-span-3{grid-column:span 3/span 3}.\33xl\:col-span-4{grid-column:span 4/span 4}.\33xl\:col-span-5{grid-column:span 5/span 5}.\33xl\:col-span-9{grid-column:span 9/span 9}.\33xl\:inline{display:inline}.\33xl\:max-w-none{max-width:none}.\33xl\:translate-x-60{--tw-translate-x:15rem;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))}.\33xl\:text-6xl{font-size:3.75rem;line-height:1}.\33xl\:text-9xl{font-size:6rem;line-height:1}.\33xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:340px){.xs\:flex-row{flex-direction:row}.xs\:items-center{align-items:center}.xs\:justify-start{justify-content:flex-start}.xs\:pl-2{padding-left:.5rem}.xs\:text-base{font-size:1rem;line-height:1.5rem}.xs\:text-sm{font-size:.875rem;line-height:1.5rem}}.\[\&\>\*\]\:bg-primary>*{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))} \ No newline at end of file +/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e9ecef}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Open Sans;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;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-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-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}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#ced4da}input::placeholder,textarea::placeholder{opacity:1;color:#ced4da}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,: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:#5e72e480;--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: }.\!container{width:100%!important;margin-right:auto!important;margin-left:auto!important;padding-right:1.5rem!important;padding-left:1.5rem!important}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:1.5rem;padding-left:1.5rem}@media (min-width:340px){.\!container{max-width:340px!important}.container{max-width:340px}}@media (min-width:576px){.\!container{max-width:576px!important}.container{max-width:576px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:992px){.\!container{max-width:992px!important}.container{max-width:992px}}@media (min-width:1200px){.\!container{max-width:1200px!important}.container{max-width:1200px}}@media (min-width:1320px){.\!container{max-width:1320px!important}.container{max-width:1320px}}@media (min-width:1920px){.\!container{max-width:1920px!important}.container{max-width:1920px}}a{letter-spacing:-.025rem}hr{margin:1rem 0;border:0;opacity:.25}img{max-width:none}label{display:inline-block}p{line-height:1.625;font-weight:400;margin-bottom:1rem}small{font-size:.875em}svg{display:inline}table{border-collapse:inherit}h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;color:#344767}h1,h2,h3,h4{letter-spacing:-.05rem}h1,h2,h3{font-weight:700}h4,h5,h6{font-weight:600}h1{font-size:3rem;line-height:1.25}h2{font-size:2.25rem;line-height:1.3}h3{font-size:1.875rem}h3,h4{line-height:1.375}h4{font-size:1.5rem}h5{font-size:1.25rem;line-height:1.375}h6{font-size:1rem;line-height:1.625}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-y-0{top:0;bottom:0}.-left-full{left:-100%}.-right-0{right:0}.-right-1{right:-.25rem}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-1\.5{bottom:.375rem}.bottom-2{bottom:.5rem}.bottom-24{bottom:6rem}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.bottom-7{bottom:1.75rem}.bottom-8{bottom:2rem}.left-0{left:0}.left-1{left:.25rem}.left-16{left:4rem}.left-20{left:5rem}.left-24{left:6rem}.left-32{left:8rem}.left-4{left:1rem}.left-40{left:10rem}.left-48{left:12rem}.left-8{left:2rem}.left-auto{left:auto}.left-full{left:100%}.right-0{right:0}.right-12{right:3rem}.right-2{right:.5rem}.right-20{right:5rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.right-7{right:1.75rem}.right-8{right:2rem}.right-\[3\.25rem\]{right:3.25rem}.top-0{top:0}.top-1{top:.25rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-10{top:2.5rem}.top-12{top:3rem}.top-14{top:3.5rem}.top-16{top:4rem}.top-2{top:.5rem}.top-24{top:6rem}.top-3{top:.75rem}.top-36{top:9rem}.top-4{top:1rem}.top-6{top:1.5rem}.top-7{top:1.75rem}.top-8{top:2rem}.top-\[38\%\]{top:38%}.top-\[4\.5rem\]{top:4.5rem}.top-\[52\%\]{top:52%}.top-\[55\%\]{top:55%}.top-\[8\.2rem\]{top:8.2rem}.-z-10{z-index:-10}.z-0{z-index:0}.z-10{z-index:10}.z-100{z-index:100}.z-110{z-index:110}.z-20{z-index:20}.z-990{z-index:990}.z-\[10000\]{z-index:10000}.z-\[1000\]{z-index:1000}.z-\[1001\]{z-index:1001}.z-\[20\]{z-index:20}.z-sticky{z-index:1020}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-11{grid-column:span 11/span 11}.col-span-12{grid-column:span 12/span 12}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.col-span-9{grid-column:span 9/span 9}.float-right{float:right}.float-left{float:left}.m-0{margin:0}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mx-0{margin-left:0;margin-right:0}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-1\.5{margin-left:.375rem;margin-right:.375rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-2\.5{margin-left:.625rem;margin-right:.625rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.ml-0{margin-left:0}.ml-1{margin-left:.25rem}.ml-12{margin-left:3rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-12{margin-right:3rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-\[15vh\]{margin-top:15vh}.mt-\[4\.5rem\]{margin-top:4.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.list-item{display:list-item}.hidden{display:none}.h-1{height:.25rem}.h-1\/3{height:33.333333%}.h-12{height:3rem}.h-14{height:3.5rem}.h-19{height:4.75rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-4\.5{height:1.125rem}.h-40{height:10rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-5\.5{height:1.375rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-\[2\.5rem\]{height:2.5rem}.h-\[250px\]{height:250px}.h-\[3\.5rem\]{height:3.5rem}.h-\[4rem\]{height:4rem}.h-\[90vh\]{height:90vh}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-sidenav{height:calc(100vh - 450px)}.max-h-100{max-height:25rem}.max-h-135{max-height:33.75rem}.max-h-30{max-height:7.5rem}.max-h-80{max-height:20rem}.max-h-90{max-height:22.5rem}.max-h-\[250px\]{max-height:250px}.max-h-\[400px\]{max-height:400px}.max-h-\[70vh\]{max-height:70vh}.max-h-\[90vh\]{max-height:90vh}.max-h-screen{max-height:100vh}.min-h-12{min-height:3rem}.min-h-20{min-height:5rem}.min-h-52{min-height:13rem}.min-h-6{min-height:1.5rem}.min-h-\[100px\]{min-height:100px}.min-h-\[350px\]{min-height:350px}.min-h-\[55vh\]{min-height:55vh}.min-h-\[75px\]{min-height:75px}.min-h-\[85vh\]{min-height:85vh}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-10{width:2.5rem}.w-11\/12{width:91.666667%}.w-12{width:3rem}.w-28{width:7rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-4\.5{width:1.125rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\.5{width:1.375rem}.w-50{width:12.5rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-90{width:22.5rem}.w-\[2\.5rem\]{width:2.5rem}.w-\[50vw\]{width:50vw}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0}.min-w-\[1150px\]{min-width:1150px}.min-w-\[1300px\]{min-width:1300px}.min-w-\[250px\]{min-width:250px}.min-w-\[350px\]{min-width:350px}.min-w-\[400px\]{min-width:400px}.min-w-\[500px\]{min-width:500px}.min-w-\[600px\]{min-width:600px}.min-w-\[800px\]{min-width:800px}.min-w-\[900px\]{min-width:900px}.max-w-180{max-width:45rem}.max-w-40{max-width:10rem}.max-w-60{max-width:15rem}.max-w-64{max-width:16rem}.max-w-\[1920px\]{max-width:1920px}.max-w-\[300px\]{max-width:300px}.max-w-\[400px\]{max-width:400px}.max-w-\[450px\]{max-width:450px}.max-w-\[550px\]{max-width:550px}.max-w-\[650px\]{max-width:650px}.max-w-\[700px\]{max-width:700px}.max-w-full{max-width:100%}.max-w-none{max-width:none}.flex-auto{flex:1 1 auto}.grow{flex-grow:1}.basis-full{flex-basis:100%}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x:-0.25rem}.-translate-x-1,.-translate-x-1\.5{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))}.-translate-x-1\.5{--tw-translate-x:-0.375rem}.-translate-x-36{--tw-translate-x:-9rem}.-translate-x-36,.-translate-x-40{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))}.-translate-x-40{--tw-translate-x:-10rem}.-translate-x-48{--tw-translate-x:-12rem}.-translate-x-48,.-translate-x-52{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))}.-translate-x-52{--tw-translate-x:-13rem}.-translate-x-56{--tw-translate-x:-14rem}.-translate-x-56,.-translate-x-60{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))}.-translate-x-60{--tw-translate-x:-15rem}.-translate-x-64{--tw-translate-x:-16rem}.-translate-x-64,.-translate-x-72{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))}.-translate-x-72{--tw-translate-x:-18rem}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.-translate-y-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))}.-translate-y-0{--tw-translate-y:-0px}.-translate-y-0\.4{--tw-translate-y:-0.1rem}.-translate-y-0\.4,.-translate-y-0\.5{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))}.-translate-y-0\.5{--tw-translate-y:-0.125rem}.-translate-y-1{--tw-translate-y:-0.25rem}.-translate-y-1,.-translate-y-1\.5{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))}.-translate-y-1\.5{--tw-translate-y:-0.375rem}.-translate-y-12{--tw-translate-y:-3rem}.-translate-y-12,.-translate-y-16{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))}.-translate-y-16{--tw-translate-y:-4rem}.-translate-y-2{--tw-translate-y:-0.5rem}.-translate-y-2,.-translate-y-20{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))}.-translate-y-20{--tw-translate-y:-5rem}.-translate-y-24{--tw-translate-y:-6rem}.-translate-y-24,.-translate-y-28{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))}.-translate-y-28{--tw-translate-y:-7rem}.-translate-y-36{--tw-translate-y:-9rem}.-translate-y-36,.-translate-y-4{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))}.-translate-y-4{--tw-translate-y:-1rem}.-translate-y-6{--tw-translate-y:-1.5rem}.-translate-y-6,.-translate-y-8{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))}.-translate-y-8{--tw-translate-y:-2rem}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-0\.5{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))}.translate-x-0\.5{--tw-translate-x:0.125rem}.translate-x-1{--tw-translate-x:0.25rem}.translate-x-1,.translate-x-16{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))}.translate-x-16{--tw-translate-x:4rem}.translate-x-2{--tw-translate-x:0.5rem}.translate-x-2,.translate-x-24{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))}.translate-x-24{--tw-translate-x:6rem}.translate-x-3{--tw-translate-x:0.75rem}.translate-x-3,.translate-x-32{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))}.translate-x-32{--tw-translate-x:8rem}.translate-x-4{--tw-translate-x:1rem}.translate-x-4,.translate-x-40{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))}.translate-x-40{--tw-translate-x:10rem}.translate-x-48{--tw-translate-x:12rem}.translate-x-48,.translate-x-52{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))}.translate-x-52{--tw-translate-x:13rem}.translate-x-56{--tw-translate-x:14rem}.translate-x-56,.translate-x-60{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))}.translate-x-60{--tw-translate-x:15rem}.translate-x-90{--tw-translate-x:22.5rem}.translate-x-90,.translate-x-\[3rem\]{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))}.translate-x-\[3rem\]{--tw-translate-x:3rem}.translate-y-0{--tw-translate-y:0px}.translate-y-0,.translate-y-0\.5{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))}.translate-y-0\.5{--tw-translate-y:0.125rem}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-1,.translate-y-16{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))}.translate-y-16{--tw-translate-y:4rem}.translate-y-2{--tw-translate-y:0.5rem}.-rotate-12,.translate-y-2{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))}.-rotate-12{--tw-rotate:-12deg}.rotate-12{--tw-rotate:12deg}.rotate-12,.rotate-180{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))}.rotate-180{--tw-rotate:180deg}.rotate-90{--tw-rotate:90deg}.rotate-90,.scale-105{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))}.scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.scale-110,.scale-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))}.scale-50{--tw-scale-x:.5;--tw-scale-y:.5}.scale-75{--tw-scale-x:.75;--tw-scale-y:.75}.scale-75,.scale-90{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))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-\[0\.55\]{--tw-scale-x:0.55;--tw-scale-y:0.55}.scale-\[0\.55\],.scale-\[0\.6\]{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))}.scale-\[0\.6\]{--tw-scale-x:0.6;--tw-scale-y:0.6}.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-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.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}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-2{row-gap:.5rem}.gap-y-4{row-gap:1rem}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded,.rounded-1{border-radius:.25rem}.rounded-1\.4{border-radius:.35rem}.rounded-10{border-radius:2.5rem}.rounded-2xl{border-radius:1rem}.rounded-circle{border-radius:50%}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-none{border-radius:0}.rounded-xl{border-radius:.75rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-lg{border-bottom-left-radius:.5rem}.rounded-b-lg,.rounded-r-lg{border-bottom-right-radius:.5rem}.rounded-r-lg{border-top-right-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-gray-100{--tw-border-opacity:1;border-color:rgb(235 239 244/var(--tw-border-opacity))}.border-gray-100\/50{border-color:#ebeff480}.border-gray-200{--tw-border-opacity:1;border-color:rgb(233 236 239/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgb(73 80 87/var(--tw-border-opacity))}.border-primary{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(17 113 239/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(45 206 137/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(233 236 239/var(--tw-bg-opacity))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(210 214 218/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(248 249 250/var(--tw-bg-opacity))}.bg-gray-600\/50{background-color:#6c757d80}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.bg-lime-600{--tw-bg-opacity:1;background-color:rgb(101 163 13/var(--tw-bg-opacity))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(251 99 64/var(--tw-bg-opacity))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(245 96 54/var(--tw-bg-opacity))}.bg-primary{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.bg-primary\/20{background-color:#0b557733}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(245 57 57/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(245 54 92/var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity))}.bg-secondary{--tw-bg-opacity:1;background-color:rgb(46 172 104/var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.bg-sky-700{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.bg-slate-800\/10{background-color:#3a416f1a}.bg-transparent{background-color:initial}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(251 177 64/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-tl{background-image:linear-gradient(to top left,var(--tw-gradient-stops))}.bg-none{background-image:none}.from-\[\#075577\]{--tw-gradient-from:#075577 var(--tw-gradient-from-position);--tw-gradient-to:#07557700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-\[\#0b5577\]{--tw-gradient-from:#0b5577 var(--tw-gradient-from-position);--tw-gradient-to:#0b557700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-black\/40{--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#0006 var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-\[\#116D70\]{--tw-gradient-to:#116d70 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.bg-150{background-size:150%}.bg-contain{background-size:contain}.bg-clip-border{background-clip:initial}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.bg-left{background-position:0}.bg-x-25{background-position:25% 0}.bg-no-repeat{background-repeat:no-repeat}.fill-amber-500{fill:#f59e0b}.fill-blue-500{fill:#5e72e4}.fill-gray-500{fill:#adb5bd}.fill-gray-600{fill:#6c757d}.fill-gray-700{fill:#495057}.fill-green-500{fill:#22c55e}.fill-primary{fill:#0b5577}.fill-red-500{fill:#f53939}.fill-red-700{fill:#b91c1c}.fill-sky-500{fill:#0ea5e9}.fill-slate-800{fill:#3a416f}.fill-white{fill:#fff}.fill-yellow-500{fill:#fbb140}.stroke-amber-500{stroke:#f59e0b}.stroke-blue-400{stroke:#60a5fa}.stroke-blue-500{stroke:#5e72e4}.stroke-emerald-600{stroke:#059669}.stroke-gray-100{stroke:#ebeff4}.stroke-gray-100\/50{stroke:#ebeff480}.stroke-gray-600{stroke:#6c757d}.stroke-gray-700{stroke:#495057}.stroke-green-500{stroke:#22c55e}.stroke-green-700{stroke:#15803d}.stroke-orange-500{stroke:#fb6340}.stroke-pink-600{stroke:#db2777}.stroke-red-500{stroke:#f53939}.stroke-sky-500{stroke:#0ea5e9}.stroke-stone-500{stroke:#78716c}.stroke-white{stroke:#fff}.stroke-yellow-500{stroke:#fbb140}.stroke-0{stroke-width:0}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-0{padding-left:0;padding-right:0}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-28{padding-left:7rem;padding-right:7rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-0{padding-bottom:0}.pb-10{padding-bottom:2.5rem}.pb-16{padding-bottom:4rem}.pb-2{padding-bottom:.5rem}.pb-24{padding-bottom:6rem}.pb-28{padding-bottom:7rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-0{padding-left:0}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-0{padding-right:0}.pr-8{padding-right:2rem}.pt-1{padding-top:.25rem}.pt-10{padding-top:2.5rem}.pt-2{padding-top:.5rem}.pt-20{padding-top:5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.pt-9{padding-top:2.25rem}.text-left{text-align:left}.text-center{text-align:center}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-sans{font-family:Open Sans}.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-6xl{font-size:3.75rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-9xl{font-size:6rem;line-height:1}.text-\[0\.7rem\]{font-size:.7rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-5\.6{line-height:1.4rem}.leading-6{line-height:1.5rem}.leading-8{line-height:2rem}.leading-default{line-height:1.6}.leading-none{line-height:1}.leading-normal{line-height:1.5}.leading-tight{line-height:1.25}.tracking-\[0\.20rem\]{letter-spacing:.2rem}.tracking-normal{letter-spacing:0}.tracking-tight-rem{letter-spacing:-.025rem}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-blue-500{--tw-text-opacity:1;color:rgb(94 114 228/var(--tw-text-opacity))}.text-emerald-500{--tw-text-opacity:1;color:rgb(45 206 137/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity))}.text-gray-100\/50{color:#ebeff480}.text-gray-300{--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(248 249 250/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(108 117 125/var(--tw-text-opacity))}.text-gray-600\/90{color:#6c757de6}.text-gray-700{--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.text-gray-700\/80{color:#495057cc}.text-gray-800{--tw-text-opacity:1;color:rgb(37 47 64/var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}.text-primary{--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity))}.text-secondary{--tw-text-opacity:1;color:rgb(46 172 104/var(--tw-text-opacity))}.text-sky-500{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity:1;color:rgb(62 161 236/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(103 116 142/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(52 71 103/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity:1;color:rgb(251 177 64/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-3xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-3xl{--tw-shadow:0 8px 26px -4px #14141426,0 8px 9px -5px #1414140f;--tw-shadow-colored:0 8px 26px -4px var(--tw-shadow-color),0 8px 9px -5px var(--tw-shadow-color)}.shadow-\[8px_8px_12px_rgb\(0\2c 0\2c 0\2c 0\.2\)\]{--tw-shadow:8px 8px 12px #0003;--tw-shadow-colored:8px 8px 12px var(--tw-shadow-color)}.shadow-\[8px_8px_12px_rgb\(0\2c 0\2c 0\2c 0\.2\)\],.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 #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.shadow-none,.shadow-sm{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 .25rem .375rem -.0625rem #1414141f,0 .125rem .25rem -.0625rem #14141412;--tw-shadow-colored:0 .25rem .375rem -.0625rem var(--tw-shadow-color),0 .125rem .25rem -.0625rem var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 0 2rem 0 #8898aa26;--tw-shadow-colored:0 0 2rem 0 var(--tw-shadow-color)}.shadow-xl,.shadow-xs{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.outline-secondary{outline-color:#2eac68}.blur{--tw-blur:blur(8px)}.blur,.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:ease;transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:ease;transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:ease;transition-duration:.15s}.delay-200{transition-delay:.2s}.duration-200{transition-duration:.2s}.duration-250{transition-duration:.25s}.duration-300{transition-duration:.3s}.duration-700{transition-duration:.7s}.ease-in{transition-timing-function:ease-in}.ease-in-out{transition-timing-function:ease-in-out}.flex-wrap-inherit{flex-wrap:inherit}@font-face{font-family:Open Sans;src:url(../webfonts/OpenSans.ttf)}*{font-family:Open Sans,sans-serif}.ace_editor,.ace_editor *{font-family:Monaco,Menlo,Ubuntu Mono,Droid Sans Mono,Consolas,monospace!important;font-weight:400!important;letter-spacing:0!important}.sr-only{display:none}.close-btn{display:inline-block;cursor:pointer;border-radius:.5rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.close-btn,.close-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.close-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.close-btn:focus,.close-btn:hover{background-color:#fffc}.close-btn:active{opacity:.85}.close-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.close-btn:disabled,.close-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.close-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .close-btn){--tw-bg-opacity:1;background-color:rgb(233 236 239/var(--tw-bg-opacity));--tw-brightness:brightness(.9)}:is(.dark .close-btn),:is(.dark .close-btn:hover){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)}:is(.dark .close-btn:hover){--tw-brightness:brightness(.75)}:is(.dark .close-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .close-btn:disabled),:is(.dark .close-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .close-btn:hover:disabled){--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));--tw-bg-opacity:1}.valid-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.valid-btn,.valid-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.valid-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.valid-btn:focus,.valid-btn:hover{background-color:#22c55ecc}.valid-btn:active{opacity:.85}.valid-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.valid-btn:disabled,.valid-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.valid-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .valid-btn){--tw-brightness:brightness(.9);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)}:is(.dark .valid-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .valid-btn:disabled),:is(.dark .valid-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .valid-btn:hover:disabled){--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));--tw-bg-opacity:1}.delete-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(245 57 57/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.delete-btn,.delete-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.delete-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.delete-btn:focus,.delete-btn:hover{background-color:#f53939cc}.delete-btn:active{opacity:.85}.delete-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.delete-btn:disabled,.delete-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.delete-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .delete-btn){--tw-brightness:brightness(.9);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)}:is(.dark .delete-btn:disabled){border-color:#49505700;--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .delete-btn:hover:disabled){--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));border-color:#49505700;--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}.edit-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(251 177 64/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.edit-btn,.edit-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.edit-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.edit-btn:focus,.edit-btn:hover{background-color:#fbb140cc}.edit-btn:active{opacity:.85}.edit-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.edit-btn:disabled,.edit-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.edit-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .edit-btn){--tw-brightness:brightness(.9);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)}:is(.dark .edit-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .edit-btn:disabled),:is(.dark .edit-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .edit-btn:hover:disabled){--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));--tw-bg-opacity:1}.info-btn{display:inline-block;cursor:pointer;border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:.025em;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 6px var(--tw-shadow-color);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.info-btn,.info-btn:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.info-btn:hover{--tw-translate-y:-1px;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-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color)}.info-btn:focus,.info-btn:hover{background-color:#0ea5e9cc}.info-btn:active{opacity:.85}.info-btn:disabled{cursor:not-allowed;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.info-btn:disabled,.info-btn:hover:disabled{border-color:#ced4da00;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.info-btn:hover:disabled{--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));--tw-bg-opacity:1}:is(.dark .info-btn){--tw-brightness:brightness(.9);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)}:is(.dark .info-btn:disabled){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .info-btn:disabled),:is(.dark .info-btn:hover:disabled){border-color:#49505700;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .info-btn:hover:disabled){--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));--tw-bg-opacity:1}.checkbox{position:relative;z-index:10;float:left;margin-top:.25rem;height:1.25rem;width:1.25rem;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.35rem;border-width:1px;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(255 255 255/var(--tw-bg-opacity));background-size:contain;background-position:50%;background-repeat:no-repeat;vertical-align:top;font-size:1rem;line-height:1.5rem;transition-property:all;transition-timing-function:ease;transition-duration:.25s}.checkbox,.checkbox:disabled{--tw-border-opacity:1;--tw-bg-opacity:1}.checkbox:disabled{cursor:default;border-color:rgb(206 212 218/var(--tw-border-opacity));background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.checkbox[data-checked=true]{z-index:0;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.checkbox:disabled[data-checked=true]{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}:is(.dark .checkbox){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .checkbox:disabled){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .checkbox[data-checked=true]){--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .checkbox:disabled[data-checked=true]){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.custom-select-btn{display:flex;width:100%;align-items:center;justify-content:space-between;border-radius:.5rem;border-width:1px;border-style:solid;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));background-clip:padding-box;padding:.25rem .375rem;text-align:left;vertical-align:middle;font-size:.875rem;font-weight:400;line-height:1.4rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.3s;transition-timing-function:ease-in-out}.custom-select-btn::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.custom-select-btn::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.custom-select-btn:focus{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.custom-select-btn:disabled{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));opacity:.75}:is(.dark .custom-select-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity));opacity:.9}:is(.dark .custom-select-btn),:is(.dark .custom-select-btn:disabled){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .custom-select-btn:disabled){border-color:rgb(37 47 64/var(--tw-border-opacity));background-color:rgb(37 47 64/var(--tw-bg-opacity))}@media (min-width:768px){.custom-select-btn{padding:.5rem .75rem}}.custom-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;min-height:38px;cursor:pointer;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.custom-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .custom-dropdown-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity))}.active.custom-dropdown-btn,:is(.dark .custom-dropdown-btn){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}.active.custom-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;min-height:38px;cursor:pointer;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(11 85 119/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.custom-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .active.custom-dropdown-btn){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(11 85 119/var(--tw-bg-opacity));color:rgb(210 214 218/var(--tw-text-opacity))}.regular-input,:is(.dark .active.custom-dropdown-btn){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.regular-input{display:block;width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.5rem;border-width:1px;border-style:solid;border-color:rgb(210 214 218/var(--tw-border-opacity));background-color:rgb(255 255 255/var(--tw-bg-opacity));background-clip:padding-box;padding:.25rem .375rem;font-size:.875rem;font-weight:400;line-height:1.4rem;color:rgb(73 80 87/var(--tw-text-opacity));outline:2px solid #0000;outline-offset:2px;transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.regular-input::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.regular-input::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.regular-input:focus{border-color:#d2d6da00;--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,0 0 #0000)}.regular-input:valid:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity))}.regular-input:invalid:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))}.regular-input:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));opacity:.75}:is(.dark .regular-input){border-color:rgb(98 117 148/var(--tw-border-opacity));background-color:rgb(52 71 103/var(--tw-bg-opacity));opacity:.9}:is(.dark .regular-input),:is(.dark .regular-input:disabled){--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .regular-input:disabled){border-color:rgb(37 47 64/var(--tw-border-opacity));background-color:rgb(37 47 64/var(--tw-bg-opacity))}@media (min-width:768px){.regular-input{padding:.5rem .75rem}}.input-title{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .input-title){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.popover-settings-container{position:absolute;left:0;bottom:0;z-index:50;--tw-translate-y:-1.75rem;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:.375rem;--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity));padding:.75rem;transition-property:all;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:ease;transition-duration:.5s}:is(.dark .popover-settings-container){--tw-brightness:brightness(.9);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)}.popover-settings-text{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .popover-settings-text){--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity));opacity:.9}.popover-settings-svg{margin-left:.5rem;height:1.25rem;width:1.25rem;cursor:pointer;fill:#5e72e4}.popover-settings-svg:hover{--tw-brightness:brightness(.75);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)}.active.settings-tabs-tab-btn{position:relative;z-index:10;margin-top:.25rem;margin-bottom:.25rem;cursor:pointer;border-radius:0;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);--tw-brightness:brightness(.9);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-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.settings-tabs-tab-btn:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .active.settings-tabs-tab-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .active.settings-tabs-tab-btn:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}.settings-tabs-tab-btn{position:relative;margin-top:.25rem;margin-bottom:.25rem;cursor:pointer;border-radius:0;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-tab-btn:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .settings-tabs-tab-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .settings-tabs-tab-btn:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}.settings-tabs-name{padding-left:.75rem;padding-right:.5rem;--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .settings-tabs-name){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.settings-tabs-popover-container{position:absolute;top:60px;left:0;z-index:50;min-width:150px;border-radius:.375rem;--tw-bg-opacity:1;background-color:rgb(94 114 228/var(--tw-bg-opacity));padding:.75rem;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:ease;transition-duration:.15s}:is(.dark .settings-tabs-popover-container){--tw-brightness:brightness(.9);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)}.settings-tabs-popover-text{margin:0;font-size:.875rem;line-height:1.5rem;font-weight:700;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.settings-tabs-mobile-btn{margin-top:.25rem;margin-bottom:.25rem;display:flex;width:100%;cursor:pointer;align-items:center;justify-content:space-between;border-radius:.5rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.75rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;font-weight:700;text-transform:uppercase;line-height:1.5;letter-spacing:-.025rem;--tw-shadow:0 7px 14px #32325d1a,0 3px 6px #00000014;--tw-shadow-colored:0 7px 14px var(--tw-shadow-color),0 3px 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);transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-mobile-btn:hover{--tw-translate-y:-1px;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:rgb(248 249 250/var(--tw-bg-opacity));--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .settings-tabs-mobile-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .settings-tabs-mobile-btn:hover){--tw-brightness:brightness(.95);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)}.settings-tabs-mobile-btn-text{--tw-text-opacity:1;color:rgb(11 85 119/var(--tw-text-opacity));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:ease;transition-duration:.3s;transition-timing-function:ease-in-out}:is(.dark .settings-tabs-mobile-btn-text){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));opacity:.9}.active.settings-tabs-mobile-dropdown-btn{position:relative;z-index:1000;margin-top:0;margin-bottom:0;cursor:pointer;border-radius:0;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.active.settings-tabs-mobile-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .active.settings-tabs-mobile-dropdown-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .active.settings-tabs-mobile-dropdown-btn:hover){--tw-brightness:brightness(.9);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)}.first.settings-tabs-mobile-dropdown-btn{border-top-left-radius:.25rem;border-top-right-radius:.25rem;border-width:1px}.settings-tabs-mobile-dropdown-btn{position:relative;margin-top:0;margin-bottom:0;display:flex;cursor:pointer;justify-content:space-between;border-radius:0;border-bottom-width:1px;border-left-width:1px;border-right-width:1px;--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding:.5rem 1.5rem;text-align:center;vertical-align:middle;font-size:.875rem;line-height:1.5rem;line-height:1.5;letter-spacing:-.025rem;--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity));transition-property:all;transition-timing-function:ease;transition-duration:.15s;transition-timing-function:ease-in}.settings-tabs-mobile-dropdown-btn:hover{--tw-brightness:brightness(.9);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)}:is(.dark .settings-tabs-mobile-dropdown-btn){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .settings-tabs-mobile-dropdown-btn:hover){--tw-brightness:brightness(.9);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)}.first-letter\:absolute:first-letter{position:absolute}.first-letter\:w-full:first-letter{width:100%}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}.before\:float-left:before{content:var(--tw-content);float:left}.before\:pr-2:before{content:var(--tw-content);padding-right:.5rem}.before\:text-white:before{content:var(--tw-content);--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.before\:content-\[\'\/\'\]:before{--tw-content:"/";content:var(--tw-content)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:top-px:after{content:var(--tw-content);top:1px}.after\:float-right:after{content:var(--tw-content);float:right}.after\:h-4:after{content:var(--tw-content);height:1rem}.after\:w-4:after{content:var(--tw-content);width:1rem}.after\:translate-x-px:after{content:var(--tw-content);--tw-translate-x:1px;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))}.after\:rounded-circle:after{content:var(--tw-content);border-radius:50%}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.after\:pl-2:after{content:var(--tw-content);padding-left:.5rem}.after\:text-gray-600:after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(108 117 125/var(--tw-text-opacity))}.after\:shadow-2xl:after{content:var(--tw-content);--tw-shadow:0 .3125rem .625rem 0 #0000001f;--tw-shadow-colored:0 .3125rem .625rem 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)}.after\:duration-300:after{content:var(--tw-content);transition-duration:.3s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.after\:content-\[\'\/\'\]:after{--tw-content:"/";content:var(--tw-content)}.checked\:z-0:checked{z-index:0}.checked\:border-primary:checked{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.checked\:bg-primary:checked{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}.checked\:bg-none:checked{background-image:none}.checked\:bg-right:checked{background-position:100%}.checked\:after\:translate-x-5:checked:after{--tw-translate-x:1.25rem}.checked\:after\:translate-x-5:checked:after,.checked\:after\:translate-x-5\.3:checked:after{content:var(--tw-content);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))}.checked\:after\:translate-x-5\.3:checked:after{--tw-translate-x:1.3rem}.valid\:\!border-red-500:valid{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.hover\:-translate-y-0:hover{--tw-translate-y:-0px}.hover\:-translate-y-0:hover,.hover\:-translate-y-0\.4:hover{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\:-translate-y-0\.4:hover{--tw-translate-y:-0.1rem}.hover\:-translate-y-0\.5:hover{--tw-translate-y:-0.125rem}.hover\:-translate-y-0\.5:hover,.hover\:-translate-y-px:hover{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\:-translate-y-px:hover{--tw-translate-y:-1px}.hover\:scale-102:hover{--tw-scale-x:1.02;--tw-scale-y:1.02;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\:rounded-lg:hover{border-radius:.5rem}.hover\:bg-emerald-500\/80:hover{background-color:#2dce89cc}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(235 239 244/var(--tw-bg-opacity))}.hover\:bg-gray-100\/10:hover{background-color:#ebeff41a}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(210 214 218/var(--tw-bg-opacity))}.hover\:bg-green-500\/80:hover{background-color:#22c55ecc}.hover\:bg-primary\/30:hover{background-color:#0b55774d}.hover\:bg-primary\/5:hover{background-color:#0b55770d}.hover\:bg-primary\/80:hover{background-color:#0b5577cc}.hover\:bg-red-500\/80:hover{background-color:#f53939cc}.hover\:bg-sky-500\/80:hover{background-color:#0ea5e9cc}.hover\:bg-yellow-500\/80:hover{background-color:#fbb140cc}.hover\:italic:hover{font-style:italic}.hover\:no-underline:hover{text-decoration-line:none}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px #32325d1a,0 1px 3px #00000014;--tw-shadow-colored:0 4px 6px var(--tw-shadow-color),0 1px 3px 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\:brightness-75:hover{--tw-brightness:brightness(.75)}.hover\:brightness-75:hover,.hover\:brightness-90:hover{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)}.hover\:brightness-90:hover{--tw-brightness:brightness(.9)}.hover\:brightness-95:hover{--tw-brightness:brightness(.95);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)}.focus\:\!border-red-500:focus{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.focus\:border-green-500:focus{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.focus\:border-primary:focus{--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}.focus\:bg-emerald-500\/80:focus{background-color:#2dce89cc}.focus\:bg-green-500\/80:focus{background-color:#22c55ecc}.focus\:bg-primary\/80:focus{background-color:#0b5577cc}.focus\:bg-red-500\/80:focus{background-color:#f53939cc}.focus\:bg-sky-500\/80:focus{background-color:#0ea5e9cc}.focus\:bg-yellow-500\/80:focus{background-color:#fbb140cc}.focus\:\!ring-red-500:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))!important}.focus\:valid\:\!border-red-500:valid:focus{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.focus\:valid\:border-green-500:valid:focus{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.focus\:valid\:\!ring-red-500:valid:focus{--tw-ring-opacity:1!important;--tw-ring-color:rgb(245 57 57/var(--tw-ring-opacity))!important}.focus\:invalid\:border-red-500:invalid:focus{--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity))}.focus\:file\:invalid\:border-red-500:invalid::file-selector-button:focus{--tw-border-opacity:1;border-color:rgb(245 57 57/var(--tw-border-opacity))}.active\:\!border-red-500:active{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.active\:opacity-85:active{opacity:.85}.active\:valid\:\!border-red-500:valid:active{--tw-border-opacity:1!important;border-color:rgb(245 57 57/var(--tw-border-opacity))!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:border-gray-400:disabled{--tw-border-opacity:1;border-color:rgb(206 212 218/var(--tw-border-opacity))}.disabled\:border-gray-400\/0:disabled{border-color:#ced4da00}.disabled\:bg-gray-400:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.disabled\:text-gray-700:disabled{--tw-text-opacity:1;color:rgb(73 80 87/var(--tw-text-opacity))}.disabled\:opacity-75:disabled{opacity:.75}.disabled\:hover\:translate-y-0:hover:disabled{--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))}.disabled\:hover\:border-gray-400\/0:hover:disabled{border-color:#ced4da00}.disabled\:hover\:bg-gray-400:hover:disabled{--tw-bg-opacity:1;background-color:rgb(206 212 218/var(--tw-bg-opacity))}.group:hover .group-hover\:z-10{z-index:10}.group:hover .group-hover\:opacity-100{opacity:1}:is(.dark .dark\:inline){display:inline}:is(.dark .dark\:hidden){display:none}:is(.dark .dark\:border-gray-300){--tw-border-opacity:1;border-color:rgb(210 214 218/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgb(73 80 87/var(--tw-border-opacity))}:is(.dark .dark\:border-slate-600){--tw-border-opacity:1;border-color:rgb(98 117 148/var(--tw-border-opacity))}:is(.dark .dark\:border-slate-800){--tw-border-opacity:1;border-color:rgb(58 65 111/var(--tw-border-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-500\/90){background-color:#22c55ee6}:is(.dark .dark\:bg-primary){--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .dark\:bg-primary\/50){background-color:#0b557780}:is(.dark .dark\:bg-red-500\/90){background-color:#f53939e6}:is(.dark .dark\:bg-slate-700){--tw-bg-opacity:1;background-color:rgb(52 71 103/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-700\/50){background-color:#34476780}:is(.dark .dark\:bg-slate-800){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-850){--tw-bg-opacity:1;background-color:rgb(17 28 68/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-900){--tw-bg-opacity:1;background-color:rgb(5 17 57/var(--tw-bg-opacity))}:is(.dark .dark\:bg-slate-900\/30){background-color:#0511394d}:is(.dark .dark\:bg-gradient-to-r){background-image:linear-gradient(to right,var(--tw-gradient-stops))}:is(.dark .dark\:from-transparent){--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}:is(.dark .dark\:via-white){--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fff var(--tw-gradient-via-position),var(--tw-gradient-to)}:is(.dark .dark\:to-transparent){--tw-gradient-to:#0000 var(--tw-gradient-to-position)}:is(.dark .dark\:fill-blue-500){fill:#5e72e4}:is(.dark .dark\:fill-gray-300){fill:#d2d6da}:is(.dark .dark\:fill-gray-500){fill:#adb5bd}:is(.dark .dark\:fill-gray-600){fill:#6c757d}:is(.dark .dark\:stroke-amber-500){stroke:#f59e0b}:is(.dark .dark\:stroke-gray-400){stroke:#ced4da}:is(.dark .dark\:stroke-gray-600){stroke:#6c757d}:is(.dark .dark\:stroke-red-500){stroke:#f53939}:is(.dark .dark\:text-gray-100){--tw-text-opacity:1;color:rgb(235 239 244/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgb(233 236 239/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgb(206 212 218/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}:is(.dark .dark\:text-red-500){--tw-text-opacity:1;color:rgb(245 57 57/var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:text-white\/80){color:#fffc}:is(.dark .dark\:text-white\/90){color:#ffffffe6}:is(.dark .dark\:opacity-60){opacity:.6}:is(.dark .dark\:opacity-75){opacity:.75}:is(.dark .dark\:opacity-80){opacity:.8}:is(.dark .dark\:opacity-90){opacity:.9}:is(.dark .dark\:shadow-dark-xl){--tw-shadow:0 2px 2px 0 #00000024,0 3px 1px -2px #0003,0 1px 5px 0 #0000001f;--tw-shadow-colored:0 2px 2px 0 var(--tw-shadow-color),0 3px 1px -2px var(--tw-shadow-color),0 1px 5px 0 var(--tw-shadow-color)}:is(.dark .dark\:shadow-dark-xl),:is(.dark .dark\:shadow-none){box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}:is(.dark .dark\:shadow-none){--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}:is(.dark .dark\:brightness-110){--tw-brightness:brightness(1.1)}:is(.dark .dark\:brightness-110),:is(.dark .dark\:brightness-125){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)}:is(.dark .dark\:brightness-125){--tw-brightness:brightness(1.25)}:is(.dark .dark\:brightness-150){--tw-brightness:brightness(1.5)}:is(.dark .dark\:brightness-150),:is(.dark .dark\:brightness-90){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)}:is(.dark .dark\:brightness-90){--tw-brightness:brightness(.9)}:is(.dark .dark\:brightness-95){--tw-brightness:brightness(.95)}:is(.dark .dark\:brightness-95),:is(.dark .dark\:brightness-\[0\.885\]){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)}:is(.dark .dark\:brightness-\[0\.885\]){--tw-brightness:brightness(0.885)}:is(.dark .dark\:after\:text-gray-500):after{content:var(--tw-content);--tw-text-opacity:1;color:rgb(173 181 189/var(--tw-text-opacity))}:is(.dark .dark\:checked\:border-primary:checked){--tw-border-opacity:1;border-color:rgb(11 85 119/var(--tw-border-opacity))}:is(.dark .dark\:checked\:bg-primary:checked){--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-primary\/20:hover){background-color:#0b557733}:is(.dark .dark\:hover\:bg-primary\/60:hover){background-color:#0b557799}:is(.dark .dark\:hover\:bg-slate-700\/50:hover){background-color:#34476780}:is(.dark .dark\:hover\:bg-slate-800:hover){--tw-bg-opacity:1;background-color:rgb(58 65 111/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:brightness-100:hover){--tw-brightness:brightness(1)}:is(.dark .dark\:hover\:brightness-100:hover),:is(.dark .dark\:hover\:brightness-105:hover){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)}:is(.dark .dark\:hover\:brightness-105:hover){--tw-brightness:brightness(1.05)}:is(.dark .dark\:hover\:brightness-110:hover){--tw-brightness:brightness(1.1)}:is(.dark .dark\:hover\:brightness-110:hover),:is(.dark .dark\:hover\:brightness-90:hover){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)}:is(.dark .dark\:hover\:brightness-90:hover){--tw-brightness:brightness(.9)}:is(.dark .dark\:disabled\:border-gray-700\/0:disabled){border-color:#49505700}:is(.dark .dark\:disabled\:border-gray-800:disabled){--tw-border-opacity:1;border-color:rgb(37 47 64/var(--tw-border-opacity))}:is(.dark .dark\:disabled\:bg-gray-700:disabled){--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}:is(.dark .dark\:disabled\:bg-gray-800:disabled){--tw-bg-opacity:1;background-color:rgb(37 47 64/var(--tw-bg-opacity))}:is(.dark .dark\:disabled\:text-gray-300:disabled){--tw-text-opacity:1;color:rgb(210 214 218/var(--tw-text-opacity))}:is(.dark .dark\:disabled\:hover\:translate-y-0:hover:disabled){--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))}:is(.dark .dark\:disabled\:hover\:border-gray-700\/0:hover:disabled){border-color:#49505700}:is(.dark .dark\:disabled\:hover\:bg-gray-700:hover:disabled){--tw-bg-opacity:1;background-color:rgb(73 80 87/var(--tw-bg-opacity))}@media (min-width:576px){.sm\:right-24{right:6rem}.sm\:right-40{right:10rem}.sm\:right-6{right:1.5rem}.sm\:top-2{top:.5rem}.sm\:top-8{top:2rem}.sm\:top-\[4\.5rem\]{top:4.5rem}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:col-start-5{grid-column-start:5}.sm\:mx-4{margin-left:1rem;margin-right:1rem}.sm\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.sm\:ml-1{margin-left:.25rem}.sm\:mr-16{margin-right:4rem}.sm\:inline{display:inline}.sm\:h-10{height:2.5rem}.sm\:h-14{height:3.5rem}.sm\:h-7{height:1.75rem}.sm\:max-h-125{max-height:31.25rem}.sm\:max-h-28{max-height:7rem}.sm\:w-36{width:9rem}.sm\:w-50{width:12.5rem}.sm\:w-7{width:1.75rem}.sm\:min-w-\[500px\]{min-width:500px}.sm\:translate-x-0{--tw-translate-x:0px}.sm\:scale-100,.sm\:translate-x-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))}.sm\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-end{justify-content:flex-end}.sm\:justify-items-start{justify-items:start}.sm\:gap-4{gap:1rem}.sm\:p-3{padding:.75rem}.sm\:px-12{padding-left:3rem;padding-right:3rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-0{padding-top:0;padding-bottom:0}.sm\:pt-6{padding-top:1.5rem}.sm\:text-left{text-align:left}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-7xl{font-size:4.5rem;line-height:1}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.5rem}}@media (min-width:768px){.md\:right-8{right:2rem}.md\:right-\[3\.75rem\]{right:3.75rem}.md\:top-\[40\%\]{top:40%}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-span-7{grid-column:span 7/span 7}.md\:col-span-8{grid-column:span 8/span 8}.md\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.md\:my-3{margin-top:.75rem;margin-bottom:.75rem}.md\:mb-0{margin-bottom:0}.md\:mb-3{margin-bottom:.75rem}.md\:mb-8{margin-bottom:2rem}.md\:mr-3{margin-right:.75rem}.md\:mt-0{margin-top:0}.md\:mt-6{margin-top:1.5rem}.md\:block{display:block}.md\:hidden{display:none}.md\:h-16{height:4rem}.md\:max-h-160{max-height:40rem}.md\:min-h-75-screen{min-height:75vh}.md\:w-1\/2{width:50%}.md\:w-60{width:15rem}.md\:max-w-\[350px\]{max-width:350px}.md\:max-w-\[400px\]{max-width:400px}.md\:max-w-\[600px\]{max-width:600px}.md\:-translate-y-20{--tw-translate-y:-5rem;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))}.md\:justify-end{justify-content:flex-end}.md\:gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.md\:px-3{padding-left:.75rem;padding-right:.75rem}.md\:px-4{padding-left:1rem;padding-right:1rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:py-2{padding-top:.5rem;padding-bottom:.5rem}.md\:py-4{padding-top:1rem;padding-bottom:1rem}.md\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width:992px){.lg\:relative{position:relative}.lg\:bottom-2{bottom:.5rem}.lg\:left-48{left:12rem}.lg\:top-24{top:6rem}.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:mx-0{margin-left:0;margin-right:0}.lg\:mx-8{margin-left:2rem;margin-right:2rem}.lg\:my-3{margin-top:.75rem;margin-bottom:.75rem}.lg\:mt-0{margin-top:0}.lg\:mt-4{margin-top:1rem}.lg\:mt-8{margin-top:2rem}.lg\:block{display:block}.lg\:inline{display:inline}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-24{height:6rem}.lg\:h-36{height:9rem}.lg\:h-9{height:2.25rem}.lg\:max-h-\[550px\]{max-height:550px}.lg\:w-36{width:9rem}.lg\:w-80{width:20rem}.lg\:w-9{width:2.25rem}.lg\:w-\[400px\]{width:400px}.lg\:max-w-\[700px\]{max-width:700px}.lg\:translate-x-0{--tw-translate-x:0px}.lg\:translate-x-0,.lg\:translate-y-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))}.lg\:translate-y-0{--tw-translate-y:0px}.lg\:flex-row{flex-direction:row}.lg\:flex-nowrap{flex-wrap:nowrap}.lg\:justify-start{justify-content:flex-start}.lg\:justify-end{justify-content:flex-end}.lg\:justify-between{justify-content:space-between}.lg\:gap-6{gap:1.5rem}.lg\:gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.lg\:bg-gray-50{--tw-bg-opacity:1;background-color:rgb(248 249 250/var(--tw-bg-opacity))}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:pb-1{padding-bottom:.25rem}.lg\:pb-28{padding-bottom:7rem}.lg\:pt-6{padding-top:1.5rem}.lg\:text-left{text-align:left}.lg\:text-base{font-size:1rem;line-height:1.5rem}.lg\:text-sm{font-size:.875rem;line-height:1.5rem}}@media (min-width:1200px){.xl\:left-0{left:0}.xl\:right-24{right:6rem}.xl\:right-6{right:1.5rem}.xl\:ml-6{margin-left:1.5rem}.xl\:ml-68{margin-left:17rem}.xl\:hidden{display:none}.xl\:h-44{height:11rem}.xl\:max-h-\[550px\]{max-height:550px}.xl\:w-1\/3{width:33.333333%}.xl\:w-44{width:11rem}.xl\:w-\[500px\]{width:500px}.xl\:max-w-\[1200px\]{max-width:1200px}.xl\:max-w-\[500px\]{max-width:500px}.xl\:max-w-\[600px\]{max-width:600px}.xl\:translate-x-0{--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))}.xl\:p-1{padding:.25rem}.xl\:p-1\.5{padding:.375rem}.xl\:pl-75{padding-left:18.75rem}.xl\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width:1320px){.\32xl\:col-span-3{grid-column:span 3/span 3}.\32xl\:col-span-4{grid-column:span 4/span 4}.\32xl\:col-span-6{grid-column:span 6/span 6}.\32xl\:mx-6{margin-left:1.5rem;margin-right:1.5rem}.\32xl\:my-3{margin-top:.75rem;margin-bottom:.75rem}.\32xl\:max-w-\[1500px\]{max-width:1500px}.\32xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}.\32xl\:text-5xl{font-size:3rem;line-height:1}.\32xl\:text-8xl{font-size:5rem;line-height:1}.\32xl\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1920px){.\33xl\:col-span-2{grid-column:span 2/span 2}.\33xl\:col-span-3{grid-column:span 3/span 3}.\33xl\:col-span-4{grid-column:span 4/span 4}.\33xl\:col-span-5{grid-column:span 5/span 5}.\33xl\:col-span-9{grid-column:span 9/span 9}.\33xl\:inline{display:inline}.\33xl\:max-w-none{max-width:none}.\33xl\:translate-x-60{--tw-translate-x:15rem;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))}.\33xl\:text-6xl{font-size:3.75rem;line-height:1}.\33xl\:text-9xl{font-size:6rem;line-height:1}.\33xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:340px){.xs\:flex-row{flex-direction:row}.xs\:items-center{align-items:center}.xs\:justify-start{justify-content:flex-start}.xs\:pl-2{padding-left:.5rem}.xs\:text-base{font-size:1rem;line-height:1.5rem}.xs\:text-sm{font-size:.875rem;line-height:1.5rem}}.\[\&\>\*\]\:bg-primary>*{--tw-bg-opacity:1;background-color:rgb(11 85 119/var(--tw-bg-opacity))} \ No newline at end of file diff --git a/src/ui/static/js/plugins/setup.js b/src/ui/static/js/plugins/setup.js new file mode 100644 index 000000000..ab624400c --- /dev/null +++ b/src/ui/static/js/plugins/setup.js @@ -0,0 +1,256 @@ +class SetupPlugin { + constructor(data) { + // Set data defaults elements and variables + // Key of this.data need to match key of fetch data json object to update values + // type : text (target el), list (el need to be first element of list) + // listNames : list of names key on item, need to set data-name="nameKey" on el + this.data = data; + /* EXAMPLE + { + info: { + el: document.querySelector("[data-info]"), + value: `Anti-bot technology is designed to detect and mitigate suspicious or + malicious bots, preventing them from reaching an organization's websites + or IT ecosystem.`, + type: "text", + }, + + items: { + el: document.querySelector("[data-item]"), + value: [], + type: "list", + listNames: ["server_name", "cn", "expire"], + }, + // value : active / inactive / unknown + status: { + el: document.querySelector("[data-status]"), + value: "unknown", + type: "status", + textEl: document.querySelector("[data-status-text]"), + }, + */ + // Hidden elements that will be shown on success, like ping buttons or list rendering + this.showOnSuccessEls = document.querySelectorAll( + "[data-fetch-success-show]" + ); + + this.init(); + } + + init() { + window.addEventListener("DOMContentLoaded", () => { + this.createAlertEl(); + // Set default values and fetch + this.updateDataDOM(); + this.updateAlert("fetch"); + + fetch(location.href, { + method: "POST", + headers: { + "X-CSRFToken": document.querySelector('input[name="csrf_token"]') + .value, + }, + }) + .then((res) => res.json()) + .then((res) => { + // Update data and DOM + this.getFetchDataByKey(res.data); + this.updateDataDOM(); + // Show hidden elements + this.showSuccessEls(); + // Feedback + this.updateAlert("success"); + }) + .catch((error) => { + this.updateAlert("error"); + }); + }); + } + + createAlertEl() { + // Container + this.alertEl = this.createEl( + "div", + [ + ["data-fetch", ""], + ["role", "alert"], + ], + "bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border", + "", + "" + ); + + this.alertCloseEl = this.createEl( + "button", + [["data-fetch-close", ""]], + "absolute right-7 top-1.5", + "", + this.alertEl + ); + + this.alertCloseIconEl = this.createEl( + "svg", + [ + ["xmlns", "http://www.w3.org/2000/svg"], + ["viewBox", "0 0 320 512"], + ], + "cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5", + "", + this.alertCloseEl + ); + + // Close icon paths + const paths = [ + "M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z", + , + "M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z", + , + "M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z", + ]; + paths.forEach((path) => { + this.createEl("path", [["d", path]], "", "", this.alertCloseIconEl); + }); + // Status + this.alertStatusEl = this.createEl( + "h5", + [["data-fetch-status", ""]], + "text-lg mb-0 text-white dark:text-gray-300", + "Fetching", + this.alertEl + ); + + this.alertMsgEl = this.createEl( + "p", + [["data-fetch-msg", ""]], + "text-white dark:text-gray-300 mb-0 text-sm", + "Please wait...", + this.alertEl + ); + + document.body.appendChild(this.alertEl); + + this.alertCloseEl.addEventListener("click", () => { + this.alertEl.classList.add("hidden"); + }); + } + + createEl(tag, attArr, className, text, parent) { + const el = document.createElement(tag); + attArr.forEach((att) => { + el.setAttribute(att[0], att[1]); + }); + if (className) el.className = className; + if (text) el.textContent = text; + if (parent) parent.appendChild(el); + return el; + } + + showSuccessEls() { + this.showOnSuccessEls.forEach((el) => { + el.classList.remove("hidden"); + }); + } + + // Key of fetch data need to match key of this.data + getFetchDataByKey(fetchDataObj) { + for (const [key, value] of Object.entries(this.data)) { + // Case list + if (Array.isArray(fetchDataObj[key])) { + value["value"] = fetchDataObj[key] || value["value"] || ""; + continue; + } + // Case number + if (!isNaN(fetchDataObj[key])) { + value["value"] = fetchDataObj[key] == 0 ? "0" : fetchDataObj[key]; + continue; + } + // Others + value["value"] = fetchDataObj[key] || value["value"] || ""; + } + } + + updateDataDOM() { + for (const [key, val] of Object.entries(this.data)) { + const el = val["el"]; + const type = val["type"]; + const value = val["value"]; + + // Case text + if (type === "text") { + el.textContent = value || ""; + continue; + } + // Case status + if (type === "status") { + const textEl = val["textEl"] || null; + + if (value === "active") + this.setStatus(el, textEl, "fill-green-500", "Active"); + if (value === "inactive") + this.setStatus(el, textEl, "fill-red-500", "Inactive"); + if (value === "unknown") + this.setStatus(el, textEl, "fill-sky-500", "Unknown"); + continue; + } + + // Case list, we will render elements after the selected elements + if (type === "list") { + // Case no list to render + if (!value || value.length <= 0) continue; + + // Clone item element + const itemEl = el.cloneNode(true); + itemEl.classList.remove("hidden"); + const parentEl = el.parentNode; + // Add item element after selected element + const items = value.forEach((item) => { + const newItemEl = itemEl.cloneNode(true); + // Update item element values + for (const [nameKey, nameValue] of Object.entries(item)) { + newItemEl.querySelector(`[data-name="${nameKey}"]`).textContent = + nameValue; + } + // Add item element after selected element + parentEl.appendChild(newItemEl); + }); + + // Delete schema + el.remove(); + continue; + } + } + } + + setStatus(el, textEl, colorClass, text) { + el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500"); + el ? el.classList.add(colorClass) : null; + textEl ? (textEl.textContent = text) : null; + } + + // Show fetch state alert + // type : fetch, success, error + updateAlert(type) { + if (!type) return; + + const [status, msg, color] = this.getAlertType(type); + + this.alertEl.classList.remove("bg-sky-500", "bg-green-500", "bg-red-500"); + + this.alertStatusEl.textContent = status; + this.alertMsgEl.textContent = msg; + this.alertEl.classList.add(color); + + this.alertEl.classList.remove("hidden"); + + if (type !== "fetch") + setTimeout(() => this.alertEl.classList.add("hidden"), 5000); + } + + getAlertType(type) { + if (type === "fetch") return ["Fetching", "Please wait...", "bg-sky-500"]; + if (type === "error") + return ["Error", "Something went wrong", "bg-red-500"]; + if (type === "success") + return ["Success", "Data fetched successfully", "bg-green-500"]; + } +} diff --git a/src/ui/static/js/services.js b/src/ui/static/js/services.js index 2eeeb95a2..c86ee4bc3 100644 --- a/src/ui/static/js/services.js +++ b/src/ui/static/js/services.js @@ -19,10 +19,10 @@ class ServiceModal { //modal forms this.formNewEdit = this.modal.querySelector("[data-services-modal-form]"); this.formDelete = this.modal.querySelector( - "[data-services-modal-form-delete]" + "[data-services-modal-form-delete]", ); this.submitBtn = document.querySelector( - "button[data-services-modal-submit]" + "button[data-services-modal-submit]", ); //container this.container = document.querySelector("main"); @@ -92,7 +92,7 @@ class ServiceModal { ) { //set form info and right form const [action, serviceName, isDraft, method] = this.getActionData( - e.target + e.target, ); const oldServName = e.target .closest("[data-services-service]") @@ -104,7 +104,7 @@ class ServiceModal { oldServName, this.formNewEdit, isDraft, - method + method, ); //get service data and parse it //multiple type logic is launch at same time on relate class @@ -116,7 +116,7 @@ class ServiceModal { this.updateModalData(obj); //show modal this.resetFilterInp(); - this.changeSubmitBtn("EDIT", "edit-btn"); + this.changeSubmitBtn("SAVE", "valid-btn"); this.openModal(); } } catch (err) {} @@ -128,7 +128,7 @@ class ServiceModal { ) { //set form info and right form const [action, serviceName, isDraft, method] = this.getActionData( - e.target + e.target, ); this.setForm( action, @@ -136,7 +136,7 @@ class ServiceModal { serviceName, this.formNewEdit, isDraft, - method + method, ); //set default value with method default //get service data and parse it @@ -168,7 +168,7 @@ class ServiceModal { ) { //set form info and right form const [action, serviceName, isDraft, method] = this.getActionData( - e.target + e.target, ); this.setForm( action, @@ -176,7 +176,7 @@ class ServiceModal { serviceName, this.formNewEdit, isDraft, - method + method, ); //set default value with method default this.setSettingsDefault(); @@ -200,7 +200,7 @@ class ServiceModal { ) { //set form info and right form const [action, serviceName, isDraft, method] = this.getActionData( - e.target + e.target, ); this.setForm( action, @@ -208,7 +208,7 @@ class ServiceModal { serviceName, this.formDelete, isDraft, - method + method, ); //show modal this.openModal(); @@ -229,7 +229,7 @@ class ServiceModal { "delete-btn", "valid-btn", "edit-btn", - "info-btn" + "info-btn", ); this.submitBtn.classList.add(btnType); } @@ -290,15 +290,15 @@ class ServiceModal { //click the custom select dropdown to update select value select.parentElement .querySelector( - `button[data-setting-select-dropdown-btn][value='${defaultVal}']` + `button[data-setting-select-dropdown-btn][value='${defaultVal}']`, ) .click(); //set state to custom visible el const btnCustom = document.querySelector( `[data-setting-select=${select.getAttribute( - "data-setting-select-default" - )}]` + "data-setting-select-default", + )}]`, ); this.setDisabledDefault(btnCustom, defaultMethod); @@ -374,9 +374,8 @@ class ServiceModal { if (action === "delete") { this.showDeleteForm(); - formEl.querySelector( - `[data-services-modal-text]` - ).textContent = `Are you sure you want to delete ${serviceName} ?`; + formEl.querySelector(`[data-services-modal-text]`).textContent = + `Are you sure you want to delete ${serviceName} ?`; const nameInp = formEl.querySelector(`input[name="SERVER_NAME"]`); nameInp.setAttribute("value", serviceName); nameInp.value = serviceName; @@ -487,7 +486,7 @@ class ServiceModal { if (inp.tagName === "SELECT") { inp.parentElement .querySelector( - `button[data-setting-select-dropdown-btn][value='${value}']` + `button[data-setting-select-dropdown-btn][value='${value}']`, ) .click(); inp.setAttribute("data-method", method); @@ -599,7 +598,7 @@ class Multiple { const attName = btn.getAttribute(`data-${this.prefix}-multiple-add`); //get all multiple groups const multipleEls = document.querySelectorAll( - `[data-${this.prefix}-settings-multiple*="${attName}"]` + `[data-${this.prefix}-settings-multiple*="${attName}"]`, ); //case no schema if (multipleEls.length <= 0) return; @@ -611,7 +610,7 @@ class Multiple { //and keep the highest num multipleEls.forEach((container) => { const ctnrName = container.getAttribute( - "data-services-settings-multiple" + "data-services-settings-multiple", ); const num = this.getSuffixNumOrFalse(ctnrName); if (!isNaN(num) && num > topNum) topNum = num; @@ -622,7 +621,7 @@ class Multiple { const setNum = +currNum === 0 ? `` : `_${currNum}`; //the default (schema) group is the last group const schema = document.querySelector( - `[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]` + `[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]`, ); //clone schema to create a group with new num const schemaClone = schema.cloneNode(true); @@ -660,7 +659,7 @@ class Multiple { .hasAttribute(`data-${this.prefix}-multiple-delete`) ) { const multContainer = e.target.closest( - "[data-services-settings-multiple]" + "[data-services-settings-multiple]", ); multContainer.remove(); } @@ -682,13 +681,13 @@ class Multiple { ? name.replace(`_${splitName[splitName.length - 1]}`, "").trim() : name.trim(); const relateSetting = document.querySelector( - `[data-setting-container=${nameSuffixLess}_SCHEMA]` + `[data-setting-container=${nameSuffixLess}_SCHEMA]`, ); const relateCtnr = relateSetting.closest( - "[data-services-settings-multiple]" + "[data-services-settings-multiple]", ); const relateCtnrName = relateCtnr.getAttribute( - "data-services-settings-multiple" + "data-services-settings-multiple", ); //then we sort the setting on the right container name by suffixe number if (!(relateCtnrName in sortMultiples)) { @@ -706,7 +705,7 @@ class Multiple { addOneMultGroup() { const settings = document.querySelector("[data-services-modal-form]"); const multAddBtns = settings.querySelectorAll( - "[data-services-multiple-add]" + "[data-services-multiple-add]", ); multAddBtns.forEach((btn) => { //check if already one (SCHEMA exclude so length >= 2) @@ -721,7 +720,7 @@ class Multiple { showMultByAtt(att) { const multContainers = document.querySelectorAll( - `[data-services-settings-multiple^=${att}]` + `[data-services-settings-multiple^=${att}]`, ); multContainers.forEach((container) => { if ( @@ -735,7 +734,7 @@ class Multiple { toggleMultByAtt(att) { const multContainers = document.querySelectorAll( - `[data-services-settings-multiple^=${att}]` + `[data-services-settings-multiple^=${att}]`, ); multContainers.forEach((container) => { if ( @@ -751,7 +750,7 @@ class Multiple { //get schema settings const multiples = {}; const schemaSettings = document.querySelectorAll( - `[data-setting-container$="SCHEMA"]` + `[data-setting-container$="SCHEMA"]`, ); // loop on every schema settings schemaSettings.forEach((schema) => { @@ -777,11 +776,11 @@ class Multiple { setMultipleToDOM(sortMultObj, setMethodUI = false) { //we loop on each multiple that contains values to render to DOM for (const [schemaCtnrName, multGroupBySuffix] of Object.entries( - sortMultObj + sortMultObj, )) { //we need to access the DOM schema container const schemaCtnr = document.querySelector( - `[data-services-settings-multiple="${schemaCtnrName}"]` + `[data-services-settings-multiple="${schemaCtnrName}"]`, ); //now we have to loop on each multiple settings group for (const [suffix, settings] of Object.entries(multGroupBySuffix)) { @@ -797,14 +796,14 @@ class Multiple { for (const [name, data] of Object.entries(settings)) { //get setting container of clone container const settingContainer = schemaCtnrClone.querySelector( - `[data-setting-container="${name}"]` + `[data-setting-container="${name}"]`, ); //replace input info and disabled state this.setSetting( data["value"], setMethodUI ? "ui" : data["method"], data["global"], - settingContainer + settingContainer, ); } //send schema clone to DOM and show it @@ -819,7 +818,7 @@ class Multiple { "data-services-settings-multiple", schemaCtnrClone .getAttribute("data-services-settings-multiple") - .replace("_SCHEMA", suffix) + .replace("_SCHEMA", suffix), ); //rename title @@ -833,18 +832,18 @@ class Multiple { //rename setting container const settingCtnrs = schemaCtnrClone.querySelectorAll( - "[data-setting-container]" + "[data-setting-container]", ); settingCtnrs.forEach((settingCtnr) => { settingCtnr.setAttribute( "data-setting-container", settingCtnr .getAttribute("data-setting-container") - .replace("_SCHEMA", suffix) + .replace("_SCHEMA", suffix), ); settingCtnr.setAttribute( "id", - settingCtnr.getAttribute("id").replace("_SCHEMA", suffix) + settingCtnr.getAttribute("id").replace("_SCHEMA", suffix), ); }); @@ -922,15 +921,15 @@ class Multiple { //click the custom select dropdown btn value to update select value select.parentElement .querySelector( - `button[data-setting-select-dropdown-btn][value='${defaultVal}']` + `button[data-setting-select-dropdown-btn][value='${defaultVal}']`, ) .click(); //set state to custom visible el const btnCustom = document.querySelector( `[data-setting-select=${select.getAttribute( - "data-setting-select-default" - )}]` + "data-setting-select-default", + )}]`, ); this.setDisabledMultServ(btnCustom, method, global); @@ -966,10 +965,10 @@ class Multiple { selects.forEach((select) => { const method = select.getAttribute("data-default-method"); const name = select.getAttribute( - "data-services-setting-select-default" + "data-services-setting-select-default", ); const selDOM = document.querySelector( - `button[data-services-setting-select='${name}']` + `button[data-services-setting-select='${name}']`, ); if (method === "ui" || method === "default") { selDOM.removeAttribute("disabled", ""); @@ -1004,7 +1003,7 @@ class Multiple { hiddenIfNoMultiples() { //hide multiple btn if no multiple exist on a plugin const multiples = document.querySelectorAll( - `[data-${this.prefix}-settings-multiple]` + `[data-${this.prefix}-settings-multiple]`, ); multiples.forEach((container) => { if (container.querySelectorAll(`[data-setting-container]`).length <= 0) @@ -1016,7 +1015,7 @@ class Multiple { removePrevMultiples() { const multiPlugins = document.querySelectorAll( - `[data-${this.prefix}-settings-multiple]` + `[data-${this.prefix}-settings-multiple]`, ); multiPlugins.forEach((multiGrp) => { if ( @@ -1054,7 +1053,7 @@ const setModal = new ServiceModal(); const format = new FormatValue(); const setFilterGlobal = new FilterSettings( "settings-filter", - "[data-service-content='settings']" + "[data-service-content='settings']", ); const setMultiple = new Multiple("services"); diff --git a/src/ui/styles.css b/src/ui/styles.css index 9bacc7433..2dc8746e1 100644 --- a/src/ui/styles.css +++ b/src/ui/styles.css @@ -8,16 +8,15 @@ } * { - font-family: "Open Sans", sans-serif !important; + font-family: "Open Sans", sans-serif; } .ace_editor, -.ace_editor div, -.ace_content { - font-family: "Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", +.ace_editor * { + font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas", monospace !important; - font-size: 16px !important; - font-weight: normal !important; + font-weight: 400 !important; + letter-spacing: 0 !important; } .sr-only { diff --git a/src/ui/templates/file_manager.html b/src/ui/templates/file_manager.html index 08e5f5f8a..9f18549c2 100644 --- a/src/ui/templates/file_manager.html +++ b/src/ui/templates/file_manager.html @@ -429,7 +429,7 @@ data-{{current_endpoint}}-modal
-